TombEngine/Scripts/Engine/Timer.lua

311 lines
9.1 KiB
Lua
Raw Normal View History

-----
--- Basic timer - after a specified number of seconds, the specified thing happens.
--
-- Timers are updated automatically every frame before OnControlPhase.
--
-- Example usage:
-- local Timer = require("Engine.Timer")
--
-- -- This will be called when the timer runs out
-- LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage)
-- -- Open a door, display a message, make an explosion... whatever you wish
-- DoSomething(healthWhenStarted, victoryMessage)
-- end
--
-- -- This function triggers the timer
-- LevelFuncs.TriggerTimer = function(obj)
-- local myTimer = Timer.Create("my_timer",
-- 5.0,
-- false,
-- {minutes = false, seconds = true, deciseconds = true},
-- LevelFuncs.FinishTimer,
-- Lara:GetHP(),
-- "Well done!")
-- myTimer:Start()
-- end
--
-- @luautil Timer
2022-07-28 22:18:20 +01:00
LevelFuncs.Engine.Timer = {}
LevelVars.Engine.Timer = {timers = {}}
2022-07-28 22:18:20 +01:00
local Timer
local unpausedColor = TEN.Color(255, 255, 255)
local pausedColor = TEN.Color(255, 255, 0)
2022-08-01 20:41:06 +01:00
local str = TEN.Strings.DisplayString("TIMER", 0, 0, unpausedColor, false, {TEN.Strings.DisplayStringOption.CENTER, TEN.Strings.DisplayStringOption.SHADOW} )
2022-07-28 22:18:20 +01:00
Timer = {
--- Create (but do not start) a new timer.
--
-- You have the option of displaying the remaining time on the clock. Timer format details:
--
-- -- deciseconds are 1/10th of a second
--
-- -- mins:secs
-- local myTimeFormat1 = {minutes = true, seconds = true, deciseconds = false}
--
-- -- also mins:secs
-- local myTimeFormat2 = {minutes = true, seconds = true}
--
-- -- secs:decisecs
-- local myTimeFormat3 = {seconds = true, deciseconds = true}
--
-- -- secs; also what is printed if you pass true instead of a table
-- local myTimeFormat4 = {seconds = true}
--
--__At any given time, only one timer can show its countdown__.
--
--__Do not give your timers a name beginning with __TEN, as this is reserved for timers used by other internal libaries__.
--
--Use this sparingly; in the classics, timed challenges did not have visible countdowns. For shorter timers, the gameplay benefit from showing the remaining time might not be necessary, and could interfere with the atmosphere of the level.
--
-- @string name A label to give this timer; used to retrieve the timer later
-- @number totalTime The duration of the timer, in seconds
-- @bool loop if true, the timer will start again immediately after the time has elapsed
-- @tparam ?table|bool timerFormat If a table is given, the remaining time will be shown as a string, formatted according to the values in the table. If true, the remaining seconds, rounded up, will show at the bottom of the screen. If false, the remaining time will not be shown on screen.
2022-11-23 20:16:37 +00:00
-- @func func The LevelFunc function to call when the time is up
-- @param[opt] ... a variable number of arguments with which the above function will be called
-- @return The timer in its paused state
--
Create = function(name, totalTime, loop, timerFormat, func, ...)
2022-07-28 22:18:20 +01:00
local obj = {}
local mt = {}
mt.__index = Timer
setmetatable(obj, mt)
obj.name = name
if LevelVars.Engine.Timer.timers[name] then
print("Warning: a timer with name " .. name .. " already exists.")
end
LevelVars.Engine.Timer.timers[name] ={}
local thisTimer = LevelVars.Engine.Timer.timers[name]
2022-07-28 22:18:20 +01:00
thisTimer.name = name
thisTimer.totalTime = totalTime
thisTimer.remainingTime = totalTime
2022-08-01 20:41:06 +01:00
thisTimer.func = func
2022-07-28 22:18:20 +01:00
thisTimer.funcArgs = {...}
thisTimer.loop = loop
thisTimer.active = false
2022-08-01 20:41:06 +01:00
thisTimer.paused = true
if type(timerFormat) == "table" then
thisTimer.timerFormat = timerFormat
elseif timerFormat then
thisTimer.timerFormat = {seconds = true}
end
2022-07-28 22:18:20 +01:00
return obj
end;
--- Get a timer by its name.
-- @string name The label that was given to the timer when it was created
-- @return The timer
2022-07-28 22:18:20 +01:00
Get = function(name)
if LevelVars.Engine.Timer.timers[name] then
2022-07-28 22:18:20 +01:00
local obj = {}
local mt = {}
mt.__index = Timer
setmetatable(obj, mt)
obj.name = name
return obj
end
return nil
end;
Update = function(t, dt)
if t.active then
2022-08-01 20:41:06 +01:00
if not t.paused then
t.remainingTime = t.remainingTime - dt
2022-07-28 22:18:20 +01:00
2022-08-01 20:41:06 +01:00
if t.remainingTime <= 0 then
t.func(table.unpack(t.funcArgs))
2022-08-01 20:41:06 +01:00
if not t.loop then
t.active = false
else
t.remainingTime = t.remainingTime + t.totalTime
end
2022-07-28 22:18:20 +01:00
end
2022-08-01 20:41:06 +01:00
end
if t.timerFormat then
2022-08-01 20:41:06 +01:00
TEN.Strings.HideString(str)
local fmt = ""
local remaining = math.max(t.remainingTime, 0)
local round = math.floor
local subSecond = remaining - math.floor(remaining)
local fmtBefore = false
-- deciseconds
if t.timerFormat.deciseconds then
fmt = math.floor(10*subSecond)
fmtBefore = true
end
-- seconds
if t.timerFormat.seconds then
if not fmtBefore then
round = math.ceil
else
round = math.floor
fmt = ":" .. fmt
end
local roundedSeconds = round(remaining)
local toBeDisplayed = roundedSeconds
if t.timerFormat.minutes then
toBeDisplayed = roundedSeconds % 60
end
fmt = string.format("%02d", toBeDisplayed) .. fmt
remaining = roundedSeconds
fmtBefore = true
end
-- minutes
if t.timerFormat.minutes then
if not fmtBefore then
round = math.ceil
else
round = math.floor
fmt = ":" .. fmt
end
local roundedMinutes = round(remaining/60)
local toBeDisplayed = roundedMinutes
fmt = string.format("%02d", toBeDisplayed) .. fmt
fmtBefore = true
end
str:SetKey(fmt)
2022-08-01 20:41:06 +01:00
local myX, myY = PercentToScreen(50, 90)
str:SetPosition(myX, myY)
-- Do this again in case the player has loaded while the timer was paused already
-- Need a better solution for this
if t.paused then
str:SetColor(pausedColor)
end
2022-08-01 20:41:06 +01:00
TEN.Strings.ShowString(str, 1)
2022-07-28 22:18:20 +01:00
end
2022-08-01 20:41:06 +01:00
2022-07-28 22:18:20 +01:00
end
end;
UpdateAll = function(dt)
print("Timer.UpdateAll is deprecated; timers and event sequences now get updated automatically pre-control phase.")
end;
--- Give the timer a new function and args
-- @function myTimer:SetFunction
-- @tparam function func The LevelFunc member to call when the time is up
-- @param[opt] ... a variable number of arguments with which the above function will be called
SetFunction = function(t, func, ...)
local thisTimer = LevelVars.Engine.Timer.timers[t.name]
thisTimer.func = func
thisTimer.funcArgs = {...}
end;
--- Begin or unpause a timer. If showing the remaining time on-screen, its color will be set to white.
-- @function myTimer:Start
Start = function(t)
local thisTimer = LevelVars.Engine.Timer.timers[t.name]
if not thisTimer.active then
thisTimer.active = true
end
thisTimer.paused = false
if thisTimer.timerFormat then
str:SetColor(unpausedColor)
end
end;
--- Stop the timer.
-- @function myTimer:Stop
Stop = function(t)
LevelVars.Engine.Timer.timers[t.name].active = false
end;
--- Get whether or not the timer is active
-- @function myTimer:IsActive
-- @return true if the timer is active, false if otherwise
IsActive = function(t)
return LevelVars.Engine.Timer.timers[t.name].active
end;
--- Pause or unpause the timer. If showing the remaining time on-screen, its color will be set to yellow (paused) or white (unpaused).
-- @function myTimer:SetPaused
-- @bool p if true, the timer will be paused; if false, it would be unpaused
SetPaused = function(t, p)
local thisTimer = LevelVars.Engine.Timer.timers[t.name]
thisTimer.paused = p
if thisTimer.timerFormat then
if p then
str:SetColor(pausedColor)
else
str:SetColor(unpausedColor)
end
end
end;
--- Get whether or not the timer is paused
-- @function myTimer:IsPaused
-- @return true if the timer is paused, false if otherwise
IsPaused = function(t)
return LevelVars.Engine.Timer.timers[t.name].paused
end;
--- Get the remaining time for a timer.
-- @function myTimer:GetRemainingTime
-- @return the time in seconds remaining on the clock
GetRemainingTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].remainingTime
end;
--- Set the remaining time for a timer
-- @function myTimer:SetRemainingTime
-- @number remainingTime the new time remaining for the timer
SetRemainingTime = function(t, remainingTime)
LevelVars.Engine.Timer.timers[t.name].remainingTime = remainingTime
end;
--- Get the total time for a timer.
-- This is the amount of time the timer will start with, as well as when starting a new loop
-- @function myTimer:GetRemainingTime
-- @return the timer's total time
GetTotalTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].totalTime
end;
--- Set the total time for a timer
-- @function myTimer:SetTotalTime
-- @number totalTime timer's new total time
SetTotalTime = function(t, totalTime)
LevelVars.Engine.Timer.timers[t.name].totalTime = totalTime
end;
--- Set whether or not the timer loops
-- @function myTimer:SetLooping
-- @bool looping whether or not the timer loops
SetLooping = function(t, looping)
LevelVars.Engine.Timer.timers[t.name].loop = looping
end;
2022-07-28 22:18:20 +01:00
}
LevelFuncs.Engine.Timer.UpdateAll = function(dt)
for _, t in pairs(LevelVars.Engine.Timer.timers) do
Timer.Update(t, dt)
end
end
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.Engine.Timer.UpdateAll)
2022-07-28 22:18:20 +01:00
return Timer