Timer and Day modules
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-03-03 19:51:22 +01:00
parent 41eea638c0
commit 03b6567c08
11 changed files with 135 additions and 98 deletions

View File

@@ -3,6 +3,8 @@
globals = { globals = {
"Focus", "Focus",
"Day",
"Timer",
"Util", "Util",
"Decision", "Decision",
"Situation", "Situation",

View File

@@ -8,6 +8,8 @@ system/system.util.lua
system/system.print.lua system/system.print.lua
system/system.input.lua system/system.input.lua
system/system.focus.lua system/system.focus.lua
system/system.day.lua
system/system.timer.lua
system/system.ui.lua system/system.ui.lua
audio/audio.manager.lua audio/audio.manager.lua
audio/audio.songs.lua audio/audio.songs.lua

View File

@@ -19,7 +19,6 @@ function Audio.music_play_room_street_1() end
--- @within Audio --- @within Audio
function Audio.music_play_room_street_2() end function Audio.music_play_room_street_2() end
--- Plays room music. --- Plays room music.
-- TODO: function name is incomplete, determine the correct room identifier
--- @within Audio --- @within Audio
function Audio.music_play_room_() end function Audio.music_play_room_() end
--- Plays room work music. --- Plays room work music.

View File

@@ -34,10 +34,12 @@ function Context.initial_data()
minigame_button_mash = Minigame.get_default_button_mash(), minigame_button_mash = Minigame.get_default_button_mash(),
minigame_rhythm = Minigame.get_default_rhythm(), minigame_rhythm = Minigame.get_default_rhythm(),
meters = Meter.get_initial(), meters = Meter.get_initial(),
timer = Timer.get_initial(),
game = { game = {
current_screen = "home", current_screen = "home",
current_situation = nil, current_situation = nil,
} },
day_count = 1,
} }
end end

View File

@@ -6,30 +6,12 @@ local COMBO_BASE_BONUS = 0.02
local COMBO_MAX_BONUS = 0.16 local COMBO_MAX_BONUS = 0.16
local COMBO_TIMEOUT_FRAMES = 600 local COMBO_TIMEOUT_FRAMES = 600
-- 1800 frames = 30 seconds (1800 ÷ 60 = 30)
local meter_timer_duration = 1800
local meter_timer_decay_per_revolution = 20
-- Internal meters for tracking game progress and player stats. -- Internal meters for tracking game progress and player stats.
Meter.COLOR_ISM = Config.colors.red Meter.COLOR_ISM = Config.colors.red
Meter.COLOR_WPM = Config.colors.blue Meter.COLOR_WPM = Config.colors.blue
Meter.COLOR_BM = Config.colors.black Meter.COLOR_BM = Config.colors.black
Meter.COLOR_BG = Config.colors.meter_bg Meter.COLOR_BG = Config.colors.meter_bg
--- Sets the number of frames for one full timer revolution.
--- @within Meter
--- @param frames number Frames per revolution (controls degradation speed).
function Meter.set_timer_duration(frames)
meter_timer_duration = frames
end
--- Sets the degradation amount applied to all meters per revolution.
--- @within Meter
--- @param amount number Amount to subtract from each meter per revolution.
function Meter.set_timer_decay(amount)
meter_timer_decay_per_revolution = amount
end
--- Gets initial meter values. --- Gets initial meter values.
--- @within Meter --- @within Meter
--- @return result table Initial meter values. </br> --- @return result table Initial meter values. </br>
@@ -39,8 +21,7 @@ end
--- * bm (number) Initial BM meter value.<br/> --- * bm (number) Initial BM meter value.<br/>
--- * combo (number) Current combo count.<br/> --- * combo (number) Current combo count.<br/>
--- * combo_timer (number) Frames since last combo action.<br/> --- * combo_timer (number) Frames since last combo action.<br/>
--- * hidden (boolean) Whether meters are hidden.<br/> --- * hidden (boolean) Whether meters are hidden.
--- * timer_progress (number) Clock timer revolution progress (0 to 1).
function Meter.get_initial() function Meter.get_initial()
return { return {
ism = METER_DEFAULT, ism = METER_DEFAULT,
@@ -49,7 +30,6 @@ function Meter.get_initial()
combo = 0, combo = 0,
combo_timer = 0, combo_timer = 0,
hidden = false, hidden = false,
timer_progress = 0,
} }
end end
@@ -96,13 +76,6 @@ function Meter.update()
m.combo_timer = 0 m.combo_timer = 0
end end
end end
m.timer_progress = m.timer_progress + (1 / meter_timer_duration)
if m.timer_progress >= 1 then
m.timer_progress = m.timer_progress - 1
m.ism = math.max(0, m.ism - meter_timer_decay_per_revolution)
m.wpm = math.max(0, m.wpm - meter_timer_decay_per_revolution)
m.bm = math.max(0, m.bm - meter_timer_decay_per_revolution)
end
end end
end end
@@ -118,13 +91,6 @@ function Meter.add(key, amount)
end end
end end
--- Gets the timer decay as a percentage of the max meter value.
--- @within Meter
--- @return number The decay percentage per revolution (e.g. 2 means -2%).
function Meter.get_timer_decay_percentage()
return math.floor(meter_timer_decay_per_revolution / METER_MAX * 100)
end
--- Called on minigame completion. --- Called on minigame completion.
--- @within Meter --- @within Meter
function Meter.on_minigame_complete() function Meter.on_minigame_complete()

