feature/IMP-112-ascension-8-9 #59

Merged
mr.two merged 13 commits from feature/IMP-112-ascension-8-9 into develop 2026-04-29 21:27:24 +00:00
20 changed files with 575 additions and 24 deletions
Showing only changes of commit 4cc0025f5e - Show all commits

View File

@@ -17,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
@@ -24,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
@@ -45,14 +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.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

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

@@ -4,10 +4,16 @@ Decision.register({
handle = function() handle = function()
local level = Ascension.get_level() local level = Ascension.get_level()
local disc_id = "coworker_disc_0" local disc_id = "coworker_disc_0"
-- TODO: Add more discussions for levels above 3
if level >= 1 and level <= 3 then if level >= 1 and level <= 3 then
local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level) local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level)
disc_id = "coworker_disc" .. suffix 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 end
Discussion.start(disc_id, "game") Discussion.start(disc_id, "game")
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

@@ -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

@@ -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",

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

@@ -54,12 +54,27 @@ local ASC_45_TEXT = [[
]] ]]
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 = {
[1] = ASC_01_TEXT, [1] = ASC_01_TEXT,
[2] = ASC_12_TEXT, [2] = ASC_12_TEXT,
[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)

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}
} }
Context.office_sprites = Sprite.list_randomize(possible_sprites, possible_positions) 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)
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

@@ -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("pizza_vendor", 19 * 8, 1 * 8) Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
Sprite.draw_at("dev_guard", 22 * 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("dev_guard", 22 * 8, 2 * 8)
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 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

@@ -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

@@ -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