Compare commits

...

4 Commits

Author SHA1 Message Date
76964f872d docs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2026-02-22 00:30:12 +01:00
3b137fd48e docs make target 2026-02-21 23:44:03 +01:00
0b25ecc793 set window objects to global
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-02-21 23:18:42 +01:00
f08e4ad1d4 Merge pull request 'sprite handling' (#10) from feature/sprite-handling into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #10
2026-02-21 22:13:31 +00:00
31 changed files with 338 additions and 40 deletions

View File

@@ -11,9 +11,20 @@ globals = {
"Print", "Print",
"Input", "Input",
"Audio", "Audio",
"Config",
"Context", "Context",
"Meters", "Meters",
"Minigames", "Minigames",
"SplashWindow",
"IntroWindow",
"MenuWindow",
"GameWindow",
"PopupWindow",
"ConfigurationWindow",
"AudioTestWindow",
"MinigameButtonMashWindow",
"MinigameRhythmWindow",
"MinigameDDRWindow",
"mset", "mset",
"mget", "mget",
"btnp", "btnp",

View File

@@ -204,7 +204,22 @@ install_precommit_hook:
@chmod +x .git/hooks/pre-commit @chmod +x .git/hooks/pre-commit
@echo "Pre-commit hook installed successfully." @echo "Pre-commit hook installed successfully."
.PHONY: all build export watch import_assets export_assets clean lint ci-version ci-export ci-upload ci-update install_precommit_hook docs: build
@echo "==> Checking for ldoc..."
@if ! command -v ldoc &> /dev/null; then \
echo "ldoc not found, attempting to install with luarocks..."; \
if command -v luarocks &> /dev/null; then \
luarocks install ldoc; \
else \
echo "Error: luarocks not found. Please install luarocks and then ldoc manually."; \
exit 1; \
fi; \
fi
@echo "==> Running ldoc..."
@ldoc ${OUTPUT} -d docs
@echo "==> Documentation generated."
.PHONY: all build export watch import_assets export_assets clean lint ci-version ci-export ci-upload ci-update install_precommit_hook docs
#-- <WAVES> #-- <WAVES>
#-- 000:224578acdeeeeddcba95434567653100 #-- 000:224578acdeeeeddcba95434567653100

View File

@@ -107,8 +107,11 @@ Songs = {
} }
} }
-- Helper function to calculate frame from beat --- Converts beats to frames.
-- Usage: frame_from_beat(beat_number, bpm, fps) -- @param beat number The beat number.
-- @param bpm number Beats per minute.
-- @param[opt] fps number Frames per second (default: 60).
-- @return number The corresponding frame number.
function frame_from_beat(beat, bpm, fps) function frame_from_beat(beat, bpm, fps)
fps = fps or 60 fps = fps or 60
local seconds_per_beat = 60 / bpm local seconds_per_beat = 60 / bpm
@@ -116,8 +119,11 @@ function frame_from_beat(beat, bpm, fps)
return math.floor(beat * frames_per_beat) return math.floor(beat * frames_per_beat)
end end
-- Helper function to convert simple beat notation to frame pattern --- Converts beat notation to frame pattern.
-- Usage: beats_to_pattern({{1, "left"}, {2, "down"}}, 120) -- @param beats table A table of beat data, e.g., {{1, "left"}, {2, "down"}}.
-- @param bpm number Beats per minute.
-- @param[opt] fps number Frames per second (default: 60).
-- @return table The generated pattern.
function beats_to_pattern(beats, bpm, fps) function beats_to_pattern(beats, bpm, fps)
fps = fps or 60 fps = fps or 60
local pattern = {} local pattern = {}

View File

@@ -1,5 +1,7 @@
local _decisions = {} local _decisions = {}
--- Registers a decision definition.
-- @param decision table The decision data table.
function Decision.register(decision) function Decision.register(decision)
if not decision or not decision.id then if not decision or not decision.id then
PopupWindow.show({"Error: Invalid decision object registered (missing id)!"}) PopupWindow.show({"Error: Invalid decision object registered (missing id)!"})
@@ -22,10 +24,15 @@ function Decision.register(decision)
_decisions[decision.id] = decision _decisions[decision.id] = decision
end end
--- Gets a decision by ID.
-- @param id string The ID of the decision.
-- @return table The decision table or nil.
function Decision.get(id) function Decision.get(id)
return _decisions[id] return _decisions[id]
end end
--- Gets all registered decisions.
-- @return table A table of all registered decisions.
function Decision.get_all() function Decision.get_all()
return _decisions return _decisions
end end

View File

