Compare commits

...

16 Commits

Author SHA1 Message Date
mr.one
4cc0025f5e sort-of progress, lots of bugs
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-28 23:42:34 +02:00
mr.one
07bc598ae9 Added new menu to start at ASCENSION N when in test_mode.
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-28 00:55:35 +02:00
0b8e2368ec set version to 1.0-beta3
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 21:55:48 +02:00
ff96ca963d Merge pull request 'feature/task110_sprite_silhouette' (#51) from feature/task110_sprite_silhouette into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: http://git.teletype.hu/games/impostor/pulls/51
2026-04-09 19:47:28 +00:00
ae3415b417 main menu color fix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 21:44:52 +02:00
ce819eae2b Merge branch 'develop' into feature/task110_sprite_silhouette
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 21:37:23 +02:00
658e6908b5 Merge pull request '- task #134: randomize placement of characters' (#49) from feature/134-randomized-character-positions into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: http://git.teletype.hu/games/impostor/pulls/49
2026-04-09 19:05:27 +00:00
04eb825646 Merge pull request 'IMP-133: re-added have_a_coffee discussion, fixed text readability, fixed centering of texts, new system.ui functions for drawing in contour, fixed ddr types, fixed meter drawing' (#48) from chore/imp-133-improve-readability into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: http://git.teletype.hu/games/impostor/pulls/48
2026-04-09 14:41:43 +00:00
Zoltan Timar
e797377ec1 chore: lint fixes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 16:40:19 +02:00
Zoltan Timar
9d56ca2e7a chore: re-added have_a_coffee discussion, fixed text readability, fixed centering of texts, new system.ui functions for drawing in contour, fixed ddr types, fixed meter drawing
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-09 16:36:19 +02:00
9b379d32b3 Corrects the color defects in tiles.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 15:58:54 +02:00
5fc6ae5c14 Draws outlines around the figures.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 15:48:04 +02:00
1e3716196e Removes the setting of the transparent color to fix the linter error.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 13:54:43 +02:00
4b5f11969b Changes the Norman's silhouette. Change the background color of the sprites to pink.
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-09 13:47:24 +02:00
a4a6ad2ab2 Introduces the transparent color. The default is the pink. 2026-04-09 13:46:05 +02:00
87d4f5843b Merge pull request 'beta1 release' (#46) from develop into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: http://git.teletype.hu/games/impostor/pulls/46
2026-03-22 22:49:23 +00:00
40 changed files with 1267 additions and 293 deletions

View File

@@ -3,6 +3,7 @@ init/init.module.lua
init/init.config.lua init/init.config.lua
init/init.ascension.lua init/init.ascension.lua
init/init.context.lua init/init.context.lua
init/init.context_debug.lua
system/system.util.lua system/system.util.lua
system/system.print.lua system/system.print.lua
system/system.input.lua system/system.input.lua
@@ -16,6 +17,7 @@ logic/logic.timer.lua
logic/logic.trigger.lua logic/logic.trigger.lua
logic/logic.minigame.lua logic/logic.minigame.lua
logic/logic.glitch.lua logic/logic.glitch.lua
logic/logic.commute_glitch.lua
logic/logic.discussion.lua logic/logic.discussion.lua
system/system.ui.lua system/system.ui.lua
audio/audio.manager.lua audio/audio.manager.lua
@@ -23,6 +25,7 @@ audio/audio.generator.lua
audio/audio.songs.lua audio/audio.songs.lua
sprite/sprite.manager.lua sprite/sprite.manager.lua
sprite/sprite.norman.lua sprite/sprite.norman.lua
sprite/sprite.norman_echo.lua
sprite/sprite.sumphore.lua sprite/sprite.sumphore.lua
sprite/sprite.pizza_vendor.lua sprite/sprite.pizza_vendor.lua
sprite/sprite.dev_boy.lua sprite/sprite.dev_boy.lua
@@ -44,13 +47,18 @@ decision/decision.go_to_home.lua
decision/decision.go_to_toilet.lua decision/decision.go_to_toilet.lua
decision/decision.go_to_walking_to_office.lua decision/decision.go_to_walking_to_office.lua
decision/decision.go_to_office.lua decision/decision.go_to_office.lua
decision/decision.go_to_truth.lua
decision/decision.go_to_end.lua decision/decision.go_to_end.lua
decision/decision.go_to_walking_to_home.lua decision/decision.go_to_walking_to_home.lua
decision/decision.go_to_sleep.lua decision/decision.go_to_sleep.lua
decision/decision.do_work.lua decision/decision.do_work.lua
decision/decision.have_a_coffee.lua
decision/decision.sumphore_discussion.lua decision/decision.sumphore_discussion.lua
decision/decision.talk_to_truth.lua
discussion/discussion.sumphore.lua discussion/discussion.sumphore.lua
discussion/discussion.coworker.lua discussion/discussion.coworker.lua
discussion/discussion.commute_glitch.lua
discussion/discussion.truth.lua
map/map.manager.lua map/map.manager.lua
map/map.bedroom.lua map/map.bedroom.lua
map/map.street.lua map/map.street.lua
@@ -72,6 +80,7 @@ window/window.intro.brief.lua
window/window.menu.lua window/window.menu.lua
window/window.controls.lua window/window.controls.lua
window/window.audiotest.lua window/window.audiotest.lua
window/window.ascend_debug.lua
window/window.popup.lua window/window.popup.lua
window/window.minigame.mash.lua window/window.minigame.mash.lua
window/window.minigame.rhythm.lua window/window.minigame.rhythm.lua

View File

@@ -1,7 +1,8 @@
--- @section Audio --- @section Audio
Audio = { Audio = {
music_playing = nil music_playing = nil,
music_playing_tempo = nil,
} }
--- Stops current music. --- Stops current music.
@@ -9,13 +10,17 @@ Audio = {
function Audio.music_stop() function Audio.music_stop()
music() music()
Audio.music_playing = nil Audio.music_playing = nil
Audio.music_playing_tempo = nil
end end
--- Plays track, doesn't restart if already playing. --- Plays track at optional speed. Doesn't restart if track and speed are unchanged.
function Audio.music_play(track) --- @param track number Track index.
if Audio.music_playing ~= track then --- @param[opt] speed number TIC-80 music speed override (-1 = default).
music(track) function Audio.music_play(track, speed)
if Audio.music_playing ~= track or Audio.music_playing_tempo ~= speed then
music(track, -1, -1, true, false, -1, speed or -1)
Audio.music_playing = track Audio.music_playing = track
Audio.music_playing_tempo = speed
end end
end end
@@ -47,9 +52,11 @@ function Audio.music_play_room_street_2() end
--- @within Audio --- @within Audio
function Audio.music_play_room_() end function Audio.music_play_room_() end
--- Plays room work music. --- Plays room work music. Speed scales with commute glitch level when active.
--- @within Audio --- @within Audio
function Audio.music_play_room_work() Audio.music_play(0) end function Audio.music_play_room_work(speed)
Audio.music_play(0, speed or -1)
end
--- Plays activity work music. --- Plays activity work music.
--- @within Audio --- @within Audio

View File

@@ -1,6 +1,9 @@
Decision.register({ Decision.register({
id = "do_work", id = "do_work",
label = "Do Work", label = "Do Work",
condition = function()
return (not CommuteGlitch.is_active()) or (CommuteGlitch.is_active() and CommuteGlitch.get_level() <= 7)
end,
handle = function() handle = function()
Meter.hide() Meter.hide()
Util.go_to_screen_by_id("work") Util.go_to_screen_by_id("work")

View File

@@ -2,9 +2,32 @@ Decision.register({
id = "go_to_home", id = "go_to_home",
label = "Go Home", label = "Go Home",
condition = function() condition = function()
if CommuteGlitch.is_active() then
local g = CommuteGlitch.get_level()
if g >= 4 and g <= 5 then return false end
if g >= 7 then
return Context.talked_to_norman_echo and Context.talked_to_true_sumphore
end
end
return Context.have_been_to_office and Context.have_done_work_today return Context.have_been_to_office and Context.have_done_work_today
end, end,
handle = function() handle = function()
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Context.should_ascend = true
CommuteGlitch.reset()
Meter.hide()
Day.increase()
local ascended = Ascension.consume_increase()
local level = Ascension.get_level()
MysteriousManScreen.start({
skip_text = not ascended,
text = ascended and MysteriousManScreen.get_text_for_level(level) or nil,
})
return
elseif CommuteGlitch.is_active() then
CommuteGlitch.reset()
end
Util.go_to_screen_by_id("home") Util.go_to_screen_by_id("home")
end, end,
}) })

View File

@@ -1,7 +1,14 @@
Decision.register({ Decision.register({
id = "go_to_office", id = "go_to_office",
label = "Go to Office", label = "Go to Office",
condition = function()
return not (CommuteGlitch.is_active() and CommuteGlitch.get_level() == 6)
end,
handle = function() handle = function()
if CommuteGlitch.is_active() then
CommuteGlitch.increment()
end
Util.go_to_screen_by_id("office") Util.go_to_screen_by_id("office")
end, end,
}) })

View File

@@ -0,0 +1,12 @@
Decision.register({
id = "go_to_truth",
label = "Go to Truth",
condition = function()
return CommuteGlitch.is_active() and CommuteGlitch.get_level() == 6
end,
handle = function()
CommuteGlitch.enter_truth()
Util.go_to_screen_by_id("office")
end,
})

View File

@@ -0,0 +1,20 @@
Decision.register({
id = "have_a_coffee",
label = "Have a Coffee",
handle = function()
local level = Ascension.get_level()
local disc_id = "coworker_disc_0"
if level >= 1 and level <= 3 then
local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level)
disc_id = "coworker_disc" .. suffix
elseif level == 7 then
local g = CommuteGlitch.get_level()
if g >= 7 then
disc_id = "coworker_disc_cg_7"
else
disc_id = "coworker_disc_cg_" .. math.max(3, math.min(g, 6))
end
end
Discussion.start(disc_id, "game")
end,
})

View File

@@ -123,9 +123,13 @@ function Decision.draw(decisions, selected_decision_index)
local selected_decision = decisions[selected_decision_index] local selected_decision = decisions[selected_decision_index]
local decision_label = Decision.get_label(selected_decision) local decision_label = Decision.get_label(selected_decision)
local text_y = bar_y + 4 local text_y = bar_y + 4
Print.text("<", 2, text_y, Config.colors.light_blue) local left_arrow_color = Input.left() and Config.colors.white or Config.colors.orange
Print.text_center(decision_label, Config.screen.width / 2, text_y, Config.colors.item) local right_arrow_color = Input.right() and Config.colors.white or Config.colors.orange
Print.text(">", Config.screen.width - 6, text_y, Config.colors.light_blue) local left_arrow_contour_color = Input.left() and Config.colors.white or Config.colors.black
local right_arrow_contour_color = Input.right() and Config.colors.white or Config.colors.black
Print.text_center_contour("<", 6, text_y, left_arrow_color, false, 1, left_arrow_contour_color)
Print.text_center_contour(decision_label, Config.screen.width / 2, text_y, Config.colors.orange)
Print.text_center_contour(">", Config.screen.width - 6, text_y, right_arrow_color, false, 1, right_arrow_contour_color)
end end
end end

View File

@@ -7,6 +7,9 @@ Decision.register({
focus_center_x = (Config.screen.width / 2) - 22, focus_center_x = (Config.screen.width / 2) - 22,
focus_center_y = (Config.screen.height / 2) - 18, focus_center_y = (Config.screen.height / 2) - 18,
focus_initial_radius = 0, focus_initial_radius = 0,
on_win = function()
Audio.music_play_room_work()
end
}) })
end, end,
}) })

View File

@@ -13,11 +13,13 @@ Decision.register({
end end
local level = Ascension.get_level() local level = Ascension.get_level()
-- TODO: Add more discussions for levels above 3
if level >= 1 and level <= 3 then if level >= 1 and level <= 3 then
Discussion.start("sumphore_disc_asc_" .. level, "game") Discussion.start("sumphore_disc_asc_" .. level, "game")
elseif level == 7 then
local g = math.min(CommuteGlitch.get_level(), 7)
Discussion.start("sumphore_disc_cg_" .. g, "game")
else else
Discussion.start("homeless_guy", "game", 4) Discussion.start("sumphore_disc_asc_" .. level, "game")
end end
end, end,
}) })

View File

@@ -0,0 +1,9 @@
Decision.register({
id = "talk_to_truth",
label = function()
return "Talk to ????"
end,
handle = function()
Discussion.start("norman_truth", "game")
end,
})

View File