View File

@@ -12,3 +12,5 @@ Input = {}
Sprite = {} Sprite = {}
Audio = {} Audio = {}
Focus = {} Focus = {}
Day = {}
Timer = {}

View File

@@ -30,8 +30,7 @@ Screen.register({
local bar_x = math.floor((sw - bar_w) / 2) local bar_x = math.floor((sw - bar_w) / 2)
local bar_h = 4 local bar_h = 4
-- TODO: Add day counter Print.text_center("day " .. Context.day_count, cx, 10, Config.colors.white)
Print.text_center("day 1", cx, 10, Config.colors.white)
local narrative = "reflecting on my past and present\n...\nboth eventually flushed." local narrative = "reflecting on my past and present\n...\nboth eventually flushed."
local wrapped = UI.word_wrap(narrative, 38) local wrapped = UI.word_wrap(narrative, 38)
@@ -43,7 +42,7 @@ Screen.register({
local m = Context.meters local m = Context.meters
local max_val = Meter.get_max() local max_val = Meter.get_max()
local decay_pct = Meter.get_timer_decay_percentage() local decay_pct = Timer.get_decay_percentage()
local decay_text = string.format("-%d%%", decay_pct) local decay_text = string.format("-%d%%", decay_pct)
local combo_mult = Meter.get_combo_multiplier() local combo_mult = Meter.get_combo_multiplier()
local combo_pct = math.floor((combo_mult - 1) * 100) local combo_pct = math.floor((combo_mult - 1) * 100)

11
inc/system/system.day.lua Normal file
View File

@@ -0,0 +1,11 @@
local _day_increase_handlers = {}
function Day.increase()
Context.day_count = Context.day_count + 1
for _, handler in ipairs(_day_increase_handlers) do
handler()
end
end
function Day.register_handler(handler)
table.insert(_day_increase_handlers, handler)
end

View File

@@ -22,8 +22,9 @@ function TIC()
handler() handler()
end end
Meter.update() Meter.update()
Timer.update()
if Context.game_in_progress then if Context.game_in_progress then
UI.draw_meters() UI.draw_meters()
UI.draw_timer() Timer.draw()
end end
end end

107
inc/system/system.timer.lua Normal file
View File

@@ -0,0 +1,107 @@
--- @section Timer
local timer_duration = 1800
local timer_decay_per_revolution = 20
--- Gets initial timer values.
--- @within Timer
--- @return result table Initial timer values. </br>
--- Fields: </br>
--- * progress (number) Clock timer revolution progress (0 to 1).
function Timer.get_initial()
return {
progress = 0,
}
end
--- Sets the number of frames for one full timer revolution.
--- @within Timer
--- @param frames number Frames per revolution.
function Timer.set_duration(frames)
timer_duration = frames
end
--- Sets the decay amount applied to all meters per revolution.
--- @within Timer
--- @param amount number Amount to subtract from each meter.
function Timer.set_decay(amount)
timer_decay_per_revolution = amount
end
--- Gets the timer decay as a percentage of the max meter value.
--- @within Timer
--- @return number The decay percentage per revolution.
function Timer.get_decay_percentage()
return math.floor(timer_decay_per_revolution / Meter.get_max() * 100)
end
--- Updates the timer and handles revolution events.
--- @within Timer
function Timer.update()
if not Context or not Context.game_in_progress or not Context.meters or not Context.timer then return end
local m = Context.meters
local t = Context.timer
local in_minigame = string.find(Window.get_current_id(), "^minigame_") ~= nil
if not in_minigame then
t.progress = t.progress + (1 / timer_duration)
if t.progress >= 1 then
Day.increase()
t.progress = t.progress - 1
m.ism = math.max(0, m.ism - timer_decay_per_revolution)
m.wpm = math.max(0, m.wpm - timer_decay_per_revolution)
m.bm = math.max(0, m.bm - timer_decay_per_revolution)
end
end
end
--- Draws the clock timer indicator as a circular progress bar.
--- @within Timer
function Timer.draw()
if not Context or not Context.game_in_progress or not Context.meters or not Context.timer then return end
if Context.meters.hidden and not Context.stat_screen_active then return end
local cx = 10
local cy = 20
local r_outer = 5
local r_inner = 3
local progress = Context.timer.progress
local fg_color
if progress <= 0.25 then
fg_color = Config.colors.white
elseif progress <= 0.5 then
fg_color = Config.colors.light_blue
elseif progress <= 0.75 then
fg_color = Config.colors.blue
elseif progress <= 1 then
fg_color = Config.colors.red
end
local bg_color = Config.colors.dark_grey
local start_angle = -math.pi * 0.5
local progress_angle = progress * 2 * math.pi
local r_outer_sq = r_outer * r_outer
local r_inner_sq = r_inner * r_inner
for dy = -r_outer, r_outer do
for dx = -r_outer, r_outer do
local dist_sq = dx * dx + dy * dy
if dist_sq <= r_outer_sq and dist_sq > r_inner_sq then
local angle = math.atan(dy, dx)
local relative = angle - start_angle
if relative < 0 then relative = relative + 2 * math.pi end
if relative <= progress_angle then
pix(cx + dx, cy + dy, fg_color)
else
pix(cx + dx, cy + dy, bg_color)
end
end
end
end
local hand_angle = start_angle + progress_angle
local hand_x = math.floor(cx + math.cos(hand_angle) * (r_inner - 1) + 0.5)
local hand_y = math.floor(cy + math.sin(hand_angle) * (r_inner - 1) + 0.5)
line(cx, cy, hand_x, hand_y, Config.colors.white)
end

View File

@@ -155,60 +155,6 @@ function UI.draw_decision_selector(decisions, selected_decision_index)
end end
end end
--- Draws the clock timer indicator as a circular progress bar in the top-left area.
--- Color transitions: white (0-50%), yellow (50-75%), red (75-100%).
--- @within UI
function UI.draw_timer()
if not Context or not Context.game_in_progress or not Context.meters then return end
if Context.meters.hidden and not Context.stat_screen_active then return end
local m = Context.meters
local cx = 10
local cy = 20
local r_outer = 5
local r_inner = 3
local progress = m.timer_progress
local fg_color
if progress <= 0.25 then
fg_color = Config.colors.white
elseif progress <= 0.5 then
fg_color = Config.colors.light_blue
elseif progress <= 0.75 then
fg_color = Config.colors.blue
elseif progress <= 1 then
fg_color = Config.colors.red
end
local bg_color = Config.colors.dark_grey
local start_angle = -math.pi * 0.5
local progress_angle = progress * 2 * math.pi
local r_outer_sq = r_outer * r_outer
local r_inner_sq = r_inner * r_inner
for dy = -r_outer, r_outer do
for dx = -r_outer, r_outer do
local dist_sq = dx * dx + dy * dy
if dist_sq <= r_outer_sq and dist_sq > r_inner_sq then
local angle = math.atan(dy, dx)
local relative = angle - start_angle
if relative < 0 then relative = relative + 2 * math.pi end
if relative <= progress_angle then
pix(cx + dx, cy + dy, fg_color)
else
pix(cx + dx, cy + dy, bg_color)
end
end
end
end
local hand_angle = start_angle + progress_angle
local hand_x = math.floor(cx + math.cos(hand_angle) * (r_inner - 1) + 0.5)
local hand_y = math.floor(cy + math.sin(hand_angle) * (r_inner - 1) + 0.5)
line(cx, cy, hand_x, hand_y, Config.colors.white)
end
--- Draws meters. --- Draws meters.
--- @within UI --- @within UI
function UI.draw_meters() function UI.draw_meters()