@@ -22,7 +22,8 @@ local DEFAULT_CONFIG = {
} }
} }
local Config = { -- Game configuration settings.
Config = {
screen = DEFAULT_CONFIG.screen, screen = DEFAULT_CONFIG.screen,
colors = DEFAULT_CONFIG.colors, colors = DEFAULT_CONFIG.colors,
player = DEFAULT_CONFIG.player, player = DEFAULT_CONFIG.player,
@@ -34,10 +35,12 @@ local CONFIG_MAGIC_VALUE_ADDRESS = 2
local CONFIG_SPLASH_DURATION_ADDRESS = 3 local CONFIG_SPLASH_DURATION_ADDRESS = 3
local CONFIG_MAGIC_VALUE = 0xDE local CONFIG_MAGIC_VALUE = 0xDE
--- Saves the current configuration.
function Config.save() function Config.save()
mset(CONFIG_MAGIC_VALUE, CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) mset(CONFIG_MAGIC_VALUE, CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK)
end end
--- Loads saved configuration.
function Config.load() function Config.load()
if mget(CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) == CONFIG_MAGIC_VALUE then if mget(CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) == CONFIG_MAGIC_VALUE then
Config.timing.splash_duration = mget(CONFIG_SPLASH_DURATION_ADDRESS, CONFIG_SAVE_BANK) Config.timing.splash_duration = mget(CONFIG_SPLASH_DURATION_ADDRESS, CONFIG_SAVE_BANK)
@@ -46,6 +49,7 @@ function Config.load()
end end
end end
--- Restores default configuration settings.
function Config.restore_defaults() function Config.restore_defaults()
Config.timing.splash_duration = DEFAULT_CONFIG.timing.splash_duration Config.timing.splash_duration = DEFAULT_CONFIG.timing.splash_duration
end end

View File

@@ -4,6 +4,8 @@ local SAVE_GAME_MAGIC_VALUE = 0xCA
local SAVE_GAME_CURRENT_SCREEN_ADDRESS = 6 local SAVE_GAME_CURRENT_SCREEN_ADDRESS = 6
--- Gets initial data for Context.
-- @return table Initial context data.
local function get_initial_data() local function get_initial_data()
return { return {
active_window = WINDOW_SPLASH, active_window = WINDOW_SPLASH,
@@ -46,13 +48,17 @@ on than meets the eye.]]
minigame_button_mash = Minigames.get_default_button_mash(), minigame_button_mash = Minigames.get_default_button_mash(),
minigame_rhythm = Minigames.get_default_rhythm(), minigame_rhythm = Minigames.get_default_rhythm(),
meters = Meters.get_initial(), meters = Meters.get_initial(),
--- Active sprites.
sprites = {}, sprites = {},
--- Current situation ID.
current_situation = nil, current_situation = nil,
} }
end end
--- Global game context.
Context = {} Context = {}
--- Resets game context to initial state.
local function reset_context_to_initial_state() local function reset_context_to_initial_state()
local initial_data = get_initial_data() local initial_data = get_initial_data()
@@ -81,12 +87,15 @@ end
reset_context_to_initial_state() reset_context_to_initial_state()
--- Starts a new game.
function Context.new_game() function Context.new_game()
reset_context_to_initial_state() reset_context_to_initial_state()
Context.game_in_progress = true Context.game_in_progress = true
MenuWindow.refresh_menu_items() MenuWindow.refresh_menu_items()
Context.screens[Context.current_screen].init()
end end
--- Saves the current game state.
function Context.save_game() function Context.save_game()
if not Context.game_in_progress then return end if not Context.game_in_progress then return end
@@ -94,6 +103,7 @@ function Context.save_game()
mset(Context.current_screen, SAVE_GAME_CURRENT_SCREEN_ADDRESS, SAVE_GAME_BANK) mset(Context.current_screen, SAVE_GAME_CURRENT_SCREEN_ADDRESS, SAVE_GAME_BANK)
end end
--- Loads a saved game state.
function Context.load_game() function Context.load_game()
if mget(SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK) ~= SAVE_GAME_MAGIC_VALUE then if mget(SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK) ~= SAVE_GAME_MAGIC_VALUE then
Context.new_game() Context.new_game()
@@ -105,4 +115,5 @@ function Context.load_game()
Context.game_in_progress = true Context.game_in_progress = true
MenuWindow.refresh_menu_items() MenuWindow.refresh_menu_items()
Context.screens[Context.current_screen].init()
end end

View File

@@ -6,11 +6,14 @@ local COMBO_BASE_BONUS = 0.02
local COMBO_MAX_BONUS = 0.16 local COMBO_MAX_BONUS = 0.16
local COMBO_TIMEOUT_FRAMES = 600 local COMBO_TIMEOUT_FRAMES = 600
-- Internal meters for tracking game progress and player stats.
Meters.COLOR_ISM = Config.colors.red Meters.COLOR_ISM = Config.colors.red
Meters.COLOR_WPM = Config.colors.blue Meters.COLOR_WPM = Config.colors.blue
Meters.COLOR_BM = Config.colors.black Meters.COLOR_BM = Config.colors.black
Meters.COLOR_BG = Config.colors.meter_bg Meters.COLOR_BG = Config.colors.meter_bg
--- Gets initial meter values.
-- @return table A table of initial meter values.
function Meters.get_initial() function Meters.get_initial()
return { return {
ism = METER_DEFAULT, ism = METER_DEFAULT,
@@ -22,18 +25,24 @@ function Meters.get_initial()
} }
end end
--- Hides meters.
function Meters.hide() function Meters.hide()
if Context and Context.meters then Context.meters.hidden = true end if Context and Context.meters then Context.meters.hidden = true end
end end
--- Shows meters.
function Meters.show() function Meters.show()
if Context and Context.meters then Context.meters.hidden = false end if Context and Context.meters then Context.meters.hidden = false end
end end
--- Gets max meter value.
-- @return number The maximum meter value.
function Meters.get_max() function Meters.get_max()
return METER_MAX return METER_MAX
end end
--- Gets combo multiplier.
-- @return number The current combo multiplier.
function Meters.get_combo_multiplier() function Meters.get_combo_multiplier()
if not Context or not Context.meters then return 1 end if not Context or not Context.meters then return 1 end
local combo = Context.meters.combo local combo = Context.meters.combo
@@ -41,6 +50,7 @@ function Meters.get_combo_multiplier()
return 1 + math.min(COMBO_MAX_BONUS, COMBO_BASE_BONUS * (2 ^ (combo - 1))) return 1 + math.min(COMBO_MAX_BONUS, COMBO_BASE_BONUS * (2 ^ (combo - 1)))
end end
--- Updates all meters.
function Meters.update() function Meters.update()
if not Context or not Context.game_in_progress or not Context.meters then return end if not Context or not Context.game_in_progress or not Context.meters then return end
local m = Context.meters local m = Context.meters
@@ -56,6 +66,9 @@ function Meters.update()
end end
end end
--- Adds amount to a meter.
-- @param key string The meter key (e.g., "wpm", "ism", "bm").
-- @param amount number The amount to add.
function Meters.add(key, amount) function Meters.add(key, amount)
if not Context or not Context.meters then return end if not Context or not Context.meters then return end
local m = Context.meters local m = Context.meters
@@ -64,6 +77,7 @@ function Meters.add(key, amount)
end end
end end
--- Called on minigame completion.
function Meters.on_minigame_complete() function Meters.on_minigame_complete()
local m = Context.meters local m = Context.meters
local gain = math.floor(METER_GAIN_PER_CHORE * Meters.get_combo_multiplier()) local gain = math.floor(METER_GAIN_PER_CHORE * Meters.get_combo_multiplier())

View File