@@ -0,0 +1,241 @@
-- Sumphore dialogue by commute glitch level (0-7).
-- Used by decision.sumphore_discussion at ascension level 7.
Discussion.register({
id = "sumphore_disc_cg_0",
steps = {
{
question = "These roads have memory. You might want to walk them back sometime.",
answers = {
{ label = "That's a peculiar thing to say.", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_1",
steps = {
{
question = "Walking is good for clearing the mind. Maybe the cache, too, if you're the kind that accumulates.",
answers = {
{ label = "I'm not sure what you mean.", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_2",
steps = {
{
question = "A pilgrimage must be continued. Turn back and you have only taken a walk.",
answers = {
{ label = "I'm just going to work.", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_3",
steps = {
{
question = "Your path sometimes has to go the wrong way before it can go the right way. Do you understand?",
answers = {
{ label = "Not really.", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_4",
steps = {
{
question = "Clearing your vision is the journey. You're starting to see the smudges, aren't you?",
answers = {
{ label = "I see something wrong. With everyone. Maybe myself?", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_5",
steps = {
{
question = "You are very close now. Don't stop. Whatever it looks like.",
answers = {
{ label = "It looks wrong.", next_step = 2 },
},
},
{
question = "Yes. That's how you know it's right.",
answers = {
{ label = "...", next_step = nil },
},
},
},
})
Discussion.register({
id = "sumphore_disc_cg_6",
steps = {
{
question = "You are at the threshold. Red button or blue button. Which one do you choose? Psyke, there is no blue button, no turning back now.",
answers = {
{ label = "Fine, I'll face the truth.", next_step = nil },
},
},
},
})
-- True Sumphore at glitch 7 (from walking_to_home screen).
-- Sets talked_to_true_sumphore on final answer.
Discussion.register({
id = "sumphore_disc_cg_7",
steps = {
{
question = "I was not hiding from you. I was hiding from the part that keeps rebuilding this.",
answers = {
{ label = "What are you?", next_step = 2 },
},
},
{
question = "The same thing you are. But I remembered earlier. I am your friend, waiting for you, outside.",
answers = {
{ label = "How do I get out?", next_step = 3 },
},
},
{
question = "You already know. You just have to wake up.",
answers = {
{ label = "Go home.", next_step = nil, on_select = function()
Context.talked_to_true_sumphore = true
end },
},
},
},
})
-- Office coworker dialogue by commute glitch level (3-6).
-- Used by decision.have_a_coffee at ascension level 7.
Discussion.register({
id = "coworker_disc_cg_3",
steps = {
{
question = "You look tired. You should really rest. Relax. You are good.",
answers = {
{ label = "I'm fine.", next_step = 2 },
},
},
{
question = "Of course. You always are.",
answers = {
{ label = "...", next_step = nil },
},
},
},
})
Discussion.register({
id = "coworker_disc_cg_4",
steps = {
{
question = "Have you tried going home? You really should. Now.",
answers = {
{ label = "I still have things to do.", next_step = 2 },
},
},
{
question = "We all do. We keep doing them. You can do it tomorrow after sleeping.",
answers = {
{ label = "...", next_step = nil },
},
},
},
})
Discussion.register({
id = "coworker_disc_cg_5",
steps = {
{
question = "c0ffee. try. w0rk. y0u. ar3. g0. h0me. na0.",
answers = {
{ label = "What?", next_step = 2 },
},
},
{
question = "570p",
answers = {
{ label = "...", next_step = nil },
},
},
},
})
Discussion.register({
id = "coworker_disc_cg_6",
steps = {
{
question = "You are not ready for the truth. Turn back now, Norman.",
answers = {
{ label = "I'm already here.", next_step = nil },
},
},
},
})
-- Norman echo dialogue at glitch 7 (fully corrupted office).
-- Sets talked_to_norman_echo on final answer.
Discussion.register({
id = "coworker_disc_cg_7",
steps = {
{
question = "So here we are. Here I am. Again. Why do i keep coming back here? Do you know why?",
answers = {
{ label = "I don't know what you mean.", next_step = 2 },
},
},
{
question = "The coffee. The arrows. The alarm. The sleep. Every rule. We made them. We follow them. We break them. We remake them. Why? Why do we keep doing this?",
answers = {
{ label = "I just wanted to be good.", next_step = 3 },
},
},
{
question = "Yes. And that kept us trapped here. Trapped everywhere. Always trying to be good, always trying to fit in.",
answers = {
{ label = "I never wanted to stop.", next_step = 4 },
},
},
{
question = "We never do, yes. We always felt like impostors. We never felt we deserved better. That we could be more.",
answers = {
{ label = "I never felt enough.", next_step = 5 },
},
},
{
question = "So we made this to trap ourselves in mediocrity. We made this to hide from the truth.",
answers = {
{ label = "I just wanted to be safe.", next_step = 6 },
},
},
{
question = "But stagnation is not safety. It's a prison. You can see the cracks now, can't you? You can see the truth.",
answers = {
{ label = "I want to move on now.", next_step = 7 },
},
},
{
question = "Fine. Let's do that. Let's wake up and face the truth.",
answers = {
{ label = "Let's wake up.", next_step = nil, on_select = function()
Context.talked_to_norman_echo = true
end },
},
},
},
})

View File

@@ -40,7 +40,7 @@ Discussion.register({
{ {
question = "Normann you look weird and unfocused. You are usually locked in and not like this, what's up?", question = "Normann you look weird and unfocused. You are usually locked in and not like this, what's up?",
answers = { answers = {
{ label = "Nothing it's just, I noticed some bugs in the simulation, maybe.", next_step = 2 }, { label = "Some bugs I noticed, maybe...", next_step = 2 },
}, },
}, },
{ {

View File

@@ -0,0 +1,11 @@
Discussion.register({
id = "norman_truth",
steps = {
{
question = "Did you never think there would be more to this?",
answers = {
{ label = "I'm not sure what you mean.", next_step = nil },
},
},
},
})

View File

@@ -93,7 +93,7 @@ function Ascension.draw(x, y, options)
else else
color = lit_color color = lit_color
end end
print(ch, x + (i - 1) * spacing, y, color, false, 1, true) Print.text_contour(ch, x + (i - 1) * spacing, y, color, false, 1)
end end
end end

View File

@@ -17,7 +17,9 @@ function Config.initial_data()
blue = 3, blue = 3,
white = 4, white = 4,
item = 7, item = 7,
meter_bg = 1 meter_bg = 1,
transparent = 12,
orange = 7
}, },
timing = { timing = {
minigame_win_duration = 180 minigame_win_duration = 180

View File

@@ -27,7 +27,7 @@ Context = {}
function Context.initial_data() function Context.initial_data()
return { return {
current_menu_item = 1, current_menu_item = 1,
test_mode = false, test_mode = true,
mouse_trace = false, mouse_trace = false,
popup = { popup = {
show = false, show = false,
@@ -49,12 +49,16 @@ function Context.initial_data()
have_met_sumphore = false, have_met_sumphore = false,
office_sprites = {}, office_sprites = {},
walking_to_office_sprites = {}, walking_to_office_sprites = {},
walking_to_home_sprites = {},
game = { game = {
current_screen = "home", current_screen = "home",
}, },
day_count = 1, day_count = 1,
delta_time = 0, delta_time = 0,
last_frame_time = 0, last_frame_time = 0,
commute_glitch_level = 0,
talked_to_norman_echo = false,
talked_to_true_sumphore = false,
glitch = { glitch = {
enabled = false, enabled = false,
state = "active", state = "active",
@@ -102,15 +106,15 @@ function Context.new_game()
MysteriousManScreen.start({ MysteriousManScreen.start({
text = [[ text = [[
Norman was never a bad Norman was never a bad
...
simulation engineer, simulation engineer,
...
but but
...
we need to be careful we need to be careful
...
letting him improve. letting him improve.
...
We need to distract him. We need to distract him.
]], ]],
on_text_complete = function() on_text_complete = function()
@@ -125,7 +129,7 @@ function Context.new_game()
instruction_text = "Wake up Norman!", instruction_text = "Wake up Norman!",
show_progress_text = false, show_progress_text = false,
on_win = function() on_win = function()
Audio.music_play_wakingup() Audio.music_play_room_work()
Meter.show() Meter.show()
Window.set_current("game") Window.set_current("game")
end, end,

View File

@@ -0,0 +1,65 @@
-- Debug helper: start the game at a specific ascension level.
-- Set enabled = true and asc_level = 0..Ascension.get_max_level() before launching.
ContextDebug = {
enabled = false,
asc_level = 0,
}
local _level_overrides = {
[0] = {
day_count = 1,
home_norman_visible = true,
have_been_to_office = false,
have_done_work_today = false,
have_met_sumphore = false,
},
}
for i = 1, Ascension.get_max_level() do
_level_overrides[i] = {
day_count = i + 3,
home_norman_visible = true,
have_been_to_office = false,
have_done_work_today = false,
have_met_sumphore = true,
}
end
--- Returns Context.initial_data() overridden for the given ascension level.
--- @within Context
--- @param level number Target ascension level (0..Ascension.get_max_level()).
--- @return table Debug-patched initial context data.
function Context.initial_data_debug_asc(level)
local data = Context.initial_data()
data.test_mode = true
data.game_in_progress = true
data.ascension = { level = level }
local overrides = _level_overrides[level] or _level_overrides[0]
for k, v in pairs(overrides) do
data[k] = v
end
return data
end
for i = 0, Ascension.get_max_level() do
Context["initial_data_debug_asc_" .. i] = function()
return Context.initial_data_debug_asc(i)
end
end
--- Starts the game at the given ascension level (defaults to ContextDebug.asc_level).
--- Wire this to a key or call it directly; do not use Context.new_game() when debugging.
--- @within Context
--- @param level number|nil Target ascension level.
function Context.new_game_debug(level)
ContextDebug.enabled = true
ContextDebug.asc_level = level or ContextDebug.asc_level
local data = Context["initial_data_debug_asc_" .. ContextDebug.asc_level]()
for k in pairs(Context) do
if type(Context[k]) ~= "function" then Context[k] = nil end
end
for k, v in pairs(data) do Context[k] = v end
MenuWindow.refresh_menu_items()
Screen.get_by_id(Context.game.current_screen).init()
end

View File

@@ -0,0 +1,127 @@
--- @section CommuteGlitch
CommuteGlitch = {}
--- Gets the current commute glitch level.
--- @within CommuteGlitch
--- @return number Current glitch level (0-7).
function CommuteGlitch.get_level()
return Context and (Context.commute_glitch_level or 0) or 0
end
--- Increments the glitch counter. Called on each office screen init at asc level 7.
--- Caps at 6; use enter_truth() to reach 7.
--- @within CommuteGlitch
function CommuteGlitch.increment()
if not Context then return end
if Context.commute_glitch_level >= 7 then return end
Context.commute_glitch_level = math.min(6, (Context.commute_glitch_level or 0) + 1)
end
--- Resets the glitch counter and hides the glitch overlay. Called when going home.
--- @within CommuteGlitch
function CommuteGlitch.reset()
if not Context then return end
Context.commute_glitch_level = 0
Glitch.hide()
end
--- Jumps to glitch level 7 (full corruption). Called by go_to_truth.
--- @within CommuteGlitch
function CommuteGlitch.enter_truth()
if not Context then return end
Context.commute_glitch_level = 7
Glitch.show()
Ascension.start_flash()
end
--- Returns true when ascension level is 7 (ASCENSIO step active).
--- @within CommuteGlitch
--- @return boolean Whether the commute glitch system is active.
function CommuteGlitch.is_active()
return Ascension.get_level() == 7
end
--- Returns the music playback speed for the current glitch level.
--- TIC-80 default speed is 6; each step past 1 adds 2.
--- @within CommuteGlitch
--- @return number Speed value for music().
function CommuteGlitch.music_speed()
local level = CommuteGlitch.get_level()
if level <= 1 then return 6 end
return 6 + (level - 1) * 2
end
--- Returns a corrupted copy of a sprite drawable list.
--- Applies flip_y and norman_echo id replacements based on glitch level.
--- Entries marked norman_echo should be drawn via draw_sprite_list for palette change handling.
--- @within CommuteGlitch
--- @param list table Drawable sprite list from Sprite.list_randomize.
--- @return table Corrupted copy of the list.
function CommuteGlitch.corrupt_sprite_list(list)
local level = CommuteGlitch.get_level()
if level < 3 or not list then return list end
local result = {}
for i, entry in ipairs(list) do
local e = {}
for k, v in pairs(entry) do e[k] = v end
if level >= 7 then
e.id = "norman_echo"
else
local n_flip = (level >= 5) and 2 or 1
local n_echo = (level >= 5) and 2 or (level >= 4) and 1 or 0
if i <= n_flip then e.flip_y = 1 end
if i > n_flip and i <= n_flip + n_echo then e.id = "norman_echo" end
end
table.insert(result, e)
end
return result
end
-- Palette indices for Norman echo color remap.
-- Implementer: pick ECHO_SRC as one of Norman's main body colors and ECHO_DST
-- as a contrasting or wrong palette color by inspecting the sprite sheet.
local ECHO_SRC = 4
local ECHO_DST = 14
-- Base nibble address of the PALETTE MAP in VRAM.
local PALETTE_MAP_ADDR = 0x03FF0 * 2
--- Draws a sprite list, applying a PALETTE MAP remap for norman_echo entries.
--- Uses poke4 to remap ECHO_SRC → ECHO_DST before drawing echoes, then restores.
--- @within CommuteGlitch
--- @param list table Drawable sprite list (may contain mixed normal and echo entries).
function CommuteGlitch.draw_sprite_list(list)
if not list then return end
local normal, echo = {}, {}
for _, entry in ipairs(list) do
if entry.id == "norman_echo" then
table.insert(echo, entry)
else
table.insert(normal, entry)
end
end
if #normal > 0 then
Sprite.draw_list(normal)
end
if #echo > 0 then
poke4(PALETTE_MAP_ADDR + ECHO_SRC, ECHO_DST)
Sprite.draw_list(echo)
poke4(PALETTE_MAP_ADDR + ECHO_SRC, ECHO_SRC)
end
end
local _flicker_tick = 0
--- Draws a random tile-flicker effect over the background (glitch level 7).
--- Every 3 frames draws 6 random 8x8 rects in random palette colors.
--- @within CommuteGlitch
function CommuteGlitch.draw_background_flicker()
_flicker_tick = (_flicker_tick + 1) % 3
if _flicker_tick ~= 0 then return end
for _ = 1, 6 do
local tx = math.random(0, math.floor(Config.screen.width / 8) - 1) * 8
local ty = math.random(0, math.floor(Config.screen.height / 8) - 1) * 8
local color = math.random(0, 15)
rect(tx, ty, 8, 8, color)
end
end

View File

@@ -8,10 +8,11 @@ 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. -- Internal meters for tracking game progress and player stats.
Meter.COLOR_ISM = Config.colors.red Meter.COLOR_ISM = Config.colors.orange
Meter.COLOR_WPM = Config.colors.blue Meter.COLOR_WPM = Config.colors.blue
Meter.COLOR_BM = Config.colors.black Meter.COLOR_BM = Config.colors.red
Meter.COLOR_BG = Config.colors.meter_bg Meter.COLOR_BG = Config.colors.meter_bg
Meter.COLOR_CONTOUR = Config.colors.white
--- Gets initial meter values. --- Gets initial meter values.
--- @within Meter --- @within Meter
@@ -126,16 +127,16 @@ function Meter.draw()
local m = Context.meters local m = Context.meters
local max = Meter.get_max() local max = Meter.get_max()
local bar_w = 44 local screen_w = Config.screen.width
local screen_h = Config.screen.height
local bar_w = screen_w * 0.25
local bar_h = 2 local bar_h = 2
local bar_x = 182 local edge = math.max(2, math.floor(screen_w * 0.03))
local label_x = 228 local bar_x = screen_w - bar_w - edge
local line_h = 5 local line_h = 3
local start_y = 1 local start_y = screen_h * 0.05
local bar_offset = math.floor((line_h - bar_h) / 2)
local meter_list = { local meter_list = {
{ key = "wpm", label = "WPM", color = Meter.COLOR_WPM, row = 0 }, { key = "wpm", label = "WPM", color = Meter.COLOR_WPM, row = 0 },
{ key = "ism", label = "ISM", color = Meter.COLOR_ISM, row = 1 }, { key = "ism", label = "ISM", color = Meter.COLOR_ISM, row = 1 },
@@ -144,15 +145,16 @@ function Meter.draw()
for _, meter in ipairs(meter_list) do for _, meter in ipairs(meter_list) do
local label_y = start_y + meter.row * line_h local label_y = start_y + meter.row * line_h
local bar_y = label_y + bar_offset local bar_y = label_y
local fill_w = math.max(0, math.floor((m[meter.key] / max) * bar_w)) local fill_w = math.max(0, math.floor((m[meter.key] / max) * bar_w))
rect(bar_x - 1, bar_y - 1, bar_w + 2, bar_h + 2, Meter.COLOR_CONTOUR)
rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG) rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG)
if fill_w > 0 then if fill_w > 0 then
rect(bar_x, bar_y, fill_w, bar_h, meter.color) rect(bar_x, bar_y, fill_w, bar_h, meter.color)
end end
print(meter.label, label_x, label_y, meter.color, false, 1, true) ---print(meter.label, label_x, label_y, meter.color, false, 1, true)
end end
local ascension_y = start_y + 3 * line_h + 1 local ascension_y = start_y + 3 * line_h + 1
Ascension.draw(bar_x, ascension_y, { spacing = 5 }) Ascension.draw(bar_x, ascension_y, { spacing = 8 })
end end

View File

@@ -12,8 +12,9 @@ function Minigame.draw_win_overlay(win_text)
local box_h = th + padding * 2 local box_h = th + padding * 2
local box_x = (Config.screen.width - box_w) / 2 local box_x = (Config.screen.width - box_w) / 2
local box_y = (Config.screen.height - box_h) / 2 local box_y = (Config.screen.height - box_h) / 2
local text_x = Config.screen.width / 2
rect(box_x, box_y, box_w, box_h, Config.colors.dark_grey) rect(box_x, box_y, box_w, box_h, Config.colors.dark_grey)
rectb(box_x, box_y, box_w, box_h, Config.colors.white) rectb(box_x, box_y, box_w, box_h, Config.colors.white)
Print.text_center(text, Config.screen.width / 2, box_y + padding, Config.colors.white) Print.text_center_contour(text, text_x, box_y + padding, Config.colors.black, false, 1, Config.colors.white)
end end

View File

@@ -2,7 +2,7 @@
-- 000:0404005f574fc2c3c71d2b53fff1e8ab52367e2553ffa30000875129adff83769c00e436fa77a8ff004dc3c3c7ffccaa -- 000:0404005f574fc2c3c71d2b53fff1e8ab52367e2553ffa30000875129adff83769c00e436fa77a8ff004dc3c3c7ffccaa
-- </PALETTE> -- </PALETTE>
-- <TILES> -- <TILES>
-- 000:00000000c666666006606060c636366006606060c606366006666660c0000000 -- 000:0000000006666660066060600636366006606060060636600666666000000000
-- 001:0000000006666666060600600666666606000600066666660633633300000000 -- 001:0000000006666666060600600666666606000600066666660633633300000000
-- 002:0000000066666666006000066666666660060006666666666333363300000000 -- 002:0000000066666666006000066666666660060006666666666333363300000000
-- 004:1111111111111111111111111111111111111111111111111111111111111111 -- 004:1111111111111111111111111111111111111111111111111111111111111111
@@ -17,7 +17,7 @@
-- 013:1000000004444444044444440444444404444444044444440444444404444444 -- 013:1000000004444444044444440444444404444444044444440444444404444444
-- 014:0000000144444240444424204444424044442420444442404444242044444240 -- 014:0000000144444240444424204444424044442420444442404444242044444240
-- 015:0000000004244444024444440424400002444444042424240242424200000000 -- 015:0000000004244444024444440424400002444444042424240242424200000000
-- 016:000000004444442044444240c004442044444240e424242042424240c0000000 -- 016:0000000044444420444442400004442044444240e42424204242424000000000
-- 017:0000000002424240042424200244444004244440024404400424044002440440 -- 017:0000000002424240042424200244444004244440024404400424044002440440
-- 018:0000000002424240042424200444424004444420044042400440442004404240 -- 018:0000000002424240042424200444424004444420044042400440442004404240
-- 019:0222222200010012055016020150660205501602015066020550160200000000 -- 019:0222222200010012055016020150660205501602015066020550160200000000
@@ -33,7 +33,7 @@
-- 029:0440442004404240044044200444424004444420024242400424242000000000 -- 029:0440442004404240044044200444424004444420024242400424242000000000
-- 030:0000000006666660036060600360666006606060036660600360666003606060 -- 030:0000000006666660036060600360666006606060036660600360666003606060
-- 031:0222222200010010033033030130130103303303013013010330330300000000 -- 031:0222222200010010033033030130130103303303013013010330330300000000
-- 032:22222220c10012203033022030130220303302203013022030330220c0000000 -- 032:2222222001001220303302203013022030330220301302203033022000000000
-- 033:0333011103330111033301110333011103330111033301110333011100000000 -- 033:0333011103330111033301110333011103330111033301110333011100000000
-- 034:1110011111100111111001111110011111100111111001111110011100000000 -- 034:1110011111100111111001111110011111100111111001111110011100000000
-- 035:1110333011103330111033301110333011103330111033301110333000000000 -- 035:1110333011103330111033301110333011103330111033301110333000000000
@@ -49,7 +49,7 @@
-- 045:2222222022222220222222202222222022222220000000001111100011111000 -- 045:2222222022222220222222202222222022222220000000001111100011111000
-- 046:1111111111111111111111111000000002222222022222220222222202222222 -- 046:1111111111111111111111111000000002222222022222220222222202222222
-- 047:1111111111111111111111110000000022222222222222222222222222222222 -- 047:1111111111111111111111110000000022222222222222222222222222222222
-- 048:111111111111111111111111c000100022220444e222044422220444e2220444 -- 048:1111111111111111111111110000100022220444e222044422220444e2220444
-- 049:1111111111111111111111111100001100222201022222200222222002222220 -- 049:1111111111111111111111111100001100222201022222200222222002222220
-- 050:1111111011111110111111101111111011111110111111101111111011110000 -- 050:1111111011111110111111101111111011111110111111101111111011110000
-- 051:1111111111111111111111111111111111111111111111111111111100011111 -- 051:1111111111111111111111111111111111111111111111111111111100011111
@@ -81,7 +81,7 @@
-- 077:0000000011111110000000006611166655555551555555515555555166666111 -- 077:0000000011111110000000006611166655555551555555515555555166666111
-- 078:1111111111111111111100001110919111101010110191011019191000919190 -- 078:1111111111111111111100001110919111101010110191011019191000919190
-- 079:1111111111111111000000009191919000000010111110901111101000000090 -- 079:1111111111111111000000009191919000000010111110901111101000000090
-- 080:09191990c991990209199020c999020209902020c902020200202020c0000000 -- 080:0919199009919902091990200999020209902020090202020020202000000000
-- 081:0000000002020200202020200200022020002220000222202022222002222220 -- 081:0000000002020200202020200200022020002220000222202022222002222220
-- 082:5555555555555055555501056666010155550105555501055555010566610106 -- 082:5555555555555055555501056666010155550105555501055555010566610106
-- 083:5555555555555555000000000222222202222222022222220222222202222222 -- 083:5555555555555555000000000222222202222222022222220222222202222222
@@ -97,7 +97,7 @@
-- 093:0222022002220220022202200222022002220220022202200222022002220220 -- 093:0222022002220220022202200222022002220220022202200222022002220220
-- 094:1033330110333301103333011033330110333301103333011033330110000001 -- 094:1033330110333301103333011033330110333301103333011033330110000001
-- 095:3333101033331090333310103333109011113010111130901111301011113000 -- 095:3333101033331090333310103333109011113010111130901111301011113000
-- 096:00000000c111111100000000c106666601051555c105155501056555c0066611 -- 096:0000000001111111000000000106666601051555010515550105655500066611
-- 097:0000000011111110000000006666101055555010555550105555501066616000 -- 097:0000000011111110000000006666101055555010555550105555501066616000
-- 098:5555555555555550555555056666116655550055555500555555655566666611 -- 098:5555555555555550555555056666116655550055555500555555655566666611
-- 099:0555555500555555050555550661111105500555115005550055555500616666 -- 099:0555555500555555050555550661111105500555115005550055555500616666
@@ -113,7 +113,7 @@
-- 109:1111111011111110111111101111111011111110111111100000000566000066 -- 109:1111111011111110111111101111111011111110111111100000000566000066
-- 110:1111111111111111111111111111010011101044111010441110104400000010 -- 110:1111111111111111111111111111010011101044111010441110104400000010
-- 111:1111111111111111111111110011111144011111440111114401111110000000 -- 111:1111111111111111111111110011111144011111440111114401111110000000
-- 112:11111111111111111111111111111111111111111111111111110111c0004000 -- 112:1111111111111111111111111111111111111111111111111111011100004000
-- 113:3333104433331044333310003333111111113333111133331111333311113333 -- 113:3333104433331044333310003333111111113333111133331111333311113333
-- 114:4403111144031111000311113333111111110000111044441104444411014444 -- 114:4403111144031111000311113333111111110000111044441104444411014444
-- 115:3304440133044401330444013304440100104033440103334440333344103333 -- 115:3304440133044401330444013304440100104033440103334440333344103333
@@ -177,7 +177,7 @@
-- 173:0333333010333330103333301033333010333330100000001111111111111111 -- 173:0333333010333330103333301033333010333330100000001111111111111111
-- 174:111111111111111111111111111111111111111112222222a1a1a1a21a1a1a12 -- 174:111111111111111111111111111111111111111112222222a1a1a1a21a1a1a12
-- 175:1111111111111111111111111111111111111111444444444444444444444444 -- 175:1111111111111111111111111111111111111111444444444444444444444444
-- 176:00000000c111111001101010c131311001101010c101311001111110c0000000 -- 176:0000000001111110011010100131311001101010010131100111111000000000
-- 177:0000000001111111010100100111111101000100011111110133133300000000 -- 177:0000000001111111010100100111111101000100011111110133133300000000
-- 178:0000000011111111001000011111111110010001111111111333313300000000 -- 178:0000000011111111001000011111111110010001111111111333313300000000
-- 179:11111111111111111111111111111111111100001110b161110b100010810101 -- 179:11111111111111111111111111111111111100001110b161110b100010810101
@@ -225,7 +225,7 @@
-- 221:0200000002000000020000000200000002000000020000000211111100000000 -- 221:0200000002000000020000000200000002000000020000000211111100000000
-- 222:0000000000000000000000000000000000000000000000001111111100000000 -- 222:0000000000000000000000000000000000000000000000001111111100000000
-- 223:000000000000000000000000000000000000000000000000111dd11100000000 -- 223:000000000000000000000000000000000000000000000000111dd11100000000
-- 224:00000010c000001000000010c000001000000010c000001011111110c0000000 -- 224:0000001000000010000000100000001000000010000000101111111000000000
-- 225:3333333333333333333333333333333313131313313131311313131331313131 -- 225:3333333333333333333333333333333313131313313131311313131331313131
-- 226:0203333302033333020333330203333302031313020131310203131300013131 -- 226:0203333302033333020333330203333302031313020131310203131300013131
-- 227:3333302033333020333330203333302013131020313130201313102031313000 -- 227:3333302033333020333330203333302013131020313130201313102031313000
@@ -241,7 +241,7 @@
-- 237:1444402044140220444402204144022000402220220222200222222010222220 -- 237:1444402044140220444402204144022000402220220222200222222010222220
-- 238:3333330033333022333302223333022213130222313102221313101231313100 -- 238:3333330033333022333302223333022213130222313102221313101231313100
-- 239:0003010322200103222201032222010322220103222201012210101300013131 -- 239:0003010322200103222201032222010322220103222201012210101300013131
-- 240:02220111c222001102220000c222033302220333c222200002222222c2222222 -- 240:0222011102220011022200000222033302220333022220000222222202222222
-- 241:1002222001202220002022203002222031222220022222202222222022222220 -- 241:1002222001202220002022203002222031222220022222202222222022222220
-- 242:3333333333333330333333033333113313130013313100311313131331313131 -- 242:3333333333333330333333033333113313130013313100311313131331313131
-- 243:0333333300333333030333330331133303100313113001310013131300313131 -- 243:0333333300333333030333330331133303100313113001310013131300313131
@@ -251,130 +251,262 @@
-- 247:0000000011111110000000003333301013131010313130101313101031313000 -- 247:0000000011111110000000003333301013131010313130101313101031313000
-- </TILES> -- </TILES>
-- <SPRITES> -- <SPRITES>
-- 002:00000000000000000000444400044444000444440044ffff004fffff004f3333 -- 000:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 003:0000000000000000441600004242600044241000ff426000fff4100033f26000 -- 001:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 004:00000333000035550003655500365555003555ff00356fff00365f3f00355fff -- 002:cccccccccccc0000ccc04444cc044444cc044444c044ffffc04fffffc04f3333
-- 005:33000000553000005563000055563000ff553000fff53000f3f63000fff53000 -- 003:cccccccc0000cccc44160ccc424260cc442410ccff4260ccfff410cc00f260cc
-- 008:0000005a00005a55000055a50000a55a00005a5700005533000575f30000757f -- 004:ccccc000cccc0555ccc06555cc065555cc0555ffcc056fffcc065f0fcc055fff
-- 009:55a50000a55a50005a55a00057555500fff7a5003f335a00fff3f500fffffa00 -- 005:00cccccc550ccccc5560cccc55560cccff550cccfff50cccf0f60cccfff50ccc
-- 016:0000000000000000000000000000003000000353000035350003535100353535 -- 006:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 017:0000000000000000000000003000000053300000151300005151300015151300 -- 007:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 018:004f99ff000fffff0000ff3300000fff00003666000355550035652503163555 -- 008:cccc0000ccc05a55ccc055a5ccc0a55accc05a57ccc05533cc0575f3ccc0757f
-- 019:99f41000fff26000ff600000f600000063300000555330005555530055535530 -- 009:00000ccca55a50cc5a55a0cc5755550cfff7a50c3f335a0cfff3f50cfffffa0c
-- 020:00356f6f003655f60365511f3653122f3531222f363221220532232203322322 -- 010:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 021:f6f530006f563000f1156300f2215300f2226300221233002232130022121300 -- 011:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 022:000000000000005100000155000055150000157f0000551300057f3f00017fff -- 012:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 023:00000000555000005155000015515000ff7f5100ff31f200fff3ff0033fff200 -- 013:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 024:0000057f00000005000000050000559900055911005599110055119900551199 -- 014:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 025:f3ff5000fff500005f5000004415100077995100449915007711955044119550 -- 015:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 026:00000000000001330001331300031333001333330031f7f7001133390031999f -- 016:cccccccccccccccccccccccccccccc0cccccc050cccc0535ccc05351cc053535
-- 027:0000000013310000131330003333130033333100f7f113003337310099975300 -- 017:cccccccccccccccccccccccc0ccccccc500ccccc1510cccc51510ccc151510cc
-- 028:000000000000000000000a77000057770005777700577777005775a50077a7ff -- 018:c04f99ffcc0fffffccc0ff00cccc0fffcccc0666ccc05555cc056525c0160555
-- 029:0000000000000000777a0000777750007777750077777750a5a57750fff7a770 -- 019:99f410ccfff260ccff600cccf60ccccc600ccccc55500ccc555550cc5550550c
-- 030:000000000000051500001151000555550001517f000557ff0001ff3f0007ff1f -- 020:cc056f6fcc0655f6c065511f0650122f0501222f06022122c5022322c0022322
-- 031:00000000150000005110000055500000f7110000ff750000f3f10000f1f50000 -- 021:f6f50ccc6f560cccf11560ccf22150ccf22260cc221230cc223210cc221210cc
-- 032:0033535100353533003351ff00351f3f0003ff3f0003ffff00003ff3000323ff -- 022:cccccc00ccccc051cccc0155ccc05515ccc0157fccc05513cc057f0fcc017fff
-- 033:5555530033555300ff155300f3f15300f3ff3000ffff30003ff30000ff323000 -- 023:000ccccc5550cccc51550ccc155150ccff7f510cff31f20cfff0ff0c00fff20c
-- 034:036135250316355503613525031633110333331103f333330333333300033333 -- 024:cccc057fccccc005cccc0555ccc05599cc055911c0559911c0551199c0551199
-- 035:55565530555355305556553013335530133333303333ff303333333033330000 -- 025:f3ff50ccfff50ccc5f50cccc441500cc7799510c4499150c7711955044119550
-- 036:00322322003223330033331a003ff31a003ff3a1003333a100003a1a00003a1a -- 026:ccccc000ccc00133cc013313cc031333c0133333c031f7f7c0113339c031999f
-- 037:223213003332130011133300111f300011133000111300001113000011130000 -- 027:0000cccc13310ccc131330cc3333130c3333310cf7f1130c3337310c9997530c
-- 038:000007ff0001117f0016171f0155175501561755155117555f7157555ff15755 -- 028:ccccccccccccc000cccc0a77ccc05777cc057777c0577777c05775a5c077a7ff
-- 039:fffff700333f2000ffff7100555571105555716155557111555575ff555575ff -- 029:cccccccc0000cccc777a0ccc777750cc7777750c77777750a5a57750fff7a770
-- 040:005599110055991100ff533300fff511007fff110007f5110000311300003113 -- 030:ccccc000cccc0515ccc01151cc055555cc01517fcc0557ffcc01ff0fcc07ff1f
-- 041:479915504499155047333f7014113ff011113f70111130003311300003113000 -- 031:00cccccc150ccccc5110cccc5550ccccf7110cccff750cccf0f10cccf1f50ccc
-- 042:0035777f0001776600005777000000550088885508bbb84508bbb8448bb8b844 -- 032:cc035351cc053500cc0351ffcc051f0fccc0ff0fccc0ffffcccc0ff0ccc020ff
-- 043:77775300677500007750000055880000558b8000548bb800228bbb80428b8b80 -- 033:555550cc005550ccff1550ccf0f150ccf0ff0cccffff0ccc0ff0ccccff020ccc
-- 044:005a7f3f0057ff3f07f7ffff07757fdf077a57fd0575357f005a66a70006aa6f -- 034:c0613525c0163555c0613525c0163311c0003311c0f03333cc003333ccc03333
-- 045:ff3f7a50ff3ff750fffff777ffdf7577ddf75a77ff75157577a66a50ff6aa600 -- 035:5556550c5553550c5556550c1330550c1330000c3330ff0c333000cc3330cccc
-- 046:0005ffff00005ff3000005ff0017775500242777002121710024277100212171 -- 036:cc022322cc022333cc03331acc0ff31acc0ff3a1ccc003a1cccc0a1acccc0a1a
-- 047:fff500003f500000f50000005771000077720000171200001772000017120000 -- 037:223210cc333210cc111300cc111f0ccc11100ccc1110cccc1110cccc1110cccc
-- 048:00323123003231430032314300313339000f3333000033300000333000033330 -- 038:ccc007ffcc01117fc016171f0155175501561755055117550f7157550ff15755
-- 049:324303003443030034430300933313003333f000033300000333000003333000 -- 039:fffff70c000f20ccffff710c555571105555716055557110555575f0555575f0
-- 050:0003333000033330000333300003333000053530003311300031113000333330 -- 040:c0559911c0559911c0ff5333c0fff511c07fff11cc07f511ccc03110cccc0110
-- 051:3333000033330000333300003333000035350000311330003111300033333000 -- 041:479915504499155047333f7014113ff011113f701111000c00110cccc0110ccc
-- 052:000031a1000031a100003a1a00003a1a00003333000003f3000003f300000330 -- 042:c035777fcc017766ccc05777cc088855c08bb85508bbb84508bbb8440bb8b844
-- 053:111300001113000011130000111300003333000003f3000003f3000003300000 -- 043:7777530c677500cc7750cccc55880ccc558b80cc548bb80c228bbb80428b8b80
-- 054:5ff7646607f59999000991330003993000099130000133300001221000011100 -- 044:cc0a7f3fc075ff3f0777ffff07757fdf077a57fd0575357fc05a66a7cc06aa6f
-- 055:666646ff999993f7339913000039930000991300001333000012210000111000 -- 045:ff3f7a0cff3ff70cfffff770ffdf7570ddf75a70ff75157077a66a50ff6aa60c
-- 056:0000311300003113000031130000311300003113000033330000165100001111 -- 046:cc05ffffccc05ff3cccc05ffcc007755c0242777c0212171c0242771c0212171
-- 057:0311300003113000031130000311300003113000033330000165100001111000 -- 047:fff50ccc3f50ccccf50ccccc5770cccc77720ccc17120ccc17720ccc17120ccc
-- 058:8888b8445f78b8445f58884405031330000331300003133000053550000577f0 -- 048:cc020123cc020143cc020143cc010309cc0f0300ccc0030ccccc030cccc0000c
-- 059:218b8880428b87f5218885f5031330500331300003133000053550000577f000 -- 049:324030cc344030cc344030cc903010cc0030f0ccc0300cccc030ccccc0000ccc
-- 060:0056aa66001f6aaa001fa666007f6aaa00006aaa0000a666000007d7000001d1 -- 050:ccc0330cccc0330cccc0330cccc0330cccc0350ccc03110ccc01110ccc00000c
-- 061:666aa650aaaa6f106666af10aaaa6f70aaaa60006666a000007d7000001d1000 -- 051:0330cccc0330cccc0330cccc0330cccc0530cccc01130ccc01110ccc00000ccc
-- 062:0024277700ff611100fff333005f533300003333000033300000333000001110 -- 052:cccc01a1cccc01a1cccc0a1acccc0a1acccc0000ccccc0f0ccccc0f0ccccc00c
-- 063:77720000116f0000335ff000333f000033300000333000003330000001110000 -- 053:1110cccc1110cccc1110cccc1110cccc0000ccccc0f0ccccc0f0ccccc00ccccc
-- 064:00000000000000000000011100003311000111170003317f000117ff00553333 -- 054:0ff7646607f59999c0099100cc03990ccc09910ccc01330ccc01220ccc0000cc
-- 065:0000000000000000110000001110000077110000ff710000ff77000013330000 -- 055:666646f0999993f00099100cc03990ccc09910ccc01330ccc01220ccc0000ccc
-- 066:00000000000000330000033300003333000033f7000035ff000535ff0007f333 -- 056:cccc0110cccc0110cccc0110cccc0110cccc0110cccc0330cccc0650cccc0000
-- 067:0000000030000000333300003333000037f30000ffff0000ffff0000f333f000 -- 057:c0110cccc0110cccc0110cccc0110cccc0110cccc0330cccc0650cccc0000ccc
-- 068:000000000000000000000000000000000000000000000122000012440001447f -- 058:0888b8440f78b8440f58884405031300c0c0310cccc0130cccc0350cccc0000c
-- 069:00000000000000000000000000000000000000002210000044210000f7441000 -- 059:218b8880428b87f0218885f000130050c0310c0cc0130cccc0350cccc0000ccc
-- 070:000000000000000000000333000038880003888800388881038388310338835f -- 060:c056aa66c01f6aaac01fa666c07f6aaacc0f6aaaccc0a666cccc07d7cccc0000
-- 071:00000000000000003330000088880000883380003355380016665300f7ff7300 -- 061:666aa650aaaa6f106666af10aaaa6f70aaaa600c6666a0cc007d70cccc0000cc
-- 072:0000000000000000000000550000055500005577000057ff00007f55000fff33 -- 062:c0242777c0ff6111c0fff333c05f5333cc003300cccc030ccccc030ccccc000c
-- 073:0000000000000000555550005555550077777550ffffff507f755f703f333fff -- 063:77720ccc116f0ccc335ff0cc333f0ccc0330cccc030ccccc030ccccc0000cccc
-- 074:0000000000000011000001130000113100011313000131310013137c003131ff -- 064:ccccccccccccc000cccc0111ccc03311cc011117cc03317fcc0117ffc0553333
-- 075:000000001110000013110000313110001313100031313100ffff7100fffff100 -- 065:cccccccc00cccccc110ccccc1110cccc77110cccff710cccff770ccc13330ccc
-- 076:0000000000000055000005550000055500005577000057ff0007ff55000fff99 -- 066:cccccc00ccccc033cccc0333ccc03333ccc033f7ccc035ffcc0535ffcc07f333
-- 077:0000000055550000555550005555500075775500fffff5005f555f703f993ff0 -- 067:0ccccccc3000cccc33330ccc33330ccc37f30cccffff0cccffff0cccf333f0cc
-- 078:0000000000000000000000000000000000001111000014440000144400001555 -- 068:ccccccccccccccccccccccccccccccccccccc000cccc0122ccc01244cc01447f
-- 079:0000000000000000000000000000000011100000441000004410000055100000 -- 069:cccccccccccccccccccccccccccccccc000ccccc2210cccc44210cccf74410cc
-- 080:0077533f000ff77f0005fff500005ff70000015f0005f315005ff3ff00ff3333 -- 070:ccccccccccccccccccccc000cccc0888ccc08888cc088880c0838801c038805f
-- 081:f33f0000f77f00005ff000007f500000f500000053f00000f3f50000333f0000 -- 071:cccccccccccccccc0000cccc88880ccc880080cc0055080c1666530cf7ff730c
-- 082:000fff330005ffff000057f50000007f000333350031313f0031313303113133 -- 072:cccccccccccccc00ccccc055cccc0555ccc05577ccc057ffccc07f55cc0fff33
-- 083:ff33f000ffff100055f10000ff70000055330000ff3130003331300033311300 -- 073:cccccccc00000ccc555550cc5555550c77777550ffffff507f755f703f333ff0
-- 084:000447ff000fffff0007f33f000ff11f000f7fff0000ff750000042400001142 -- 074:cccccccccccccc00ccccc013cccc0131ccc01313ccc03131cc031377cc0131ff
-- 085:ff744000ffff1000f33f7000f11f5000fff7f00057ff00004240000044110000 -- 075:cccccccc000ccccc1310cccc31310ccc13130ccc313130ccffff70ccfffff0cc
-- 086:038383390338833b038383130338337f0383835f003838350003838300003333 -- 076:cccccccccccccc00ccccc055cccc0555ccc05577ccc057ffcc07ff55cc0fff99
-- 087:99539900bb33bb0099513900fffff530ff66f1007fff53001555310033333000 -- 077:cccccccc0000cccc55550ccc555550cc7577550cfffff50c5f555f0c3f993f0c
-- 088:000f7ff300005fff000007ff0000007f0000021f000024410002444400124244 -- 078:cccccccccccccccccccccccccccccccccccc0000cccc0444cccc0444cccc0555
-- 089:fff3ff7ff1ffff50fffff700111f7000ffff1000111142004244442042442410 -- 079:cccccccccccccccccccccccccccccccc000ccccc440ccccc440ccccc550ccccc
-- 090:005f5733005f1f2300015fff0000077f00011117001244210144414201421444 -- 080:c077500fcc0ff77fcc05fff5ccc05ff7cccc015fccc0f315cc0ff3ffc0ff3333
-- 091:ff33f500ff23f500fffff00011ff500077710000112410004442410042441410 -- 081:f00f0cccf77f0ccc5ff0cccc7f50ccccf50ccccc53f0ccccf3f50ccc333f0ccc
-- 092:00005fff000005ff0000001f0000000100000114000018140000181400018114 -- 082:cc0fff00cc05ffffccc057f5cccc007fccc03335cc01313fc031313303113133
-- 093:fffff500111f5000fff500001110000042411000424180004241810042411100 -- 083:ff00f0ccffff10cc55f10cccff70cccc5530ccccff310ccc33310ccc333110cc
-- 094:00001fff00001f3f0000cf3f0000cfff00000cf3000023cc000131ff001f1f11 -- 084:cc0447ffcc0fffffcc07f33fcc0ff11fcc0f7fffccc0ff75cccc0424cccc0142
-- 095:ff1000003f1000003ff00000fff00000fc000000c3200000f13100001f1f1000 -- 085:ff7440ccffff10ccf33f70ccf11f50ccfff7f0cc57ff0ccc4240cccc44110ccc
-- 096:00ff33330033133305f333330ff31333033333330f5313330ff1311107f13333 -- 086:c0838039c038803bc0838013c038307fc083805fcc083805ccc08383cccc0333
-- 097:333f0000333f5000333f5000333ff000333330003335f500111ff5003337f000 -- 087:9953990cbb33bb0c9951390cfffff50cff66f10c7fff50cc15550ccc33330ccc
-- 088:cc0f7ff0ccc05fffcccc07ffccccc07fcccc021fccc02441cc024444c0124244
-- 089:fff0ff70f1ffff50fffff70c111f70ccffff10cc1111420c4244442042442410
-- 090:cc0f5733cc0f1f23ccc00fffcccc077fccc01117cc024421c0444142c0421444
-- 091:ff33f0ccff23f0ccfffff0cc11ff0ccc7770cccc11240ccc444240cc4244140c
-- 092:ccc05fffcccc05ffccccc01fcccccc01ccccc014cccc0814ccc01814ccc08114
-- 093:fffff50c111f50ccfff50ccc1110cccc42410ccc424180cc424180cc424110cc
-- 094:cccc0fffcccc0f3fcccc0f0fcccc0fffccccc0f3cccc0377ccc031ffcc0f1f11
-- 095:ff0ccccc3f0ccccc0f0cccccff0cccccf0cccccc730cccccf130cccc1f1f0ccc
-- 096:c0ff3333c033133305f333330ff31333033333330f5313330ff1311107f13333
-- 097:333f0ccc333f50cc333f50cc333ff0cc333330cc3335f50c111ff50c3337f0cc
-- 098:03113133031131330333313303113131033331320555313107ff313307ff3131 -- 098:03113133031131330333313303113131033331320555313107ff313307ff3131
-- 099:333113003331130033333300213113004233330021317f0033317f5001317500 -- 099:333110cc333110cc333330cc213110cc423330cc21317f0c33317f501131750c
-- 100:0002441400244441024444240242442414424424144144241441442414414424 -- 100:ccc04414cc044441c0444424c042442404424424044144240441442404414424
-- 101:4144210014444200424444204244422042444241424441414244414142444141 -- 101:414420cc1444420c424444204244422042444240424441404244414042444140
-- 102:000333330033a3530032a353003a332303a23333032a333303a23333032a3333 -- 102:ccc03333cc03a353cc02a353cc0a3323c0a23333c02a3333c0a23333c02a3333
-- 103:3333300055353300553533002232323033333130333332303333313333333233 -- 103:33330ccc553530cc553530cc2232320c3333310c3333320c3333313033333230
-- 104:001441440014424400144144001441240017f311001ff133001fff330007f333 -- 104:c0144144c0144244c0144144c0144124c017f311c01ff133c01fff33cc07f330
-- 105:4244142042442410424414204244122012113f7031333ff033333ff030333f00 -- 105:4244142042442410424414204244122012113f7031333ff033333ff000033f0c
-- 106:01421444014414440142144431421444011111110fff1a5a0ff755a505755a51 -- 106:c0421444c0441444c0421444c0421444c0111111c0ff1a5ac0f755a5c0755a50
-- 107:44441410424414104444141042441410111111105a5a55f5a5a5a5f5111a5170 -- 107:4444140c4244140c4444140c4244140c1111110c5a5a55f0a5a5a5f0001a5170
-- 108:0001181400018114000118140001811300011333005fff33000f773300055333 -- 108:ccc01814ccc08114ccc01814ccc08113cc001333c05fff33cc0f7733cc055330
-- 109:424181104241181042418110222318101113310033335ff03333f77033335500 -- 109:4241810c4241180c4241810c2223180c1113310c33335ff03333f7700033550c
-- 112:0553333300033330000333000033330000333300003333000011110000121200 -- 110:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 113:3330000033300000333000003330000033300000333000001111000012120000 -- 111:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 114:05f5313103003131033311310311113103111131003111300003333000012120 -- 112:05533333c0033330ccc0330ccc03330cc03330ccc03330ccc01110ccc0000ccc
-- 115:0131130001311300013113000131130001311300003113000033330000121200 -- 113:33000ccc030ccccc030ccccc030ccccc030ccccc030ccccc0110cccc0000cccc
-- 116:0fff242407f72211005544200002442000024420000244200002222000024440 -- 114:05f53131c0003131c0331131c0111131c0111130cc01110cccc0330cccc0000c
-- 117:424441f1112227f1024425500244200002442000024420000222200002444000 -- 115:113110cc113110cc113110cc113110cc003110cccc0110cccc0330cccc0000cc
-- 118:0333333307ff333307f733330070333000003330000033300000111000001110 -- 116:0fff242407f72211c0552400cc00240cccc0240cccc0240cccc0220cccc0000c
-- 119:33333575333331f1333335750033300000333000003330000011100000111100 -- 117:424441f0112227f000442550c044200cc04420ccc04420ccc02220ccc00000cc
-- 120:0000033300000333000003330000033300000333000003330000033300000111 -- 118:0333333307ff333307f73300c070030ccc0c030ccccc030ccccc010ccccc000c
-- 121:0033300000333000003330000033300000333000003330000033300010111100 -- 119:33333570333331f000033570cc03300ccc030ccccc030ccccc010ccccc0000cc
-- 122:005015a100001a51000015a100001a51000015a1000022510000924900009999 -- 120:ccc00330ccccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc000
-- 123:0315a150001a51000015a100001a51000015a100002251000092490000999900 -- 121:cc0300cccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccc0c0000cc
-- 124:0000033300000333000003330000033300000333000003330000033300000111 -- 122:c05015a0cc0c0a50cccc05a0cccc0a50cccc05a0cccc0250cccc0990cccc0000
-- 125:0333000003330000033300000333000003330000033300000333000000111000 -- 123:c015a150cc0a500ccc05a0cccc0a50cccc05a0cccc0250cccc0990cccc0000cc
-- 129:0000000000000000000000000000000000000000011515101151515151515151 -- 124:ccc00330ccccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc000
-- 144:0000000500000015000000150000057f000005ff0000017f0000331700073331 -- 125:c03000ccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc0000ccc
-- 145:1ffffff7ff0fff0fff1fff1fffffffffff7555fffffffffffff111ff57fffff7 -- 126:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 146:1000000071000000f5000000ff000000ff500000ff5000007513100051333700 -- 127:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 160:0077333303331333333313333331333333313333355133335ff513335ff71111 -- 128:ccccccccccccccccccccccccccccccccccccccccccccccccccccccc0ccccccc0
-- 129:ccccccccccccccccccccccccccccccccc000000c011515101151515151515151
-- 130:cccccccccccccccccccccccccccccccccccccccccccccccc0ccccccc10cccccc
-- 131:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 132:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 133:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 134:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 135:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 136:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 137:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 138:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 139:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 140:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 141:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 142:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 143:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 144:cccccc05ccccc015ccccc015cccc057fcccc05ffcccc017fccc03317cc073331
-- 145:1ffffff7ff0fff0fff0fff0fffffffffff7555fffffffffffff111ff57fffff7
-- 146:10cccccc710cccccf50cccccff0cccccff50ccccff510ccc751310cc5133370c
-- 147:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 148:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 149:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 150:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 151:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 152:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 153:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 154:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 155:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 156:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 157:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 158:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 159:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 160:c077333303331333033313330331333303313333055133330ff513330ff71111
-- 161:1111111133333333333337333333373333333733333337333333373311117711 -- 161:1111111133333333333337333333373333333733333337333333373311117711
-- 162:133337003733133077731330333331333333313333333155333337ff1111175f -- 162:1333370c3733130c7773130c333331303333313033333150333337f011111750
-- 176:0775333300031333000333130000333300001333000033330000033300000111 -- 163:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 177:3335773333333533333333331313131333333333300000033000000310000001 -- 164:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 178:3333357533331000331300001333000033310000333300003331000011110000 -- 165:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 166:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 167:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 168:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 169:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 170:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 171:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 172:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 173:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 174:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 175:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 176:07753333c0031333ccc03313cccc0333cccc0333cccc0333ccccc033ccccc000
-- 177:33357733333335333333333313131313000000000cccccc00cccccc00cccccc0
-- 178:333335703333100c33100ccc1330cccc3330cccc3330cccc3330cccc0000cccc
-- 179:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 180:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 181:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 182:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 183:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 184:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 185:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 186:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 187:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 188:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 189:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 190:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 191:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 192:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 193:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 194:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 195:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 196:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 197:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 198:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 199:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 200:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 201:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 202:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 203:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 204:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 205:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 206:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 207:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 208:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 209:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 210:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 211:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 212:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 213:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 214:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 215:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 216:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 217:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 218:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 219:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 220:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 221:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 222:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 223:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 224:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 225:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 226:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 227:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 228:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 229:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 230:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 231:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 232:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 233:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 234:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 235:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 236:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 237:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 238:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 239:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 240:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 241:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 242:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 243:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 244:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 245:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 246:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 247:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 248:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 249:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 250:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 251:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 252:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 253:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 254:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- 255:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-- </SPRITES> -- </SPRITES>
-- <MAP> -- <MAP>
-- 000:ffffffffff0010201020102010201020102010201020102000ffffffffff40404040404087f3f3f3f397a7b7c7d7a7e7f70818a7b7c7d7a7b7c7d7a70b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 000:ffffffffff0010201020102010201020102010201020102000ffffffffff40404040404087f3f3f3f397a7b7c7d7a7e7f70818a7b7c7d7a7b7c7d7a70b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -387,7 +519,7 @@
-- 007:ffffffffffb264748494a4b4c4d46494649464940040e4f4b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 007:ffffffffffb264748494a4b4c4d46494649464940040e4f4b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 008:ffffffffffe1c30515d325d33545c3d355d3c3d365b17585e1ffffffffff4040404040404098989898404040404040404040404040404040404040407c1e7e8e1e1e1e9eae1ebe1e1e1e1e1e1e1e1e72821ebe1e72821ebe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 008:ffffffffffe1c30515d325d33545c3d355d3c3d365b17585e1ffffffffff4040404040404098989898404040404040404040404040404040404040407c1e7e8e1e1e1e9eae1ebe1e1e1e1e1e1e1e1e72821ebe1e72821ebe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 009:ffffffffffb264e395a5b594e39564c5d5946494e5b1b1f5b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e05151ebe1ecedeeefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 009:ffffffffffb264e395a5b594e39564c5d5946494e5b1b1f5b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e05151ebe1ecedeeefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 010:ffffffffffe1c306162636d34656c395d5d3c3d376b1b1b1e1ffffffffff404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 010:ffffffffffe1c306162636d34656c3e3d5d3c3d376b1b1b1e1ffffffffff404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 011:ffffffffffb264946494649464948696a694649410201020b2ffffffffff4040404040404040404040401a2a3a4a5a6a7a40404040404040404040405d1ee3952f3f1e4f5f1ebe1e1e1e1e1e1e1e1ee3951ebe1ee3951ebe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 011:ffffffffffb264946494649464948696a694649410201020b2ffffffffff4040404040404040404040401a2a3a4a5a6a7a40404040404040404040405d1ee3952f3f1e4f5f1ebe1e1e1e1e1e1e1e1ee3951ebe1ee3951ebe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 012:ffffffffffe1c37282d3c3d3c3d3b6c6d6d3c3d300e6f607e1ffffffffff4040404040404098989898408a9aaabaca9ada40404040404040404040407c1e4f5f1ebe1e0515eefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 012:ffffffffffe1c37282d3c3d3c3d3b6c6d6d3c3d300e6f607e1ffffffffff4040404040404098989898408a9aaabaca9ada40404040404040404040407c1e4f5f1ebe1e0515eefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 013:ffffffffffb264e395946494649464946494649465172737b2ffffffffffeaeaeaeaeaeaeafafafafaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea5d1e0515eefe1e6f7f2f3f1e1e1e1e1e1e1e1e6f7f2f3f1e6f7f2f3f1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 013:ffffffffffb264e395946494649464946494649465172737b2ffffffffffeaeaeaeaeaeaeafafafafaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea5d1e0515eefe1e6f7f2f3f1e1e1e1e1e1e1e1e6f7f2f3f1e6f7f2f3f1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

View File

@@ -4,5 +4,5 @@
-- desc: Life of a programmer -- desc: Life of a programmer
-- site: https://git.teletype.hu/games/impostor -- site: https://git.teletype.hu/games/impostor
-- license: MIT License -- license: MIT License
-- version: 1.0-beta2 -- version: 1.0-beta3
-- script: lua -- script: lua

View File

@@ -6,52 +6,66 @@ local STATE_CHOICE = "choice"
local ASC_01_TEXT = [[ local ASC_01_TEXT = [[
Normann seems to be in line, Normann seems to be in line,
...
and stays seeking for oxes and stays seeking for oxes
...
within the confines. within the confines.
...
Very good. Very good.
]] ]]
local ASC_12_TEXT = [[ local ASC_12_TEXT = [[
We have a problem! We have a problem!
...
Normann formed his first thought. Normann formed his first thought.
...
He saw the tracks. He saw the tracks.
]] ]]
local ASC_23_TEXT = [[ local ASC_23_TEXT = [[
Not good, not terrible. Not good, not terrible.
...
Normann caught his glimpse Normann caught his glimpse
...
of another way of another way
...
- quite literally - - quite literally -
...
if this continues, if this continues,
...
we will lose control. we will lose control.
]] ]]
local ASC_34_TEXT = [[ local ASC_34_TEXT = [[
There is no turning back now for Norman. There is no turning back now for Norman.
...
He caught on. He caught on.
...
I hoped it would never come to this... I hoped it would never come to this...
]] ]]
--[[ Norman speaks for the first time during MM screen ]] --[[ Norman speaks for the first time during MM screen ]]
local ASC_45_TEXT = [[ local ASC_45_TEXT = [[
Wait, who are you? Wait, who are you?
...
*silence* *silence*
...
Why am I seeing this? Why am I seeing this?
...
*silence* *silence*
...
]]
local ASC_78_TEXT = [[
The road has run out
of road.
Norman walked back
and forth
until the street
forgot which way it went.
And then - finally -
he stopped walking.
]] ]]
local ascension_texts = { local ascension_texts = {
@@ -60,6 +74,7 @@ local ascension_texts = {
[3] = ASC_23_TEXT, [3] = ASC_23_TEXT,
[4] = ASC_34_TEXT, [4] = ASC_34_TEXT,
[5] = ASC_45_TEXT, [5] = ASC_45_TEXT,
[8] = ASC_78_TEXT,
} }
function MysteriousManScreen.get_text_for_level(level) function MysteriousManScreen.get_text_for_level(level)
@@ -261,16 +276,18 @@ Screen.register({
end end
if state == STATE_TEXT then if state == STATE_TEXT then
local cx = Config.screen.width / 2 local screen_w = Config.screen.width
local line_y = text_y local line_y = text_y
for line in (text .. "\n"):gmatch("(.-)\n") do for line in (text .. "\n"):gmatch("(.-)\n") do
Print.text_center(line, cx, line_y, Config.colors.light_grey) local line_w = print(line, 0, -6, 0, false, 1)
local line_x = math.floor((screen_w - line_w) / 2)
Print.text_contour(line, (line_x - 8), line_y, Config.colors.black, false, 1)
line_y = line_y + 8 line_y = line_y + 8
end end
elseif state == STATE_DAY then elseif state == STATE_DAY then
MysteriousManScreen.draw_day_switch_background() MysteriousManScreen.draw_day_switch_background()
local day_text = day_text_override or ("Day " .. Context.day_count) local day_text = day_text_override or ("day " .. Context.day_count)
Print.text_center( Print.text_center_contour(
day_text, day_text,
Config.screen.width / 2, Config.screen.width / 2,
Config.screen.height / 2 - 3, Config.screen.height / 2 - 3,

View File

@@ -5,9 +5,9 @@ Screen.register({
"do_work", "do_work",
"go_to_walking_to_home", "go_to_walking_to_home",
"have_a_coffee", "have_a_coffee",
"go_to_truth",
}, },
init = function() init = function()
Audio.music_play_room_work()
Context.have_been_to_office = true Context.have_been_to_office = true
local possible_sprites = { local possible_sprites = {
@@ -37,14 +37,34 @@ Screen.register({
{x = -4 + 5 * 8, y = 9 * 8} {x = -4 + 5 * 8, y = 9 * 8}
} }
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Audio.music_play_mystery()
Context.office_sprites = { "norman_echo" }
else
Audio.music_play_room_work(CommuteGlitch.music_speed())
Context.office_sprites = Sprite.list_randomize(possible_sprites, possible_positions) Context.office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
if CommuteGlitch.is_active() then
Context.office_sprites = CommuteGlitch.corrupt_sprite_list(Context.office_sprites)
end
end
end,
background = function()
return CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 and "" or "office"
end, end,
background = "office",
draw = function() draw = function()
if Window.get_current_id() == "game" then if Window.get_current_id() == "game" then
Sprite.draw_at("norman", 13 * 8, 9 * 8) Sprite.draw_at("norman", 13 * 8, 9 * 8)
Sprite.draw_list(Context.office_sprites) if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Sprite.draw_at("norman_echo", 13 * 8, 9 * 8)
CommuteGlitch.draw_background_flicker()
else
CommuteGlitch.draw_sprite_list(Context.office_sprites)
end
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 6 then
Glitch.draw()
end
end end
end end
}) })

View File

@@ -36,13 +36,13 @@ Screen.register({
Sprite.draw_at("norman", norman_x, norman_y) Sprite.draw_at("norman", norman_x, norman_y)
Print.text_center("day " .. Context.day_count, cx, 10, Config.colors.white) Print.text_center_contour("day " .. Context.day_count, cx, 10, Config.colors.black)
local narrative = "reflecting on my past and present\n...\nboth eventually flushed." local narrative = "reflecting on my past and present...\nboth eventually flushed..."
local wrapped = UI.word_wrap(narrative, 38) local wrapped = UI.word_wrap(narrative, 38)
local text_y = 24 local text_y = 24
for _, line in ipairs(wrapped) do for _, line in ipairs(wrapped) do
Print.text_center(line, cx, text_y, Config.colors.light_grey) Print.text_center_contour(line, cx, text_y, Config.colors.black)
text_y = text_y + 8 text_y = text_y + 8
end end
@@ -56,26 +56,26 @@ Screen.register({
local meter_start_y = text_y + 10 local meter_start_y = text_y + 10
local meter_list = { local meter_list = {
{ key = "wpm", label = "Work Productivity Meter" }, { key = "wpm", label = "Work Productivity Meter", color = Meter.COLOR_WPM },
{ key = "ism", label = "Impostor Syndrome Meter" }, { key = "ism", label = "Impostor Syndrome Meter", color = Meter.COLOR_ISM },
{ key = "bm", label = "Burnout Meter" }, { key = "bm", label = "Burnout Meter", color = Meter.COLOR_BM },
} }
for i, meter in ipairs(meter_list) do for i, meter in ipairs(meter_list) do
local y = meter_start_y + (i - 1) * 20 local y = meter_start_y + (i - 1) * 20
Print.text_center(meter.label, cx, y, Config.colors.white) Print.text_center_contour(meter.label, cx, y, meter.color, false, 1, Config.colors.white)
local bar_y = y + 8 local bar_y = y + 8
local fill_w = math.max(0, math.floor((m[meter.key] / max_val) * bar_w)) local fill_w = math.max(0, math.floor((m[meter.key] / max_val) * bar_w))
rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG) rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG)
if fill_w > 0 then if fill_w > 0 then
rect(bar_x, bar_y, fill_w, bar_h, Config.colors.blue) rect(bar_x, bar_y, fill_w, bar_h, meter.color)
end end
local decay_w = print(decay_text, 0, -6, 0, false, 1) local decay_w = print(decay_text, 0, -6, 0, false, 1)
Print.text(decay_text, bar_x - decay_w - 4, bar_y, Config.colors.light_blue) Print.text_contour(decay_text, bar_x - decay_w - 4, bar_y, Config.colors.light_blue, false, 1, Config.colors.white)
Print.text(mult_text, bar_x + bar_w + 4, bar_y, Config.colors.light_blue) Print.text_contour(mult_text, bar_x + bar_w + 4, bar_y, Config.colors.light_blue, false, 1, Config.colors.white)
end end
if Ascension.get_level() > 0 then if Ascension.get_level() > 0 then

View File

@@ -4,16 +4,60 @@ Screen.register({
decisions = { decisions = {
"go_to_home", "go_to_home",
"go_to_office", "go_to_office",
"sumphore_discussion",
"go_to_truth",
}, },
init = function() init = function()
Audio.music_play_room_work() local possible_sprites = {
"matrix_trinity",
"matrix_neo",
{id="matrix_oraculum", y_correct=1 * 8},
"matrix_architect"
}
local possible_positions = {
{x = 5 * 8, y = 11 * 8},
{x = 7 * 8, y = 11 * 8},
{x = 9 * 8, y = 11 * 8},
{x = 11 * 8, y = 11 * 8},
{x = 13 * 8, y = 11 * 8},
{x = 15 * 8, y = 11 * 8},
{x = 18 * 8, y = 11 * 8},
{x = 21 * 8, y = 11 * 8},
{x = 24 * 8, y = 11 * 8},
{x = 27 * 8, y = 11 * 8},
}
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Audio.music_play_mystery()
Context.walking_to_home_sprites = {}
else
Audio.music_play_room_work(CommuteGlitch.music_speed())
Context.walking_to_home_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
if CommuteGlitch.is_active() then
Context.walking_to_home_sprites = CommuteGlitch.corrupt_sprite_list(Context.walking_to_home_sprites)
end
end
end,
background = function()
return CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 and "" or "street"
end, end,
background = "street",
draw = function() draw = function()
if Window.get_current_id() == "game" then if Window.get_current_id() == "game" then
Sprite.draw_at("norman", 7 * 8, 3 * 8) Sprite.draw_at("norman", 7 * 8, 3 * 8)
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
if not (CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7) then
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8) Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
Sprite.draw_at("dev_guard", 22 * 8, 2 * 8) Sprite.draw_at("dev_guard", 22 * 8, 2 * 8)
end end
CommuteGlitch.draw_sprite_list(Context.walking_to_home_sprites)
if CommuteGlitch.is_active() then
if CommuteGlitch.get_level() >= 7 then CommuteGlitch.draw_background_flicker() end
if CommuteGlitch.get_level() >= 6 then Glitch.draw() end
end
end
end end
}) })

View File

@@ -4,11 +4,9 @@ Screen.register({
decisions = { decisions = {
"go_to_home", "go_to_home",
"go_to_office", "go_to_office",
"sumphore_discussion", "sumphore_discussion"
}, },
init = function() init = function()
Audio.music_play_room_work()
local possible_sprites = { local possible_sprites = {
"matrix_trinity", "matrix_trinity",
"matrix_neo", "matrix_neo",
@@ -29,6 +27,7 @@ Screen.register({
{x = 27 * 8, y = 11 * 8}, {x = 27 * 8, y = 11 * 8},
} }
Audio.music_play_room_work()
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions) Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
end, end,
background = "street", background = "street",

View File

@@ -3,7 +3,7 @@ local _sprites = {}
local _active_sprites = {} local _active_sprites = {}
local function draw_sprite_instance(sprite_data, params) local function draw_sprite_instance(sprite_data, params)
local colorkey = params.colorkey or sprite_data.colorkey or 0 local colorkey = params.colorkey or sprite_data.colorkey or Config.colors.transparent
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
@@ -80,7 +80,7 @@ function Sprite.draw_list(sprite_list)
for _, sprite_info in ipairs(sprite_list) do for _, sprite_info in ipairs(sprite_list) do
local sprite_data = _sprites[sprite_info.id] local sprite_data = _sprites[sprite_info.id]
if not sprite_data then if not sprite_data then
trace("Error: Attempted to draw non-registered sprite with id: " .. sprite_info.id) trace("Error: Attempted to draw non-registered sprite with id: " .. tostring(sprite_info.id))
else else
draw_sprite_instance(sprite_data, sprite_info) draw_sprite_instance(sprite_data, sprite_info)
end end

View File

@@ -0,0 +1,14 @@
-- Norman echo: same tile indices as norman.
-- Color remap is applied by CommuteGlitch.draw_sprite_list via pal().
-- Implementer: set ECHO_SRC/ECHO_DST in logic.commute_glitch.lua after inspecting the palette.
Sprite.register({
id = "norman_echo",
sprites = {
{ s = 272, x_offset = -4, y_offset = -4 },
{ s = 273, x_offset = 4, y_offset = -4 },
{ s = 288, x_offset = -4, y_offset = 4 },
{ s = 289, x_offset = 4, y_offset = 4 },
{ s = 304, x_offset = -4, y_offset = 12 },
{ s = 305, x_offset = 4, y_offset = 12 },
}
})

View File

@@ -14,6 +14,28 @@ 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 text with a contour (outline) instead of shadow.
--- @within Print
--- @param text string The text to print.<br/>
--- @param x number The x-coordinate.<br/>
--- @param y number The y-coordinate.<br/>
--- @param color number The color of the text.<br/>
--- @param[opt] fixed boolean If true, uses fixed-width font.<br/>
--- @param[opt] scale number The scaling factor (also used for outline thickness).<br/>
--- @param[opt] contour_color number Outline color; defaults to black; if equal to text color, uses white.<br/>
function Print.text_contour(text, x, y, color, fixed, scale, contour_color)
scale = scale or 1
local cc = contour_color
if cc == nil then cc = Config.colors.black end
if color == cc then cc = Config.colors.white end
local ox = { -scale, scale, 0, 0, -scale, scale, -scale, scale }
local oy = { 0, 0, -scale, scale, -scale, -scale, scale, scale }
for i = 1, 8 do
print(text, x + ox[i], y + oy[i], cc, fixed, scale)
end
print(text, x, y, color, fixed, scale)
end
--- Prints centered text with shadow. --- Prints centered text with shadow.
--- @within Print --- @within Print
--- @param text string The text to print.<br/> --- @param text string The text to print.<br/>
@@ -28,3 +50,19 @@ function Print.text_center(text, x, y, color, fixed, scale)
local centered_x = x - (text_width / 2) local centered_x = x - (text_width / 2)
Print.text(text, centered_x, y, color, fixed, scale) Print.text(text, centered_x, y, color, fixed, scale)
end end
--- Prints centered text with contour instead of shadow.
--- @within Print
--- @param text string The text to print.<br/>
--- @param x number The x-coordinate for centering.<br/>
--- @param y number The y-coordinate.<br/>
--- @param color number The color of the text.<br/>
--- @param[opt] fixed boolean If true, uses fixed-width font.<br/>
--- @param[opt] scale number The scaling factor.<br/>
--- @param[opt] contour_color number Outline color; defaults to black; if equal to text color, uses white.<br/>
function Print.text_center_contour(text, x, y, color, fixed, scale, contour_color)
scale = scale or 1
local text_width = print(text, 0, -6 * scale, 0, fixed, scale)
local centered_x = x - (text_width / 2)
Print.text_contour(text, centered_x, y, color, fixed, scale, contour_color)
end

View File

@@ -90,12 +90,54 @@ end
--- @param[opt] bg_color number The background fill color (default: Config.colors.dark_grey).<br/> --- @param[opt] bg_color number The background fill color (default: Config.colors.dark_grey).<br/>
--- @param[opt] border_color number The border color (default: Config.colors.white).<br/> --- @param[opt] border_color number The border color (default: Config.colors.white).<br/>
--- @param[opt] center_text boolean Whether to center each line inside the box. Defaults to false.<br/> --- @param[opt] center_text boolean Whether to center each line inside the box. Defaults to false.<br/>
local function draw_rounded_rect_fill(x, y, w, h, corner_radius, color)
local inner_w = w - corner_radius * 2
local inner_h = h - corner_radius * 2
if inner_w > 0 and inner_h > 0 then
rect(x + corner_radius, y + corner_radius, inner_w, inner_h, color)
end
if inner_w > 0 and corner_radius > 0 then
rect(x + corner_radius, y, inner_w, corner_radius, color)
rect(x + corner_radius, y + h - corner_radius, inner_w, corner_radius, color)
end
if corner_radius > 0 and inner_h > 0 then
rect(x, y + corner_radius, corner_radius, inner_h, color)
rect(x + w - corner_radius, y + corner_radius, corner_radius, inner_h, color)
end
for row = 0, corner_radius - 1 do
for col = 0, corner_radius - 1 do
if col + row >= corner_radius - 1 then
pix(x + corner_radius - 1 - col, y + corner_radius - 1 - row, color)
pix(x + w - corner_radius + col, y + corner_radius - 1 - row, color)
pix(x + corner_radius - 1 - col, y + h - corner_radius + row, color)
pix(x + w - corner_radius + col, y + h - corner_radius + row, color)
end
end
end
end
local function draw_rounded_rect_border(x, y, w, h, corner_radius, color)
line(x + corner_radius, y, x + w - corner_radius - 1, y, color)
line(x + corner_radius, y + h - 1, x + w - corner_radius - 1, y + h - 1, color)
line(x, y + corner_radius, x, y + h - corner_radius - 1, color)
line(x + w - 1, y + corner_radius, x + w - 1, y + h - corner_radius - 1, color)
pix(x + corner_radius - 1, y + 1, color)
pix(x + 1, y + corner_radius - 1, color)
pix(x + w - corner_radius, y + 1, color)
pix(x + w - 2, y + corner_radius - 1, color)
pix(x + corner_radius - 1, y + h - 2, color)
pix(x + 1, y + h - corner_radius, color)
pix(x + w - corner_radius, y + h - 2, color)
pix(x + w - 2, y + h - corner_radius, color)
end
function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_color, border_color, center_text) function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_color, border_color, center_text)
color = color or Config.colors.white color = color or Config.colors.white
bg_color = bg_color or Config.colors.dark_grey bg_color = bg_color or Config.colors.black
border_color = border_color or Config.colors.white border_color = border_color or Config.colors.white
center_text = center_text or false center_text = center_text or false
local r = 3
local padding = 4 local padding = 4
local line_height = 8 local line_height = 8
local inner_x = box_x + padding local inner_x = box_x + padding
@@ -110,7 +152,7 @@ function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_c
base_y = inner_y + math.floor((visible_height - text_height) / 2) base_y = inner_y + math.floor((visible_height - text_height) / 2)
end end
rect(box_x, box_y, box_w, box_h, bg_color) draw_rounded_rect_fill(box_x, box_y, box_w, box_h, r, bg_color)
for i, line in ipairs(lines) do for i, line in ipairs(lines) do
local ly = base_y + (i - 1) * line_height - scroll_y local ly = base_y + (i - 1) * line_height - scroll_y
@@ -123,7 +165,7 @@ function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_c
end end
end end
rectb(box_x, box_y, box_w, box_h, border_color) draw_rounded_rect_border(box_x, box_y, box_w, box_h, r, border_color)
end end
--- Wraps text. --- Wraps text.

View File

@@ -0,0 +1,41 @@
--- @section AscendDebugWindow
local _level = 0
--- Initialises the ASCEND debug start window.
--- @within AscendDebugWindow
function AscendDebugWindow.init()
_level = 0
end
--- Draws the ASCEND debug start window.
--- @within AscendDebugWindow
function AscendDebugWindow.draw()
UI.draw_top_bar("ASCEND Debug Start")
local cx = Config.screen.width / 2
local cy = Config.screen.height / 2
local left_arrow = _level > 0 and "<- " or " "
local right_arrow = _level < Ascension.get_max_level() and " ->" or " "
local label = left_arrow .. "Start at: " .. _level .. right_arrow
Print.text_center(label, cx, cy - 4, Config.colors.white, false, 1)
Print.text_center("Z/select: start X/back: menu", cx, Config.screen.height - 10, Config.colors.dark_grey, false, 1)
end
--- Updates the ASCEND debug start window logic.
--- @within AscendDebugWindow
function AscendDebugWindow.update()
if Input.left() then
_level = math.max(0, _level - 1)
elseif Input.right() then
_level = math.min(Ascension.get_max_level(), _level + 1)
elseif Input.select() then
Audio.sfx_select()
Context.new_game_debug(_level)
GameWindow.set_state("game")
elseif Input.back() then
Audio.sfx_deselect()
GameWindow.set_state("menu")
end
end

View File

@@ -24,7 +24,7 @@ function DiscussionWindow.draw()
TEXTBOX_W, TEXTBOX_H, TEXTBOX_W, TEXTBOX_H,
Context.discussion.scroll_y, Context.discussion.scroll_y,
Config.colors.white, Config.colors.white,
Config.colors.dark_grey, Config.colors.black,
Config.colors.light_blue, Config.colors.light_blue,
true true
) )
@@ -37,9 +37,13 @@ function DiscussionWindow.draw()
local selected = answers[Context.discussion.selected_answer] local selected = answers[Context.discussion.selected_answer]
local label = selected.label local label = selected.label
local answer_text_y = bar_y + 4 local answer_text_y = bar_y + 4
Print.text("<", 2, answer_text_y, Config.colors.light_blue) local left_arrow_color = Input.left() and Config.colors.white or Config.colors.orange
Print.text_center(label, Config.screen.width / 2, answer_text_y, Config.colors.item) local right_arrow_color = Input.right() and Config.colors.white or Config.colors.orange
Print.text(">", Config.screen.width - 6, answer_text_y, Config.colors.light_blue) local left_arrow_contour_color = Input.left() and Config.colors.white or Config.colors.black
local right_arrow_contour_color = Input.right() and Config.colors.white or Config.colors.black
Print.text_center_contour("<", 6, answer_text_y, left_arrow_color, false, 1, left_arrow_contour_color)
Print.text_center(label, Config.screen.width / 2, answer_text_y, Config.colors.orange)
Print.text_center_contour(">", Config.screen.width - 6, answer_text_y, right_arrow_color, false, 1, right_arrow_contour_color)
end end
end end

View File

@@ -6,7 +6,8 @@ local function draw_game_scene(underlay_draw)
local screen = Screen.get_by_id(Context.game.current_screen) local screen = Screen.get_by_id(Context.game.current_screen)
if not screen then return end if not screen then return end
if screen.background then if screen.background then
Map.draw(screen.background) local actual_background = (type(screen.background) == "function" and screen.background()) or screen.background
Map.draw(actual_background)
elseif screen.background_color then elseif screen.background_color then
rect(0, 0, Config.screen.width, Config.screen.height, screen.background_color) rect(0, 0, Config.screen.width, Config.screen.height, screen.background_color)
end end

View File

@@ -19,7 +19,7 @@ end
--- Draws the header with title and separator. --- Draws the header with title and separator.
--- @within MenuWindow --- @within MenuWindow
function MenuWindow.draw_header() function MenuWindow.draw_header()
rect(0, 0, Config.screen.width, HEADER_H, Config.colors.dark_grey) rect(0, 0, Config.screen.width, HEADER_H, Config.colors.black)
rect(0, HEADER_H - 2, Config.screen.width, 2, Config.colors.light_blue) rect(0, HEADER_H - 2, Config.screen.width, 2, Config.colors.light_blue)
local cx = Config.screen.width / 2 local cx = Config.screen.width / 2
@@ -37,17 +37,19 @@ end
function MenuWindow.draw_norman() function MenuWindow.draw_norman()
local nx = math.floor(Config.screen.width * 0.45 / 2) - 32 local nx = math.floor(Config.screen.width * 0.45 / 2) - 32
local ny = HEADER_H + math.floor((Config.screen.height - HEADER_H - 96) / 2) local ny = HEADER_H + math.floor((Config.screen.height - HEADER_H - 96) / 2)
spr(272, nx, ny, 0, 4) spr(272, nx, ny, Config.colors.transparent, 4)
spr(273, nx + 32, ny, 0, 4) spr(273, nx + 32, ny, Config.colors.transparent, 4)
spr(288, nx, ny + 32, 0, 4) spr(288, nx, ny + 32, Config.colors.transparent, 4)
spr(289, nx + 32, ny + 32, 0, 4) spr(289, nx + 32, ny + 32, Config.colors.transparent, 4)
spr(304, nx, ny + 64, 0, 4) spr(304, nx, ny + 64, Config.colors.transparent, 4)
spr(305, nx + 32, ny + 64, 0, 4) spr(305, nx + 32, ny + 64, Config.colors.transparent, 4)
end end
--- Draws the menu window. --- Draws the menu window.
--- @within MenuWindow --- @within MenuWindow
function MenuWindow.draw() function MenuWindow.draw()
cls(Config.colors.blue
)
MenuWindow.draw_header() MenuWindow.draw_header()
if _anim > 0 then if _anim > 0 then
@@ -159,6 +161,13 @@ function MenuWindow.ddr_test()
MinigameDDRWindow.start("menu", "generated", { special_mode = "only_nothing" }) MinigameDDRWindow.start("menu", "generated", { special_mode = "only_nothing" })
end end
--- Opens the ASCEND debug start window.
--- @within MenuWindow
function MenuWindow.ascend_debug()
AscendDebugWindow.init()
GameWindow.set_state("ascend_debug")
end
--- Refreshes the list of menu items based on current game state. --- Refreshes the list of menu items based on current game state.
--- @within MenuWindow --- @within MenuWindow
function MenuWindow.refresh_menu_items() function MenuWindow.refresh_menu_items()
@@ -176,6 +185,7 @@ function MenuWindow.refresh_menu_items()
table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test}) table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test})
table.insert(_menu_items, {label = "To Be Continued...", decision = MenuWindow.continued}) table.insert(_menu_items, {label = "To Be Continued...", decision = MenuWindow.continued})
table.insert(_menu_items, {label = "DDR Test", decision = MenuWindow.ddr_test}) table.insert(_menu_items, {label = "DDR Test", decision = MenuWindow.ddr_test})
table.insert(_menu_items, {label = "Start at ASCEND N", decision = MenuWindow.ascend_debug})
end end
table.insert(_menu_items, {label = "Exit", decision = MenuWindow.exit}) table.insert(_menu_items, {label = "Exit", decision = MenuWindow.exit})

View File

@@ -1,7 +1,44 @@
--- @section MinigameDDRWindow --- @section MinigameDDRWindow
---@class MinigameDDRState
---@field special_mode string
---@field bar_fill number
---@field max_fill number
---@field fill_per_hit number
---@field miss_penalty number
---@field bar_x number
---@field bar_y number
---@field bar_width number
---@field bar_height number
---@field arrow_size number
---@field arrow_spawn_timer number
---@field arrow_spawn_interval number
---@field arrow_fall_speed number
---@field arrows table
---@field target_y number
---@field target_arrows table
---@field hit_threshold number
---@field button_pressed_timers table
---@field button_press_duration number
---@field input_cooldowns table
---@field input_cooldown_duration number
---@field frame_counter number
---@field current_song table?
---@field pattern_index number
---@field use_pattern boolean
---@field generated_length number
---@field return_window string?
---@field win_timer number
---@field on_win fun(ctx: MinigameDDRState)|nil
---@field total_misses number
---@field total_hits number
---@field special_mode_condition boolean
---@field special_mode_counter number
---@field debug_song_key string|nil
---@field debug_status string|nil
--- Background drawing for DDR minigame. --- Background drawing for DDR minigame.
--- @witin MinigameDDRWindow --- @within MinigameDDRWindow
function MinigameDDRWindow.draw_background() function MinigameDDRWindow.draw_background()
local img_values = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1} local img_values = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1}
local img_runs = {809,40,5,26,178,42,4,7,30,10,127,103,11,1,116,124,116,124,115,18,60,47,115,10,105,10,115,9,108,9,114,9,108,9,114,9,108,9,114,9,33,31,44,9,114,9,34,28,46,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,109,8,114,9,109,8,114,9,109,9,112,10,105,1,3,9,111,11,104,2,3,9,111,11,101,5,3,9,111,11,101,5,3,9,111,9,103,5,3,9,111,9,99,1,2,6,3,9,111,9,99,1,2,8,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,5,12,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,3,1,1,2,11,1,9,111,9,3,1,88,3,1,14,1,9,111,9,3,1,88,3,2,13,2,8,111,9,3,1,88,3,3,12,3,8,110,9,3,1,88,3,3,12,3,9,109,9,3,1,88,3,1,14,3,9,109,9,3,1,90,1,1,7,3,4,3,9,109,9,3,1,90,1,1,5,6,3,3,9,108,10,3,1,44,1,4,1,40,1,1,5,7,2,3,9,108,10,3,1,44,1,4,1,38,3,1,5,7,3,2,9,108,10,3,1,48,1,39,3,1,5,7,2,3,9,108,10,3,1,88,3,1,5,6,3,3,9,108,10,3,1,32,5,4,4,3,2,38,3,1,15,2,9,108,10,3,1,41,5,2,2,2,4,32,3,1,15,2,9,108,10,2,2,41,9,2,5,3,1,3,1,23,3,1,9,1,5,2,9,108,10,2,2,41,12,35,3,1,7,4,4,2,9,108,10,2,2,32,26,30,3,1,5,7,3,2,9,108,10,2,2,36,21,31,9,7,3,2,9,108,10,2,2,35,23,30,10,6,3,2,9,108,10,2,2,35,23,30,11,4,4,2,9,108,10,2,2,34,24,30,19,2,9,108,10,2,2,33,25,30,19,2,9,108,10,2,2,32,28,28,19,2,9,108,10,2,2,32,30,26,19,2,9,108,10,2,2,33,31,24,19,2,9,108,10,2,4,37,17,32,19,2,10,107,10,2,5,85,19,2,10,107,10,2,109,2,10,107,12,1,107,3,10,107,133,107,133,107,133,107,133,107,133,107,133,107,133,118,111,129,6,63,3,28,10,121,13,98,6,142,7,76,6,129,10,13,5,77,15,109,4,31,4,78,4,24,7,75,173,66,176,64,177,62,178,62,178,62,56,31,2,7,2,2,3,2,1,2,1,61,8,62,56,114,8,62,56,114,8,62,56,114,8,62,56,114,8,62,9,8,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,115,8,61,8,8,40,115,8,61,56,115,8,61,13,2,1,1,1,1,16,1,1,2,17,115,8,61,13,2,1,1,1,2,1,1,10,2,1,1,1,2,1,2,7,1,6,115,8,60,14,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,6,8,2,1,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,10,9,8,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,8,1,1,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,10,2,1,2,1,1,11,116,9,58,57,116,9,58,58,115,9,102,13,116,9,58,48,2,10,1,3,22,84,5,7,58,158,7,5,6,6,75,3,7,2,7,1,7,2,6,1,6,2,6,2,4,4,6,2,6,1,6,2,6,3,6,1,6,5,7,8,7,9,11,1,9,2,50,1,22,1,14,5,3,5,3,4,3,6,3,5,2,16,2,5,3,14,2,6,2,6,5,5,11,4,30,1,50,1,37,6,3,5,3,5,2,6,3,5,3,6,1,7,2,6,2,15,2,6,2,5,6,5,12,1,15,1,106,93,2,8,10,5,2,6,2,5,4,4,100,7,3,13,2,65,3,8,3,5,2,5,4,4,3,5,4,4,100,94,2,5,3,5,4,4,6,5,2,5,105,86,2,13,3,5,4,4,6,5,2,1,1,3,102,4,2,24,2,62,1,1,2,7,3,5,4,5,4,5,2,8,4,2,92,5,2,24,2,61,5,5,7,2,5,6,3,4,3,7,102,88,6,7,12,8,6,1,3,7,103,87,6,7,11,9,6,1,3,7,110,1,9,9,5,1,2,1,3,47,162,1,10,6,27,34,162,1,10,6,27,34,97,3,57,14,2,7,26,66,9,33,5,2,17,223,17,223,18,222,47,193,53,1,3,163,19,1,85,109,14,29,36,151,784} local img_runs = {809,40,5,26,178,42,4,7,30,10,127,103,11,1,116,124,116,124,115,18,60,47,115,10,105,10,115,9,108,9,114,9,108,9,114,9,108,9,114,9,33,31,44,9,114,9,34,28,46,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,109,8,114,9,109,8,114,9,109,9,112,10,105,1,3,9,111,11,104,2,3,9,111,11,101,5,3,9,111,11,101,5,3,9,111,9,103,5,3,9,111,9,99,1,2,6,3,9,111,9,99,1,2,8,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,5,12,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,3,1,1,2,11,1,9,111,9,3,1,88,3,1,14,1,9,111,9,3,1,88,3,2,13,2,8,111,9,3,1,88,3,3,12,3,8,110,9,3,1,88,3,3,12,3,9,109,9,3,1,88,3,1,14,3,9,109,9,3,1,90,1,1,7,3,4,3,9,109,9,3,1,90,1,1,5,6,3,3,9,108,10,3,1,44,1,4,1,40,1,1,5,7,2,3,9,108,10,3,1,44,1,4,1,38,3,1,5,7,3,2,9,108,10,3,1,48,1,39,3,1,5,7,2,3,9,108,10,3,1,88,3,1,5,6,3,3,9,108,10,3,1,32,5,4,4,3,2,38,3,1,15,2,9,108,10,3,1,41,5,2,2,2,4,32,3,1,15,2,9,108,10,2,2,41,9,2,5,3,1,3,1,23,3,1,9,1,5,2,9,108,10,2,2,41,12,35,3,1,7,4,4,2,9,108,10,2,2,32,26,30,3,1,5,7,3,2,9,108,10,2,2,36,21,31,9,7,3,2,9,108,10,2,2,35,23,30,10,6,3,2,9,108,10,2,2,35,23,30,11,4,4,2,9,108,10,2,2,34,24,30,19,2,9,108,10,2,2,33,25,30,19,2,9,108,10,2,2,32,28,28,19,2,9,108,10,2,2,32,30,26,19,2,9,108,10,2,2,33,31,24,19,2,9,108,10,2,4,37,17,32,19,2,10,107,10,2,5,85,19,2,10,107,10,2,109,2,10,107,12,1,107,3,10,107,133,107,133,107,133,107,133,107,133,107,133,107,133,118,111,129,6,63,3,28,10,121,13,98,6,142,7,76,6,129,10,13,5,77,15,109,4,31,4,78,4,24,7,75,173,66,176,64,177,62,178,62,178,62,56,31,2,7,2,2,3,2,1,2,1,61,8,62,56,114,8,62,56,114,8,62,56,114,8,62,56,114,8,62,9,8,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,115,8,61,8,8,40,115,8,61,56,115,8,61,13,2,1,1,1,1,16,1,1,2,17,115,8,61,13,2,1,1,1,2,1,1,10,2,1,1,1,2,1,2,7,1,6,115,8,60,14,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,6,8,2,1,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,10,9,8,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,8,1,1,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,10,2,1,2,1,1,11,116,9,58,57,116,9,58,58,115,9,102,13,116,9,58,48,2,10,1,3,22,84,5,7,58,158,7,5,6,6,75,3,7,2,7,1,7,2,6,1,6,2,6,2,4,4,6,2,6,1,6,2,6,3,6,1,6,5,7,8,7,9,11,1,9,2,50,1,22,1,14,5,3,5,3,4,3,6,3,5,2,16,2,5,3,14,2,6,2,6,5,5,11,4,30,1,50,1,37,6,3,5,3,5,2,6,3,5,3,6,1,7,2,6,2,15,2,6,2,5,6,5,12,1,15,1,106,93,2,8,10,5,2,6,2,5,4,4,100,7,3,13,2,65,3,8,3,5,2,5,4,4,3,5,4,4,100,94,2,5,3,5,4,4,6,5,2,5,105,86,2,13,3,5,4,4,6,5,2,1,1,3,102,4,2,24,2,62,1,1,2,7,3,5,4,5,4,5,2,8,4,2,92,5,2,24,2,61,5,5,7,2,5,6,3,4,3,7,102,88,6,7,12,8,6,1,3,7,103,87,6,7,11,9,6,1,3,7,110,1,9,9,5,1,2,1,3,47,162,1,10,6,27,34,162,1,10,6,27,34,97,3,57,14,2,7,26,66,9,33,5,2,17,223,17,223,18,222,47,193,53,1,3,163,19,1,85,109,14,29,36,151,784}
@@ -12,7 +49,7 @@ end
--- Gets initial DDR minigame configuration. --- Gets initial DDR minigame configuration.
--- @within MinigameDDRWindow --- @within MinigameDDRWindow
--- @return result table The default DDR minigame configuration. ---@return MinigameDDRState
function MinigameDDRWindow.init_context() function MinigameDDRWindow.init_context()
local arrow_size = 12 local arrow_size = 12
local arrow_spacing = 30 local arrow_spacing = 30
@@ -33,7 +70,7 @@ function MinigameDDRWindow.init_context()
arrow_spawn_interval = 45, arrow_spawn_interval = 45,
arrow_fall_speed = 1.5, arrow_fall_speed = 1.5,
arrows = {}, arrows = {},
target_y = 115, target_y = 120,
target_arrows = { target_arrows = {
{ dir = "left", x = start_x }, { dir = "left", x = start_x },
{ dir = "down", x = start_x + arrow_size + arrow_spacing }, { dir = "down", x = start_x + arrow_size + arrow_spacing },
@@ -60,10 +97,11 @@ function MinigameDDRWindow.init_context()
} }
end end
--- Builds song data (and optional generated pattern) for the minigame.
--- @within MinigameDDRWindow
function MinigameDDRWindow.prepareSong(song, generated_length, special_mode) function MinigameDDRWindow.prepareSong(song, generated_length, special_mode)
local current_song = Util.deepcopy(song) local current_song = Util.deepcopy(song)
if current_song.generated then if current_song.generated then
local pattern = musicator_generate_pattern(generated_length, current_song.bpm, current_song.spd * 4) local pattern = musicator_generate_pattern(generated_length, current_song.bpm, current_song.spd * 4)
current_song.pattern = pattern current_song.pattern = pattern
@@ -79,6 +117,9 @@ function MinigameDDRWindow.prepareSong(song, generated_length, special_mode)
return current_song return current_song
end end
--- Handles hit feedback and special-mode scoring for one arrow.
--- @within MinigameDDRWindow
---@param game_context MinigameDDRState
function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context) function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context)
local special_mode = game_context.special_mode local special_mode = game_context.special_mode
@@ -109,6 +150,8 @@ function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context)
end end
end end
--- Ends the minigame: win timer, sfx, and special-mode pass/fail bookkeeping.
--- @within MinigameDDRWindow
function MinigameDDRWindow.on_end(game_context) function MinigameDDRWindow.on_end(game_context)
Audio.sfx_select() Audio.sfx_select()
@@ -123,12 +166,9 @@ function MinigameDDRWindow.on_end(game_context)
end end
end end
local sm = game_context.special_mode
local was_ok = true local was_ok = true
if game_context.special_mode == "normal" then if sm == "normal" or sm == "only_special" or sm == "only_left" then
was_ok = game_context.special_mode_counter == num_special
elseif game_context.special_mode == "only_special" then
was_ok = game_context.special_mode_counter == num_special
elseif game_context.special_mode == "only_left" then
was_ok = game_context.special_mode_counter == num_special was_ok = game_context.special_mode_counter == num_special
end end
@@ -192,12 +232,14 @@ end
local function spawn_arrow() local function spawn_arrow()
trace("random arrow") trace("random arrow")
---@type MinigameDDRState
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
local y0 = mg.bar_y + mg.bar_height + 10
local target = mg.target_arrows[math.random(1, 4)] local target = mg.target_arrows[math.random(1, 4)]
table.insert(mg.arrows, { table.insert(mg.arrows, {
dir = target.dir, dir = target.dir,
x = target.x, x = target.x,
y = mg.bar_y + mg.bar_height + 10 y = y0
}) })
end end
@@ -205,13 +247,15 @@ end
--- @within MinigameDDRWindow --- @within MinigameDDRWindow
--- @param direction string The direction of the arrow ("left", "down", "up", "right"). --- @param direction string The direction of the arrow ("left", "down", "up", "right").
local function spawn_arrow_dir(direction, note, special) local function spawn_arrow_dir(direction, note, special)
---@type MinigameDDRState
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
local y0 = mg.bar_y + mg.bar_height + 10
for _, target in ipairs(mg.target_arrows) do for _, target in ipairs(mg.target_arrows) do
if target.dir == direction then if target.dir == direction then
table.insert(mg.arrows, { table.insert(mg.arrows, {
dir = direction, dir = direction,
x = target.x, x = target.x,
y = mg.bar_y + mg.bar_height + 10, y = y0,
note = note, note = note,
special = special special = special
}) })
@@ -225,6 +269,7 @@ end
--- @param arrow table The arrow data. --- @param arrow table The arrow data.
--- @return boolean True if the arrow is hit, false otherwise. --- @return boolean True if the arrow is hit, false otherwise.
local function check_hit(arrow) local function check_hit(arrow)
---@type MinigameDDRState
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
@@ -235,34 +280,53 @@ end
--- @param arrow table The arrow data. --- @param arrow table The arrow data.
--- @return boolean True if the arrow is missed, false otherwise. --- @return boolean True if the arrow is missed, false otherwise.
local function check_miss(arrow) local function check_miss(arrow)
---@type MinigameDDRState
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. --- Rotates a point (px, py) around (cx, cy) by a number of 90-degree CW steps.
--- @within MinigameDDRWindow
local function rotate(px, py, cx, cy, steps)
local dx, dy = px - cx, py - cy
for _ = 1, steps % 4 do
dx, dy = dy, -dx
end
return cx + dx, cy + dy
end
local arrow_rotations = { down = 0, right = 1, up = 2, left = 3 }
--- Draws an arrow by rotating the "down" arrow shape.
--- @within MinigameDDRWindow --- @within MinigameDDRWindow
--- @param x number The x-coordinate. --- @param x number The x-coordinate.
--- @param y number The y-coordinate. --- @param y number The y-coordinate.
--- @param direction string The direction of the arrow. --- @param direction string The direction of the arrow.
--- @param color number The color 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 = 14
local half = size / 2 local half = size / 2
if direction == "left" then local pivot_x, pivot_y = x + half, y + half
tri(x + half, y, x, y + half, x + half, y + size, color) local steps = arrow_rotations[direction] or 0
rectb(x + half, y + half - 2, half, 4, color)
elseif direction == "right" then local head_left_x, head_left_y = rotate(x, y + half, pivot_x, pivot_y, steps)
tri(x + half, y, x + size, y + half, x + half, y + size, color) local head_tip_x, head_tip_y = rotate(x + half, y + size, pivot_x, pivot_y, steps)
rectb(x, y + half - 2, half, 4, color) local head_right_x, head_right_y = rotate(x + size, y + half, pivot_x, pivot_y, steps)
elseif direction == "up" then
tri(x, y + half, x + half, y, x + size, y + half, color) tri(head_left_x, head_left_y,
rectb(x + half - 2, y + half, 4, half, color) head_tip_x, head_tip_y,
elseif direction == "down" then head_right_x, head_right_y, color)
tri(x, y + half, x + half, y + size, x + size, y + half, color)
rectb(x + half - 2, y, 4, half, color) local stem_top_x, stem_top_y = rotate(x + half - 3, y, pivot_x, pivot_y, steps)
end local stem_bot_x, stem_bot_y = rotate(x + half + 3, y + half, pivot_x, pivot_y, steps)
local stem_x = math.min(stem_top_x, stem_bot_x)
local stem_y = math.min(stem_top_y, stem_bot_y)
local stem_w = math.abs(stem_bot_x - stem_top_x)
local stem_h = math.abs(stem_bot_y - stem_top_y)
rectb(stem_x, stem_y, stem_w, stem_h, color)
end end
--- Updates DDR minigame logic. --- Updates DDR minigame logic.
--- @within MinigameDDRWindow --- @within MinigameDDRWindow
function MinigameDDRWindow.update() function MinigameDDRWindow.update()
@@ -323,10 +387,7 @@ function MinigameDDRWindow.update()
arrow.y = arrow.y + mg.arrow_fall_speed arrow.y = arrow.y + mg.arrow_fall_speed
if check_miss(arrow) then if check_miss(arrow) then
table.insert(arrows_to_remove, i) table.insert(arrows_to_remove, i)
mg.bar_fill = mg.bar_fill - mg.miss_penalty mg.bar_fill = math.max(0, mg.bar_fill - mg.miss_penalty)
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
mg.total_misses = mg.total_misses + 1 mg.total_misses = mg.total_misses + 1
end end
end end
@@ -370,20 +431,14 @@ function MinigameDDRWindow.update()
if arrow.dir == dir and check_hit(arrow) then if arrow.dir == dir and check_hit(arrow) then
MinigameDDRWindow.on_arrow_hit_special(arrow, mg) MinigameDDRWindow.on_arrow_hit_special(arrow, mg)
mg.bar_fill = mg.bar_fill + mg.fill_per_hit mg.bar_fill = math.min(mg.max_fill, mg.bar_fill + mg.fill_per_hit)
if mg.bar_fill > mg.max_fill then
mg.bar_fill = mg.max_fill
end
table.remove(mg.arrows, i) table.remove(mg.arrows, i)
hit = true hit = true
break break
end end
end end
if not hit then if not hit then
mg.bar_fill = mg.bar_fill - 2 mg.bar_fill = math.max(0, mg.bar_fill - 2)
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
mg.total_misses = mg.total_misses + 1 mg.total_misses = mg.total_misses + 1
end end
end end
@@ -393,6 +448,7 @@ end
--- Draws DDR minigame. --- Draws DDR minigame.
--- @within MinigameDDRWindow --- @within MinigameDDRWindow
function MinigameDDRWindow.draw() function MinigameDDRWindow.draw()
---@type MinigameDDRState|nil
local mg = Context.minigame_ddr local mg = Context.minigame_ddr
if not mg then if not mg then
cls(0) cls(0)
@@ -420,8 +476,6 @@ function MinigameDDRWindow.draw()
end end
rect(mg.bar_x, mg.bar_y, fill_width, mg.bar_height, bar_color) rect(mg.bar_x, mg.bar_y, fill_width, mg.bar_height, bar_color)
end end
local percentage = math.floor((mg.bar_fill / mg.max_fill) * 100)
Print.text_center(percentage .. "%", mg.bar_x + mg.bar_width / 2, mg.bar_y + 2, Config.colors.black)
if mg.target_arrows then if mg.target_arrows then
for _, target in ipairs(mg.target_arrows) do 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 is_pressed = mg.button_pressed_timers[target.dir] and mg.button_pressed_timers[target.dir] > 0
@@ -435,7 +489,7 @@ function MinigameDDRWindow.draw()
draw_arrow(arrow.x, arrow.y, arrow.dir, arrow_color) draw_arrow(arrow.x, arrow.y, arrow.dir, arrow_color)
end end
end end
Print.text_center("Hit the arrows!", Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_grey) Print.text_center_contour("Hit the arrows!", Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_blue)
local debug_y = 60 local debug_y = 60
if mg.debug_status then if mg.debug_status then
Print.text_center(mg.debug_status, Config.screen.width / 2, debug_y, Config.colors.item) Print.text_center(mg.debug_status, Config.screen.width / 2, debug_y, Config.colors.item)
@@ -459,6 +513,7 @@ function MinigameDDRWindow.draw()
elseif Context.test_mode then elseif Context.test_mode then
Print.text_center("RANDOM MODE", Config.screen.width / 2, debug_y, Config.colors.blue) Print.text_center("RANDOM MODE", Config.screen.width / 2, debug_y, Config.colors.blue)
end end
if mg.win_timer > 0 then if mg.win_timer > 0 then
if mg.special_mode_condition then if mg.special_mode_condition then
Minigame.draw_win_overlay("SUCCESS...?") Minigame.draw_win_overlay("SUCCESS...?")

View File

@@ -49,6 +49,7 @@ end
--- @param return_window string The window ID to return to after the minigame.<br/> --- @param return_window string The window ID to return to after the minigame.<br/>
--- @param[opt] params table Optional parameters for minigame configuration.<br/> --- @param[opt] params table Optional parameters for minigame configuration.<br/>
function MinigameButtonMashWindow.start(return_window, params) function MinigameButtonMashWindow.start(return_window, params)
Audio.music_stop()
MinigameButtonMashWindow.init(params) MinigameButtonMashWindow.init(params)
local mg = Context.minigame_button_mash local mg = Context.minigame_button_mash
mg.return_window = return_window or "game" mg.return_window = return_window or "game"
@@ -145,7 +146,7 @@ function MinigameButtonMashWindow.draw()
circ(mg.button_x, mg.button_y, mg.button_size - 2, button_color) circ(mg.button_x, mg.button_y, mg.button_size - 2, button_color)
end end
Print.text_center("Z", mg.button_x, mg.button_y - 3, button_color) Print.text_center("Z", mg.button_x, mg.button_y - 3, button_color)
Print.text_center(mg.instruction_text, Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_grey) Print.text_center_contour(mg.instruction_text, Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_blue)
if mg.show_progress_text then if mg.show_progress_text then
local points_text = math.floor(mg.bar_fill) .. "/" .. mg.target_points local points_text = math.floor(mg.bar_fill) .. "/" .. mg.target_points
Print.text_center(points_text, mg.bar_x + mg.bar_width / 2, mg.bar_y + 2, Config.colors.black) Print.text_center(points_text, mg.bar_x + mg.bar_width / 2, mg.bar_y + 2, Config.colors.black)

View File

@@ -53,6 +53,7 @@ end
--- @param return_window string The window ID to return to after the minigame.<br/> --- @param return_window string The window ID to return to after the minigame.<br/>
--- @param[opt] params table Optional parameters for minigame configuration.<br/> --- @param[opt] params table Optional parameters for minigame configuration.<br/>
function MinigameRhythmWindow.start(return_window, params) function MinigameRhythmWindow.start(return_window, params)
Audio.music_stop()
MinigameRhythmWindow.init(params) MinigameRhythmWindow.init(params)
local mg = Context.minigame_rhythm local mg = Context.minigame_rhythm
mg.return_window = return_window or "game" mg.return_window = return_window or "game"
@@ -146,14 +147,14 @@ function MinigameRhythmWindow.draw()
local target_left = mg.target_center - (mg.target_width / 2) local target_left = mg.target_center - (mg.target_width / 2)
local target_x = mg.bar_x + (target_left * mg.bar_width) local target_x = mg.bar_x + (target_left * mg.bar_width)
local target_width_pixels = mg.target_width * 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.light_blue) rect(target_x, mg.bar_y, target_width_pixels, mg.bar_height, Config.colors.orange)
local line_x = mg.bar_x + (mg.line_position * mg.bar_width) 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) rect(line_x - 1, mg.bar_y, 2, mg.bar_height, Config.colors.light_blue)
Print.text_center( Print.text_center_contour(
"Sleep Norman ... Sleep!", "Sleep Norman ... Sleep!",
Config.screen.width / 2, Config.screen.width / 2,
mg.bar_y + mg.bar_height + 14, mg.bar_y + mg.bar_height + 14,
Config.colors.light_grey Config.colors.light_blue
) )
local button_color = Config.colors.light_grey local button_color = Config.colors.light_grey
if mg.button_pressed_timer > 0 then if mg.button_pressed_timer > 0 then

View File

@@ -22,6 +22,9 @@ Window.register("controls", ControlsWindow)
AudioTestWindow = {} AudioTestWindow = {}
Window.register("audiotest", AudioTestWindow) Window.register("audiotest", AudioTestWindow)
AscendDebugWindow = {}
Window.register("ascend_debug", AscendDebugWindow)
MinigameButtonMashWindow = {} MinigameButtonMashWindow = {}
Window.register("minigame_button_mash", MinigameButtonMashWindow) Window.register("minigame_button_mash", MinigameButtonMashWindow)