From 95b4e08ebd5350fc98de85edde683aa95bcee957 Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Sat, 6 Jun 2020 23:15:09 +0930 Subject: [PATCH] winning, losing, war2 music types --- snd_music.lua | 308 ++++++++++++++++++++++++++------------------------ 1 file changed, 163 insertions(+), 145 deletions(-) diff --git a/snd_music.lua b/snd_music.lua index 5b16c85..0b6a03b 100644 --- a/snd_music.lua +++ b/snd_music.lua @@ -42,30 +42,56 @@ options = { desc = "Music pauses with game", noHotkey = true, }, + attritionRatioLosing = { + name = "Attrition Ratio: Losing", + type = "number", + value = 0.4, + min = 0.1, + max = 1.0, + step = 0.05, + desc = "Music switches to Losing when recent attrition ratio falls below this value", + noHotkey = true, + }, + attritionRatioWinning = { + name = "Attrition Ratio: Winning", + type = "number", + value = 3.0, + min = 1.0, + max = 8.0, + step = 0.25, + desc = "Music switches to Winning when recent attrition ratio rises above this value", + noHotkey = true, + }, } local unitExceptions = include("Configs/snd_music_exception.lua") local windows = {} -local warThreshold = 5000 -local peaceThreshold = 1000 +local moodPriorities = {peace=0, war=1, war2=2, winning=4, losing=4, briefing=10, victory=10, defeat=10} -- Determines which music moods will instantly interrupt others, and which will wait for playing track to finish +local moodDynamic = {peace=true, war=true, war2=true, winning=true, losing=true, briefing=false, victory=false, defeat=false} -- Determines which music moods will instantly interrupt others, and which will wait for playing track to finish +local war2Threshold = 400000 +local warThreshold = 50000 +local peaceThreshold = 10000 local PLAYLIST_FILE = "sounds/music/playlist.lua" -local LOOP_BUFFER = 0.015 -- if looping track is this close to the end, go ahead and loop +local LOOP_BUFFER = 0.015 -- if looping track is this close to the end, go ahead and loop local UPDATE_PERIOD = 1 local musicType = "peace" local warPointsIter = 1 -- Position in circular buffer. 1-indexed because L[ew]a -local warPointsSize = 30 -- Size of circular buffer. Sampling is currently hardcoded but might change later. -local warPointsFriendly = {} -- keeps track of the number of doods killed in each time frame -local warPointsHostile = {} +local warPointsSize = 90 -- Size of circular buffer. Sampling is currently hardcoded but might change later. +local dmgPointsFriendly = {} -- keeps track of the number of doods killed in each time frame +local dmgPointsHostile = {} +local deathPointsFriendly = {} -- metal costs of destroyed units +local deathPointsHostile = {} local warPointsRollover = 4000000000 -- Roll back to zero after this many have accumulated local timeframetimer = 0 local timeframetimer_short = 0 local loopTrack = "" local previousTrack = "" -local previousTrackType = "" +local prevMusicType = "" local newTrackWait = 1000 +local numVisibleFriendly = 0 local numVisibleEnemy = 0 local fadeVol local curTrac = "no name" @@ -120,7 +146,7 @@ local function StartTrack(track) local newTrack = previousTrack if musicType == "custom" then - previousTrackType = "peace" + prevMusicType = "peace" musicType = "peace" end if (not gameStarted) then @@ -139,20 +165,10 @@ local function StartTrack(track) end end - -- for key, val in pairs(oggInfo) do - -- Spring.Echo(key, val) - -- end firstFade = false previousTrack = newTrack - - -- if (oggInfo.comments.TITLE and oggInfo.comments.TITLE) then - -- Spring.Echo("Song changed to: " .. oggInfo.comments.TITLE .. " By: " .. oggInfo.comments.ARTIST) - -- else - -- Spring.Echo("Song changed but unable to get the artist and title info") - -- end - Spring.Echo ("game_message: Changing music to " .. newTrack) curTrack = newTrack - Spring.PlaySoundStream(newTrack,WG.music_volume or 0.5) + Spring.PlaySoundStream(newTrack, WG.music_volume or 0.5) WG.music_start_volume = WG.music_volume end @@ -197,131 +213,156 @@ function InitializeTracks() local vfsMode = (options.useIncludedTracks.value and VFS.RAW_FIRST) or VFS.RAW tracks.war = tracks.war or VFS.DirList("sounds/music/war/", "*.ogg", vfsMode) + tracks.war2 = tracks.war2 or VFS.DirList("sounds/music/war2/", "*.ogg", vfsMode) + tracks.winning = tracks.winning or VFS.DirList("sounds/music/winning/", "*.ogg", vfsMode) + tracks.losing = tracks.losing or VFS.DirList("sounds/music/losing/", "*.ogg", vfsMode) tracks.peace = tracks.peace or VFS.DirList("sounds/music/peace/", "*.ogg", vfsMode) tracks.briefing = tracks.briefing or VFS.DirList("sounds/music/briefing/", "*.ogg", vfsMode) tracks.victory = tracks.victory or VFS.DirList("sounds/music/victory/", "*.ogg", vfsMode) tracks.defeat = tracks.defeat or VFS.DirList("sounds/music/defeat/", "*.ogg", vfsMode) end -function widget:Update(dt) - if gameOver then - return +function CheckLoop() + local playedTime, totalTime = Spring.GetSoundStreamTime() + paused = (playedTime == lastTrackTime) + lastTrackTime = playedTime + if looping then + if looping == 0.5 then + looping = 1 + elseif playedTime >= totalTime - LOOP_BUFFER then + Spring.StopSoundStream() + Spring.PlaySoundStream(loopTrack, WG.music_volume or 0.5) + end end +end + +function EvaluateMood() +-- (Spring.GetGameRulesParam("recentNukeLaunch") == 1) -- Might need this for superweapon music later + newTrackWait = newTrackWait + 1 + + numVisibleFriendly = 0 + numVisibleEnemy = 0 + local doods = Spring.GetVisibleUnits(-1, nil, true) + for i=1,#doods do + if Spring.IsUnitAllied(doods[i]) then + numVisibleFriendly = numVisibleFriendly + 1 + else + numVisibleEnemy = numVisibleEnemy + 1 + end + end + + local totalKilled, friendliesKilled, hostilesKilled = 0, 0, 0 + local totalDmg, friendlyDmg, hostileDmg = 0, 0, 0 + local iLast = ((warPointsIter-16) % warPointsSize) + 1 -- Look back 15 periods. + local iLast2 = ((warPointsIter-61) % warPointsSize) + 1 -- Look back 60 periods. + -- Last 10 seconds count for double + friendliesKilled = friendliesKilled + ((deathPointsFriendly[warPointsIter] - deathPointsFriendly[iLast]) % warPointsRollover) + friendliesKilled = friendliesKilled + ((deathPointsFriendly[warPointsIter] - deathPointsFriendly[iLast2]) % warPointsRollover) + hostilesKilled = hostilesKilled + ((deathPointsHostile[warPointsIter] - deathPointsHostile[iLast]) % warPointsRollover) + hostilesKilled = hostilesKilled + ((deathPointsHostile[warPointsIter] - deathPointsHostile[iLast2]) % warPointsRollover) + + friendlyDmg = friendlyDmg + ((dmgPointsFriendly[warPointsIter] - dmgPointsFriendly[iLast]) % warPointsRollover) + friendlyDmg = friendlyDmg + ((dmgPointsFriendly[warPointsIter] - dmgPointsFriendly[iLast2]) % warPointsRollover) + hostileDmg = hostileDmg + ((dmgPointsHostile[warPointsIter] - dmgPointsHostile[iLast]) % warPointsRollover) + hostileDmg = hostileDmg + ((dmgPointsHostile[warPointsIter] - dmgPointsHostile[iLast2]) % warPointsRollover) + + totalKilled = friendliesKilled + hostilesKilled + local attritionRatio = (hostilesKilled+1)/(friendliesKilled+1) -- 1 metal is virtually nothing in the ratio, but this simplifies edge cases + totalDmg = friendlyDmg + hostileDmg + + -- Roll to next index in the circular buffers, continue cumulative sum + local iNext = (warPointsIter % warPointsSize) + 1 + dmgPointsFriendly[iNext] = dmgPointsFriendly[warPointsIter] % warPointsRollover + dmgPointsHostile[iNext] = dmgPointsHostile[warPointsIter] % warPointsRollover + deathPointsFriendly[iNext] = deathPointsFriendly[warPointsIter] % warPointsRollover + deathPointsHostile[iNext] = deathPointsHostile[warPointsIter] % warPointsRollover + warPointsIter = iNext + + if moodDynamic[musicType] then + if (totalKilled >= warThreshold) then + musicType = "war" + if (totalKilled >= war2Threshold) then musicType = "war2" end + if attritionRatio < options.attritionRatioLosing then + musicType = "losing" + elseif attritionRatio > options.attritionRatioWinning then + musicType = "winning" + end + else --if (totalKilled <= peaceThreshold) then + musicType = "peace" + end + end + + if (not firstTime) then + StartTrack() + firstTime = true + end + + local playedTime, totalTime = Spring.GetSoundStreamTime() +-- playedTime = math.floor(playedTime) +-- totalTime = math.floor(totalTime) + + --Spring.Echo(playedTime, totalTime, newTrackWait) + + --if((totalTime - playedTime) <= 6 and (totalTime >= 1) ) then + --Spring.Echo("time left:", (totalTime - playedTime)) + --Spring.Echo("volume:", (totalTime - playedTime)/6) + --if ((totalTime - playedTime)/6 >= 0) then + -- Spring.SetSoundStreamVolume((totalTime - playedTime)/6) + --else + -- Spring.SetSoundStreamVolume(0.1) + --end + --elseif(playedTime <= 5 )then--and not firstFade + --Spring.Echo("time playing:", playedTime) + --Spring.Echo("volume:", playedTime/5) + --Spring.SetSoundStreamVolume( playedTime/5) + --end + --Spring.Echo(prevMusicType, musicType) + if (prevMusicType ~= musicType and moodPriorities[musicType] >= moodPriorities[prevMusicType]) + or (playedTime >= totalTime) -- both zero means track stopped + and not(haltMusic or looping) then + prevMusicType = musicType + StartTrack() + newTrackWait = 0 + end + + local _, _, paused = Spring.GetGameSpeed() + if (paused ~= wasPaused) and options.pausemusic.value then + Spring.PauseSoundStream() + wasPaused = paused + end +end + +function widget:Update(dt) + if gameOver then return end + if not initialized then math.randomseed(os.clock()* 100) initialized=true - -- these are here to give epicmenu time to set the values properly - -- (else it's always default at startup) + -- these are here to give epicmenu time to set the values properly (else it's always default at startup) InitializeTracks() end timeframetimer_short = timeframetimer_short + dt if timeframetimer_short > 0.03 then - local playedTime, totalTime = Spring.GetSoundStreamTime() - playedTime = tonumber( ("%.2f"):format(playedTime) ) - paused = (playedTime == lastTrackTime) - lastTrackTime = playedTime - if looping then - if looping == 0.5 then - looping = 1 - elseif playedTime >= totalTime - LOOP_BUFFER then - Spring.StopSoundStream() - Spring.PlaySoundStream(loopTrack, WG.music_volume or 0.5) - end - end + CheckLoop() timeframetimer_short = 0 end --- (Spring.GetGameRulesParam("recentNukeLaunch") == 1) -- Might need this for superweapon music later - timeframetimer = timeframetimer + dt if (timeframetimer > UPDATE_PERIOD) then -- every second + EvaluateMood() timeframetimer = 0 - newTrackWait = newTrackWait + 1 - numVisibleEnemy = 0 - local doods = Spring.GetVisibleUnits(-1, nil, true) - for i=1,#doods do - if (Spring.IsUnitAllied(doods[i]) ~= true) then - numVisibleEnemy = numVisibleEnemy + 1 - end - end - - local totalKilled, friendliesKilled, hostilesKilled = 0, 0, 0 - local iLast = ((warPointsIter-11) % warPointsSize) + 1 -- Look back 10 periods. - local iLast2 = ((warPointsIter-21) % warPointsSize) + 1 -- Look back 20 periods. - -- Last 10 seconds count for double - friendliesKilled = friendliesKilled + ((warPointsFriendly[warPointsIter] - warPointsFriendly[iLast]) % warPointsRollover) - friendliesKilled = friendliesKilled + ((warPointsFriendly[warPointsIter] - warPointsFriendly[iLast2]) % warPointsRollover) - hostilesKilled = hostilesKilled + ((warPointsHostile[warPointsIter] - warPointsHostile[iLast]) % warPointsRollover) - hostilesKilled = hostilesKilled + ((warPointsHostile[warPointsIter] - warPointsHostile[iLast2]) % warPointsRollover) - totalKilled = (friendliesKilled * 1.5) + hostilesKilled - - -- Roll to next index in the circular buffers, continue cumulative sum - local iNext = (warPointsIter % warPointsSize) + 1 - warPointsFriendly[iNext] = warPointsFriendly[warPointsIter] % warPointsRollover - warPointsHostile[iNext] = warPointsHostile[warPointsIter] % warPointsRollover - warPointsIter = iNext - - if (musicType == "war" or musicType == "peace") then - if (totalKilled >= warThreshold) then - musicType = "war" - elseif (totalKilled <= peaceThreshold) then - musicType = "peace" - end - end - - if (not firstTime) then - StartTrack() - firstTime = true - end - - local playedTime, totalTime = Spring.GetSoundStreamTime() - playedTime = math.floor(playedTime) - totalTime = math.floor(totalTime) - --Spring.Echo(playedTime, totalTime) - - --Spring.Echo(playedTime, totalTime, newTrackWait) - - --if((totalTime - playedTime) <= 6 and (totalTime >= 1) ) then - --Spring.Echo("time left:", (totalTime - playedTime)) - --Spring.Echo("volume:", (totalTime - playedTime)/6) - --if ((totalTime - playedTime)/6 >= 0) then - -- Spring.SetSoundStreamVolume((totalTime - playedTime)/6) - --else - -- Spring.SetSoundStreamVolume(0.1) - --end - --elseif(playedTime <= 5 )then--and not firstFade - --Spring.Echo("time playing:", playedTime) - --Spring.Echo("volume:", playedTime/5) - --Spring.SetSoundStreamVolume( playedTime/5) - --end - --Spring.Echo(previousTrackType, musicType) - if ( previousTrackType == "peace" and musicType == "war" ) - or (playedTime >= totalTime) -- both zero means track stopped - and not(haltMusic or looping) then - previousTrackType = musicType - StartTrack() - - --Spring.Echo("Track: " .. newTrack) - newTrackWait = 0 - end - local _, _, paused = Spring.GetGameSpeed() - if (paused ~= wasPaused) and options.pausemusic.value then - Spring.PauseSoundStream() - wasPaused = paused - end end end function widget:GameStart() if not gameStarted then gameStarted = true - previousTrackType = musicType + prevMusicType = musicType musicType = "peace" StartTrack() end - --Spring.Echo("Track: " .. newTrack) newTrackWait = 0 end @@ -333,46 +374,24 @@ end function widget:UnitDamaged(unitID, unitDefID, unitTeam, damage, paralyzer) if unitExceptions[unitDefID] then return end - if (damage < 1.5) then return end if (UnitDefs[unitDefID] == nil) then return end if paralyzer then return end - local multifactor = 1 - if (numVisibleEnemy > 3) then - multifactor = math.log(numVisibleEnemy) - end if (teamID == myTeam) then - warPointsFriendly[warPointsIter] = warPointsFriendly[warPointsIter] + (damage * multifactor); + dmgPointsFriendly[warPointsIter] = dmgPointsFriendly[warPointsIter] + damage else - warPointsHostile[warPointsIter] = warPointsHostile[warPointsIter] + (damage * multifactor); + dmgPointsHostile[warPointsIter] = dmgPointsHostile[warPointsIter] + damage end end function widget:UnitDestroyed(unitID, unitDefID, teamID) if unitExceptions[unitDefID] then return end + local unitCost = UnitDefs[unitDefID].metalCost - local unitWorth = 50 - if (UnitDefs[unitDefID].metalCost > 500) then - unitWorth = 200 - end - if (UnitDefs[unitDefID].metalCost > 1000) then - unitWorth = 300 - end - if (UnitDefs[unitDefID].metalCost > 3000) then - unitWorth = 500 - end - if (UnitDefs[unitDefID].metalCost > 8000) then - unitWorth = 700 - end - - local multifactor = 1 - if (numVisibleEnemy > 3) then - multifactor = math.log(numVisibleEnemy) - end if (teamID == myTeam) then - warPointsFriendly[warPointsIter] = warPointsFriendly[warPointsIter] + (unitWorth*multifactor); + deathPointsFriendly[warPointsIter] = deathPointsFriendly[warPointsIter] + unitCost else - warPointsHostile[warPointsIter] = warPointsHostile[warPointsIter] + (unitWorth*multifactor); + deathPointsHostile[warPointsIter] = deathPointsHostile[warPointsIter] + unitCost end end @@ -413,9 +432,6 @@ function widget:Initialize() WG.Music.GetMusicType = GetMusicType WG.Music.PlayGameOverMusic = PlayGameOverMusic - -- Spring.Echo(math.random(), math.random()) - -- Spring.Echo(os.clock()) - -- for TrackName,TrackDef in pairs(tracks.peace) do -- Spring.Echo("Track: " .. TrackDef) -- end @@ -423,8 +439,10 @@ function widget:Initialize() -- for i=1,20 do Spring.Echo(math.random()) end for i = 1, warPointsSize do - warPointsFriendly[i] = 0 - warPointsHostile[i] = 0 + dmgPointsFriendly[i] = 0 + dmgPointsHostile[i] = 0 + deathPointsFriendly[i] = 0 + deathPointsHostile[i] = 0 end end