@@ -1,5 +1,10 @@
-- Manages minigame configurations and initial states.
Minigames = {} Minigames = {}
--- Applies parameters to defaults.
-- @param defaults table The default configuration table.
-- @param params table The parameters to apply.
-- @return table The updated configuration table.
local function apply_params(defaults, params) local function apply_params(defaults, params)
if not params then return defaults end if not params then return defaults end
for k, v in pairs(params) do for k, v in pairs(params) do
@@ -8,6 +13,8 @@ local function apply_params(defaults, params)
return defaults return defaults
end end
--- Gets default DDR minigame configuration.
-- @return table The default DDR minigame configuration.
function Minigames.get_default_ddr() function Minigames.get_default_ddr()
local arrow_size = 12 local arrow_size = 12
local arrow_spacing = 30 local arrow_spacing = 30
@@ -47,6 +54,8 @@ function Minigames.get_default_ddr()
} }
end end
--- Gets default button mash minigame configuration.
-- @return table The default button mash minigame configuration.
function Minigames.get_default_button_mash() function Minigames.get_default_button_mash()
return { return {
bar_fill = 0, bar_fill = 0,
@@ -67,6 +76,8 @@ function Minigames.get_default_button_mash()
} }
end end
--- Gets default rhythm minigame configuration.
-- @return table The default rhythm minigame configuration.
function Minigames.get_default_rhythm() function Minigames.get_default_rhythm()
return { return {
line_position = 0, line_position = 0,
@@ -94,14 +105,23 @@ function Minigames.get_default_rhythm()
} }
end end
--- Configures DDR minigame.
-- @param params table Optional parameters to override defaults.
-- @return table The configured DDR minigame state.
function Minigames.configure_ddr(params) function Minigames.configure_ddr(params)
return apply_params(Minigames.get_default_ddr(), params) return apply_params(Minigames.get_default_ddr(), params)
end end
--- Configures button mash minigame.
-- @param params table Optional parameters to override defaults.
-- @return table The configured button mash minigame state.
function Minigames.configure_button_mash(params) function Minigames.configure_button_mash(params)
return apply_params(Minigames.get_default_button_mash(), params) return apply_params(Minigames.get_default_button_mash(), params)
end end
--- Configures rhythm minigame.
-- @param params table Optional parameters to override defaults.
-- @return table The configured rhythm minigame state.
function Minigames.configure_rhythm(params) function Minigames.configure_rhythm(params)
return apply_params(Minigames.get_default_rhythm(), params) return apply_params(Minigames.get_default_rhythm(), params)
end end

View File

@@ -1,13 +1,13 @@
local SplashWindow = {} SplashWindow = {}
local IntroWindow = {} IntroWindow = {}
local MenuWindow = {} MenuWindow = {}
local GameWindow = {} GameWindow = {}
local PopupWindow = {} PopupWindow = {}
local ConfigurationWindow = {} ConfigurationWindow = {}
local AudioTestWindow = {} AudioTestWindow = {}
local MinigameButtonMashWindow = {} MinigameButtonMashWindow = {}
local MinigameRhythmWindow = {} MinigameRhythmWindow = {}
local MinigameDDRWindow = {} MinigameDDRWindow = {}
Util = {} Util = {}
Meters = {} Meters = {}
Minigames = {} Minigames = {}

View File

@@ -1,5 +1,9 @@
-- Manages game maps.
local _maps = {} local _maps = {}
--- Gets all registered maps as an array.
-- @return table An array of registered map data.
function Map.get_maps_array() function Map.get_maps_array()
local maps_array = {} local maps_array = {}
for _, map_data in pairs(_maps) do for _, map_data in pairs(_maps) do
@@ -8,6 +12,8 @@ function Map.get_maps_array()
return maps_array return maps_array
end end
--- Registers a map definition.
-- @param map_data table The map data table.
function Map.register(map_data) function Map.register(map_data)
if _maps[map_data.id] then if _maps[map_data.id] then
trace("Warning: Overwriting map with id: " .. map_data.id) trace("Warning: Overwriting map with id: " .. map_data.id)
@@ -15,10 +21,15 @@ function Map.register(map_data)
_maps[map_data.id] = map_data _maps[map_data.id] = map_data
end end
--- Gets a map by ID.
-- @param map_id string The ID of the map.
-- @return table The map data table or nil.
function Map.get_by_id(map_id) function Map.get_by_id(map_id)
return _maps[map_id] return _maps[map_id]
end end
--- Draws a map.
-- @param map_id string The ID of the map to draw.
function Map.draw(map_id) function Map.draw(map_id)
local map_data = Map.get_by_id(map_id) local map_data = Map.get_by_id(map_id)
if not map_data then if not map_data then

View File

@@ -1,5 +1,7 @@
local _screens = {} local _screens = {}
--- Registers a screen definition.
-- @param screen_data table The screen data table.
function Screen.register(screen_data) function Screen.register(screen_data)
if _screens[screen_data.id] then if _screens[screen_data.id] then
trace("Warning: Overwriting screen with id: " .. screen_data.id) trace("Warning: Overwriting screen with id: " .. screen_data.id)
@@ -7,9 +9,18 @@ function Screen.register(screen_data)
if not screen_data.situations then if not screen_data.situations then
screen_data.situations = {} screen_data.situations = {}
end end
if not screen_data.init then
screen_data.init = function() end
end
if not screen_data.update then
screen_data.update = function() end
end
_screens[screen_data.id] = screen_data _screens[screen_data.id] = screen_data
end end
--- Gets a screen by ID.
-- @param screen_id string The ID of the screen.
-- @return table The screen table or nil.
function Screen.get_by_id(screen_id) function Screen.get_by_id(screen_id)
return _screens[screen_id] return _screens[screen_id]
end end

View File

@@ -1,5 +1,7 @@
local _situations = {} local _situations = {}
--- Registers a situation definition.
-- @param situation table The situation data table.
function Situation.register(situation) function Situation.register(situation)
if not situation or not situation.id then if not situation or not situation.id then
PopupWindow.show({"Error: Invalid situation object registered (missing id)!"}) PopupWindow.show({"Error: Invalid situation object registered (missing id)!"})
@@ -8,16 +10,24 @@ function Situation.register(situation)
if not situation.handle then if not situation.handle then
situation.handle = function() end situation.handle = function() end
end end
if not situation.update then
situation.update = function() end
end
if _situations[situation.id] then if _situations[situation.id] then
trace("Warning: Overwriting situation with id: " .. situation.id) trace("Warning: Overwriting situation with id: " .. situation.id)
end end
_situations[situation.id] = situation _situations[situation.id] = situation
end end
--- Gets a situation by ID.
-- @param id string The situation ID.
-- @return table The situation table or nil.
function Situation.get(id) function Situation.get(id)
return _situations[id] return _situations[id]
end end
--- Applies a situation.
-- @param id string The situation ID to apply.
function Situation.apply(id) function Situation.apply(id)
local situation = Situation.get(id) local situation = Situation.get(id)
if not situation then if not situation then
@@ -34,4 +44,3 @@ function Situation.apply(id)
Context.current_situation = id Context.current_situation = id
situation.handle() situation.handle()
end end

View File

