feat: ring timer drawn at top-left of screen, Meter.set_timer_duration(f) controls speed, Meter.set_timer_decay(a) controls decay amount, all decay pauses during any minigame window
This commit is contained in:
@@ -13,7 +13,7 @@ function Config.initial_data()
|
||||
light_grey = 13,
|
||||
dark_grey = 14,
|
||||
red = 0,
|
||||
green = 7,
|
||||
light_blue = 7,
|
||||
blue = 9,
|
||||
white = 12,
|
||||
item = 12,
|
||||
|
||||
@@ -7,12 +7,30 @@ 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 A table of initial meter values.
|
||||
@@ -22,6 +40,7 @@ Meter.COLOR_BG = Config.colors.meter_bg
|
||||
--- @return result.combo number Current combo count.
|
||||
--- @return result.combo_timer number Frames since last combo action.
|
||||
--- @return result.hidden boolean Whether meters are hidden.
|
||||
--- @return result.timer_progress number Clock timer revolution progress (0 to 1).
|
||||
function Meter.get_initial()
|
||||
return {
|
||||
ism = METER_DEFAULT,
|
||||
@@ -30,6 +49,7 @@ function Meter.get_initial()
|
||||
combo = 0,
|
||||
combo_timer = 0,
|
||||
hidden = false,
|
||||
timer_progress = 0,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -67,14 +87,24 @@ end
|
||||
function Meter.update()
|
||||
if not Context or not Context.game_in_progress or not Context.meters then return end
|
||||
local m = Context.meters
|
||||
m.ism = math.max(0, m.ism - METER_DECAY_PER_FRAME)
|
||||
m.wpm = math.max(0, m.wpm - METER_DECAY_PER_FRAME)
|
||||
m.bm = math.max(0, m.bm - METER_DECAY_PER_FRAME)
|
||||
if m.combo > 0 then
|
||||
m.combo_timer = m.combo_timer + 1
|
||||
if m.combo_timer >= COMBO_TIMEOUT_FRAMES then
|
||||
m.combo = 0
|
||||
m.combo_timer = 0
|
||||
local in_minigame = string.find(Window.get_current_id(), "^minigame_") ~= nil
|
||||
if not in_minigame then
|
||||
m.ism = math.max(0, m.ism - METER_DECAY_PER_FRAME)
|
||||
m.wpm = math.max(0, m.wpm - METER_DECAY_PER_FRAME)
|
||||
m.bm = math.max(0, m.bm - METER_DECAY_PER_FRAME)
|
||||
if m.combo > 0 then
|
||||
m.combo_timer = m.combo_timer + 1
|
||||
if m.combo_timer >= COMBO_TIMEOUT_FRAMES then
|
||||
m.combo = 0
|
||||
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
|
||||
|
||||
@@ -23,5 +23,6 @@ function TIC()
|
||||
Meter.update()
|
||||
if Context.game_in_progress then
|
||||
UI.draw_meters()
|
||||
UI.draw_timer()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
--- @param title string The title text to display.
|
||||
function UI.draw_top_bar(title)
|
||||
rect(0, 0, Config.screen.width, 10, Config.colors.dark_grey)
|
||||
Print.text(title, 3, 2, Config.colors.green)
|
||||
Print.text(title, 3, 2, Config.colors.light_blue)
|
||||
end
|
||||
|
||||
--- Draws dialog window.
|
||||
@@ -24,9 +24,9 @@ function UI.draw_menu(items, selected_item, x, y)
|
||||
for i, item in ipairs(items) do
|
||||
local current_y = y + (i-1)*10
|
||||
if i == selected_item then
|
||||
Print.text(">", x - 8, current_y, Config.colors.green)
|
||||
Print.text(">", x - 8, current_y, Config.colors.light_blue)
|
||||
end
|
||||
Print.text(item.label, x, current_y, Config.colors.green)
|
||||
Print.text(item.label, x, current_y, Config.colors.light_blue)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -147,12 +147,67 @@ function UI.draw_decision_selector(decisions, selected_decision_index)
|
||||
local text_width = #decision_label * 4
|
||||
local text_y = bar_y + 4
|
||||
local text_x = (Config.screen.width - text_width) / 2
|
||||
Print.text("<", 2, text_y, Config.colors.green)
|
||||
Print.text("<", 2, text_y, Config.colors.light_blue)
|
||||
Print.text(decision_label, text_x, text_y, Config.colors.item)
|
||||
Print.text(">", Config.screen.width - 6, text_y, Config.colors.green)
|
||||
Print.text(">", Config.screen.width - 6, text_y, Config.colors.light_blue)
|
||||
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%).
|
||||
--- Only visible when meters are visible.
|
||||
--- @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 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.black)
|
||||
end
|
||||
|
||||
--- Draws meters.
|
||||
--- @within UI
|
||||
function UI.draw_meters()
|
||||
|
||||
@@ -30,7 +30,7 @@ function ConfigurationWindow.draw()
|
||||
local char_width = 4
|
||||
for i, control in ipairs(ConfigurationWindow.controls) do
|
||||
local current_y = y_start + (i - 1) * 12
|
||||
local color = Config.colors.green
|
||||
local color = Config.colors.light_blue
|
||||
if control.type == "numeric_stepper" then
|
||||
local value = control.get()
|
||||
local label_text = control.label
|
||||
|
||||
@@ -18,7 +18,7 @@ on than meets the eye.
|
||||
--- @within IntroWindow
|
||||
function IntroWindow.draw()
|
||||
local x = (Config.screen.width - 132) / 2
|
||||
Print.text(IntroWindow.text, x, IntroWindow.y, Config.colors.green)
|
||||
Print.text(IntroWindow.text, x, IntroWindow.y, Config.colors.light_blue)
|
||||
end
|
||||
|
||||
--- Updates the intro window logic.
|
||||
|
||||
@@ -219,7 +219,7 @@ function MinigameDDRWindow.draw()
|
||||
rectb(mg.bar_x - 2, mg.bar_y - 2, mg.bar_width + 4, mg.bar_height + 4, Config.colors.dark_grey)
|
||||
local fill_width = (mg.bar_fill / mg.max_fill) * mg.bar_width
|
||||
if fill_width > 0 then
|
||||
local bar_color = Config.colors.green
|
||||
local bar_color = Config.colors.light_blue
|
||||
if mg.bar_fill > 66 then
|
||||
bar_color = Config.colors.item
|
||||
elseif mg.bar_fill > 33 then
|
||||
@@ -232,7 +232,7 @@ function MinigameDDRWindow.draw()
|
||||
if mg.target_arrows then
|
||||
for _, target in ipairs(mg.target_arrows) do
|
||||
local is_pressed = mg.button_pressed_timers[target.dir] and mg.button_pressed_timers[target.dir] > 0
|
||||
local color = is_pressed and Config.colors.green or Config.colors.light_grey
|
||||
local color = is_pressed and Config.colors.light_blue or Config.colors.light_grey
|
||||
draw_arrow(target.x, mg.target_y, target.dir, color)
|
||||
end
|
||||
end
|
||||
@@ -252,14 +252,14 @@ function MinigameDDRWindow.draw()
|
||||
"PATTERN MODE - Frame:" .. mg.frame_counter,
|
||||
Config.screen.width / 2,
|
||||
debug_y,
|
||||
Config.colors.green
|
||||
Config.colors.light_blue
|
||||
)
|
||||
if mg.current_song and mg.current_song.pattern then
|
||||
Print.text_center(
|
||||
"Pattern Len:" .. #mg.current_song.pattern .. " Index:" .. mg.pattern_index,
|
||||
Config.screen.width / 2,
|
||||
debug_y + 10,
|
||||
Config.colors.green
|
||||
Config.colors.light_blue
|
||||
)
|
||||
end
|
||||
else
|
||||
|
||||
@@ -68,7 +68,7 @@ function MinigameButtonMashWindow.draw()
|
||||
rectb(mg.bar_x - 2, mg.bar_y - 2, mg.bar_width + 4, mg.bar_height + 4, Config.colors.dark_grey)
|
||||
local fill_width = (mg.bar_fill / mg.max_fill) * mg.bar_width
|
||||
if fill_width > 0 then
|
||||
local bar_color = Config.colors.green
|
||||
local bar_color = Config.colors.light_blue
|
||||
if mg.bar_fill > 66 then
|
||||
bar_color = Config.colors.item
|
||||
elseif mg.bar_fill > 33 then
|
||||
@@ -78,7 +78,7 @@ function MinigameButtonMashWindow.draw()
|
||||
end
|
||||
local button_color = Config.colors.light_grey
|
||||
if mg.button_pressed_timer > 0 then
|
||||
button_color = Config.colors.green
|
||||
button_color = Config.colors.light_blue
|
||||
end
|
||||
circb(mg.button_x, mg.button_y, mg.button_size, button_color)
|
||||
if mg.button_pressed_timer > 0 then
|
||||
|
||||
@@ -87,7 +87,7 @@ function MinigameRhythmWindow.draw()
|
||||
local target_left = mg.target_center - (mg.target_width / 2)
|
||||
local target_x = mg.bar_x + (target_left * mg.bar_width)
|
||||
local target_width_pixels = mg.target_width * mg.bar_width
|
||||
rect(target_x, mg.bar_y, target_width_pixels, mg.bar_height, Config.colors.green)
|
||||
rect(target_x, mg.bar_y, target_width_pixels, mg.bar_height, Config.colors.light_blue)
|
||||
local line_x = mg.bar_x + (mg.line_position * mg.bar_width)
|
||||
rect(line_x - 1, mg.bar_y, 2, mg.bar_height, Config.colors.item)
|
||||
local score_text = "SCORE: " .. mg.score .. " / " .. mg.max_score
|
||||
@@ -100,7 +100,7 @@ function MinigameRhythmWindow.draw()
|
||||
)
|
||||
local button_color = Config.colors.light_grey
|
||||
if mg.button_pressed_timer > 0 then
|
||||
button_color = Config.colors.green
|
||||
button_color = Config.colors.light_blue
|
||||
end
|
||||
circb(mg.button_x, mg.button_y, mg.button_size, button_color)
|
||||
if mg.button_pressed_timer > 0 then
|
||||
|
||||
@@ -39,7 +39,7 @@ end
|
||||
function PopupWindow.draw()
|
||||
if Context.popup.show then
|
||||
rect(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.black)
|
||||
rectb(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.green)
|
||||
rectb(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.light_blue)
|
||||
|
||||
local current_y = TEXT_MARGIN_Y
|
||||
for _, line in ipairs(Context.popup.content) do
|
||||
@@ -47,6 +47,6 @@ function PopupWindow.draw()
|
||||
current_y = current_y + LINE_HEIGHT
|
||||
end
|
||||
|
||||
Print.text("[A] Close", TEXT_MARGIN_X, POPUP_Y + POPUP_HEIGHT - LINE_HEIGHT - 2, Config.colors.green)
|
||||
Print.text("[A] Close", TEXT_MARGIN_X, POPUP_Y + POPUP_HEIGHT - LINE_HEIGHT - 2, Config.colors.light_blue)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user