From 3e31398d9db8e32cf05ee32c0a683ca6610f4575 Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Wed, 29 Apr 2026 16:20:11 +0200 Subject: [PATCH] added 89 ascension --- CLAUDE.md | 148 ++++++++++++++++ impostor.inc | 1 - inc/decision/decision.go_to_end.lua | 10 -- inc/decision/decision.go_to_home.lua | 7 + inc/decision/decision.go_to_sleep.lua | 11 +- inc/decision/decision.have_a_coffee.lua | 3 + inc/decision/decision.sumphore_discussion.lua | 3 + inc/init/init.ascension.lua | 9 +- inc/init/init.context.lua | 2 +- inc/init/init.context_debug.lua | 2 +- inc/logic/logic.commute_glitch.lua | 6 +- inc/logic/logic.meter.lua | 15 ++ inc/screen/screen.home.lua | 15 +- inc/screen/screen.mysterious_man.lua | 158 ++++++++++++++++-- inc/screen/screen.toilet.lua | 4 + inc/screen/screen.walking_to_office.lua | 31 +++- inc/window/window.end.lua | 92 ++-------- 17 files changed, 399 insertions(+), 118 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 inc/decision/decision.go_to_end.lua diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5ab9c34 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,148 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Definitely not an Impostor** is a narrative-driven fantasy game built for [TIC-80](https://tic80.com/), a fantasy console. The game is written entirely in Lua. All source modules in `inc/` are concatenated at build time into a single `impostor.lua` file that TIC-80 loads. + +## Build Commands + +```bash +make build # Concatenate inc/**/*.lua into impostor.lua (order from impostor.inc) +make minify # Build then minify (downloads minify.lua if missing) +make lint # Run luacheck with source mapping to original files +make watch # Auto-rebuild on file changes in inc/ +make export # Export minified game to HTML and .tic formats +make import_assets # Import PNG sprite/tile assets into the TIC-80 cartridge +make export_assets # Extract TIC-80 asset sections into inc/meta/meta.assets.lua +make docs # Generate documentation with ldoc +make clean # Remove build artifacts +``` + +To run the game locally: `tic80 --fs=. impostor.lua` + +VSCode tasks are available for "Run TIC80", "Build & Run TIC80", "Export assets", and "Make build". + +There is no test framework — validation is done via `make lint` (luacheck). + +## Important Workflow Note + +**Do not run `git add` or `git commit`** — git operations are the user's responsibility. + +## Code Conventions (from GEMINI.md) + +- **Functions**: `PascalCase` (e.g., `UpdatePlayer`, `DrawHUD`) +- **Variables**: `snake_case` (e.g., `player_x`, `game_state`) +- **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `MAX_SPEED`) +- **Indentation**: 2 spaces +- **Tables**: Always multi-line with one key-value pair per line +- **Code sections**: Delimited with `--- @section SectionName` comments +- **TIC-80 APIs**: Use `btn()` for input, `spr()` for sprites, `map()` for tilemaps, `Print.text()` for text + +## Architecture + +The game is a **state machine** driven by a window manager. The build order is defined in `impostor.inc` — 99 source files are concatenated in dependency order. + +### Main Loop + +`TIC()` in `inc/system/system.main.lua` is TIC-80's per-frame callback. It: +1. Initializes game state once on first call +2. Updates mouse/context timing +3. Delegates to the current active window handler +4. Updates meters, timers, triggers, and glitch effects +5. Draws UI overlays + +### Window Manager (`inc/window/window.manager.lua`) + +Central UI state machine. Windows register with `id`, `update()`, and `draw()` handlers. Only one window is active at a time. All windows are declared in `window.register.lua`. + +| Window | Purpose | +|--------|---------| +| `intro_title` | Title screen | +| `intro_ttg` | "Thanks To Grandma" credits | +| `intro_brief` | Game briefing | +| `menu` | Main menu | +| `game` | Main gameplay (screens + decisions) | +| `popup` | General popup overlay | +| `discussion` | NPC dialogue/conversation | +| `minigame_button_mash` | Button Mash minigame | +| `minigame_rhythm` | Rhythm minigame | +| `minigame_ddr` | DDR minigame | +| `game_over` | Game over / restart screen | +| `end` | End game choice screen | +| `continued` | Day-continued notification | +| `credits` | Credits roll | +| `controls` | Control scheme display | +| `audiotest` | Audio testing utility | +| `player_name` | 3-character name entry before new game | +| `ascend_debug` | Debug utility: start at a specific ascension level | + +### Screen & Decision System (`inc/screen/`, `inc/decision/`) + +- **Screens** are gameplay scenes. Registered with `Screen.register({id, name, decisions[], background, init, update, draw, exit})`. They manage background maps and NPC sprite placement. +- **Decisions** are player choices available on a screen. Registered with `Decision.register({id, label, condition, handle})`. A `condition` function gates visibility; `handle` drives transitions (to new screens, dialogue, minigames). + +Screens: `home`, `office`, `work`, `toilet`, `walking_to_office`, `walking_to_home`, `mysterious_man`, `manager` + +Maps (`inc/map/`): `bedroom`, `office`, `street` — rendered via `map.manager.lua`. + +### Game Logic (`inc/logic/`) + +| Module | Purpose | +|--------|---------| +| `logic.meter.lua` | Tracks ISM/WPM/BM stats (0–1000), combo multipliers, daily decay (20/day) | +| `logic.day.lua` | Day counter; ascension triggers at day 3, game over at day 100 | +| `logic.timer.lua` | Event scheduling/delayed callbacks, one-shot and repeating | +| `logic.trigger.lua` | Conditional event handlers with start/stop callbacks | +| `logic.discussion.lua` | Dialogue parsing, branching answers, NPC portrait rendering | +| `logic.minigame.lua` | Config and win-overlay for Button Mash, Rhythm, and DDR | +| `logic.focus.lua` | Circular reveal/hide overlay transitions (expanding/shrinking circle) | +| `logic.glitch.lua` | Visual glitch effect (random vertical stripes), toggled via `Glitch.show()/hide()` | +| `logic.commute_glitch.lua` | 7-level glitch progression during ascension 7: corrupts sprite lists, remaps Norman to `norman_echo`, speeds up music, blocks/redirects decisions | +| `logic.codegenerator.lua` | Encodes player's 3-char name to a 6-char base-36 completion code shown on the end screen | + +### Global State (`inc/init/`) + +- `init.context.lua`: All runtime game state (current screen, meter values, progress flags). Persisted in memory bank 6. Key fields: `player_name` (3-char string), `commute_glitch_level` (0–7), `talked_to_norman_echo`, `talked_to_true_sumphore`, `have_been_to_office`, `have_done_work_today`. +- `init.config.lua`: Screen dimensions (240×136), palette colors, timing constants. Persisted in memory bank 7. +- `init.ascension.lua`: 9-level meta-progression system ("ASCENSION" letters progressively lit). Level 7 activates CommunteGlitch; level 9 unlocks the final "Break the cycle" decision. +- `init.context_debug.lua`: `Context.new_game_debug(level)` — starts a new game at a specific ascension level for testing. + +### Audio (`inc/audio/`) + +- `audio.manager.lua`: Music playback (no-restart if already playing). Named tracks: `room_work` (0), `activity_work` (1), `mystery` (2). +- `audio.generator.lua` / `audio.songs.lua`: Sound generation and song definitions. + +### Sprites (`inc/sprite/`) + +`sprite.manager.lua` handles registration. Supports single and composite sprites with offset layers. + +NPCs: `norman`, `norman_echo` (palette-remapped glitch variant of Norman, shown at commute glitch level 7), `sumphore`, `pizza_vendor`, and 10 developer archetypes (`dev_boy`, `dev_buddy`, `dev_extrovert`, `dev_girl`, `dev_guard`, `dev_guru`, `dev_hr_girl`, `dev_introvert`, `dev_operator`, `dev_project_manager`). Matrix characters: `matrix_architect`, `matrix_neo`, `matrix_oraculum`, `matrix_trinity`. + +### Discussions (`inc/discussion/`) + +Branching dialogue files loaded by `logic.discussion.lua`. Each file defines one or more named dialogue trees (keyed strings with answer arrays that apply meter deltas). + +| File | Dialogues | +|------|-----------| +| `discussion.sumphore.lua` | Sumphore conversations (glitch-aware variants at commute glitch level 7) | +| `discussion.coworker.lua` | Coworker coffee-chat variants per ascension level (`disc_0`, `disc_1`, `disc_asc_1`, `disc_2`, `disc_asc_2`, …) | +| `discussion.commute_glitch.lua` | 8 commute glitch encounter variants (`cg_0`–`cg_7`) + truth/Sumphore variant | +| `discussion.truth.lua` | Dialogue with the "truth" mysterious man | +| `discussion.pizza_vendor.lua` | Pizza vendor interaction | + +### Input Utilities (`inc/system/`) + +- `system.textinput.lua`: 3-character uppercase letter selector. Supports next/prev letter cycling (A↔Z wrapping) and cursor navigation. Used by `PlayerNameWindow`. + +### Key Directories + +``` +inc/ Source modules (concatenated at build) +assets/ Game assets (sprites, tiles, SFX, music) +assets_src/ Source art (Aseprite files, PNGs for import) +docs/ Design documentation (mostly Hungarian) +tools/ Build utilities (musicator: MIDI→TIC-80 converter) +prompts/ Feature templates +``` diff --git a/impostor.inc b/impostor.inc index d187cb3..5378ef8 100644 --- a/impostor.inc +++ b/impostor.inc @@ -50,7 +50,6 @@ decision/decision.go_to_toilet.lua decision/decision.go_to_walking_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_walking_to_home.lua decision/decision.go_to_sleep.lua decision/decision.do_work.lua diff --git a/inc/decision/decision.go_to_end.lua b/inc/decision/decision.go_to_end.lua deleted file mode 100644 index 7e86146..0000000 --- a/inc/decision/decision.go_to_end.lua +++ /dev/null @@ -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, -}) diff --git a/inc/decision/decision.go_to_home.lua b/inc/decision/decision.go_to_home.lua index bed3e88..8003f3f 100644 --- a/inc/decision/decision.go_to_home.lua +++ b/inc/decision/decision.go_to_home.lua @@ -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() diff --git a/inc/decision/decision.go_to_sleep.lua b/inc/decision/decision.go_to_sleep.lua index a7bbee2..0eceb2e 100644 --- a/inc/decision/decision.go_to_sleep.lua +++ b/inc/decision/decision.go_to_sleep.lua @@ -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, }) diff --git a/inc/decision/decision.have_a_coffee.lua b/inc/decision/decision.have_a_coffee.lua index a3323aa..7cf163d 100644 --- a/inc/decision/decision.have_a_coffee.lua +++ b/inc/decision/decision.have_a_coffee.lua @@ -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" diff --git a/inc/decision/decision.sumphore_discussion.lua b/inc/decision/decision.sumphore_discussion.lua index 2d43533..90a64b9 100644 --- a/inc/decision/decision.sumphore_discussion.lua +++ b/inc/decision/decision.sumphore_discussion.lua @@ -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") diff --git a/inc/init/init.ascension.lua b/inc/init/init.ascension.lua index 84017e0..96f807a 100644 --- a/inc/init/init.ascension.lua +++ b/inc/init/init.ascension.lua @@ -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 diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index 0681b7d..4a14749 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -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, diff --git a/inc/init/init.context_debug.lua b/inc/init/init.context_debug.lua index 7480f38..1c6f951 100644 --- a/inc/init/init.context_debug.lua +++ b/inc/init/init.context_debug.lua @@ -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] diff --git a/inc/logic/logic.commute_glitch.lua b/inc/logic/logic.commute_glitch.lua index 768b350..9608b6e 100644 --- a/inc/logic/logic.commute_glitch.lua +++ b/inc/logic/logic.commute_glitch.lua @@ -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. diff --git a/inc/logic/logic.meter.lua b/inc/logic/logic.meter.lua index aa114f1..d746d93 100644 --- a/inc/logic/logic.meter.lua +++ b/inc/logic/logic.meter.lua @@ -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 + diff --git a/inc/screen/screen.home.lua b/inc/screen/screen.home.lua index 86916b3..4a3cbb8 100644 --- a/inc/screen/screen.home.lua +++ b/inc/screen/screen.home.lua @@ -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 diff --git a/inc/screen/screen.mysterious_man.lua b/inc/screen/screen.mysterious_man.lua index f8f458c..f4eb740 100644 --- a/inc/screen/screen.mysterious_man.lua +++ b/inc/screen/screen.mysterious_man.lua @@ -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, }) diff --git a/inc/screen/screen.toilet.lua b/inc/screen/screen.toilet.lua index 664ac66..cf399bf 100644 --- a/inc/screen/screen.toilet.lua +++ b/inc/screen/screen.toilet.lua @@ -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, }) diff --git a/inc/screen/screen.walking_to_office.lua b/inc/screen/screen.walking_to_office.lua index 771f067..d870a7e 100644 --- a/inc/screen/screen.walking_to_office.lua +++ b/inc/screen/screen.walking_to_office.lua @@ -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 }) diff --git a/inc/window/window.end.lua b/inc/window/window.end.lua index 25c56fe..185387f 100644 --- a/inc/window/window.end.lua +++ b/inc/window/window.end.lua @@ -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