@@ -1,5 +1,7 @@
local _sprites = {} local _sprites = {}
--- Registers a sprite definition.
-- @param sprite_data table A table containing the sprite definition.
function Sprite.register(sprite_data) function Sprite.register(sprite_data)
if not sprite_data or not sprite_data.id then if not sprite_data or not sprite_data.id then
trace("Error: Invalid sprite object registered (missing id)!") trace("Error: Invalid sprite object registered (missing id)!")
@@ -11,8 +13,16 @@ function Sprite.register(sprite_data)
_sprites[sprite_data.id] = sprite_data _sprites[sprite_data.id] = sprite_data
end end
--- Schedules a sprite for drawing.
-- @param id string The unique identifier of the sprite.
-- @param x number The x-coordinate.
-- @param y number The y-coordinate.
-- @param[opt] colorkey number The color index for transparency.
-- @param[opt] scale number The scaling factor.
-- @param[opt] flip_x number Set to 1 to flip horizontally.
-- @param[opt] flip_y number Set to 1 to flip vertically.
-- @param[opt] rot number The rotation in degrees.
function Sprite.show(id, x, y, colorkey, scale, flip_x, flip_y, rot) function Sprite.show(id, x, y, colorkey, scale, flip_x, flip_y, rot)
-- Ensure the sprite exists before attempting to show it
if not _sprites[id] then if not _sprites[id] then
trace("Error: Attempted to show non-registered sprite with id: " .. id) trace("Error: Attempted to show non-registered sprite with id: " .. id)
return return
@@ -30,28 +40,28 @@ function Sprite.show(id, x, y, colorkey, scale, flip_x, flip_y, rot)
} }
end end
--- Hides a displayed sprite.
-- @param id string The unique identifier of the sprite.
function Sprite.hide(id) function Sprite.hide(id)
Context.sprites[id] = nil Context.sprites[id] = nil
end end
--- Draws all scheduled sprites.
function Sprite.draw() function Sprite.draw()
for id, params in pairs(Context.sprites) do for id, params in pairs(Context.sprites) do
local sprite_data = _sprites[id] local sprite_data = _sprites[id]
if not sprite_data then if not sprite_data then
trace("Error: Sprite id " .. id .. " in Context.sprites is not registered.") trace("Error: Sprite id " .. id .. " in Context.sprites is not registered.")
Context.sprites[id] = nil -- Clean up invalid entry Context.sprites[id] = nil
-- We should probably continue to the next sprite instead of returning
-- so that other valid sprites can still be drawn.
end end
-- Use parameters from Context.sprites, or fall back to sprite_data, then to defaults
local colorkey = params.colorkey or sprite_data.colorkey or 0 local colorkey = params.colorkey or sprite_data.colorkey or 0
local scale = params.scale or sprite_data.scale or 1 local scale = params.scale or sprite_data.scale or 1
local flip_x = params.flip_x or sprite_data.flip_x or 0 local flip_x = params.flip_x or sprite_data.flip_x or 0
local flip_y = params.flip_y or sprite_data.flip_y or 0 local flip_y = params.flip_y or sprite_data.flip_y or 0
local rot = params.rot or sprite_data.rot or 0 local rot = params.rot or sprite_data.rot or 0
if sprite_data.sprites then -- Complex sprite if sprite_data.sprites then
for i = 1, #sprite_data.sprites do for i = 1, #sprite_data.sprites do
local sub_sprite = sprite_data.sprites[i] local sub_sprite = sprite_data.sprites[i]
spr( spr(
@@ -65,7 +75,7 @@ function Sprite.draw()
sub_sprite.rot or rot sub_sprite.rot or rot
) )
end end
else -- Simple sprite else
spr(sprite_data.s, params.x, params.y, colorkey, scale, flip_x, flip_y, rot) spr(sprite_data.s, params.x, params.y, colorkey, scale, flip_x, flip_y, rot)
end end
end end

View File

@@ -1,17 +1,17 @@
Sprite.register({ Sprite.register({
id = "norman", id = "norman",
sprites = { sprites = {
-- Body -- Body (sprite index 0)
{ s = 0, x_offset = 0, y_offset = 0 }, { s = 0, x_offset = 0, y_offset = 0 },
-- Head -- Head (sprite index 1)
{ s = 1, x_offset = 0, y_offset = -8 }, { s = 1, x_offset = 0, y_offset = -8 },
-- Left Arm -- Left Arm (sprite index 2)
{ s = 2, x_offset = -4, y_offset = 4 }, { s = 2, x_offset = -4, y_offset = 4 },
-- Right Arm -- Right Arm (sprite index 3, flipped)
{ s = 3, x_offset = 4, y_offset = 4, flip_x = 1 }, -- Flipped arm { s = 3, x_offset = 4, y_offset = 4, flip_x = 1 }, -- Flipped arm
-- Left Leg -- Left Leg (sprite index 4)
{ s = 4, x_offset = -2, y_offset = 8 }, { s = 4, x_offset = -2, y_offset = 8 },
-- Right Leg -- Right Leg (sprite index 5, flipped)
{ s = 5, x_offset = 2, y_offset = 8, flip_x = 1 } -- Flipped leg { s = 5, x_offset = 2, y_offset = 8, flip_x = 1 } -- Flipped leg
} }
}) })

View File

@@ -1,14 +1,27 @@
--- Stops current music.
function Audio.music_stop() music() end function Audio.music_stop() music() end
--- Plays main menu music.
function Audio.music_play_mainmenu() end function Audio.music_play_mainmenu() end
--- Plays waking up music.
function Audio.music_play_wakingup() end function Audio.music_play_wakingup() end
--- Plays room morning music.
function Audio.music_play_room_morning() end function Audio.music_play_room_morning() end
--- Plays room street 1 music.
function Audio.music_play_room_street_1() end function Audio.music_play_room_street_1() end
--- Plays room street 2 music.
function Audio.music_play_room_street_2() end function Audio.music_play_room_street_2() end
--- Plays room music.
function Audio.music_play_room_() end function Audio.music_play_room_() end
--- Plays room work music.
function Audio.music_play_room_work() end function Audio.music_play_room_work() end
--- Plays select sound effect.
function Audio.sfx_select() sfx(17, 'C-7', 30) end function Audio.sfx_select() sfx(17, 'C-7', 30) end
--- Plays deselect sound effect.
function Audio.sfx_deselect() sfx(18, 'C-7', 30) end function Audio.sfx_deselect() sfx(18, 'C-7', 30) end
--- Plays beep sound effect.
function Audio.sfx_beep() sfx(19, 'C-6', 30) end function Audio.sfx_beep() sfx(19, 'C-6', 30) end
--- Plays success sound effect.
function Audio.sfx_success() sfx(16, 'C-7', 60) end function Audio.sfx_success() sfx(16, 'C-7', 60) end
--- Plays bloop sound effect.
function Audio.sfx_bloop() sfx(21, 'C-3', 60) end function Audio.sfx_bloop() sfx(21, 'C-3', 60) end

