From f553b09004b3b83271e865bfaea67c2034f3440e Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Fri, 13 Feb 2026 15:25:01 +0100 Subject: [PATCH] fix: added minigames description to Gemini.md, fixed song end note, fixed custom songs not loading, added another test song --- GEMINI.md | 105 ++++++++++++++++++++++++++++- inc/data/data.songs.lua | 55 ++++++++++++++- inc/init/init.context.lua | 7 ++ inc/window/window.minigame.ddr.lua | 19 +++--- inc/window/window.popup.lua | 14 ++-- 5 files changed, 184 insertions(+), 16 deletions(-) diff --git a/GEMINI.md b/GEMINI.md index 4b70aa1..7687e0c 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -51,4 +51,107 @@ Based on the analysis of `impostor.lua`, the following regularities and conventi ## Agent Directives -- **Git Operations:** In the future, do not perform `git add` or `git commit` operations. This responsibility will be handled by the user. \ No newline at end of file +- **Git Operations:** In the future, do not perform `git add` or `git commit` operations. This responsibility will be handled by the user. + +--- + +# Impostor Minigames Documentation + +This document provides comprehensive documentation for all three minigames implemented in the Impostor game: Button Mash, Rhythm, and DDR (Dance Dance Revolution). + +## Table of Contents + +- [Overview](#overview) +- [Button Mash Minigame](#button-mash-minigame) +- [Rhythm Minigame](#rhythm-minigame) +- [DDR Minigame](#ddr-minigame) +- [Integration Guide](#integration-guide) +- [Common Features](#common-features) + +--- + +## Overview + +The Impostor game includes three interactive minigames that can be triggered during gameplay. Each minigame presents a unique challenge that the player must complete to progress. All minigames feature: + +- **Overlay rendering** - Minigames render over the current game window +- **Progress tracking** - Visual indicators show completion status +- **Return mechanism** - Automatic return to the calling window upon completion +- **Visual feedback** - Button presses and hits are visually indicated + +--- + +## Button Mash Minigame + +**File**: `inc/window/window.minigame.mash.lua` + +### Description + +A fast-paced minigame where the player must repeatedly press the Z button to fill a progress bar. The bar automatically degrades over time, and the degradation rate increases as the bar fills up, creating increasing difficulty. + +### Gameplay Mechanics + +- **Objective**: Fill the progress bar to 100% by pressing Z repeatedly +- **Challenge**: The bar degrades automatically, with degradation increasing as it fills +- **Win Condition**: Bar reaches 100% +- **No Fail State**: Player can keep trying until successful + +### Visual Elements + +#### Simple sketch +![button_mash.png](/button_mash.png) + +``` +Progress Bar (200x12px at 20,10): +┌────────────────────────────────────┐ +│████████████████░░░░░░░░░░░░░░ 45%│ +└────────────────────────────────────┘ + +Button Indicator (Bottom): +╔═══╗ +║ Z ║ ← Press repeatedly! +╚═══╝ + +Text: "MASH Z!" +``` + +#### Configuration Parameters + +```lua +bar_fill = 0 -- Current fill level (0-100) +max_fill = 100 -- Target fill level +fill_per_press = 8 -- Points gained per button press +base_degradation = 0.15 -- Base degradation per frame +degradation_multiplier = 0.006 -- Increases degradation with bar fill +button_press_duration = 8 -- Visual feedback duration (frames) +``` + +#### Usage Example + +```lua +-- Start button mash minigame +MinigameButtonMashWindow.start(WINDOW_GAME) + +-- Or from any window +MinigameButtonMashWindow.start(WINDOW_POPUP) +``` + +#### Color Coding + +- **Green**: Low fill (0-33%) +- **Medium (NPC color)**: Medium fill (34-66%) +- **Yellow (Item color)**: High fill (67-100%) + +--- + +## Rhythm Minigame + +**File**: `inc/window/window.minigame.rhythm.lua` + +### Description + +A timing-based minigame where the player must press Z when a moving line crosses a green target zone. The target zone shrinks with each successful hit, making the game progressively more challenging. + +### Gameplay Mechanics + +- **Objective**: Score 10 points by timing button presses correctly diff --git a/inc/data/data.songs.lua b/inc/data/data.songs.lua index 90fcca3..d52dc68 100644 --- a/inc/data/data.songs.lua +++ b/inc/data/data.songs.lua @@ -7,6 +7,59 @@ Songs = { name = "Test Song", bpm = 120, -- Beats per minute (for reference) fps = 60, -- Frames per second (TIC-80 default) + end_frame = 570, -- Frame when song ends (last note) + + -- Arrow spawn pattern + -- Each entry defines when (in frames) and which direction arrow spawns + -- Formula: frame = (beat / bpm) * 60 * fps + -- For 120 BPM: 1 beat = 30 frames, 2 beats = 60 frames, etc. + pattern = { + -- Beat 1-4 (intro) + {frame = 30, dir = "left"}, + {frame = 60, dir = "down"}, + {frame = 90, dir = "up"}, + {frame = 120, dir = "right"}, + + -- Beat 5-8 (faster) + {frame = 135, dir = "left"}, + {frame = 150, dir = "right"}, + {frame = 165, dir = "left"}, + {frame = 180, dir = "right"}, + + -- Beat 9-12 (complex pattern) + {frame = 210, dir = "left"}, + {frame = 210, dir = "right"}, -- simultaneous + {frame = 240, dir = "up"}, + {frame = 240, dir = "down"}, -- simultaneous + {frame = 270, dir = "left"}, + {frame = 300, dir = "right"}, + + -- Beat 13-16 (rapid sequence) + {frame = 330, dir = "left"}, + {frame = 345, dir = "down"}, + {frame = 360, dir = "up"}, + {frame = 375, dir = "right"}, + {frame = 390, dir = "left"}, + {frame = 405, dir = "down"}, + {frame = 420, dir = "up"}, + {frame = 435, dir = "right"}, + + -- Beat 17-20 (finale) + {frame = 465, dir = "up"}, + {frame = 465, dir = "down"}, + {frame = 495, dir = "left"}, + {frame = 495, dir = "right"}, + {frame = 525, dir = "up"}, + {frame = 540, dir = "down"}, + {frame = 555, dir = "left"}, + {frame = 570, dir = "right"} + } + }, + test_song_2 = { + name = "Test Song 2", + bpm = 120, -- Beats per minute (for reference) + fps = 60, -- Frames per second (TIC-80 default) + end_frame = 570, -- Frame when song ends (last note) -- Arrow spawn pattern -- Each entry defines when (in frames) and which direction arrow spawns @@ -54,12 +107,12 @@ Songs = { {frame = 570, dir = "right"} } }, - -- Random mode (no predefined pattern, spawns randomly) random = { name = "Random Mode", bpm = 0, -- Not applicable for random mode fps = 60, + end_frame = nil, -- No end frame for random mode pattern = {} -- Empty, will spawn randomly in game } } diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index 5612d42..9199b60 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -309,6 +309,7 @@ local function get_initial_data() text = "Test your reflexes! Hit the arrows in time with the music. Choose your difficulty:", options = { {label = "Test Song", next_node = "test"}, + {label = "Test Song 2", next_node = "test_2"}, {label = "Random Mode", next_node = "random"}, {label = "Not now.", next_node = "dialog_end"} } @@ -319,6 +320,12 @@ local function get_initial_data() {label = "Start!", next_node = "__MINIGAME_DDR:test_song__"} } }, + test_2 = { + text = "Test song 2 selected. Show me what you got!", + options = { + {label = "Start!", next_node = "__MINIGAME_DDR:test_song_2__"} + } + }, random = { text = "Random arrows! No pattern, just react!", options = { diff --git a/inc/window/window.minigame.ddr.lua b/inc/window/window.minigame.ddr.lua index 1673ed2..5c24460 100644 --- a/inc/window/window.minigame.ddr.lua +++ b/inc/window/window.minigame.ddr.lua @@ -150,7 +150,7 @@ end function MinigameDDRWindow.update() local mg = Context.minigame_ddr - -- Check for completion + -- Check for completion (bar filled to 100%) if mg.bar_fill >= mg.max_fill then Context.active_window = mg.return_window return @@ -159,6 +159,16 @@ function MinigameDDRWindow.update() -- Increment frame counter mg.frame_counter = mg.frame_counter + 1 + -- Check if song has ended (pattern mode only) + if mg.use_pattern and mg.current_song and mg.current_song.end_frame then + -- Song has ended if we've passed the end frame AND all arrows are cleared + if mg.frame_counter > mg.current_song.end_frame and #mg.arrows == 0 then + -- Song complete! Return to previous window + Context.active_window = mg.return_window + return + end + end + -- Spawn arrows based on mode (pattern or random) if mg.use_pattern and mg.current_song and mg.current_song.pattern then -- Pattern-based spawning (synced to song) @@ -177,13 +187,6 @@ function MinigameDDRWindow.update() break end end - - -- If we've finished the pattern, check if we should loop or end - if mg.pattern_index > #pattern then - -- Pattern complete - could loop or switch to random mode - -- For now, keep playing but stop spawning new arrows - -- Optionally: mg.pattern_index = 1 to loop - end else -- Random spawning mode (original behavior) mg.arrow_spawn_timer = mg.arrow_spawn_timer + 1 diff --git a/inc/window/window.popup.lua b/inc/window/window.popup.lua index 4e1a2dc..501a45c 100644 --- a/inc/window/window.popup.lua +++ b/inc/window/window.popup.lua @@ -13,16 +13,18 @@ function PopupWindow.set_dialog_node(node_key) -- Special handling for DDR minigame trigger -- Format: __MINIGAME_DDR__ or __MINIGAME_DDR:song_key__ + local song_key = node_key:match("^__MINIGAME_DDR:(.+)__$") + if song_key then + -- Extract song key from the node (format: __MINIGAME_DDR/test_song__) + trace('Playing song: ' .. song_key) + MinigameDDRWindow.start(WINDOW_GAME, song_key) + return + end if node_key == "__MINIGAME_DDR__" then MinigameDDRWindow.start(WINDOW_GAME, nil) return end - if node_key:sub(1, 16) == "__MINIGAME_DDR:" then - -- Extract song key from the node (format: __MINIGAME_DDR:test_song__) - local song_key = node_key:sub(17, -3) -- Remove prefix "__MINIGAME_DDR:" and trailing "__" - MinigameDDRWindow.start(WINDOW_GAME, song_key) - return - end + local npc = Context.dialog.active_entity local node = npc.dialog[node_key] -- 2.49.1