added 89 ascension
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
Zoltan Timar
2026-04-29 16:20:11 +02:00
parent 395208f814
commit 3e31398d9d
17 changed files with 399 additions and 118 deletions

View File

@@ -1,10 +0,0 @@
Decision.register({
id = "go_to_end",
label = "Break the cycle",
condition = function()
return Ascension.is_complete()
end,
handle = function()
Window.set_current("end")
end,
})

View File

@@ -2,6 +2,9 @@ Decision.register({
id = "go_to_home",
label = "Go Home",
condition = function()
if Ascension.get_level() >= 8 then
return Context.have_been_to_office and Context.have_done_work_today
end
if CommuteGlitch.is_active() then
local g = CommuteGlitch.get_level()
if g >= 4 and g <= 5 then return false end
@@ -12,6 +15,10 @@ Decision.register({
return Context.have_been_to_office and Context.have_done_work_today
end,
handle = function()
if Ascension.get_level() >= 8 then
Util.go_to_screen_by_id("home")
return
end
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Context.should_ascend = true
CommuteGlitch.reset()

View File

@@ -1,6 +1,11 @@
Decision.register({
id = "go_to_sleep",
label = "Go to Sleep",
label = function()
if Ascension.get_level() >= 8 then
return "Break the Loop"
end
return "Go to Sleep"
end,
condition = function()
return Context.have_been_to_office and Context.have_done_work_today
end,
@@ -12,11 +17,15 @@ Decision.register({
focus_center_y = (Config.screen.height / 2) - 18,
focus_initial_radius = 0,
on_win = function()
if Ascension.get_level() == 8 then
Ascension.increase()
end
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,
break_mode = level >= 9,
})
end,
})

View File

@@ -1,6 +1,9 @@
Decision.register({
id = "have_a_coffee",
label = "Have a Coffee",
condition = function()
return Ascension.get_level() < 8
end,
handle = function()
local level = Ascension.get_level()
local disc_id = "coworker_disc_0"

View File

@@ -6,6 +6,9 @@ Decision.register({
end
return "Talk to the homeless guy"
end,
condition = function()
return Ascension.get_level() < 8
end,
handle = function()
if not Context.have_met_sumphore then
Discussion.start("homeless_guy", "game")

View File

@@ -21,7 +21,7 @@ local FADE_COLORS = nil
function Ascension.get_initial()
_increased_this_cycle = false
return {
level = 0, -- FYI: change this to test ascension levels without having to play through them
level = 8, -- FYI: change this to test ascension levels without having to play through them
}
end
@@ -167,3 +167,10 @@ end
function Ascension.is_flashing()
return _flash_active
end
--- Returns whether the fade-in effect is currently active.
--- @within Ascension
--- @return boolean Whether the letter fade-in is playing.
function Ascension.is_fading()
return _fade_active
end

View File

@@ -31,7 +31,7 @@ Context = {}
function Context.initial_data()
return {
current_menu_item = 1,
test_mode = true,
test_mode = false,
mouse_trace = false,
popup = {
show = false,

View File

@@ -30,7 +30,7 @@ end
--- @return table Debug-patched initial context data.
function Context.initial_data_debug_asc(level)
local data = Context.initial_data()
data.test_mode = true
data.test_mode = false
data.game_in_progress = true
data.ascension = { level = level }
local overrides = _level_overrides[level] or _level_overrides[0]

View File

@@ -2,9 +2,11 @@
CommuteGlitch = {}
--- Gets the current commute glitch level.
--- At ascension level 8+, always returns 7 (max) regardless of stored value.
--- @within CommuteGlitch
--- @return number Current glitch level (0-7).
function CommuteGlitch.get_level()
if Ascension.get_level() >= 8 then return 7 end
return Context and (Context.commute_glitch_level or 0) or 0
end
@@ -34,11 +36,11 @@ function CommuteGlitch.enter_truth()
Ascension.start_flash()
end
--- Returns true when ascension level is 7 (ASCENSIO step active).
--- Returns true when ascension level is 7 or 8 (ASCENSIO/N steps active).
--- @within CommuteGlitch
--- @return boolean Whether the commute glitch system is active.
function CommuteGlitch.is_active()
return Ascension.get_level() == 7
return Ascension.get_level() >= 7
end
--- Returns the music playback speed for the current glitch level.

View File

@@ -312,3 +312,18 @@ function Meter.draw()
Ascension.draw(bar_x - 4, ascension_y, { spacing = 8 })
end
--- Draws only the ascension letters at the same position as in Meter.draw().
--- Used when meters are hidden but ascension letters still need to be visible.
--- @within Meter
function Meter.draw_ascension_only()
local screen_w = Config.screen.width
local screen_h = Config.screen.height
local bar_w = screen_w * 0.25
local edge = math.max(2, math.floor(screen_w * 0.03))
local bar_x = screen_w - bar_w - edge
local line_h = 3
local start_y = screen_h * 0.05
local ascension_y = start_y + 3 * line_h + 1
Ascension.draw(bar_x - 4, ascension_y, { spacing = 8 })
end

View File

@@ -5,14 +5,23 @@ Screen.register({
"go_to_toilet",
"go_to_walking_to_office",
"go_to_sleep",
"go_to_end",
},
init = function()
Audio.music_play_room_work()
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Audio.music_play_mystery()
Glitch.show()
else
Audio.music_play_room_work()
end
end,
background = "bedroom",
draw = function()
if Context.home_norman_visible and Window.get_current_id() == "game" then
if Window.get_current_id() ~= "game" then return end
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
CommuteGlitch.draw_background_flicker()
Glitch.draw()
end
if Context.home_norman_visible then
Sprite.draw_at("norman", 100, 80)
end
end

View File

@@ -93,6 +93,38 @@ local ASC_78_TEXT = [[
And then - finally -
he stopped walking.
]]
local ASC_89_TEXT = [[
Normann
you created this simulation
in the first place,
because you could never
cope with reality.
You were always
an impostor.
also,
you have beed talking to
yourself
in your sleep
]]
local ascension_texts = {
[1] = ASC_01_TEXT,
@@ -103,6 +135,7 @@ local ascension_texts = {
[6] = ASC_56_TEXT,
[7] = ASC_67_TEXT,
[8] = ASC_78_TEXT,
[9] = ASC_89_TEXT,
}
function MysteriousManScreen.get_text_for_level(level)
@@ -123,6 +156,7 @@ local day_text_override = nil
local on_text_complete = nil
local show_mysterious_screen = true
local trigger_flash_on_wake = false
local break_mode = false
MysteriousManScreen.choices = {
{
@@ -227,6 +261,8 @@ function MysteriousManScreen.start(options)
text_y = Config.screen.height
day_text_override = options.day_text
on_text_complete = options.on_text_complete
break_mode = options.break_mode or false
MysteriousManScreen.pending_end = false
Meter.hide()
trigger_flash_on_wake = not options.skip_text
if options.skip_text then
@@ -281,7 +317,7 @@ Screen.register({
if text_done_timer <= 0 or Input.select() then
MysteriousManScreen.go_to_day_state()
-- to be continued
if 4 <= Ascension.get_level() then
if 4 <= Ascension.get_level() and not break_mode then
Window.set_current("continued")
end
end
@@ -290,7 +326,10 @@ Screen.register({
day_timer = day_timer - Context.delta_time
if day_timer <= 0 or Input.select() then
if trigger_flash_on_wake or Ascension.get_level() < 1 then
if break_mode then
state = STATE_CHOICE
selected_choice = 1
elseif trigger_flash_on_wake or Ascension.get_level() ~= 4 then
MysteriousManScreen.wake_up()
else
state = STATE_CHOICE
@@ -298,23 +337,77 @@ Screen.register({
end
end
elseif state == STATE_CHOICE then
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
local confirmed
selected_choice, confirmed = UI.update_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
if break_mode then
if MysteriousManScreen.pending_end then
if not Ascension.is_flashing() and not Ascension.is_fading() then
MysteriousManScreen.pending_end = false
Window.set_current("end")
end
return
end
if Input.select() or confirmed then
Audio.sfx_select()
if selected_choice == 1 then
MysteriousManScreen.wake_up()
else
MysteriousManScreen.stay_in_bed()
if Input.left() or Input.up() then
if selected_choice == 2 then
Audio.sfx_beep()
selected_choice = 1
end
elseif Input.right() or Input.down() then
if selected_choice == 1 then
Audio.sfx_beep()
selected_choice = 2
end
end
if Input.select() then
Audio.sfx_select()
if selected_choice == 1 then
Ascension.start_flash()
MysteriousManScreen.pending_end = true
else
Context.reset()
Context.game_in_progress = true
Context.home_norman_visible = true
Glitch.hide()
Meter.show()
MenuWindow.refresh_menu_items()
Util.go_to_screen_by_id("home")
Window.set_current("game")
local home_screen = Screen.get_by_id("home")
if home_screen and home_screen.init then
home_screen.init()
end
end
end
else
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
local confirmed
selected_choice, confirmed = UI.update_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
if Input.select() or confirmed then
Audio.sfx_select()
if selected_choice == 1 then
MysteriousManScreen.wake_up()
else
MysteriousManScreen.stay_in_bed()
end
end
end
end
end,
draw = function()
if show_mysterious_screen then
if state == STATE_CHOICE and break_mode then
if not MysteriousManScreen.pending_end then
local nx = math.floor((Config.screen.width - 64) / 2)
local ny = math.floor((Config.screen.height - 96) / 2)
spr(272, nx, ny, Config.colors.transparent, 4)
spr(273, nx + 32, ny, Config.colors.transparent, 4)
spr(288, nx, ny + 32, Config.colors.transparent, 4)
spr(289, nx + 32, ny + 32, Config.colors.transparent, 4)
spr(304, nx, ny + 64, Config.colors.transparent, 4)
spr(305, nx + 32, ny + 64, Config.colors.transparent, 4)
end
elseif show_mysterious_screen then
MysteriousManScreen.draw_background()
end
@@ -337,9 +430,42 @@ Screen.register({
Config.colors.white
)
elseif state == STATE_CHOICE then
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
UI.draw_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
if break_mode then
if MysteriousManScreen.pending_end then
Meter.draw_ascension_only()
else
local lines = {
"This is not a workplace.",
"This is a cycle.",
"And if it is a cycle...",
"it can be broken."
}
local y = 40
for _, line in ipairs(lines) do
Print.text_center_contour(line, Config.screen.width / 2, y, Config.colors.orange, false, 1, Config.colors.white)
y = y + 10
end
y = y + 20
local break_color = selected_choice == 1 and Config.colors.light_blue or Config.colors.white
local cont_color = selected_choice == 2 and Config.colors.light_blue or Config.colors.white
local break_text = (selected_choice == 1 and "> BREAK" or " BREAK")
local cont_text = (selected_choice == 2 and "> CONTINUE" or " CONTINUE")
local centerX = Config.screen.width / 2
local choice_gap = 20
local break_width = print(break_text, 0, -6, 0)
local cont_width = print(cont_text, 0, -6, 0)
local total_width = break_width + choice_gap + cont_width
local break_x = math.floor(centerX - (total_width / 2))
local cont_x = break_x + break_width + choice_gap
Print.text(break_text, break_x, y, break_color)
Print.text(cont_text, cont_x, y, cont_color)
end
else
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
UI.draw_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
end
end
end,
})

View File

@@ -92,5 +92,9 @@ Screen.register({
local asc_x = math.floor((sw - asc_total_w) / 2)
Ascension.draw(asc_x, asc_letter_y, { spacing = asc_spacing })
end
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Glitch.draw()
end
end,
})

View File

@@ -28,10 +28,18 @@ Screen.register({
{x = 27 * 8, y = 11 * 8},
}
Audio.music_play_room_work()
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Audio.music_play_mystery()
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
Context.walking_to_office_sprites = CommuteGlitch.corrupt_sprite_list(Context.walking_to_office_sprites)
else
Audio.music_play_room_work()
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
end
end,
background = function()
return CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 and "" or "street"
end,
background = "street",
update = function()
end,
draw = function()
@@ -40,12 +48,19 @@ Screen.register({
local norman_x = Context.fast_food_approaching and (19 * 8) or (7 * 8)
Sprite.draw_at("norman", norman_x, 3 * 8)
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
if Context.fast_food_eaten_today < 3 then
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
end
Sprite.draw_at("dev_guard", 22 * 8, 3 * 8)
Sprite.draw_list(Context.walking_to_office_sprites)
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7 then
Sprite.draw_at("norman_echo", norman_x, 3 * 8)
CommuteGlitch.draw_sprite_list(Context.walking_to_office_sprites)
CommuteGlitch.draw_background_flicker()
Glitch.draw()
else
if Context.fast_food_eaten_today < 3 then
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
end
Sprite.draw_at("dev_guard", 22 * 8, 3 * 8)
Sprite.draw_list(Context.walking_to_office_sprites)
end
end
end
})

View File

@@ -5,89 +5,33 @@
function EndWindow.draw()
cls(Config.colors.black)
if Context._end.state == "choice" then
local lines = {
"This is not a workplace.",
"This is a cycle.",
"And if it is a cycle...",
"it can be broken."
}
local cx = Config.screen.width / 2
local name = Context.player_name or "AAA"
local code = CodeGenerator.encrypt(name)
local y = 40
for _, line in ipairs(lines) do
Print.text_center(line, Config.screen.width / 2, y, Config.colors.white)
y = y + 10
end
Print.text_center("~ GOOD ENDING ~", cx, 8, Config.colors.light_blue)
Print.text_center("Congratulations, " .. name .. "!", cx, 20, Config.colors.white)
y = y + 20
local yes_color = Context._end.selection == 1 and Config.colors.light_blue or Config.colors.white
local no_color = Context._end.selection == 2 and Config.colors.light_blue or Config.colors.white
rectb(40, 29, 160, 36, Config.colors.blue)
Print.text_center("your code", cx, 33, Config.colors.light_grey)
Print.text_center(code, cx, 44, Config.colors.white, false, 2)
local yes_text = (Context._end.selection == 1 and "> YES" or " YES")
local no_text = (Context._end.selection == 2 and "> NO" or " NO")
Print.text_center("Write it down!", cx, 70, Config.colors.item)
local centerX = Config.screen.width / 2
Print.text(yes_text, centerX - 40, y, yes_color)
Print.text(no_text, centerX + 10, y, no_color)
elseif Context._end.state == "ending" then
local cx = Config.screen.width / 2
local name = Context.player_name or "AAA"
local code = CodeGenerator.encrypt(name)
line(20, 82, 219, 82, Config.colors.dark_grey)
Print.text_center("To continue via telnet:", cx, 87, Config.colors.light_grey)
Print.text_center("games.teletype.hu 2324", cx, 98, Config.colors.white)
line(20, 110, 219, 110, Config.colors.dark_grey)
Print.text_center("~ GOOD ENDING ~", cx, 8, Config.colors.light_blue)
Print.text_center("Congratulations, " .. name .. "!", cx, 20, Config.colors.white)
rectb(40, 29, 160, 36, Config.colors.blue)
Print.text_center("your code", cx, 33, Config.colors.light_grey)
Print.text_center(code, cx, 44, Config.colors.white, false, 2)
Print.text_center("Write it down!", cx, 70, Config.colors.item)
line(20, 82, 219, 82, Config.colors.dark_grey)
Print.text_center("To continue via telnet:", cx, 87, Config.colors.light_grey)
Print.text_center("games.teletype.hu 2324", cx, 98, Config.colors.white)
line(20, 110, 219, 110, Config.colors.dark_grey)
Print.text_center("Press Z to return to menu", cx, 116, Config.colors.dark_grey)
end
Print.text_center("Press Z to return to menu", cx, 116, Config.colors.dark_grey)
end
--- Updates the end screen logic.
--- @within EndWindow
function EndWindow.update()
if Context._end.state == "choice" then
if Input.left() or Input.up() then
if Context._end.selection == 2 then
Audio.sfx_beep()
Context._end.selection = 1
end
elseif Input.right() or Input.down() then
if Context._end.selection == 1 then
Audio.sfx_beep()
Context._end.selection = 2
end
end
if Input.select() then
Audio.sfx_select()
if Context._end.selection == 1 then
Context._end.state = "ending"
else
-- NO: increment day and go home
Day.increase()
Util.go_to_screen_by_id("home")
Window.set_current("game")
-- Initialize home screen
local home_screen = Screen.get_by_id("home")
if home_screen and home_screen.init then
home_screen.init()
end
end
end
elseif Context._end.state == "ending" then
if Input.select() then
Window.set_current("menu")
MenuWindow.refresh_menu_items()
end
if Input.select() then
Context.reset()
Window.set_current("menu")
MenuWindow.refresh_menu_items()
end
end