View File

@@ -7,11 +7,21 @@ local INPUT_KEY_SPACE = 48
local INPUT_KEY_BACKSPACE = 51 local INPUT_KEY_BACKSPACE = 51
local INPUT_KEY_ENTER = 50 local INPUT_KEY_ENTER = 50
--- Checks if Up is pressed.
function Input.up() return btnp(INPUT_KEY_UP) end function Input.up() return btnp(INPUT_KEY_UP) end
--- Checks if Down is pressed.
function Input.down() return btnp(INPUT_KEY_DOWN) end function Input.down() return btnp(INPUT_KEY_DOWN) end
--- Checks if Left is pressed.
function Input.left() return btnp(INPUT_KEY_LEFT) end function Input.left() return btnp(INPUT_KEY_LEFT) end
--- Checks if Right is pressed.
function Input.right() return btnp(INPUT_KEY_RIGHT) end function Input.right() return btnp(INPUT_KEY_RIGHT) end
--- Checks if Select is pressed.
function Input.select() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_SPACE) end function Input.select() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_SPACE) end
--- Checks if Menu Confirm is pressed.
function Input.menu_confirm() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_ENTER) end function Input.menu_confirm() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_ENTER) end
function Input.player_interact() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_ENTER) end function Input.menu_back() return btnp(INPUT_KEY_Y) or keyp(INPUT_KEY_BACKSPACE) end --- Checks if Player Interact is pressed.
function Input.player_interact() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_ENTER) end
--- Checks if Menu Back is pressed.
function Input.menu_back() return btnp(INPUT_KEY_Y) or keyp(INPUT_KEY_BACKSPACE) end
--- Checks if Toggle Popup is pressed.
function Input.toggle_popup() return keyp(INPUT_KEY_ENTER) end function Input.toggle_popup() return keyp(INPUT_KEY_ENTER) end

View File

