fix: added minigames description to Gemini.md, fixed song end note, fixed custom songs not loading, added another test song
This commit is contained in:
103
GEMINI.md
103
GEMINI.md
@@ -52,3 +52,106 @@ Based on the analysis of `impostor.lua`, the following regularities and conventi
|
|||||||
## Agent Directives
|
## Agent Directives
|
||||||
|
|
||||||
- **Git Operations:** In the future, do not perform `git add` or `git commit` operations. This responsibility will be handled by the user.
|
- **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
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Songs = {
|
|||||||
name = "Test Song",
|
name = "Test Song",
|
||||||
bpm = 120, -- Beats per minute (for reference)
|
bpm = 120, -- Beats per minute (for reference)
|
||||||
fps = 60, -- Frames per second (TIC-80 default)
|
fps = 60, -- Frames per second (TIC-80 default)
|
||||||
|
end_frame = 570, -- Frame when song ends (last note)
|
||||||
|
|
||||||
-- Arrow spawn pattern
|
-- Arrow spawn pattern
|
||||||
-- Each entry defines when (in frames) and which direction arrow spawns
|
-- Each entry defines when (in frames) and which direction arrow spawns
|
||||||
@@ -54,12 +55,64 @@ Songs = {
|
|||||||
{frame = 570, dir = "right"}
|
{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
|
||||||
|
-- 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"}
|
||||||
|
}
|
||||||
|
},
|
||||||
-- Random mode (no predefined pattern, spawns randomly)
|
-- Random mode (no predefined pattern, spawns randomly)
|
||||||
random = {
|
random = {
|
||||||
name = "Random Mode",
|
name = "Random Mode",
|
||||||
bpm = 0, -- Not applicable for random mode
|
bpm = 0, -- Not applicable for random mode
|
||||||
fps = 60,
|
fps = 60,
|
||||||
|
end_frame = nil, -- No end frame for random mode
|
||||||
pattern = {} -- Empty, will spawn randomly in game
|
pattern = {} -- Empty, will spawn randomly in game
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ local function get_initial_data()
|
|||||||
text = "Test your reflexes! Hit the arrows in time with the music. Choose your difficulty:",
|
text = "Test your reflexes! Hit the arrows in time with the music. Choose your difficulty:",
|
||||||
options = {
|
options = {
|
||||||
{label = "Test Song", next_node = "test"},
|
{label = "Test Song", next_node = "test"},
|
||||||
|
{label = "Test Song 2", next_node = "test_2"},
|
||||||
{label = "Random Mode", next_node = "random"},
|
{label = "Random Mode", next_node = "random"},
|
||||||
{label = "Not now.", next_node = "dialog_end"}
|
{label = "Not now.", next_node = "dialog_end"}
|
||||||
}
|
}
|
||||||
@@ -319,6 +320,12 @@ local function get_initial_data()
|
|||||||
{label = "Start!", next_node = "__MINIGAME_DDR:test_song__"}
|
{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 = {
|
random = {
|
||||||
text = "Random arrows! No pattern, just react!",
|
text = "Random arrows! No pattern, just react!",
|
||||||
options = {
|
options = {
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ end
|
|||||||
function MinigameDDRWindow.update()
|
function MinigameDDRWindow.update()
|
||||||
local mg = Context.minigame_ddr
|
local mg = Context.minigame_ddr
|
||||||
|
|
||||||
-- Check for completion
|
-- Check for completion (bar filled to 100%)
|
||||||
if mg.bar_fill >= mg.max_fill then
|
if mg.bar_fill >= mg.max_fill then
|
||||||
Context.active_window = mg.return_window
|
Context.active_window = mg.return_window
|
||||||
return
|
return
|
||||||
@@ -159,6 +159,16 @@ function MinigameDDRWindow.update()
|
|||||||
-- Increment frame counter
|
-- Increment frame counter
|
||||||
mg.frame_counter = mg.frame_counter + 1
|
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)
|
-- Spawn arrows based on mode (pattern or random)
|
||||||
if mg.use_pattern and mg.current_song and mg.current_song.pattern then
|
if mg.use_pattern and mg.current_song and mg.current_song.pattern then
|
||||||
-- Pattern-based spawning (synced to song)
|
-- Pattern-based spawning (synced to song)
|
||||||
@@ -177,13 +187,6 @@ function MinigameDDRWindow.update()
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
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
|
else
|
||||||
-- Random spawning mode (original behavior)
|
-- Random spawning mode (original behavior)
|
||||||
mg.arrow_spawn_timer = mg.arrow_spawn_timer + 1
|
mg.arrow_spawn_timer = mg.arrow_spawn_timer + 1
|
||||||
|
|||||||
@@ -13,16 +13,18 @@ function PopupWindow.set_dialog_node(node_key)
|
|||||||
|
|
||||||
-- Special handling for DDR minigame trigger
|
-- Special handling for DDR minigame trigger
|
||||||
-- Format: __MINIGAME_DDR__ or __MINIGAME_DDR:song_key__
|
-- 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
|
if node_key == "__MINIGAME_DDR__" then
|
||||||
MinigameDDRWindow.start(WINDOW_GAME, nil)
|
MinigameDDRWindow.start(WINDOW_GAME, nil)
|
||||||
return
|
return
|
||||||
end
|
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 npc = Context.dialog.active_entity
|
||||||
local node = npc.dialog[node_key]
|
local node = npc.dialog[node_key]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user