From 03b6567c08cf184b41ada83b5278e7cbfe1e4629 Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Tue, 3 Mar 2026 19:51:22 +0100 Subject: [PATCH] Timer and Day modules --- .luacheckrc | 2 + impostor.inc | 2 + inc/audio/audio.manager.lua | 1 - inc/init/init.context.lua | 4 +- inc/init/init.meter.lua | 36 +----------- inc/init/init.module.lua | 2 + inc/screen/screen.toilet.lua | 5 +- inc/system/system.day.lua | 11 ++++ inc/system/system.main.lua | 9 +-- inc/system/system.timer.lua | 107 +++++++++++++++++++++++++++++++++++ inc/system/system.ui.lua | 54 ------------------ 11 files changed, 135 insertions(+), 98 deletions(-) create mode 100644 inc/system/system.day.lua create mode 100644 inc/system/system.timer.lua diff --git a/.luacheckrc b/.luacheckrc index 73a56b4..8142729 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -3,6 +3,8 @@ globals = { "Focus", + "Day", + "Timer", "Util", "Decision", "Situation", diff --git a/impostor.inc b/impostor.inc index ab69e62..8f53b9b 100644 --- a/impostor.inc +++ b/impostor.inc @@ -8,6 +8,8 @@ system/system.util.lua system/system.print.lua system/system.input.lua system/system.focus.lua +system/system.day.lua +system/system.timer.lua system/system.ui.lua audio/audio.manager.lua audio/audio.songs.lua diff --git a/inc/audio/audio.manager.lua b/inc/audio/audio.manager.lua index 583ffd2..2e9f0eb 100644 --- a/inc/audio/audio.manager.lua +++ b/inc/audio/audio.manager.lua @@ -19,7 +19,6 @@ function Audio.music_play_room_street_1() end --- @within Audio function Audio.music_play_room_street_2() end --- Plays room music. --- TODO: function name is incomplete, determine the correct room identifier --- @within Audio function Audio.music_play_room_() end --- Plays room work music. diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index 62cf5bb..2710a95 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -34,10 +34,12 @@ function Context.initial_data() minigame_button_mash = Minigame.get_default_button_mash(), minigame_rhythm = Minigame.get_default_rhythm(), meters = Meter.get_initial(), + timer = Timer.get_initial(), game = { current_screen = "home", current_situation = nil, - } + }, + day_count = 1, } end diff --git a/inc/init/init.meter.lua b/inc/init/init.meter.lua index 31d1ef1..1548383 100644 --- a/inc/init/init.meter.lua +++ b/inc/init/init.meter.lua @@ -6,30 +6,12 @@ local COMBO_BASE_BONUS = 0.02 local COMBO_MAX_BONUS = 0.16 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. Meter.COLOR_ISM = Config.colors.red Meter.COLOR_WPM = Config.colors.blue Meter.COLOR_BM = Config.colors.black 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. --- @within Meter --- @return result table Initial meter values.
@@ -39,8 +21,7 @@ end --- * bm (number) Initial BM meter value.
--- * combo (number) Current combo count.
--- * combo_timer (number) Frames since last combo action.
---- * hidden (boolean) Whether meters are hidden.
---- * timer_progress (number) Clock timer revolution progress (0 to 1). +--- * hidden (boolean) Whether meters are hidden. function Meter.get_initial() return { ism = METER_DEFAULT, @@ -49,7 +30,6 @@ function Meter.get_initial() combo = 0, combo_timer = 0, hidden = false, - timer_progress = 0, } end @@ -96,13 +76,6 @@ function Meter.update() m.combo_timer = 0 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 @@ -118,13 +91,6 @@ function Meter.add(key, amount) 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. --- @within Meter function Meter.on_minigame_complete() diff --git a/inc/init/init.module.lua b/inc/init/init.module.lua index 700caa5..d99472a 100644 --- a/inc/init/init.module.lua +++ b/inc/init/init.module.lua @@ -12,3 +12,5 @@ Input = {} Sprite = {} Audio = {} Focus = {} +Day = {} +Timer = {} \ No newline at end of file diff --git a/inc/screen/screen.toilet.lua b/inc/screen/screen.toilet.lua index dae74c3..4c9b288 100644 --- a/inc/screen/screen.toilet.lua +++ b/inc/screen/screen.toilet.lua @@ -30,8 +30,7 @@ Screen.register({ local bar_x = math.floor((sw - bar_w) / 2) local bar_h = 4 - -- TODO: Add day counter - Print.text_center("day 1", cx, 10, Config.colors.white) + Print.text_center("day " .. Context.day_count, cx, 10, Config.colors.white) local narrative = "reflecting on my past and present\n...\nboth eventually flushed." local wrapped = UI.word_wrap(narrative, 38) @@ -43,7 +42,7 @@ Screen.register({ local m = Context.meters 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 combo_mult = Meter.get_combo_multiplier() local combo_pct = math.floor((combo_mult - 1) * 100) diff --git a/inc/system/system.day.lua b/inc/system/system.day.lua new file mode 100644 index 0000000..8b03d01 --- /dev/null +++ b/inc/system/system.day.lua @@ -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 diff --git a/inc/system/system.main.lua b/inc/system/system.main.lua index bed938a..bc9f315 100644 --- a/inc/system/system.main.lua +++ b/inc/system/system.main.lua @@ -19,11 +19,12 @@ function TIC() cls(Config.colors.black) local handler = Window.get_current_handler() -- Get handler from Window manager if handler then - handler() + handler() end Meter.update() + Timer.update() if Context.game_in_progress then - UI.draw_meters() - UI.draw_timer() + UI.draw_meters() + Timer.draw() + end end -end diff --git a/inc/system/system.timer.lua b/inc/system/system.timer.lua new file mode 100644 index 0000000..005a607 --- /dev/null +++ b/inc/system/system.timer.lua @@ -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.
+--- Fields:
+--- * 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 diff --git a/inc/system/system.ui.lua b/inc/system/system.ui.lua index 31a76ed..7f355c2 100644 --- a/inc/system/system.ui.lua +++ b/inc/system/system.ui.lua @@ -155,60 +155,6 @@ function UI.draw_decision_selector(decisions, selected_decision_index) 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. --- @within UI function UI.draw_meters()