@@ -44,6 +44,7 @@ local STATE_HANDLERS = {
local initialized_game = false local initialized_game = false
--- Initializes game state.
local function init_game() local function init_game()
if initialized_game then return end if initialized_game then return end
@@ -51,6 +52,7 @@ local function init_game()
initialized_game = true initialized_game = true
end end
--- Main game loop (TIC-80 callback).
function TIC() function TIC()
init_game() init_game()
cls(Config.colors.black) cls(Config.colors.black)

View File

@@ -1,4 +1,10 @@
--- Prints text with shadow.
-- @param text string The text to print.
-- @param x number The x-coordinate.
-- @param y number The y-coordinate.
-- @param color number The color of the text.
-- @param[opt] fixed boolean If true, uses fixed-width font.
-- @param[opt] scale number The scaling factor.
function Print.text(text, x, y, color, fixed, scale) function Print.text(text, x, y, color, fixed, scale)
local shadow_color = Config.colors.black local shadow_color = Config.colors.black
if color == shadow_color then shadow_color = Config.colors.light_grey end if color == shadow_color then shadow_color = Config.colors.light_grey end
@@ -7,6 +13,13 @@ function Print.text(text, x, y, color, fixed, scale)
print(text, x, y, color, fixed, scale) print(text, x, y, color, fixed, scale)
end end
--- Prints centered text with shadow.
-- @param text string The text to print.
-- @param x number The x-coordinate for centering.
-- @param y number The y-coordinate.
-- @param color number The color of the text.
-- @param[opt] fixed boolean If true, uses fixed-width font.
-- @param[opt] scale number The scaling factor.
function Print.text_center(text, x, y, color, fixed, scale) function Print.text_center(text, x, y, color, fixed, scale)
scale = scale or 1 scale = scale or 1
local text_width = print(text, 0, -6, 0, fixed, scale) local text_width = print(text, 0, -6, 0, fixed, scale)

View File

@@ -1,12 +1,20 @@
--- Draws the top bar.
-- @param title string The title text to display.
function UI.draw_top_bar(title) function UI.draw_top_bar(title)
rect(0, 0, Config.screen.width, 10, Config.colors.dark_grey) 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.green)
end end
--- Draws dialog window.
function UI.draw_dialog() function UI.draw_dialog()
PopupWindow.draw() PopupWindow.draw()
end end
--- Draws a menu.
-- @param items table A table of menu items.
-- @param selected_item number The index of the currently selected item.
-- @param x number The x-coordinate for the menu.
-- @param y number The y-coordinate for the menu.
function UI.draw_menu(items, selected_item, x, y) function UI.draw_menu(items, selected_item, x, y)
for i, item in ipairs(items) do for i, item in ipairs(items) do
local current_y = y + (i-1)*10 local current_y = y + (i-1)*10
@@ -17,6 +25,10 @@ function UI.draw_menu(items, selected_item, x, y)
end end
end end
--- Updates menu selection.
-- @param items table A table of menu items.
-- @param selected_item number The current index of the selected item.
-- @return number The updated index of the selected item.
function UI.update_menu(items, selected_item) function UI.update_menu(items, selected_item)
if Input.up() then if Input.up() then
Audio.sfx_beep() Audio.sfx_beep()
@@ -34,6 +46,10 @@ function UI.update_menu(items, selected_item)
return selected_item return selected_item
end end
--- Wraps text.
-- @param text string The text to wrap.
-- @param max_chars_per_line number The maximum characters per line.
-- @return table A table of wrapped lines.
function UI.word_wrap(text, max_chars_per_line) function UI.word_wrap(text, max_chars_per_line)
if text == nil then return {""} end if text == nil then return {""} end
local lines = {} local lines = {}
@@ -63,6 +79,15 @@ function UI.word_wrap(text, max_chars_per_line)
return lines return lines
end end
--- Creates a numeric stepper.
-- @param label string The label for the stepper.
-- @param value_getter function Function to get the current value.
-- @param value_setter function Function to set the current value.
-- @param min number The minimum value.
-- @param max number The maximum value.
-- @param step number The step increment.
-- @param[opt] format string The format string for displaying the value.
-- @return table A numeric stepper control definition.
function UI.create_numeric_stepper(label, value_getter, value_setter, min, max, step, format) function UI.create_numeric_stepper(label, value_getter, value_setter, min, max, step, format)
return { return {
label = label, label = label,
@@ -76,6 +101,10 @@ function UI.create_numeric_stepper(label, value_getter, value_setter, min, max,
} }
end end
--- Creates an action item.
-- @param label string The label for the action item.
-- @param action function The function to execute when the item is selected.
-- @return table An action item control definition.
function UI.create_action_item(label, action) function UI.create_action_item(label, action)
return { return {
label = label, label = label,
@@ -84,6 +113,9 @@ function UI.create_action_item(label, action)
} }
end end
--- Draws decision selector.
-- @param decisions table A table of decision items.
-- @param selected_decision_index number The index of the selected decision.
function UI.draw_decision_selector(decisions, selected_decision_index) function UI.draw_decision_selector(decisions, selected_decision_index)
local bar_height = 16 local bar_height = 16
local bar_y = Config.screen.height - bar_height local bar_y = Config.screen.height - bar_height
@@ -97,6 +129,7 @@ function UI.draw_decision_selector(decisions, selected_decision_index)
Print.text(decision_label, text_x, text_y, Config.colors.item) Print.text(">", Config.screen.width - 6, text_y, Config.colors.green) end Print.text(decision_label, text_x, text_y, Config.colors.item) Print.text(">", Config.screen.width - 6, text_y, Config.colors.green) end
end end
--- Draws meters.
function UI.draw_meters() function UI.draw_meters()
if not Context or not Context.game_in_progress or not Context.meters then return end if not Context or not Context.game_in_progress or not Context.meters then return end
if Context.meters.hidden then return end if Context.meters.hidden then return end
@@ -129,6 +162,10 @@ function UI.draw_meters()
end end
end end
--- Updates decision selector.
-- @param decisions table A table of decision items.
-- @param selected_decision_index number The current index of the selected decision.
-- @return number The updated index of the selected decision.
function UI.update_decision_selector(decisions, selected_decision_index) function UI.update_decision_selector(decisions, selected_decision_index)
if Input.left() then if Input.left() then
Audio.sfx_beep() Audio.sfx_beep()

View File

@@ -1,9 +1,16 @@
--- Utility functions.
Util = {} Util = {}
--- Safely wraps an index for an array.
-- @param array table The array to index.
-- @param index number The desired index (can be out of bounds).
-- @return number The wrapped index within the array's bounds.
function Util.safeindex(array, index) function Util.safeindex(array, index)
return ((index - 1 + #array) % #array) + 1 return ((index - 1 + #array) % #array) + 1
end end
--- Navigates to a screen by its ID.
-- @param screen_id string The ID of the screen to go to.
function Util.go_to_screen_by_id(screen_id) function Util.go_to_screen_by_id(screen_id)
local screen_index = Context.screen_indices_by_id[screen_id] local screen_index = Context.screen_indices_by_id[screen_id]
if screen_index then if screen_index then

View File

@@ -6,6 +6,10 @@ AudioTestWindow = {
last_pressed = false last_pressed = false
} }
--- Generates menu items for audio test.
-- @param list_func table List of audio functions.
-- @param index_func number Current index of selected function.
-- @return table Generated menu items.
function AudioTestWindow.generate_menuitems(list_func, index_func) function AudioTestWindow.generate_menuitems(list_func, index_func)
return { return {
{ {
@@ -34,6 +38,8 @@ function AudioTestWindow.generate_menuitems(list_func, index_func)
} }
end end
--- Generates list of audio functions.
-- @return table A sorted list of audio function names.
function AudioTestWindow.generate_listfunc() function AudioTestWindow.generate_listfunc()
local result = {} local result = {}
@@ -48,11 +54,13 @@ function AudioTestWindow.generate_listfunc()
return result return result
end end
--- Navigates back from audio test window.
function AudioTestWindow.back() function AudioTestWindow.back()
Audio.sfx_deselect() Audio.sfx_deselect()
GameWindow.set_state(WINDOW_MENU) GameWindow.set_state(WINDOW_MENU)
end end
--- Initializes audio test window.
function AudioTestWindow.init() function AudioTestWindow.init()
AudioTestWindow.last_pressed = false AudioTestWindow.last_pressed = false
AudioTestWindow.index_menu = 1 AudioTestWindow.index_menu = 1
@@ -63,11 +71,13 @@ function AudioTestWindow.init()
) )
end end
--- Draws audio test window.
function AudioTestWindow.draw() function AudioTestWindow.draw()
UI.draw_top_bar("Audio test") UI.draw_top_bar("Audio test")
UI.draw_menu(AudioTestWindow.menuitems, AudioTestWindow.index_menu, 20, 50) UI.draw_menu(AudioTestWindow.menuitems, AudioTestWindow.index_menu, 20, 50)
end end
--- Updates audio test window logic.
function AudioTestWindow.update() function AudioTestWindow.update()
if Input.up() then if Input.up() then
AudioTestWindow.index_menu = Util.safeindex(AudioTestWindow.menuitems, AudioTestWindow.index_menu - 1) AudioTestWindow.index_menu = Util.safeindex(AudioTestWindow.menuitems, AudioTestWindow.index_menu - 1)

View File

@@ -3,6 +3,7 @@ ConfigurationWindow = {
selected_control = 1, selected_control = 1,
} }
--- Initializes configuration window.
function ConfigurationWindow.init() function ConfigurationWindow.init()
ConfigurationWindow.controls = { ConfigurationWindow.controls = {
UI.create_decision_item( UI.create_decision_item(
@@ -16,6 +17,7 @@ function ConfigurationWindow.init()
} }
end end
--- Draws configuration window.
function ConfigurationWindow.draw() function ConfigurationWindow.draw()
UI.draw_top_bar("Configuration") UI.draw_top_bar("Configuration")
@@ -55,6 +57,7 @@ function ConfigurationWindow.draw()
Print.text("Press B to go back", x_start, 120, Config.colors.light_grey) Print.text("Press B to go back", x_start, 120, Config.colors.light_grey)
end end
--- Updates configuration window logic.
function ConfigurationWindow.update() function ConfigurationWindow.update()
if Input.menu_back() then if Input.menu_back() then
GameWindow.set_state(WINDOW_MENU) GameWindow.set_state(WINDOW_MENU)

View File

@@ -1,3 +1,4 @@
--- Draws the game window.
function GameWindow.draw() function GameWindow.draw()
local screen = Context.screens[Context.current_screen] local screen = Context.screens[Context.current_screen]
Map.draw(screen.background) Map.draw(screen.background)
@@ -17,7 +18,10 @@ function GameWindow.draw()
Sprite.draw() Sprite.draw()
end end
--- Updates the game window logic.
function GameWindow.update() function GameWindow.update()
local previous_screen_index = Context.current_screen
if Input.menu_back() then if Input.menu_back() then
Context.active_window = WINDOW_MENU Context.active_window = WINDOW_MENU
MenuWindow.refresh_menu_items() MenuWindow.refresh_menu_items()
@@ -37,6 +41,19 @@ function GameWindow.update()
Context.selected_decision_index = 1 end Context.selected_decision_index = 1 end
local screen = Context.screens[Context.current_screen] local screen = Context.screens[Context.current_screen]
screen.update()
if previous_screen_index ~= Context.current_screen then
screen.init()
end
if Context.current_situation then
local current_situation_obj = Situation.get(Context.current_situation)
if current_situation_obj and current_situation_obj.update then
current_situation_obj.update()
end
end
if screen and screen.decisions and #screen.decisions > 0 then if screen and screen.decisions and #screen.decisions > 0 then
local available_decisions = {} local available_decisions = {}
for _, decision_id in ipairs(screen.decisions) do for _, decision_id in ipairs(screen.decisions) do
@@ -64,6 +81,8 @@ function GameWindow.update()
end end
end end
--- Sets the active window.
-- @param new_state number The ID of the new active window.
function GameWindow.set_state(new_state) function GameWindow.set_state(new_state)
Context.active_window = new_state Context.active_window = new_state
end end

View File

@@ -1,8 +1,10 @@
--- Draws the intro window.
function IntroWindow.draw() function IntroWindow.draw()
local x = (Config.screen.width - 132) / 2 local x = (Config.screen.width - 132) / 2
Print.text(Context.intro.text, x, Context.intro.y, Config.colors.green) Print.text(Context.intro.text, x, Context.intro.y, Config.colors.green)
end end
--- Updates the intro window logic.
function IntroWindow.update() function IntroWindow.update()
Context.intro.y = Context.intro.y - Context.intro.speed Context.intro.y = Context.intro.y - Context.intro.speed

View File

@@ -1,8 +1,10 @@
--- Draws the menu window.
function MenuWindow.draw() function MenuWindow.draw()
UI.draw_top_bar("Main Menu") UI.draw_top_bar("Main Menu")
UI.draw_menu(Context.menu_items, Context.selected_menu_item, 108, 70) UI.draw_menu(Context.menu_items, Context.selected_menu_item, 108, 70)
end end
--- Updates the menu window logic.
function MenuWindow.update() function MenuWindow.update()
Context.selected_menu_item = UI.update_menu(Context.menu_items, Context.selected_menu_item) Context.selected_menu_item = UI.update_menu(Context.menu_items, Context.selected_menu_item)
@@ -15,35 +17,43 @@ function MenuWindow.update()
end end
end end
--- Starts a new game from the menu.
function MenuWindow.new_game() function MenuWindow.new_game()
Context.new_game() GameWindow.set_state(WINDOW_GAME) Context.new_game() GameWindow.set_state(WINDOW_GAME)
end end
--- Loads a game from the menu.
function MenuWindow.load_game() function MenuWindow.load_game()
Context.load_game() GameWindow.set_state(WINDOW_GAME) Context.load_game() GameWindow.set_state(WINDOW_GAME)
end end
--- Saves the current game from the menu.
function MenuWindow.save_game() function MenuWindow.save_game()
Context.save_game() end Context.save_game() end
--- Resumes the game from the menu.
function MenuWindow.resume_game() function MenuWindow.resume_game()
GameWindow.set_state(WINDOW_GAME) GameWindow.set_state(WINDOW_GAME)
end end
--- Exits the game.
function MenuWindow.exit() function MenuWindow.exit()
exit() exit()
end end
--- Opens the configuration menu.
function MenuWindow.configuration() function MenuWindow.configuration()
ConfigurationWindow.init() ConfigurationWindow.init()
GameWindow.set_state(WINDOW_CONFIGURATION) GameWindow.set_state(WINDOW_CONFIGURATION)
end end
--- Opens the audio test menu.
function MenuWindow.audio_test() function MenuWindow.audio_test()
AudioTestWindow.init() AudioTestWindow.init()
GameWindow.set_state(WINDOW_AUDIOTEST) GameWindow.set_state(WINDOW_AUDIOTEST)
end end
--- Refreshes menu items.
function MenuWindow.refresh_menu_items() function MenuWindow.refresh_menu_items()
Context.menu_items = {} Context.menu_items = {}
if Context.game_in_progress then if Context.game_in_progress then

View File

@@ -1,7 +1,13 @@
--- Initializes DDR minigame state.
-- @param params table Optional parameters for configuration.
function MinigameDDRWindow.init(params) function MinigameDDRWindow.init(params)
Context.minigame_ddr = Minigames.configure_ddr(params) Context.minigame_ddr = Minigames.configure_ddr(params)
end end
--- Starts the DDR minigame.
-- @param return_window number The window ID to return to after the minigame.
-- @param[opt] song_key string The key of the song to play.
-- @param[opt] params table Optional parameters for minigame configuration.
function MinigameDDRWindow.start(return_window, song_key, params) function MinigameDDRWindow.start(return_window, song_key, params)
MinigameDDRWindow.init(params) MinigameDDRWindow.init(params)
Context.minigame_ddr.return_window = return_window or WINDOW_GAME Context.minigame_ddr.return_window = return_window or WINDOW_GAME
@@ -22,6 +28,7 @@ function MinigameDDRWindow.start(return_window, song_key, params)
Context.active_window = WINDOW_MINIGAME_DDR Context.active_window = WINDOW_MINIGAME_DDR
end end
--- Spawns a random arrow.
local function spawn_arrow() local function spawn_arrow()
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
local target = mg.target_arrows[math.random(1, 4)] local target = mg.target_arrows[math.random(1, 4)]
@@ -32,6 +39,8 @@ local function spawn_arrow()
}) })
end end
--- Spawns an arrow in a specific direction.
-- @param direction string The direction of the arrow ("left", "down", "up", "right").
local function spawn_arrow_dir(direction) local function spawn_arrow_dir(direction)
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
for _, target in ipairs(mg.target_arrows) do for _, target in ipairs(mg.target_arrows) do
@@ -46,17 +55,28 @@ local function spawn_arrow_dir(direction)
end end
end end
--- Checks if an arrow is hit.
-- @param arrow table The arrow data.
-- @return boolean True if the arrow is hit, false otherwise.
local function check_hit(arrow) local function check_hit(arrow)
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
local distance = math.abs(arrow.y - mg.target_y) local distance = math.abs(arrow.y - mg.target_y)
return distance <= mg.hit_threshold return distance <= mg.hit_threshold
end end
--- Checks if an arrow is missed.
-- @param arrow table The arrow data.
-- @return boolean True if the arrow is missed, false otherwise.
local function check_miss(arrow) local function check_miss(arrow)
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
return arrow.y > mg.target_y + mg.hit_threshold return arrow.y > mg.target_y + mg.hit_threshold
end end
--- Draws an arrow.
-- @param x number The x-coordinate.
-- @param y number The y-coordinate.
-- @param direction string The direction of the arrow.
-- @param color number The color of the arrow.
local function draw_arrow(x, y, direction, color) local function draw_arrow(x, y, direction, color)
local size = 12 local size = 12
local half = size / 2 local half = size / 2
@@ -75,6 +95,7 @@ local function draw_arrow(x, y, direction, color)
end end
end end
--- Updates DDR minigame logic.
function MinigameDDRWindow.update() function MinigameDDRWindow.update()
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
if mg.bar_fill >= mg.max_fill then if mg.bar_fill >= mg.max_fill then
@@ -167,6 +188,7 @@ function MinigameDDRWindow.update()
end end
end end
--- Draws DDR minigame.
function MinigameDDRWindow.draw() function MinigameDDRWindow.draw()
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
if not mg then if not mg then

