1 Star 1 Fork 1

游戏程序龙之介/黑帝斯源码拆解学习

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
AudioScripts.lua 31.86 KB
一键复制 编辑 原始数据 按行查看 历史
赵元恺 提交于 2023-03-08 16:11 . feat: 新增TraitScript相关注释
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
-- **MINOS AUDIO**
AudioState = {}
OnAnyLoad{
function( triggerArgs )
DeferredPlayVoiceLines = {}
if MusicName ~= nil and MusicId == nil then
-- Out of sync (presumably from a load)
local restoreTrackName = MusicName
local restoreMusicSection = MusicSection
MusicName = nil
MusicSection = nil
MusicPlayer( restoreTrackName )
SetMusicSection( restoreMusicSection )
if MusicId ~= nil then
if MusicActiveStems ~= nil then
SetSoundCueValue({ Names = MusicActiveStems, Id = MusicId, Value = 1 })
end
if MusicMutedStems ~= nil then
SetSoundCueValue({ Names = MusicMutedStems, Id = MusicId, Value = 0 })
end
else
MusicActiveStems = nil
MusicMutedStems = nil
end
if MusicPlayerTrackPaused and MusicPlayerTrackData[MusicName] ~= nil then
PauseMusic()
end
end
if AmbientTrackName ~= nil and AmbientMusicSource ~= nil and AmbientMusicId == nil then
-- Out of sync (presumably from a load)
local restoreTrackName = AmbientTrackName
AmbientTrackName = nil
wait(0.02) -- Need to wait for potential stored targets to spawn
local source = ActiveEnemies[AmbientMusicSource.ObjectId]
if source ~= nil then
MusicianMusic( source, { TrackName = restoreTrackName } )
if CurrentDeathAreaRoom ~= nil then
if CurrentDeathAreaRoom.AmbientMusicParams ~= nil then
for param, value in pairs( CurrentDeathAreaRoom.AmbientMusicParams ) do
SetSoundCueValue({ Id = AmbientMusicId, Name = param, Value = value, Duration = 0.5 })
end
end
SetVolume({ Id = AmbientMusicId, Value = CurrentDeathAreaRoom.AmbientMusicVolume, Duration = 0.5 })
end
end
end
end
}
function DeferredAudioScripts()
for index, params in ipairs(DeferredPlayVoiceLines) do
thread( PlayVoiceLinesReal, params[1], params[2], params[3], params[4] )
end
DeferredPlayVoiceLines = {}
end
-- **Music**
function MusicPlayerEvent( source, args )
MusicPlayer( args.TrackName, args.MusicInfo, args.DestinationId )
end
function MusicPlayer( trackName, musicInfo, destinationId )
if trackName == nil then
return false
end
if MusicName ~= nil and MusicName == trackName then
-- Don't play an identical track that's already playing
-- But do still update the source if it is being changed
SetSoundSource({ Id = MusicId, DestinationId = destinationId })
return false
end
if MusicId ~= nil then
-- Quick cut any music still playing
StopSound({ Id = MusicId, Duration = 0.25 })
MusicId = nil
end
if StoppingMusicId ~= nil then
-- Quick cut any music still fading out
StopSound({ Id = StoppingMusicId, Duration = 0.25 })
StoppingMusicId = nil
end
MusicName = trackName
MusicId = PlaySound({ Name = MusicName, AddCallbacks = true, Id = destinationId })
SetDefaultMusicParams( MusicName, MusicId )
if musicInfo ~= nil then
musicInfo.Id = MusicId
musicInfo.Name = MusicName
end
if SecretMusicId ~= nil then
-- Secret music has priority and is mutually exclusive so this must wait
SetVolume({ Id = MusicId, Value = 0.0, Duration = 0.0 })
PauseSound({ Id = MusicId, Duration = 0.0 })
end
return true
end
function SecretMusicPlayer( trackName, musicInfo )
if trackName == nil then
return false
end
if SecretMusicName == trackName then
-- Don't play an identical track that's already playing
return
end
if MusicId ~= nil then
-- Quick cut any music still playing
PauseMusic()
end
if SecretMusicId ~= nil then
-- Quick cut any music still playing
StopSound({ Id = SecretMusicId, Duration = 0.25 })
end
SecretMusicName = trackName
SecretMusicId = PlaySound({ Name = SecretMusicName, AddCallbacks = true })
SetDefaultMusicParams( SecretMusicName, SecretMusicId )
if musicInfo ~= nil then
musicInfo.Id = SecretMusicId
musicInfo.Name = SecretMusicName
end
return true
end
function StopSecretMusic( smoothStop )
if SecretMusicId == nil then
return
end
if smoothStop then
EndMusic( SecretMusicId, SecretMusicName )
else
StopSound({ Id = SecretMusicId, Duration = 0.25 })
end
SecretMusicId = nil
SecretMusicName = nil
end
function ChaosBassStart()
SetSoundCueValue({ Names = { "ChaosBass" }, Id = SecretMusicId, Value = 1, Duration = 0.5 })
end
function ChaosBassStop()
SetSoundCueValue({ Names = { "ChaosBass" }, Id = SecretMusicId, Value = 0, Duration = 3 })
end
function SingingPresentation( source, ars )
if source.SingingFx ~= nil then
CreateAnimation({ Name = source.SingingFx, DestinationId = source.ObjectId, OffsetX = source.SingingAnimOffsetX or source.AnimOffsetX, OffsetZ = source.AnimOffsetZ, Group = "Combat_UI_World" })
end
if source.SingingAnimation ~= nil then
SetAnimation({ Name = source.SingingAnimation, DestinationId = source.ObjectId })
end
if source.PartnerSingingAnimation ~= nil and source.PartnerObjectId ~= nil then
SetAnimation({ Name = source.PartnerSingingAnimation, DestinationId = source.PartnerObjectId })
end
end
function MusicianMusic( source, args )
if CurrentRun.BlockAmbientMusic then
return
end
CurrentRun.EventState[source.ObjectId] = { FunctionName = "SingingPresentation", Args = args }
SingingPresentation( source, args )
if args.TrackName == AmbientTrackName then
-- Don't play an identical track that's already playing
-- But do still update the source if it is being changed
SetSoundSource({ Id = AmbientMusicId, DestinationId = source.ObjectId })
AmbientMusicSource = source
return
end
if AmbientMusicId ~= nil then
-- Quick cut the previously playing id
StopSound({ Id = AmbientMusicId, Duration = 0.25 })
AmbientMusicId = nil
end
--Shake({ Id = source.ObjectId, Distance = 1, Speed = 3, Duration = 9999, Angle = 0 })
AmbientMusicSource = source
AmbientMusicId = PlaySound({ Name = args.TrackName, Id = source.ObjectId })
SetSoundCueValue({ Names = { "Vocals", }, Id = AmbientMusicId, Value = 1 })
AmbientTrackName = args.TrackName
SetVolume({ Id = AmbientMusicId, Value = 1 })
if args.TrackOffsetMin ~= nil then
SetSoundPosition({ Id = AmbientMusicId, Position = RandomFloat( args.TrackOffsetMin, args.TrackOffsetMax ) })
end
-- Workaround for FMOD bug, after a long play-session VO played in 2D can become inaudible. Pausing and unpausing the sound fixes it.
thread( PauseUnpauseSoundWorkaround, AmbientMusicId )
end
-- Workaround for FMOD bug, after a long play-session VO played in 2D can become inaudible. Pausing and unpausing the sound fixes it.
function PauseUnpauseSoundWorkaround( soundId )
wait( 0.03 )
PauseSound({ Id = soundId, Duration = 0 })
ResumeSound({ Id = soundId, Duration = 0 })
end
function StopMusicianMusic( source, args )
StopSound({ Id = AmbientMusicId, Duration = args.Duration or 0.2 })
AmbientMusicId = nil
AmbientTrackName = nil
if source ~= nil and source.ObjectId ~= nil then
CurrentRun.EventState[source.ObjectId] = nil
end
end
function SetDefaultMusicParams( trackName, musicId )
SetSoundCueValue({ Names = { "Keys" }, Id = musicId, Value = 1 })
SetSoundCueValue({ Names = { "Guitar", "Drums", "Bass" }, Id = musicId, Value = 1 })
SetMusicSection( 1, musicId )
end
function RandomStemMixer( currentRoom, musicId )
if musicId == nil then
return
end
if currentRoom.IgnoreStemMixer then
return
end
local musicSetup = RandomInt( 1, currentRoom.RandomStemMixerOptions or 3 )
if musicSetup == 1 then
-- guitar, bass, drums
SetSoundCueValue({ Names = { "Guitar" }, Id = musicId, Value = 1, Duration = 2.5 })
SetSoundCueValue({ Names = { "Bass" }, Id = musicId, Value = 1, Duration = 2.5 })
SetSoundCueValue({ Names = { "Drums" }, Id = musicId, Value = 1, Duration = 0.25 })
elseif musicSetup == 2 then
-- drums only
SetSoundCueValue({ Names = { "Guitar" }, Id = musicId, Value = 0, Duration = 10 })
SetSoundCueValue({ Names = { "Bass" }, Id = musicId, Value = 0, Duration = 10 })
SetSoundCueValue({ Names = { "Drums" }, Id = musicId, Value = 1, Duration = 0.25 })
elseif musicSetup == 3 then
-- bass and drums only
SetSoundCueValue({ Names = { "Guitar" }, Id = musicId, Value = 0, Duration = 10 })
SetSoundCueValue({ Names = { "Bass" }, Id = musicId, Value = 1, Duration = 2.5 })
SetSoundCueValue({ Names = { "Drums" }, Id = musicId, Value = 1, Duration = 0.25 })
else
-- guitar and drums only
SetSoundCueValue({ Names = { "Guitar" }, Id = musicId, Value = 1, Duration = 10 })
SetSoundCueValue({ Names = { "Bass" }, Id = musicId, Value = 0, Duration = 2.5 })
SetSoundCueValue({ Names = { "Drums" }, Id = musicId, Value = 1, Duration = 0.25 })
end
end
function MusicMixer( mixArgs )
if mixArgs == nil then
return
end
if mixArgs.MusicMixerRequirements ~= nil and not IsGameStateEligible( CurrentRun, mixArgs, mixArgs.MusicMixerRequirements ) then
return
end
wait( mixArgs.MusicStartDelay )
if mixArgs.PlayBiomeMusic then
local biomeName = CurrentRun.CurrentRoom.RoomSetName
local biomeMusicTracks = MusicTrackData[biomeName]
if biomeMusicTracks ~= nil then
if BiomeMusicPlayCounts[biomeName] == nil then
BiomeMusicPlayCounts[biomeName] = 0
end
local trackIndex = (BiomeMusicPlayCounts[biomeName] % #biomeMusicTracks) + 1
local trackData = biomeMusicTracks[trackIndex]
MusicPlayer( trackData.Name, trackData )
BiomeMusicPlayCounts[biomeName] = BiomeMusicPlayCounts[biomeName] + 1
end
end
if MusicId == nil then
return
end
if mixArgs.MusicActiveStems ~= nil then
MusicActiveStems = mixArgs.MusicActiveStems
SetSoundCueValue({ Names = mixArgs.MusicActiveStems, Id = MusicId, Value = 1, Duration = 0.75 })
end
if mixArgs.MusicMutedStems ~= nil then
MusicMutedStems = mixArgs.MusicMutedStems
SetSoundCueValue({ Names = mixArgs.MusicMutedStems, Id = MusicId, Value = 0, Duration = mixArgs.MusicMutedStemsDuration or 0.75 })
end
if mixArgs.MusicSection ~= nil then
SetMusicSection( mixArgs.MusicSection )
end
if mixArgs.UseRoomMusicSection ~= nil and CurrentRun.CurrentRoom.MusicSection ~= nil then
SetMusicSection( CurrentRun.CurrentRoom.MusicSection )
end
end
function CheckMusicEvents( currentRun, musicEvents )
if musicEvents == nil then
return
end
for k, musicEvent in ipairs( musicEvents ) do
if IsGameStateEligible( currentRun, musicEvent, musicEvent.GameStateRequirements ) then
if musicEvent.EndMusic then
EndMusic()
end
thread( MusicMixer, musicEvent )
end
end
end
function StartBossRoomMusic()
SetMusicSection( 2 )
local activeStemTable = { "Guitar", "Bass", "Drums" }
SetSoundCueValue({ Names = activeStemTable, Id = MusicId, Value = 1, Duration = 0.75 })
end
function EndMusic( musicId, musicName, hardStopTime )
if musicId == nil then
musicId = MusicId
end
if musicName == nil then
musicName = MusicName
end
if hardStopTime == nil then
hardStopTime = 20
end
if musicId == nil then
return
end
SetMusicSection( 10, musicId )
if hardStopTime ~= nil then
StopSound({ Id = musicId, Value = 0.0, Duration = hardStopTime })
StoppingMusicId = musicId
end
if musicId == MusicId then
MusicId = nil
MusicName = nil
MusicSection = nil
end
end
function PauseMusic()
PauseSound({ Id = MusicId, Duration = 0.2 })
end
function ResumeMusic( duration, delay )
if MusicId == nil then
return
end
wait( delay )
ResumeSound({ Id = MusicId, Duration = duration or 0.2 })
end
function PauseAmbientMusic()
PauseSound({ Id = AmbientMusicId, Duration = 0.2 })
PauseSound({ Id = SecretMusicId, Duration = 0.2 })
end
function ResumeAmbientMusic()
ResumeSound({ Id = AmbientMusicId, Duration = 0.2 })
SetVolume({ Id = AmbientMusicId, Value = 1.0 })
ResumeSound({ Id = SecretMusicId, Duration = 0.2 })
end
function SetMusicSection( section, musicId )
if section == nil then
return
end
if musicId == nil then
musicId = MusicId
end
SetSoundCueValue({ Names = { "Section", }, Id = musicId, Value = section })
if musicId == MusicId then
MusicSection = section
MusicSectionStartDepth = CurrentRun.RunDepthCache
end
end
function SetAdvancedTooltipMixing( value )
value = value or 0
SetSoundCueValue({ Id = GetMixingId({}), Names = { "Wagon" }, Value = value })
end
function IsMusicPlaying()
if MusicId ~= nil and MusicId > 0 then
return true
end
return false
end
-- Music Marker logic
OnMusicMarker{ "Marker End",
function( triggerArgs )
local markerName = triggerArgs.name
if markerName == "Marker End" then
notifyExistingWaiters( "MusicTrackEnded" )
end
end
}
OnMusicMarker{ "Shout",
function( triggerArgs )
if not AllowShout then
return
end
if MusicName ~= "/Music/MusicExploration4_MC" and MusicName ~= "/Music/MusicHadesReset_MC" then
return
end
if CurrentRun.Hero.LastKillTime == nil or _worldTime > CurrentRun.Hero.LastKillTime + 20 then
return
end
if IsEmpty( RequiredKillEnemies ) then
return
end
PlaySound({ Name = "/SFX/Shout1" })
AllowShout = false
end
}
-- **AMBIENCE**
function StartRoomAmbience( currentRun, currentRoom )
local newTrackName = nil
if currentRoom.Encounter then
newTrackName = currentRoom.Encounter.Ambience
end
newTrackName = newTrackName or currentRoom.Ambience
if newTrackName == "None" then
-- Silence requested
StopSound({ Id = AmbienceId, Duration = 0.5 })
AmbienceId = nil
AmbienceName = nil
elseif newTrackName ~= nil then
-- Specific track requested
if newTrackName ~= AmbienceName then
StopSound({ Id = AmbienceId, Duration = 0.5 })
AmbienceId = PlaySound({ Name = newTrackName, Duration = 0.5 })
AmbienceName = newTrackName
end
else
-- Nothing requested, use default biome track
local ambienceTrackName = nil
local biomeAmbienceTracks = AmbienceTracks[currentRoom.RoomSetName]
if biomeAmbienceTracks ~= nil then
local trackIndex = (GetCompletedRuns() % #biomeAmbienceTracks) + 1
local trackData = biomeAmbienceTracks[trackIndex]
if trackData.Name ~= nil and trackData.Name ~= AmbienceName then
StopSound({ Id = AmbienceId, Duration = 0.5 })
AmbienceId = PlaySound({ Name = trackData.Name, Duration = 0.5 })
AmbienceName = trackData.Name
end
if trackData.ReverbValue ~= nil then
SetAudioEffectState({ Name = "Reverb", Value = trackData.ReverbValue })
end
end
end
if currentRoom.ReverbValue ~= nil then
SetAudioEffectState({ Name = "Reverb", Value = currentRoom.ReverbValue })
end
end
function EndAmbience( duration )
StopSound({ Id = AmbienceId, Duration = duration or 0.2 })
AmbienceId = nil
StopSound({ Id = AmbientMusicId, Duration = duration or 0.2 })
AmbientMusicId = nil
AmbientTrackName = nil
end
-- **VO**
-- Minos Voice Debug
OnKeyPressed{ "Alt V", Name = "ToggleVoice",
function( triggerArgs )
if CurrentRun.CurrentRoom.IntroVO ~= nil then
PlayRemainingSpeech( CurrentRun.CurrentRoom.IntroVO )
end
end
}
function AudioInit()
PlayedRandomLines = PlayedRandomLines or {}
AllowShout = true
BiomeMusicPlayCounts = BiomeMusicPlayCounts or {}
end
function PlayRandomEligibleVoiceLines( voiceLineSets )
while not IsEmpty( voiceLineSets ) do
local voiceLines = RemoveRandomValue( voiceLineSets )
local playedSomething = PlayVoiceLines( voiceLines, true )
if playedSomething then
return
end
end
end
function PlayFirstEligibleVoiceLines( voiceLineSets )
local highestIndex = GetHighestIndex( voiceLineSets )
for index = 1, highestIndex do
local voiceLines = voiceLineSets[index]
if voiceLines ~= nil then
local playedSomething = PlayVoiceLines( voiceLines, true )
if playedSomething then
return
end
end
end
end
function PlayVoiceLines( voiceLines, neverQueue, source, args )
if args ~= nil and args.Defer then
for k, v in pairs(DeferredPlayVoiceLines) do
if v[1] == voiceLines then
--DebugPrint({ Text = "voice lines play request de-duped"})
return
end
end
table.insert( DeferredPlayVoiceLines, { voiceLines, neverQueue, source, args } )
return
end
return PlayVoiceLinesReal( voiceLines, neverQueue, source, args )
end
function PlayVoiceLinesReal( voiceLines, neverQueue, source, args )
if voiceLines == nil then
return
end
args = args or {}
source = GetLineSource( voiceLines, source, args )
if source == nil then
-- Never play a line if the source doesn't exist
return
end
if not IsVoiceLineEligible( CurrentRun, voiceLines, nil, nil, source, args ) then
-- First check requirements of the whole set
if voiceLines.PlayedNothingFunctionName ~= nil then
local playedNothingFunction = _G[voiceLines.PlayedNothingFunctionName]
if playedNothingFunction ~= nil then
playedNothingFunction( source, voiceLines.PlayedNothingFunctionArgs )
end
end
return
end
if source.PlayingVoiceLines then
if voiceLines.Queue == "Interrupt" then
-- Play as normal
else
if neverQueue then
--DebugPrint({ Text = "Skipped voiceLines on "..GetTableString( source.Name, source ) })
return
end
if source.QueuedVoiceLines == nil then
source.QueuedVoiceLines = {}
end
table.insert( source.QueuedVoiceLines, voiceLines )
--DebugPrint({ Text = "Queued voiceLines on "..GetTableString( source.Name, source ) })
return
end
end
local playedSomething = false
local parentLine = voiceLines
source.PlayingVoiceLines = true
if PlayVoiceLine( voiceLines, nil, nil, source, args ) then
playedSomething = true
else
if voiceLines.PlayedNothingFunctionName ~= nil then
local playedNothingFunction = _G[voiceLines.PlayedNothingFunctionName]
if playedNothingFunction ~= nil then
playedNothingFunction( source, voiceLines.PlayedNothingFunctionArgs )
end
end
end
source.PlayingVoiceLines = false
if not IsEmpty( source.QueuedVoiceLines ) then
local nextVoiceLines = RemoveFirstValue( source.QueuedVoiceLines )
if PlayVoiceLines( nextVoiceLines, false, source, args ) then
playedSomething = true
end
end
return playedSomething
end
function GetLineSource( line, source, args )
if line.ObjectType ~= nil then
local typeIds = GetIdsByType({ Name = line.ObjectType })
if IsEmpty( typeIds ) then
return nil
end
local objectId = typeIds[1]
source = ActiveEnemies[objectId]
if source == nil then
return nil
else
if line.RequiredSourceValue ~= nil and not source[line.RequiredSourceValue] then
return nil
end
if line.RequiredSourceValueFalse ~= nil and source[line.RequiredSourceValueFalse] then
return nil
end
return source
end
end
if line.UsePlayerSource then
return CurrentRun.Hero
end
if line.Source ~= nil then
return line.Source
end
if source ~= nil then
return source
end
return CurrentRun.Hero
end
function PlayVoiceLine( line, prevLine, parentLine, source, args )
local playedSomething = false
if parentLine == nil then
parentLine = line
end
args = args or {}
args.ThreadName = line.ThreadName or args.ThreadName
args.Queue = line.Queue or args.Queue
args.BreakIfPlayed = line.BreakIfPlayed or args.BreakIfPlayed
args.PlayOnceContext = line.PlayOnceContext or args.PlayOnceContext
args.SubtitleMinDistance = line.SubtitleMinDistance or args.SubtitleMinDistance
args.Actor = line.Actor or args.Actor
args.AllowTalkOverTextLines = line.AllowTalkOverTextLines or args.AllowTalkOverTextLines
-- By default, the player object is the ListenerId, though could be something else
args.ListenerId = line.ListenerId or args.ListenerId or CurrentRun.Hero.ObjectId
source = GetLineSource( line, source, args )
if source == nil then
-- Never play a line if the source doesn't exist
return
end
if line.SetFlagTrue ~= nil then
GameState.Flags[line.SetFlagTrue] = true
end
if line.SetFlagFalse ~= nil then
GameState.Flags[line.SetFlagFalse] = false
end
if line.TriggerCooldowns ~= nil then
for k, cooldownName in pairs( line.TriggerCooldowns ) do
TriggerCooldown( cooldownName )
end
end
-- Play this line
if line.Cue ~= nil then
if args.OnPlayedSomethingFunctionName ~= nil and not args.PlayedSomething then
local onPlayedSomethingFunction = _G[args.OnPlayedSomethingFunctionName]
if onPlayedSomethingFunction ~= nil then
thread( onPlayedSomethingFunction, source, args.OnPlayedSomethingFunctionArgs )
end
end
if line.PreLineThreadedFunctionName ~= nil then
local preLineThreadedFunction = _G[line.PreLineThreadedFunctionName]
thread(preLineThreadedFunction, source, line.PreLineThreadedFunctionArgs )
end
wait( line.PreLineWait or parentLine.PreLineWait, args.ThreadName )
local preLineAnim = line.PreLineAnim or parentLine.PreLineAnim
if preLineAnim ~= nil then
SetAnimation({ Name = preLineAnim, DestinationId = source.ObjectId })
end
local playedSpeechId = 0
local useSubtitles = false
if not source.Mute then
if args.SubtitleMinDistance then
local dist = GetDistance({ Id = args.ListenerId, DestinationId = source.ObjectId })
if dist > args.SubtitleMinDistance then
useSubtitles = false
else
useSubtitles = true
end
else
useSubtitles = true
end
if line.NoTarget or parentLine.NoTarget then
playedSpeechId = PlaySpeech({ Name = line.Cue, Queue = args.Queue, SubtitleColor = source.SubtitleColor, UseSubtitles = useSubtitles, Actor = args.Actor })
elseif line.SkipAnim or parentLine.SkipAnim then
playedSpeechId = PlaySpeechCueFromSource( line.Cue, source, false, args.Queue, useSubtitles, source.SubtitleColor, args )
else
playedSpeechId = PlaySpeechCueFromSource( line.Cue, source, true, args.Queue, useSubtitles, source.SubtitleColor, args )
end
end
if line.UseOcclusion then
SetSoundCueValue({ Id = playedSpeechId, Names = { "VoiceOcclusion" }, Value = 1.0, Duration = 0.01 })
end
if playedSpeechId > 0 then
prevLine = line
LastLinePlayed = line.Cue
table.insert( CurrentRun.CurrentRoom.VoiceLinesPlayed, line.Cue )
playedSomething = true
args.PlayedSomething = true
-- @refactor The SpeechRecord recording is pretty scattered / redundant
SpeechRecord[line.Cue] = true
CurrentRun.SpeechRecord[line.Cue] = true
if args.PlayOnceContext ~= nil then
GameState.SpeechRecordContexts[args.PlayOnceContext] = GameState.SpeechRecordContexts[args.PlayOnceContext] or {}
GameState.SpeechRecordContexts[args.PlayOnceContext][line.Cue] = true
end
-- Intentionally leaving this on raw data for now to be wiped out on load
line.LastPlayTime = _worldTime
parentLine.LastPlayTime = _worldTime
waitUntil( line.Cue )
wait( line.PostLineWait or parentLine.PostLineWait, args.ThreadName )
if line.PostLineFunctionName ~= nil then
local postLineFunction = _G[line.PostLineFunctionName]
postLineFunction( source, line.PostLineFunctionArgs )
end
if args.BreakIfPlayed then
return playedSomething
end
else
--DebugAssert({ Condition = playedSpeechId > 0, Text = "Speech failed to play: "..line.Cue })
end
end
-- Play sublines
if line.RandomRemaining then
local eligibleUnplayedLines = {}
local allEligibleLines = {}
for k, subLine in ipairs( line ) do
if IsVoiceLineEligible( CurrentRun, subLine, prevLine, line, source, args ) then
table.insert( allEligibleLines, subLine )
if not PlayedRandomLines[subLine.Cue] then
table.insert( eligibleUnplayedLines, subLine )
end
end
end
if not IsEmpty( allEligibleLines ) then
local randomLine = nil
if IsEmpty( eligibleUnplayedLines ) then
-- All lines played, start the record over
for k, subLine in ipairs( line ) do
PlayedRandomLines[subLine.Cue] = nil
end
randomLine = GetRandomValue( allEligibleLines )
else
randomLine = GetRandomValue( eligibleUnplayedLines )
end
PlayedRandomLines[randomLine.Cue] = true
-- Effectively pass down by value rather than reference
local subLineArgs = ShallowCopyTable( args )
if PlayVoiceLine( randomLine, prevLine, line, source, subLineArgs ) then
prevLine = randomLine
playedSomething = true
args.PlayedSomething = true
if args.BreakIfPlayed or randomLine.BreakIfPlayed or subLineArgs.BreakIfPlayed then
return playedSomething
end
end
end
else
for k, subLine in ipairs( line ) do
if IsVoiceLineEligible( CurrentRun, subLine, prevLine, line, source, args ) then
-- Effectively pass down by value rather than reference
local subLineArgs = ShallowCopyTable( args )
if PlayVoiceLine( subLine, prevLine, line, source, subLineArgs ) then
prevLine = subLine
playedSomething = true
args.PlayedSomething = true
if args.BreakIfPlayed or subLine.BreakIfPlayed or subLineArgs.BreakIfPlayed then
return playedSomething
end
end
end
end
end
return playedSomething
end
function CleanUpCurrentSpeechId( cue, speechId, source, animation )
if speechId == nil then
return
end
waitUntil( cue )
if animation ~= nil then
if StopStatusAnimation( source, animation ) and not source.BlockStatusAnimations and ConfigOptionCache.ShowUIAnimations and ConfigOptionCache.ShowSpeechBubble then
local endAnimation = animation.."_End"
CreateAnimation({ Name = endAnimation, DestinationId = source.ObjectId, OffsetX = source.AnimOffsetX, OffsetZ = source.AnimOffsetZ })
end
end
if CurrentSpeechId == speechId then
CurrentSpeechId = nil
end
end
function PlaySpeechCue( cue, source, animation, queue, useSubtitles, subtitleColor, args )
if cue == nil or cue == "" then
return 0
end
args = args or {}
local sourceId = nil
if source ~= nil then
sourceId = source.ObjectId
end
local speechId = PlaySpeech({ Name = cue, Id = sourceId, Queue = queue, UseSubtitles = useSubtitles, SubtitleColor = subtitleColor, Actor = args.Actor })
if speechId > 0 then
if source ~= nil and animation ~= nil and ConfigOptionCache.ShowUIAnimations and ConfigOptionCache.ShowSpeechBubble then
PlayStatusAnimation( source, { Animation = animation } )
end
CurrentSpeechId = speechId
-- @refactor The SpeechRecord recording is pretty scattered / redundant
SpeechRecord[cue] = true
CurrentRun.SpeechRecord[cue] = true
thread( CleanUpCurrentSpeechId, cue, speechId, source, animation )
end
return speechId
end
function PlaySpeechCueFromSource( cue, source, useDefaultAnim, queue, useSubtitles, subtitleColor, args )
if PlayingTextLines and not args.AllowTalkOverTextLines then
return 0
end
if source == nil then
source = CurrentRun.Hero
end
if useDefaultAnim == nil then
useDefaultAnim = true
end
local anim = nil
if useDefaultAnim then
anim = StatusAnimations.Speaking
end
return PlaySpeechCue( cue, source, anim, queue, useSubtitles, subtitleColor, args )
end
function IsVoiceLineEligible( currentRun, line, prevLine, parentLine, source, args )
if line == nil then
return false
end
args = args or {}
if source ~= nil then
if line.RequiresFalseCharmed ~= nil and source.Charmed then
return false
end
if line.RequiresCharmed ~= nil and not source.Charmed then
return false
end
if line.RequiredSourceValue ~= nil and not source[line.RequiredSourceValue] then
return false
end
if line.RequiredSourceValueFalse ~= nil and source[line.RequiredSourceValueFalse] then
return false
end
end
if args ~= nil and args.ElapsedTime ~= nil then
if line.RequiredMinElapsedTime ~= nil and args.ElapsedTime < line.RequiredMinElapsedTime then
return false
end
if line.RequiredMaxElapsedTime ~= nil and args.ElapsedTime > line.RequiredMaxElapsedTime then
return false
end
end
if line.ExplicitRequirements or ( parentLine ~= nil and parentLine.ExplicitRequirements ) then
if line.GameStateRequirements ~= nil and not IsGameStateEligible( currentRun, line, line.GameStateRequirements ) then
return false
end
else
if not IsGameStateEligible( currentRun, line, line.GameStateRequirements ) then
return false
end
end
if line.PlayOnceFromTableThisRun then
for k, subLine in ipairs( line ) do
if subLine.Cue ~= nil and currentRun.SpeechRecord[subLine.Cue] then
return false
end
end
end
if line.SuccessiveChanceToPlay ~= nil then
local anyLinePlayed = false
for k, subLine in ipairs( line ) do
if subLine.Cue ~= nil and SpeechRecord[subLine.Cue] then
anyLinePlayed = true
break
end
end
if anyLinePlayed and not RandomChance( line.SuccessiveChanceToPlay ) then
return false
end
end
if line.SuccessiveChanceToPlayAll ~= nil then
local allLinesPlayed = true
for k, subLine in ipairs( line ) do
if subLine.Cue ~= nil and not SpeechRecord[subLine.Cue] then
allLinesPlayed = false
break
end
end
if allLinesPlayed and not RandomChance( line.SuccessiveChanceToPlayAll ) then
return false
end
end
if line.Cue ~= nil then
if line.PlayOnce or ( parentLine ~= nil and parentLine.PlayOnce ) then
if args.PlayOnceContext ~= nil then
DebugPrint({ Text = "checking context = "..tostring(args.PlayOnceContext) })
GameState.SpeechRecordContexts[args.PlayOnceContext] = GameState.SpeechRecordContexts[args.PlayOnceContext] or {}
if GameState.SpeechRecordContexts[args.PlayOnceContext][line.Cue] then
return false
end
elseif SpeechRecord[line.Cue] then
return false
end
end
if line.PlayOnceThisRun or ( parentLine ~= nil and parentLine.PlayOnceThisRun ) then
if currentRun.SpeechRecord[line.Cue] then
return false
end
end
local chanceToPlayAgain = line.ChanceToPlayAgain or ( parentLine ~= nil and parentLine.ChanceToPlayAgain )
if chanceToPlayAgain and SpeechRecord[line.Cue] and not RandomChance( chanceToPlayAgain ) then
return false
end
if line.CooldownTime ~= nil then
if not CheckCooldown( line.CooldownName or line.Cue, line.CooldownTime ) then
--DebugPrint({ Text = "VO blocked from cooldown: "..tostring(line.CooldownName or line.Cue) })
return false
end
end
if line.Cooldowns ~= nil then
for k, cooldown in pairs( line.Cooldowns ) do
local cooldownTime = cooldown.Time or line.CooldownTime
if cooldownTime == nil and source ~= nil then
cooldownTime = source.SpeechCooldownTime
end
if not CheckCooldown( cooldown.Name, cooldownTime ) then
--DebugPrint({ Text = "VO blocked from cooldown: "..tostring(cooldown.Name) })
return false
end
end
end
else
if line.CooldownTime ~= nil then
local cooldownName = line.CooldownName
if cooldownName == nil and line[1] ~= nil then
cooldownName = line[1].CooldownName or line[1].Cue
end
if cooldownName ~= nil and not CheckCooldown( cooldownName, line.CooldownTime ) then
--DebugPrint({ Text = "VO blocked from cooldown: "..tostring(cooldownName) })
return false
end
end
if line.Cooldowns ~= nil then
for k, cooldown in pairs( line.Cooldowns ) do
local cooldownTime = cooldown.Time or line.CooldownTime
if cooldownTime == nil and source ~= nil then
cooldownTime = source.SpeechCooldownTime
end
if not CheckCooldown( cooldown.Name, cooldownTime ) then
--DebugPrint({ Text = "VO blocked from cooldown: "..tostring(cooldown.Name) })
return false
end
end
end
end
if line.RequiresSourceAlive then
if line.ObjectType ~= nil then
local typeIds = GetIdsByType({ Name = line.ObjectType })
local objectId = typeIds[1]
source = ActiveEnemies[objectId]
end
if source == nil or source.IsDead then
return false
end
end
return true
end
function SometimesPlayCombatResolvedVoiceLines()
if not RandomChance( 0.25 ) then
return
end
local currentHealthFraction = CurrentRun.Hero.Health / CurrentRun.Hero.MaxHealth
if currentHealthFraction >= 0.95 then
thread( PlayVoiceLines, GlobalVoiceLines.CombatResolvedHighHealthLines, true )
elseif currentHealthFraction >= 0.3 and currentHealthFraction < 0.95 then
thread( PlayVoiceLines, GlobalVoiceLines.CombatResolvedMediumHealthLines, true )
elseif currentHealthFraction < 0.3 then
thread( PlayVoiceLines, GlobalVoiceLines.CombatResolvedLowHealthLines, true )
end
end
function AmbientChatting( source, args )
-- play a random line from ChattingRepeatable or similar table, from the character source position, with speech bubble; when player gets sufficiently close, play an 'interrupt' line, interrupting the ambient line if it's still playing
if args.StartDistance ~= nil then
local notifyName = "StartDistance"..source.Name
NotifyWithinDistanceAny({ Ids = { CurrentRun.Hero.ObjectId }, DestinationIds = { source.ObjectId }, Distance = args.StartDistance, Notify = notifyName })
waitUntil( notifyName )
end
thread( CheckDistanceTrigger, args.DistanceTrigger, source )
PlayVoiceLines( args.VoiceLines, nil, source )
-- source.BlockStatusAnimations = true
wait( 1.5 )
-- source.BlockStatusAnimations = false
end
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/NoGameNoLife1234_admin/HadesSource.git
[email protected]:NoGameNoLife1234_admin/HadesSource.git
NoGameNoLife1234_admin
HadesSource
黑帝斯源码拆解学习
master

搜索帮助