Zero-K_Custom_Widgets/snd_music.lua

442 lines
13 KiB
Lua

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- file: gui_music.lua
-- brief: yay music
-- author: cake
--
-- Copyright (C) 2007.
-- Licensed under the terms of the GNU GPL, v2 or later.
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function widget:GetInfo()
return {
name = "Music Player",
desc = "Plays music based on situation",
author = "cake, trepan, Smoth, Licho, xponen",
date = "Mar 01, 2008, Aug 20 2009, Nov 23 2011",
license = "GNU GPL, v2 or later",
layer = 0,
enabled = true -- loaded by default?
}
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
options_path = "Settings/Audio"
options = {
useIncludedTracks = {
name = "Use Included Tracks",
type = "bool",
value = true,
desc = "Use the tracks included with Zero-K",
noHotkey = true,
},
pausemusic = {
name = "Pause Music",
type = "bool",
value = false,
desc = "Music pauses with game",
noHotkey = true,
},
}
local unitExceptions = include("Configs/snd_music_exception.lua")
local windows = {}
local warThreshold = 5000
local peaceThreshold = 1000
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 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 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 newTrackWait = 1000
local numVisibleEnemy = 0
local fadeVol
local curTrac = "no name"
local songText = "no name"
local haltMusic = false
local looping = false
local paused = false
local lastTrackTime = -1
local tracks = {}
local firstTime = false
local wasPaused = false
local firstFade = true
local initSeed = 0
local initialized = false
local gameStarted = Spring.GetGameFrame() > 0
local gameOver = false
local myTeam = Spring.GetMyTeamID()
local isSpec = Spring.GetSpectatingState() or Spring.IsReplay()
local defeat = false
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function GetMusicType()
return musicType
end
local function StartLoopingTrack(trackInit, trackLoop)
if not (VFS.FileExists(trackInit) and VFS.FileExists(trackLoop)) then
Spring.Log(widget:GetInfo().name, LOG.ERROR, "Missing one or both tracks for looping")
end
haltMusic = true
Spring.StopSoundStream()
musicType = "custom"
curTrack = trackInit
loopTrack = trackLoop
Spring.PlaySoundStream(trackInit, WG.music_volume or 0.5)
looping = 0.5
end
local function StartTrack(track)
if not tracks.peace then
Spring.Echo("Missing tracks.peace file, no music started")
return
end
haltMusic = false
looping = false
Spring.StopSoundStream()
local newTrack = previousTrack
if musicType == "custom" then
previousTrackType = "peace"
musicType = "peace"
end
if (not gameStarted) then
musicType = "briefing"
end
if track then
newTrack = track -- play specified track
musicType = "custom"
else
local numTypeTracks = #tracks[musicType]
if (numTypeTracks == 0) then return end
for tries=1,10 do
newTrack = tracks[musicType][math.random(1, numTypeTracks)]
if newTrack ~= previousTrack then break end
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)
WG.music_start_volume = WG.music_volume
end
local function StopTrack(noContinue)
looping = false
Spring.StopSoundStream()
if noContinue then
haltMusic = true
else
haltMusic = false
StartTrack()
end
end
local function SetWarThreshold(num)
if num and num >= 0 then
warThreshold = num
else
warThreshold = 5000
end
end
local function SetPeaceThreshold(num)
if num and num >= 0 then
peaceThreshold = num
else
peaceThreshold = 1000
end
end
function InitializeTracks()
Spring.Echo("Initializing music tracks")
if VFS.FileExists(PLAYLIST_FILE, VFS.RAW_FIRST) then
local plTracks = VFS.Include(PLAYLIST_FILE, nil, VFS.RAW_FIRST)
tracks.war = plTracks.war
tracks.peace = plTracks.peace
tracks.briefing = plTracks.briefing
tracks.victory = plTracks.victory
tracks.defeat = plTracks.defeat
end
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.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
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)
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
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
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
musicType = "peace"
StartTrack()
end
--Spring.Echo("Track: " .. newTrack)
newTrackWait = 0
end
-- Safety of a heisenbug
function widget:GameFrame()
widget:GameStart()
widgetHandler:RemoveCallIn("GameFrame")
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);
else
warPointsHostile[warPointsIter] = warPointsHostile[warPointsIter] + (damage * multifactor);
end
end
function widget:UnitDestroyed(unitID, unitDefID, teamID)
if unitExceptions[unitDefID] then return end
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);
else
warPointsHostile[warPointsIter] = warPointsHostile[warPointsIter] + (unitWorth*multifactor);
end
end
function widget:TeamDied(team)
if team == myTeam and not isSpec then
defeat = true
end
end
local function PlayGameOverMusic(gameWon)
local track
if gameWon then
if #tracks.victory <= 0 then return end
track = tracks.victory[math.random(1, #tracks.victory)]
musicType = "victory"
else
if #tracks.defeat <= 0 then return end
track = tracks.defeat[math.random(1, #tracks.defeat)]
musicType = "defeat"
end
looping = false
Spring.StopSoundStream()
Spring.PlaySoundStream(track,WG.music_volume or 0.5)
WG.music_start_volume = WG.music_volume
end
function widget:GameOver()
PlayGameOverMusic(not defeat)
end
function widget:Initialize()
WG.Music = WG.Music or {}
WG.Music.StartTrack = StartTrack
WG.Music.StartLoopingTrack = StartLoopingTrack
WG.Music.StopTrack = StopTrack
WG.Music.SetWarThreshold = SetWarThreshold
WG.Music.SetPeaceThreshold = SetPeaceThreshold
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
--math.randomseed(os.clock()* 101.01)--lurker wants you to burn in hell rgn
-- for i=1,20 do Spring.Echo(math.random()) end
for i = 1, warPointsSize do
warPointsFriendly[i] = 0
warPointsHostile[i] = 0
end
end
function widget:Shutdown()
Spring.StopSoundStream()
WG.Music = nil
for i=1,#windows do
(windows[i]):Dispose()
end
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------