View File

@@ -1,13 +1,19 @@
--- Initializes button mash minigame state.
-- @param params table Optional parameters for configuration.
function MinigameButtonMashWindow.init(params) function MinigameButtonMashWindow.init(params)
Context.minigame_button_mash = Minigames.configure_button_mash(params) Context.minigame_button_mash = Minigames.configure_button_mash(params)
end end
--- Starts the button mash minigame.
-- @param return_window number The window ID to return to after the minigame.
-- @param[opt] params table Optional parameters for minigame configuration.
function MinigameButtonMashWindow.start(return_window, params) function MinigameButtonMashWindow.start(return_window, params)
MinigameButtonMashWindow.init(params) MinigameButtonMashWindow.init(params)
Context.minigame_button_mash.return_window = return_window or WINDOW_GAME Context.minigame_button_mash.return_window = return_window or WINDOW_GAME
Context.active_window = WINDOW_MINIGAME_BUTTON_MASH Context.active_window = WINDOW_MINIGAME_BUTTON_MASH
end end
--- Updates button mash minigame logic.
function MinigameButtonMashWindow.update() function MinigameButtonMashWindow.update()
local mg = Context.minigame_button_mash local mg = Context.minigame_button_mash
if Input.select() then if Input.select() then
@@ -33,6 +39,7 @@ function MinigameButtonMashWindow.update()
end end
end end
--- Draws button mash minigame.
function MinigameButtonMashWindow.draw() function MinigameButtonMashWindow.draw()
local mg = Context.minigame_button_mash local mg = Context.minigame_button_mash
if mg.return_window == WINDOW_GAME then if mg.return_window == WINDOW_GAME then

