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()