View File

@@ -1,13 +1,19 @@
--- Initializes rhythm minigame state.
-- @param params table Optional parameters for configuration.
function MinigameRhythmWindow.init(params) function MinigameRhythmWindow.init(params)
Context.minigame_rhythm = Minigames.configure_rhythm(params) Context.minigame_rhythm = Minigames.configure_rhythm(params)
end end
--- Starts the rhythm minigame.
-- @param return_window number The window ID to return to after the minigame.
-- @param[opt] params table Optional parameters for minigame configuration.
function MinigameRhythmWindow.start(return_window, params) function MinigameRhythmWindow.start(return_window, params)
MinigameRhythmWindow.init(params) MinigameRhythmWindow.init(params)
Context.minigame_rhythm.return_window = return_window or WINDOW_GAME Context.minigame_rhythm.return_window = return_window or WINDOW_GAME
Context.active_window = WINDOW_MINIGAME_RHYTHM Context.active_window = WINDOW_MINIGAME_RHYTHM
end end
--- Updates rhythm minigame logic.
function MinigameRhythmWindow.update() function MinigameRhythmWindow.update()
local mg = Context.minigame_rhythm local mg = Context.minigame_rhythm
mg.line_position = mg.line_position + (mg.line_speed * mg.line_direction) mg.line_position = mg.line_position + (mg.line_speed * mg.line_direction)
@@ -50,6 +56,7 @@ function MinigameRhythmWindow.update()
end end
end end
--- Draws rhythm minigame.
function MinigameRhythmWindow.draw() function MinigameRhythmWindow.draw()
local mg = Context.minigame_rhythm local mg = Context.minigame_rhythm
if mg.return_window == WINDOW_GAME then if mg.return_window == WINDOW_GAME then

View File

@@ -5,14 +5,18 @@ local POPUP_HEIGHT = 80
local TEXT_MARGIN_X = POPUP_X + 10 local TEXT_MARGIN_X = POPUP_X + 10
local TEXT_MARGIN_Y = POPUP_Y + 10 local TEXT_MARGIN_Y = POPUP_Y + 10
local LINE_HEIGHT = 8 local LINE_HEIGHT = 8
--- Displays a popup window.
-- @param content_strings table A table of strings to display in the popup.
function PopupWindow.show(content_strings) function PopupWindow.show(content_strings)
Context.popup.show = true Context.popup.show = true
Context.popup.content = content_strings or {} GameWindow.set_state(WINDOW_POPUP) end Context.popup.content = content_strings or {} GameWindow.set_state(WINDOW_POPUP) end
--- Hides the popup window.
function PopupWindow.hide() function PopupWindow.hide()
Context.popup.show = false Context.popup.show = false
Context.popup.content = {} GameWindow.set_state(WINDOW_GAME) end Context.popup.content = {} GameWindow.set_state(WINDOW_GAME) end
--- Updates popup window logic.
function PopupWindow.update() function PopupWindow.update()
if Context.popup.show then if Context.popup.show then
if Input.menu_confirm() or Input.menu_back() then PopupWindow.hide() if Input.menu_confirm() or Input.menu_back() then PopupWindow.hide()
@@ -20,6 +24,7 @@ function PopupWindow.update()
end end
end end
--- Draws the popup window.
function PopupWindow.draw() function PopupWindow.draw()
if Context.popup.show then if Context.popup.show then
rect(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.black) rect(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.black)

View File

@@ -1,3 +1,4 @@
--- Draws the splash window.
function SplashWindow.draw() function SplashWindow.draw()
local txt = "Definitely not an Impostor" local txt = "Definitely not an Impostor"
local w = #txt * 6 local w = #txt * 6
@@ -6,6 +7,7 @@ function SplashWindow.draw()
print(txt, x, y, 12) print(txt, x, y, 12)
end end
--- Updates the splash window logic.
function SplashWindow.update() function SplashWindow.update()
Context.splash_timer = Context.splash_timer - 1 Context.splash_timer = Context.splash_timer - 1
if Context.splash_timer <= 0 or Input.menu_confirm() then if Context.splash_timer <= 0 or Input.menu_confirm() then