From 3ed06353b825515a47292aeabc7e6efd8a10d58b Mon Sep 17 00:00:00 2001 From: Bela Mezo Date: Mon, 27 Apr 2026 18:22:41 +0200 Subject: [PATCH 1/9] Drawing both sides of the home screen. --- inc/meta/meta.assets.lua | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/inc/meta/meta.assets.lua b/inc/meta/meta.assets.lua index 51b17a7..4441204 100644 --- a/inc/meta/meta.assets.lua +++ b/inc/meta/meta.assets.lua @@ -509,23 +509,23 @@ -- 255:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc -- -- --- 000:ffffffffff0010201020102010201020102010201020102000ffffffffff40404040404087f3f3f3f397a7b7c7d7a7e7f70818a7b7c7d7a7b7c7d7a70b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 001:ffffffffff0040404040404040404040404040404040404000ffffffffff40404040404087f3f3f3f328a7384858a76878f388a7384858a7384858a70b40403b4b4040404040404040404040404040404040404040404040400b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 002:ffffffffff00406070408090a040b0c0d0e0f001f001112100ffffffffff984098409840a8f3f3f3f3b8a7a7a7a7a7c8d8e8f8a7a7a7a7a7a7a7a7a70b405b6b7b4040404040404040404040404040d0e0f001f001f00111210b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 003:ffffffffff004031414051617140814091a1b1b1b1b1c1d100ffffffffff984098409840a8f3f3f3f3091919191919293949591919191919191919190b8b9babbb4040cbdbebfb0c401c2c2c2c3c4091a14c5c6c6c6c6cc1d10b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 004:ffffffffffe140f1024012223240814042a15262728292a2e1ffffffffff984098409840a8f3f3f3f369f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f37c8c9cacbc7282ccdcecfc0d401d3030302d4042a13d4d7282728292a27c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 005:ffffffffffb240c2d240e2f203132333435363738393a3b3b2ffffffffff984098409840a8f3f3f3f369f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f35d406d7d40e3958d9dadbdcd40ddedfded0e404353839383938393a3b35d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 006:ffffffffffe1c3d3c3d3e3f30414c3d32434445410201020e1ffffffffff404040404040798989898999a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a97c1e1e1e1e44542e1e1e1e3e1e444e4e4e541e243444544454445444547c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 007:ffffffffffb264748494a4b4c4d46494649464940040e4f4b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 008:ffffffffffe1c30515d325d33545c3d355d3c3d365b17585e1ffffffffff4040404040404098989898404040404040404040404040404040404040407c1e7e8e1e1e1e9eae1ebe1e1e1e1e1e1e1e1e72821ebe1e72821ebe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 009:ffffffffffb264e395a5b594e39564c5d5946494e5b1b1f5b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e05151ebe1ecedeeefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 010:ffffffffffe1c306162636d34656c3e3d5d3c3d376b1b1b1e1ffffffffff404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 011:ffffffffffb264946494649464948696a694649410201020b2ffffffffff4040404040404040404040401a2a3a4a5a6a7a40404040404040404040405d1ee3952f3f1e4f5f1ebe1e1e1e1e1e1e1e1ee3951ebe1ee3951ebe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 012:ffffffffffe1c37282d3c3d3c3d3b6c6d6d3c3d300e6f607e1ffffffffff4040404040404098989898408a9aaabaca9ada40404040404040404040407c1e4f5f1ebe1e0515eefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 013:ffffffffffb264e395946494649464946494649465172737b2ffffffffffeaeaeaeaeaeaeafafafafaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea5d1e0515eefe1e6f7f2f3f1e1e1e1e1e1e1e1e6f7f2f3f1e6f7f2f3f1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 014:ffffffffffe1c34454d3c3d3c3d3c3d3c3d3c3d3e5b14757e1fffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f37c1e6f7f2f3f1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 015:ffffffffffb2649464946494649464946494649476b1b1b1b2fffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f35d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --- 016:ffffffffff0010201020766777001020102010201020102000fffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f30b1b2b1b2b7667776777761b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 000:20102010200010201020102010201020102010201020102000102010201040404040404087f3f3f3f397a7b7c7d7a7e7f70818a7b7c7d7a7b7c7d7a70b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 001:40404040400040404040404040404040404040404040404000404040404040404040404087f3f3f3f328a7384858a76878f388a7384858a7384858a70b40403b4b4040404040404040404040404040404040404040404040400b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 002:408090a04000406070408090a040b0c0d0e0f001f001112100408090a040984098409840a8f3f3f3f3b8a7a7a7a7a7c8d8e8f8a7a7a7a7a7a7a7a7a70b405b6b7b4040404040404040404040404040d0e0f001f001f00111210b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 003:4051617140004031414051617140814091a1b1b1b1b1c1d1004051617140984098409840a8f3f3f3f3091919191919293949591919191919191919190b8b9babbb4040cbdbebfb0c401c2c2c2c3c4091a14c5c6c6c6c6cc1d10b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 004:4012223240e140f1024012223240814042a15262728292a2e14012223240984098409840a8f3f3f3f369f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f37c8c9cacbc7282ccdcecfc0d401d3030302d4042a13d4d7282728292a27c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 005:4040404040b240c2d240e2f203132333435363738393a3b3b24040404040984098409840a8f3f3f3f369f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f35d406d7d40e3958d9dadbdcd40ddedfded0e404353839383938393a3b35d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 006:d3c3d3c3d3e1c3d3c3d3e3f30414c3d32434445410201020e1c3d3c3d3c3404040404040798989898999a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a97c1e1e1e1e44542e1e1e1e3e1e444e4e4e541e243444544454445444547c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 007:9464354594b264748494a4b4c4d46494649464940040e4f4b264354594644040404040404040404040404040404040404040404040404040404040405d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 008:d3c3e395d3e1c30515d325d33545c3d355d3c3d365b17585e1c3e395d3254040404040404098989898404040404040404040404040404040404040407c1e7e8e1e1e1e9eae1ebe1e1e1e1e1e1e1e1e72821ebe1e72821ebe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 009:9464465694b264e395a5b594e39564c5d5946494e5b1b1f5b264e395a5b54040404040404040404040404040404040404040404040404040404040405d1e05151ebe1ecedeeefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 010:d3c3d3c3d3e1c306162636d34656c3e3d5d3c3d376b1b1b1e1c3e3952636404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 011:9464946494b264946494649464948696a694649410201020b264e395d3254040404040404040404040401a2a3a4a5a6a7a40404040404040404040405d1ee3952f3f1e4f5f1ebe1e1e1e1e1e1e1e1ee3951ebe1ee3951ebe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 012:d3c37282d3e1c37282d3c3d3c3d3b6c6d6d3c3d300e6f607e1c3e395a5b54040404040404098989898408a9aaabaca9ada40404040404040404040407c1e4f5f1ebe1e0515eefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 013:9464e39594b264e395946494649464946494649465172737b26406162636eaeaeaeaeaeaeafafafafaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea5d1e0515eefe1e6f7f2f3f1e1e1e1e1e1e1e1e6f7f2f3f1e6f7f2f3f1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 014:d3c34454d3e1c34454d3c3d3c3d3c3d3c3d3c3d3e5b14757e1c3d3c3d3c3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f37c1e6f7f2f3f1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 015:9464946494b2649464946494649464946494649476b1b1b1b26494649464f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f35d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- 016:201020102000102010207667770010201020102010201020001020102010f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f30b1b2b1b2b7667776777761b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- -- -- 016:00000000000000400040004000700070007000400040004000700070007000c000c000c000c000c000c000c000c000c000c000c000c000c000c000c0470000000000 From 4ba02c894c9bfdc8d1a3475a9745cb0241c3ac7b Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Mon, 27 Apr 2026 21:22:38 +0200 Subject: [PATCH 2/9] credit window --- .luacheckrc | 1 + impostor.inc | 1 + inc/window/window.credits.lua | 197 +++++++++++++++++++++++++++++++++ inc/window/window.menu.lua | 8 ++ inc/window/window.register.lua | 3 + 5 files changed, 210 insertions(+) create mode 100644 inc/window/window.credits.lua diff --git a/.luacheckrc b/.luacheckrc index c8dfabd..4dd86b6 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -24,6 +24,7 @@ globals = { "Minigame", "Window", "ContinuedWindow", + "CreditsWindow", "TTGIntroWindow", "BriefIntroWindow", "TitleIntroWindow", diff --git a/impostor.inc b/impostor.inc index 4f7d075..d7c4457 100644 --- a/impostor.inc +++ b/impostor.inc @@ -79,6 +79,7 @@ window/window.minigame.rhythm.lua window/window.minigame.ddr.lua window/window.discussion.lua window/window.continued.lua +window/window.credits.lua window/window.game.lua system/system.main.lua meta/meta.assets.lua diff --git a/inc/window/window.credits.lua b/inc/window/window.credits.lua new file mode 100644 index 0000000..174dbd8 --- /dev/null +++ b/inc/window/window.credits.lua @@ -0,0 +1,197 @@ +--- @section CreditsWindow + +local _time = 0.0 +local _scroll_x = 0.0 +local _scroll_total_w = 0 +local _scroll_chars = {} +local _title_chars = {} +local _title_total_w = 0 +local _stars = {} + +local TITLE = "TELETYPE GAMES" + +local SCROLL_PARTS = { + "WEB: GAMES.TELETYPE.HU", + "BBS: GAMES.TELETYPE.HU:2323", + "IRC: LIBERA.CHAT #TELETYPEGAMES", + "YOUTUBE.COM/@TELETYPEGAMES", +} + +local SCROLL_SEP = " * " + +local SCROLL_SPEED = 55.0 +local SCROLL_Y = 129 +local SCROLL_ZONE_COLS = { 7, 4, 9 } + +local TITLE_Y = 4 +local TITLE_FALL_DUR = 0.45 +local TITLE_DELAY_STEP = 0.18 + +local RASTER_COLS = { 1, 3, 9, 10, 11, 4, 11, 10, 9, 3, 1 } +local RASTER_Y_TOP = 26 +local RASTER_Y_BOT = 110 + +local AUTHORS = { + "Mr. Zero - Zsolt Tasnadi", + "Mr. One - Balazs Tari", + "Mr. Two - Zoltan Timar", + "Mr. Three - Bela Mezo", +} +local AUTHORS_BASE_Y = 56 +local AUTHORS_LINE_H = 12 +local AUTHORS_ENTRY_DT = 0.65 +local AUTHORS_ENTRY_V = 2.5 + +local RAINBOW = { 4, 9, 3, 7, 13, 2, 9, 4 } +local NUM_STARS = 40 + +--- Initialises credits state and pre-computes character metrics. +--- @within CreditsWindow +function CreditsWindow.init() + _time = 0.0 + _scroll_x = Config.screen.width + 4.0 + + _title_chars = {} + _title_total_w = 0 + for i = 1, #TITLE do + local ch = TITLE:sub(i, i) + local w = print(ch, 0, -100, 0, false, 2) + _title_chars[i] = { ch = ch, ox = _title_total_w, w = w } + _title_total_w = _title_total_w + w + end + + _scroll_chars = {} + _scroll_total_w = 0 + local function append_str(str, col) + for i = 1, #str do + local ch = str:sub(i, i) + local w = print(ch, 0, -100, 0, false, 1) + _scroll_chars[#_scroll_chars + 1] = { ch = ch, ox = _scroll_total_w, w = w, col = col } + _scroll_total_w = _scroll_total_w + w + end + end + for _, part in ipairs(SCROLL_PARTS) do + append_str(part, RAINBOW[math.random(#RAINBOW)]) + append_str(SCROLL_SEP, Config.colors.white) + end + + _stars = {} + for i = 1, NUM_STARS do + _stars[i] = { + x = math.random(0, Config.screen.width - 1) + 0.0, + y = math.random(0, Config.screen.height - 1), + spd = (i % 3 + 1) * 10.0, + col = ({ 1, 2, 4, 4 })[(i % 4) + 1], + } + end +end + +local function draw_stars() + for _, s in ipairs(_stars) do + pix(math.floor(s.x), s.y, s.col) + end +end + +local function draw_rasters() + local cy = RASTER_Y_TOP + math.floor(math.sin(_time * 1.3) * 6) + for i, col in ipairs(RASTER_COLS) do + local y = cy + i - 1 + if y >= 0 and y < Config.screen.height then + line(0, y, Config.screen.width - 1, y, col) + end + end + local cy2 = RASTER_Y_BOT + math.floor(math.sin(_time * 1.7 + 1.5) * 5) + for i, col in ipairs(RASTER_COLS) do + local y = cy2 + i - 1 + if y >= 0 and y < Config.screen.height then + line(0, y, Config.screen.width - 1, y, col) + end + end +end + +local function bounce_out(p) + local n1, d1 = 7.5625, 2.75 + if p < 1 / d1 then + return n1 * p * p + elseif p < 2 / d1 then + p = p - 1.5 / d1; return n1 * p * p + 0.75 + elseif p < 2.5 / d1 then + p = p - 2.25 / d1; return n1 * p * p + 0.9375 + else + p = p - 2.625 / d1; return n1 * p * p + 0.984375 + end +end + +local function draw_title() + local sx = math.floor((Config.screen.width - _title_total_w) / 2) + local n = #_title_chars + local max_dist = (n - 1) / 2.0 + for i, tc in ipairs(_title_chars) do + local dist = math.abs(i - (n + 1) / 2.0) + local delay = (max_dist - dist) * TITLE_DELAY_STEP + local t = math.max(0, _time - delay) + local p = math.min(1, t / TITLE_FALL_DUR) + local y = math.floor(-14 + bounce_out(p) * (TITLE_Y + 14)) + print(tc.ch, sx + tc.ox + 1, y + 1, 0, false, 2) + print(tc.ch, sx + tc.ox, y, Config.colors.light_blue, false, 2) + end +end + +local function draw_authors() + local col = Config.colors.light_blue + for i, lbl in ipairs(AUTHORS) do + local enter_t = math.max(0, _time - (i - 1) * AUTHORS_ENTRY_DT) + local slide = math.max(0, 1 - enter_t * AUTHORS_ENTRY_V) + local x_off = math.floor(slide * (Config.screen.width + 40)) + local yo = (slide < 0.01) and math.floor(math.sin(_time * 2.0 + i * 1.1) * 2) or 0 + Print.text(lbl, 12 + x_off, AUTHORS_BASE_Y + (i - 1) * AUTHORS_LINE_H + yo, col) + end +end + +local function draw_scroller() + local third = Config.screen.width / 3 + for pass = 0, 1 do + local base = _scroll_x + pass * _scroll_total_w + for _, sc in ipairs(_scroll_chars) do + local x = math.floor(base + sc.ox) + if x >= Config.screen.width then break end + if x + sc.w > 0 then + local zone = math.max(1, math.min(3, math.floor(x / third) + 1)) + print(sc.ch, x, SCROLL_Y, SCROLL_ZONE_COLS[zone], false, 1) + end + end + end +end + +--- Draws the credits window. +--- @within CreditsWindow +function CreditsWindow.draw() + cls(Config.colors.black) + draw_stars() + draw_rasters() + draw_title() + Print.text_center("Authors", Config.screen.width / 2, 47, Config.colors.light_grey) + draw_authors() + draw_scroller() + +end + +--- Updates credits window logic. +--- @within CreditsWindow +function CreditsWindow.update() + _time = _time + Context.delta_time + + for _, s in ipairs(_stars) do + s.x = s.x + s.spd * Context.delta_time + if s.x >= Config.screen.width then s.x = s.x - Config.screen.width end + end + + _scroll_x = _scroll_x - SCROLL_SPEED * Context.delta_time + if _scroll_x <= -_scroll_total_w then + _scroll_x = _scroll_x + _scroll_total_w + end + + if Input.back() or Input.select() then + Window.set_current("menu") + end +end diff --git a/inc/window/window.menu.lua b/inc/window/window.menu.lua index ad33971..ce45395 100644 --- a/inc/window/window.menu.lua +++ b/inc/window/window.menu.lua @@ -139,6 +139,13 @@ function MenuWindow.controls() Window.set_current("controls") end +--- Opens the credits screen. +--- @within MenuWindow +function MenuWindow.credits() + CreditsWindow.init() + Window.set_current("credits") +end + --- Opens the audio test menu. --- @within MenuWindow function MenuWindow.audio_test() @@ -173,6 +180,7 @@ function MenuWindow.refresh_menu_items() table.insert(_menu_items, {label = "New Game", decision = MenuWindow.new_game}) table.insert(_menu_items, {label = "Load Game", decision = MenuWindow.load_game}) table.insert(_menu_items, {label = "Controls", decision = MenuWindow.controls}) + table.insert(_menu_items, {label = "Credits", decision = MenuWindow.credits}) if Context.test_mode then table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test}) diff --git a/inc/window/window.register.lua b/inc/window/window.register.lua index a3388ac..a65b691 100644 --- a/inc/window/window.register.lua +++ b/inc/window/window.register.lua @@ -39,3 +39,6 @@ Window.register("discussion", DiscussionWindow) ContinuedWindow = {} Window.register("continued", ContinuedWindow) + +CreditsWindow = {} +Window.register("credits", CreditsWindow) From 7df42dd2cde3628e06295ab250676e407229767e Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Mon, 27 Apr 2026 22:26:16 +0200 Subject: [PATCH 3/9] feat: added game over screen, fixed bar filling on ddr, applied tamagochi logic to game --- .luacheckrc | 1 + impostor.inc | 1 + inc/decision/decision.go_to_toilet.lua | 16 +++ inc/discussion/discussion.coworker.lua | 7 ++ inc/discussion/discussion.sumphore.lua | 4 + inc/init/init.context.lua | 9 ++ inc/logic/logic.day.lua | 11 +++ inc/logic/logic.meter.lua | 129 +++++++++++++++++++++++-- inc/screen/screen.mysterious_man.lua | 1 + inc/screen/screen.toilet.lua | 10 +- inc/window/window.gameover.lua | 59 +++++++++++ inc/window/window.minigame.ddr.lua | 9 +- inc/window/window.minigame.mash.lua | 49 +++++++++- inc/window/window.minigame.rhythm.lua | 3 +- inc/window/window.register.lua | 3 + 15 files changed, 295 insertions(+), 17 deletions(-) create mode 100644 inc/window/window.gameover.lua diff --git a/.luacheckrc b/.luacheckrc index c8dfabd..5cd7769 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -38,6 +38,7 @@ globals = { "MysteriousManScreen", "DiscussionWindow", "EndWindow", + "GameOverWindow", "mset", "mget", "btnp", diff --git a/impostor.inc b/impostor.inc index 4f7d075..7ce73a8 100644 --- a/impostor.inc +++ b/impostor.inc @@ -66,6 +66,7 @@ screen/screen.work.lua screen/screen.mysterious_man.lua window/window.manager.lua window/window.register.lua +window/window.gameover.lua window/window.end.lua window/window.intro.title.lua window/window.intro.ttg.lua diff --git a/inc/decision/decision.go_to_toilet.lua b/inc/decision/decision.go_to_toilet.lua index 1a6d8b9..c28f6ec 100644 --- a/inc/decision/decision.go_to_toilet.lua +++ b/inc/decision/decision.go_to_toilet.lua @@ -1,7 +1,23 @@ +local function apply_home_toilet_meter_delta() + local max = Meter.get_max() + Meter.add("bm", -math.floor(max * 0.15)) + Meter.add("ism", -math.floor(max * 0.10)) + Meter.add("wpm", math.floor(max * 0.05)) +end + Decision.register({ id = "go_to_toilet", label = "Go to Toilet", handle = function() + if not Context.have_done_work_today and not Context.toilet_meters_today_morning then + apply_home_toilet_meter_delta() + Context.toilet_meters_today_morning = true + elseif Context.have_been_to_office + and Context.have_done_work_today + and not Context.toilet_meters_today_evening then + apply_home_toilet_meter_delta() + Context.toilet_meters_today_evening = true + end Util.go_to_screen_by_id("toilet") end, }) diff --git a/inc/discussion/discussion.coworker.lua b/inc/discussion/discussion.coworker.lua index 344e04c..a7c4c1c 100644 --- a/inc/discussion/discussion.coworker.lua +++ b/inc/discussion/discussion.coworker.lua @@ -1,5 +1,6 @@ Discussion.register({ id = "coworker_disc_0", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Good morning Normal, enjoying your coffee as usual, huh?", @@ -18,6 +19,7 @@ Discussion.register({ Discussion.register({ id = "coworker_disc_1", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Norman, you look confused, what's up?", @@ -36,6 +38,7 @@ Discussion.register({ Discussion.register({ id = "coworker_disc_asc_1", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Normann you look weird and unfocused. You are usually locked in and not like this, what's up?", @@ -54,6 +57,7 @@ Discussion.register({ Discussion.register({ id = "coworker_disc_2", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Hey Norman, do you have new socks on? That's a weird color!", @@ -79,6 +83,7 @@ Discussion.register({ Discussion.register({ id = "coworker_disc_asc_2", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Normann, are you ok? You were doing weird things while typing?", @@ -97,6 +102,7 @@ Discussion.register({ Discussion.register({ id = "coworker_disc_3", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "You look so happy, did you catch a bull or something?", @@ -120,6 +126,7 @@ Discussion.register({ }) Discussion.register({ id = "coworker_disc_asc_3", + on_end = Meter.apply_coworker_discussion_reward, steps = { { question = "Normal, you should take a break, you don't live up to your name today", diff --git a/inc/discussion/discussion.sumphore.lua b/inc/discussion/discussion.sumphore.lua index adc604b..340c8d1 100644 --- a/inc/discussion/discussion.sumphore.lua +++ b/inc/discussion/discussion.sumphore.lua @@ -1,5 +1,6 @@ Discussion.register({ id = "sumphore_disc_asc_1", + on_end = Meter.apply_sumphore_discussion_reward, steps = { { question = "Are you still seeking the ox?", @@ -19,6 +20,7 @@ Discussion.register({ Discussion.register({ id = "sumphore_disc_asc_2", + on_end = Meter.apply_sumphore_discussion_reward, steps = { { question = "How's work? Your face looks strange", @@ -61,6 +63,7 @@ Discussion.register({ Discussion.register({ id = "sumphore_disc_asc_3", + on_end = Meter.apply_sumphore_discussion_reward, steps = { { question = "Do you think it's work you're doing?", @@ -88,6 +91,7 @@ Discussion.register({ Discussion.register({ id = "homeless_guy", + on_end = Meter.apply_sumphore_discussion_reward, steps = { { question = "Sup bro, how are you?", diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index 53d4d99..d13c6fb 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -23,6 +23,10 @@ Context = {} --- * have_met_sumphore (boolean) Whether the player has talked to the homeless guy.
--- * have_been_to_office (boolean) Whether the player has been to the office.
--- * have_done_work_today (boolean) Whether the player has done work today.
+--- * toilet_meters_today_morning (boolean) Whether the home (before work) toilet meter delta was already applied this day.
+--- * toilet_meters_today_evening (boolean) Whether the home (after work) toilet meter delta was already applied this day.
+--- * coworker_discussion_meter_applied_today (boolean) Whether the daily coworker discussion meter roll was already applied this day.
+--- * sumphore_discussion_meter_applied_today (boolean) Whether the daily sumphore discussion meter roll was already applied this day.
--- * game (table) Current game progress state. Contains: `current_screen` (string) active screen ID
function Context.initial_data() return { @@ -45,6 +49,10 @@ function Context.initial_data() home_norman_visible = false, have_been_to_office = false, have_done_work_today = false, + toilet_meters_today_morning = false, + toilet_meters_today_evening = false, + coworker_discussion_meter_applied_today = false, + sumphore_discussion_meter_applied_today = false, should_ascend = false, have_met_sumphore = false, office_sprites = {}, @@ -124,6 +132,7 @@ function Context.new_game() target_points = 100, instruction_text = "Wake up Norman!", show_progress_text = false, + meter_on_complete = Meter.apply_wakeup_reward, on_win = function() Audio.music_play_room_work() Meter.show() diff --git a/inc/logic/logic.day.lua b/inc/logic/logic.day.lua index ae9d760..1be8991 100644 --- a/inc/logic/logic.day.lua +++ b/inc/logic/logic.day.lua @@ -8,6 +8,10 @@ function Day.increase() if Context.day_count == 3 then Context.should_ascend = true end + if Context.day_count >= 100 and not Ascension.is_complete() then + GameOverWindow.show("days") + return + end for _, handler in ipairs(_day_increase_handlers) do handler() end @@ -27,6 +31,13 @@ Day.register_handler(function() m.bm = math.max(0, m.bm - METER_DECAY_PER_DAY) end) +Day.register_handler(function() + Context.toilet_meters_today_morning = false + Context.toilet_meters_today_evening = false + Context.coworker_discussion_meter_applied_today = false + Context.sumphore_discussion_meter_applied_today = false +end) + Day.register_handler(function() if Context.should_ascend then Ascension.increase() diff --git a/inc/logic/logic.meter.lua b/inc/logic/logic.meter.lua index 2607582..552ddd0 100644 --- a/inc/logic/logic.meter.lua +++ b/inc/logic/logic.meter.lua @@ -1,6 +1,8 @@ --- @section Meter local METER_MAX = 1000 -local METER_DEFAULT = 500 +local BM_METER_DEFAULT = 200 +local ISM_METER_DEFAULT = 500 +local WPM_METER_DEFAULT = 200 local METER_GAIN_PER_CHORE = 100 local METER_DECAY_PER_DAY = 20 local COMBO_BASE_BONUS = 0.02 @@ -26,9 +28,9 @@ Meter.COLOR_CONTOUR = Config.colors.white --- * hidden (boolean) Whether meters are hidden. function Meter.get_initial() return { - ism = METER_DEFAULT, - wpm = METER_DEFAULT, - bm = METER_DEFAULT, + ism = ISM_METER_DEFAULT, + wpm = WPM_METER_DEFAULT, + bm = BM_METER_DEFAULT, combo = 0, combo_timer = 0, hidden = false, @@ -103,22 +105,129 @@ function Meter.add(key, amount) if not Context or not Context.meters then return end local m = Context.meters if m[key] ~= nil then - m[key] = math.min(METER_MAX, m[key] + amount) + if amount > 0 and (key == "ism" or key == "bm") and m[key] >= METER_MAX then + GameOverWindow.show(key) + return + end + m[key] = math.max(0, math.min(METER_MAX, m[key] + amount)) end end --- Called on minigame completion. --- @within Meter -function Meter.on_minigame_complete() +--- @param is_work boolean If true (work-style minigame), apply combo to WPM/ISM/BM and advance combo. DDR uses `Meter.apply_ddr_reward` instead. Otherwise flat equal gain, combo unchanged. +function Meter.on_minigame_complete(is_work) local m = Context.meters - local gain = math.floor(METER_GAIN_PER_CHORE * Meter.get_combo_multiplier()) - Meter.add("wpm", gain) - Meter.add("ism", gain) - Meter.add("bm", gain) + if is_work then + local mult = Meter.get_combo_multiplier() + local wpm_delta = math.floor(METER_GAIN_PER_CHORE / mult) + local ism_bm_delta = math.floor(METER_GAIN_PER_CHORE * mult) + Meter.add("wpm", wpm_delta) + Meter.add("ism", ism_bm_delta) + Meter.add("bm", ism_bm_delta) + m.combo = m.combo + 1 + m.combo_timer = 0 + else + local flat = METER_GAIN_PER_CHORE + Meter.add("wpm", flat) + Meter.add("ism", flat) + Meter.add("bm", flat) + end +end + +--- Meter changes after DDR: uses max-meter percentages; combo advances like other work minigames. +--- 0 mistakes: WPM −10%, ISM +5%, BM +5%. 1–3: WPM −5%, ISM +10%, BM +10%. More than 3: WPM unchanged, ISM +10%, BM +10%. +--- @within Meter +--- @param mistake_count number Total mistakes (missed arrows, wrong inputs, and special-mode rule violations). +function Meter.apply_ddr_reward(mistake_count) + if not Context or not Context.meters then return end + local max = Meter.get_max() + local m = Context.meters + local wpm_pct, ism_pct, bm_pct + if mistake_count == 0 then + wpm_pct, ism_pct, bm_pct = -0.10, 0.05, 0.05 + elseif mistake_count <= 3 then + wpm_pct, ism_pct, bm_pct = -0.05, 0.10, 0.10 + else + wpm_pct, ism_pct, bm_pct = 0, 0.10, 0.10 + end + if wpm_pct ~= 0 then + Meter.add("wpm", math.floor(max * wpm_pct)) + end + if ism_pct ~= 0 then + Meter.add("ism", math.floor(max * ism_pct)) + end + if bm_pct ~= 0 then + Meter.add("bm", math.floor(max * bm_pct)) + end m.combo = m.combo + 1 m.combo_timer = 0 end +--- Meter changes for the wake-up button mash: faster completion is better for WPM. +--- Perfect: under 2s — WPM +20%. Good: 2–3s — WPM +10%, ISM +5%, BM +5%. Bad: over 3s — WPM −5%, ISM +10%, BM +10%. +--- @within Meter +--- @param elapsed_sec number Seconds from minigame start until the bar was filled. +function Meter.apply_wakeup_reward(elapsed_sec) + if not Context or not Context.meters then return end + local max = Meter.get_max() + local wpm_pct, ism_pct, bm_pct + if elapsed_sec < 2 then + wpm_pct, ism_pct, bm_pct = 0.20, 0, 0 + elseif elapsed_sec <= 3 then + wpm_pct, ism_pct, bm_pct = 0.10, 0.05, 0.05 + else + wpm_pct, ism_pct, bm_pct = -0.05, 0.10, 0.10 + end + if wpm_pct ~= 0 then + Meter.add("wpm", math.floor(max * wpm_pct)) + end + if ism_pct ~= 0 then + Meter.add("ism", math.floor(max * ism_pct)) + end + if bm_pct ~= 0 then + Meter.add("bm", math.floor(max * bm_pct)) + end +end + +--- Random single meter shift after finishing a coworker discussion: ISM +10%, WPM −10%, or BM +10%. +--- @within Meter +function Meter.apply_coworker_discussion_reward() + if not Context or not Context.meters then return end + if Context.coworker_discussion_meter_applied_today then return end + local max = Meter.get_max() + local delta = math.floor(max * 0.10) + local roll = math.random(1, 3) + if roll == 1 then + Meter.add("ism", delta) + elseif roll == 2 then + Meter.add("wpm", -delta) + else + Meter.add("bm", delta) + end + Context.coworker_discussion_meter_applied_today = true +end + +--- After finishing a sumphore discussion: reduce whichever of ISM / WPM / BM is highest by 10% of max (stable tie to ISM, then WPM, then BM). +--- @within Meter +function Meter.apply_sumphore_discussion_reward() + if not Context or not Context.meters then return end + if Context.sumphore_discussion_meter_applied_today then return end + local m = Context.meters + local max = Meter.get_max() + local delta = math.floor(max * 0.10) + local biggest_val_key = "ism" + local biggest_val = m.ism + for _, key in ipairs({ "wpm", "bm" }) do + if m[key] > biggest_val then + biggest_val = m[key] + biggest_val_key = key + end + end + Meter.add(biggest_val_key, -delta) + Context.sumphore_discussion_meter_applied_today = true +end + --- Draws meters. --- @within Meter function Meter.draw() diff --git a/inc/screen/screen.mysterious_man.lua b/inc/screen/screen.mysterious_man.lua index 5a07a4c..e55d11f 100644 --- a/inc/screen/screen.mysterious_man.lua +++ b/inc/screen/screen.mysterious_man.lua @@ -132,6 +132,7 @@ function MysteriousManScreen.wake_up() target_points = 100, instruction_text = "Wake up Norman!", show_progress_text = false, + meter_on_complete = Meter.apply_wakeup_reward, on_win = function() Audio.music_play_wakingup() Meter.show() diff --git a/inc/screen/screen.toilet.lua b/inc/screen/screen.toilet.lua index 908787f..664ac66 100644 --- a/inc/screen/screen.toilet.lua +++ b/inc/screen/screen.toilet.lua @@ -51,8 +51,8 @@ Screen.register({ local decay_pct = Meter.get_decay_percentage() local decay_text = string.format("-%d%%", decay_pct) local combo_mult = Meter.get_combo_multiplier() - local combo_pct = math.floor((combo_mult - 1) * 100) - local mult_text = string.format("+%d%%", combo_pct) + local ism_bm_combo_pct = math.floor((combo_mult - 1) * 100) + local wpm_combo_pct = math.floor((1 / combo_mult - 1) * 100 + 0.5) local meter_start_y = text_y + 10 local meter_list = { @@ -73,6 +73,12 @@ Screen.register({ rect(bar_x, bar_y, fill_w, bar_h, meter.color) end + local mult_text + if meter.key == "wpm" then + mult_text = string.format("%+d%%", wpm_combo_pct) + else + mult_text = string.format("+%d%%", ism_bm_combo_pct) + end local decay_w = print(decay_text, 0, -6, 0, false, 1) Print.text_contour(decay_text, bar_x - decay_w - 4, bar_y, Config.colors.light_blue, false, 1, Config.colors.white) Print.text_contour(mult_text, bar_x + bar_w + 4, bar_y, Config.colors.light_blue, false, 1, Config.colors.white) diff --git a/inc/window/window.gameover.lua b/inc/window/window.gameover.lua new file mode 100644 index 0000000..f62d1f4 --- /dev/null +++ b/inc/window/window.gameover.lua @@ -0,0 +1,59 @@ +--- @section GameOverWindow +local GAME_OVER_ART = [[ +_###_ __#__ #___# ##### +#____ _#_#_ ##_## #____ +#_### ##### #_#_# ####_ +#___# #___# #___# #____ +_###_ #___# #___# ##### + +_###_ #___# ##### ####_ +#___# #___# #____ #___# +#___# _#_#_ ####_ ####_ +#___# __#__ #____ #_#__ +_###_ __#__ ##### #__## +]] + +local REASON_MESSAGES = { + ism = "Your impostor syndrome consumed you.", + bm = "You burned out like a cheap candle.", + days = "100 days passed. The cycle never broke.", +} + +--- Shows the game over screen. +--- @within GameOverWindow +--- @param reason string One of "ism", "bm", "days". +function GameOverWindow.show(reason) + GameOverWindow.reason = reason + Context.game_in_progress = false + Glitch.show() + Window.set_current("game_over") +end + +--- Draws the game over screen. +--- @within GameOverWindow +function GameOverWindow.draw() + cls(Config.colors.black) + + local cx = Config.screen.width / 2 + local bounds = AsciiArt.draw(GAME_OVER_ART, { + char_w = 4, + char_h = 6, + line_gap = 1, + word_gap = 10, + color = Config.colors.red, + }) + + local msg = REASON_MESSAGES[GameOverWindow.reason] or "" + Print.text_center(msg, cx, bounds.bottom + 8, Config.colors.white) + Print.text_center("Press Z to restart", cx, Config.screen.height - 10, Config.colors.light_grey) +end + +--- Updates the game over screen logic. +--- @within GameOverWindow +function GameOverWindow.update() + if Input.select() then + Context.reset() + MenuWindow.refresh_menu_items() + Window.set_current("menu") + end +end diff --git a/inc/window/window.minigame.ddr.lua b/inc/window/window.minigame.ddr.lua index 6a0167c..1231206 100644 --- a/inc/window/window.minigame.ddr.lua +++ b/inc/window/window.minigame.ddr.lua @@ -130,6 +130,7 @@ function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context) Audio.sfx_arrowhit(arrow.note) game_context.special_mode_counter = game_context.special_mode_counter + 1 else + game_context.total_misses = game_context.total_misses + 1 if game_context.special_mode_condition then Audio.sfx_bloop() end game_context.special_mode_condition = false end @@ -141,10 +142,12 @@ function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context) game_context.bar_fill = game_context.bar_fill - game_context.fill_per_hit end else + game_context.total_misses = game_context.total_misses + 1 if game_context.special_mode_condition then Audio.sfx_bloop() end game_context.special_mode_condition = false end elseif special_mode == "only_nothing" then + game_context.total_misses = game_context.total_misses + 1 if game_context.special_mode_condition then Audio.sfx_bloop() end game_context.special_mode_condition = false end @@ -173,6 +176,9 @@ function MinigameDDRWindow.on_end(game_context) end game_context.special_mode_condition = game_context.special_mode_condition and was_ok + if game_context.special_mode_condition and sm ~= "normal" then + game_context.bar_fill = game_context.max_fill + end end --- Initializes DDR minigame state. @@ -336,7 +342,8 @@ function MinigameDDRWindow.update() mg.win_timer = mg.win_timer - 1 if mg.win_timer == 0 then Audio.music_stop() - Meter.on_minigame_complete() + Meter.apply_ddr_reward(mg.total_misses) + if not Context.game_in_progress then return end if mg.on_win then mg.on_win(mg) else diff --git a/inc/window/window.minigame.mash.lua b/inc/window/window.minigame.mash.lua index 467eaf1..7213b59 100644 --- a/inc/window/window.minigame.mash.lua +++ b/inc/window/window.minigame.mash.lua @@ -1,6 +1,35 @@ +--- @section MinigameButtonMashWindow + +---@class MinigameButtonMashState +---@field bar_fill number +---@field target_points number +---@field fill_per_press number +---@field base_degradation number +---@field degradation_multiplier number +---@field button_pressed_timer number +---@field button_press_duration number +---@field instruction_text string +---@field show_progress_text boolean +---@field return_window string? +---@field bar_x number +---@field bar_y number +---@field bar_width number +---@field bar_height number +---@field button_x number +---@field button_y number +---@field button_size number +---@field focus_center_x number? +---@field focus_center_y number? +---@field focus_initial_radius number +---@field win_timer number +---@field on_win (fun())? +---@field meter_on_complete (fun(elapsed_sec: number))? +---@field start_ms number? +---@field elapsed_sec number? + --- Gets initial button mash minigame configuration. --- @within MinigameButtonMashWindow ---- @return result table The default button mash minigame configuration. +---@return MinigameButtonMashState function MinigameButtonMashWindow.init_context() return { bar_fill = 0, @@ -24,7 +53,11 @@ function MinigameButtonMashWindow.init_context() focus_center_y = nil, focus_initial_radius = 0, win_timer = 0, - on_win = nil + on_win = nil, + --- If set, called with elapsed_sec instead of Meter.on_minigame_complete() + meter_on_complete = nil, + start_ms = nil, + elapsed_sec = nil, } end @@ -51,8 +84,10 @@ end function MinigameButtonMashWindow.start(return_window, params) Audio.music_stop() MinigameButtonMashWindow.init(params) + ---@type MinigameButtonMashState local mg = Context.minigame_button_mash mg.return_window = return_window or "game" + mg.start_ms = time() if mg.focus_center_x then Focus.start_driven(mg.focus_center_x, mg.focus_center_y, { initial_radius = mg.focus_initial_radius @@ -64,12 +99,18 @@ end --- Updates button mash minigame logic. --- @within MinigameButtonMashWindow function MinigameButtonMashWindow.update() + ---@type MinigameButtonMashState local mg = Context.minigame_button_mash if mg.win_timer > 0 then mg.win_timer = mg.win_timer - 1 if mg.win_timer == 0 then - Meter.on_minigame_complete() + if mg.meter_on_complete then + mg.meter_on_complete(mg.elapsed_sec or 0) + else + Meter.on_minigame_complete(false) + end + if not Context.game_in_progress then return end if mg.focus_center_x then Focus.stop() end Context.home_norman_visible = true Context.have_done_work_today = false @@ -97,6 +138,7 @@ function MinigameButtonMashWindow.update() end if mg.bar_fill >= mg.target_points then Audio.sfx_select() + mg.elapsed_sec = (time() - mg.start_ms) / 1000 mg.win_timer = Config.timing.minigame_win_duration return end @@ -116,6 +158,7 @@ end --- Draws button mash minigame. --- @within MinigameButtonMashWindow function MinigameButtonMashWindow.draw() + ---@type MinigameButtonMashState local mg = Context.minigame_button_mash if mg.return_window == "game" then GameWindow.draw_with_underlay(function() diff --git a/inc/window/window.minigame.rhythm.lua b/inc/window/window.minigame.rhythm.lua index d3ba312..dd2eee6 100644 --- a/inc/window/window.minigame.rhythm.lua +++ b/inc/window/window.minigame.rhythm.lua @@ -73,7 +73,8 @@ function MinigameRhythmWindow.update() if mg.win_timer > 0 then mg.win_timer = mg.win_timer - 1 if mg.win_timer == 0 then - Meter.on_minigame_complete() + Meter.on_minigame_complete(false) + if not Context.game_in_progress then return end if mg.focus_center_x then Focus.stop() end if mg.on_win then mg.on_win() diff --git a/inc/window/window.register.lua b/inc/window/window.register.lua index a3388ac..f1037e4 100644 --- a/inc/window/window.register.lua +++ b/inc/window/window.register.lua @@ -31,6 +31,9 @@ Window.register("minigame_rhythm", MinigameRhythmWindow) MinigameDDRWindow = {} Window.register("minigame_ddr", MinigameDDRWindow) +GameOverWindow = {} +Window.register("game_over", GameOverWindow) + EndWindow = {} Window.register("end", EndWindow) From 47e5e0ca17d1b3b0f5fc158ae7542598aa1bd45c Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Mon, 27 Apr 2026 22:54:50 +0200 Subject: [PATCH 4/9] codegenerator --- .luacheckrc | 4 ++ impostor.inc | 4 ++ inc/logic/logic.codegenerator.lua | 60 ++++++++++++++++ inc/system/system.input.lua | 6 ++ inc/system/system.textinput.lua | 81 +++++++++++++++++++++ inc/window/window.end.lua | 12 +++- inc/window/window.menu.lua | 41 ++++++++--- inc/window/window.player_name.lua | 115 ++++++++++++++++++++++++++++++ inc/window/window.register.lua | 6 ++ 9 files changed, 316 insertions(+), 13 deletions(-) create mode 100644 inc/logic/logic.codegenerator.lua create mode 100644 inc/system/system.textinput.lua create mode 100644 inc/window/window.player_name.lua diff --git a/.luacheckrc b/.luacheckrc index c8dfabd..e71c179 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -38,6 +38,10 @@ globals = { "MysteriousManScreen", "DiscussionWindow", "EndWindow", + "PlayerNameWindow", + "TextInput", + "CodeGenerator", + "CreditsWindow", "mset", "mget", "btnp", diff --git a/impostor.inc b/impostor.inc index 4f7d075..ddefee6 100644 --- a/impostor.inc +++ b/impostor.inc @@ -6,6 +6,7 @@ init/init.context.lua system/system.util.lua system/system.print.lua system/system.input.lua +system/system.textinput.lua system/system.mouse.lua system/system.asciiart.lua system/system.rle.lua @@ -16,6 +17,7 @@ logic/logic.timer.lua logic/logic.trigger.lua logic/logic.minigame.lua logic/logic.glitch.lua +logic/logic.codegenerator.lua logic/logic.discussion.lua system/system.ui.lua audio/audio.manager.lua @@ -79,6 +81,8 @@ window/window.minigame.rhythm.lua window/window.minigame.ddr.lua window/window.discussion.lua window/window.continued.lua +window/window.credits.lua +window/window.player_name.lua window/window.game.lua system/system.main.lua meta/meta.assets.lua diff --git a/inc/logic/logic.codegenerator.lua b/inc/logic/logic.codegenerator.lua new file mode 100644 index 0000000..50190cf --- /dev/null +++ b/inc/logic/logic.codegenerator.lua @@ -0,0 +1,60 @@ +--- @section CodeGenerator + +CodeGenerator = {} + +local SALT = 27471 +local BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +local NAME_LEN = 3 + +-- Per-position offsets derived from SALT so each character slot +-- maps to a different region of the 2-char base-36 space. +local SALTS = { + SALT % 36, + math.floor(SALT / 36) % 36, + math.floor(SALT / 1296) % 36, +} + +--- Encodes a number (0–935) as exactly 2 base-36 characters. +--- @within CodeGenerator +function CodeGenerator.encode_pair(n) + return BASE36:sub(math.floor(n / 36) + 1, math.floor(n / 36) + 1) + .. BASE36:sub(n % 36 + 1, n % 36 + 1) +end + +--- Decodes 2 base-36 characters back to a number. +--- @within CodeGenerator +function CodeGenerator.decode_pair(s) + local d1 = BASE36:find(s:sub(1, 1), 1, true) - 1 + local d2 = BASE36:find(s:sub(2, 2), 1, true) - 1 + return d1 * 36 + d2 +end + +--- Encrypts a player name into a code twice its length. +--- Each input character (A-Z, value 0-25) is encoded as +--- c + SALTS[i] * 26, producing 2 base-36 output characters. +--- @within CodeGenerator +--- @param text string NAME_LEN-character uppercase player name. +--- @return string Encrypted code (2 * NAME_LEN base-36 characters). +function CodeGenerator.encrypt(text) + local result = "" + for i = 1, NAME_LEN do + local c = math.max(0, (string.byte(text, i) or 65) - 65) + result = result .. CodeGenerator.encode_pair(c + SALTS[i] * 26) + end + return result +end + +--- Decrypts a personal code back to the original player name. +--- @within CodeGenerator +--- @param encrypted_text string The code to decrypt (2 * NAME_LEN chars). +--- @return string Original player name, or "???" if the code is invalid. +function CodeGenerator.decrypt(encrypted_text) + local t = encrypted_text:upper() + if #t ~= NAME_LEN * 2 then return "???" end + local result = "" + for i = 1, NAME_LEN do + local pair = CodeGenerator.decode_pair(t:sub((i - 1) * 2 + 1, i * 2)) + result = result .. string.char(pair % 26 + 65) + end + return result +end diff --git a/inc/system/system.input.lua b/inc/system/system.input.lua index 5f0cc15..6869f19 100644 --- a/inc/system/system.input.lua +++ b/inc/system/system.input.lua @@ -30,3 +30,9 @@ function Input.back() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_BACKSPACE) end --- Checks if Enter is pressed. --- @within Input function Input.enter() return keyp(INPUT_KEY_ENTER) end +--- Checks if Up is pressed or held (with repeat). +--- @within Input +function Input.up_repeat() return btnp(INPUT_KEY_UP, 20, 4) end +--- Checks if Down is pressed or held (with repeat). +--- @within Input +function Input.down_repeat() return btnp(INPUT_KEY_DOWN, 20, 4) end diff --git a/inc/system/system.textinput.lua b/inc/system/system.textinput.lua new file mode 100644 index 0000000..3584d18 --- /dev/null +++ b/inc/system/system.textinput.lua @@ -0,0 +1,81 @@ +--- @section TextInput + +TextInput = {} + +local LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +local _pos = {} +local _cursor = 1 +local _max_len = 3 + +--- Initialises a new text input session. +--- @within TextInput +--- @param max_len number Maximum character count (default 3). +function TextInput.init(max_len) + _max_len = max_len or 3 + _pos = {} + for i = 1, _max_len do _pos[i] = 1 end + _cursor = 1 +end + +--- Advances to the next letter at the cursor position (wraps Z→A). +--- @within TextInput +function TextInput.next_letter() + _pos[_cursor] = (_pos[_cursor] % #LETTERS) + 1 +end + +--- Goes back to the previous letter at the cursor position (wraps A→Z). +--- @within TextInput +function TextInput.prev_letter() + _pos[_cursor] = ((_pos[_cursor] - 2) % #LETTERS) + 1 +end + +--- Confirms the current letter and advances the cursor to the next position. +--- When called on the last position the cursor moves into the done state. +--- @within TextInput +function TextInput.select_letter() + if _cursor <= _max_len then _cursor = _cursor + 1 end +end + +--- Moves the cursor one position to the right (stops at last position). +--- @within TextInput +function TextInput.next_position() + if _cursor < _max_len then _cursor = _cursor + 1 end +end + +--- Moves the cursor one position to the left (stops at first position). +--- Also steps back out of the done state. +--- @within TextInput +function TextInput.prev_position() + if _cursor > 1 then _cursor = _cursor - 1 end +end + +--- Returns the assembled name string. +--- @within TextInput +--- @return string +function TextInput.get_name() + local s = "" + for i = 1, _max_len do s = s .. LETTERS:sub(_pos[i], _pos[i]) end + return s +end + +--- Returns the current 1-based cursor position. +--- @within TextInput +--- @return number +function TextInput.get_position() + return _cursor +end + +--- Returns the letter at the given 1-based position. +--- @within TextInput +--- @param i number +--- @return string +function TextInput.get_letter(i) + return LETTERS:sub(_pos[i], _pos[i]) +end + +--- Returns true when all positions have been confirmed. +--- @within TextInput +--- @return boolean +function TextInput.is_done() + return _cursor > _max_len +end diff --git a/inc/window/window.end.lua b/inc/window/window.end.lua index 291b054..63f5e4e 100644 --- a/inc/window/window.end.lua +++ b/inc/window/window.end.lua @@ -30,9 +30,15 @@ function EndWindow.draw() Print.text(yes_text, centerX - 40, y, yes_color) Print.text(no_text, centerX + 10, y, no_color) elseif Context._end.state == "ending" then - Print.text_center("Game over -- good ending.", Config.screen.width / 2, 50, Config.colors.light_blue) - Print.text_center("Congratulations!", Config.screen.width / 2, 70, Config.colors.white) - Print.text_center("Press Z to return to menu", Config.screen.width / 2, 110, Config.colors.light_grey) + local cx = Config.screen.width / 2 + local name = Context.player_name or "AAA" + local code = CodeGenerator.encrypt(name) + Print.text_center("Game over -- good ending.", cx, 40, Config.colors.light_blue) + Print.text_center("Congrats " .. name .. "!", cx, 54, Config.colors.white) + Print.text_center("Your personal code:", cx, 72, Config.colors.light_grey) + Print.text_center(code, cx, 84, Config.colors.white, false, 3) + Print.text_center("Write it down!", cx, 112, Config.colors.item) + Print.text_center("Press Z to return to menu", cx, 126, Config.colors.dark_grey) end end diff --git a/inc/window/window.menu.lua b/inc/window/window.menu.lua index ad33971..8d159f9 100644 --- a/inc/window/window.menu.lua +++ b/inc/window/window.menu.lua @@ -102,17 +102,13 @@ function MenuWindow.update() end end ---- Starts a new game from the menu. +--- Opens player name entry then starts a new game. --- @within MenuWindow function MenuWindow.new_game() - Context.new_game() -end - ---- Loads a game from the menu. ---- @within MenuWindow -function MenuWindow.load_game() - Context.load_game() - GameWindow.set_state("game") + PlayerNameWindow.init(function() + Context.new_game() + end) + Window.set_current("player_name") end --- Saves the current game from the menu. @@ -139,6 +135,21 @@ function MenuWindow.controls() Window.set_current("controls") end +--- Opens the player name entry screen (test mode shortcut). +--- @within MenuWindow +function MenuWindow.player_name() + PlayerNameWindow.init() + Window.set_current("player_name") +end + +--- Opens the credits screen. +--- @within MenuWindow +function MenuWindow.credits() + CreditsWindow.init() + Window.set_current("credits") +end + + --- Opens the audio test menu. --- @within MenuWindow function MenuWindow.audio_test() @@ -153,6 +164,14 @@ function MenuWindow.continued() GameWindow.set_state("continued") end +--- Opens the end screen for testing. +--- @within MenuWindow +function MenuWindow.end_screen() + Context._end.state = "ending" + Context._end.selection = 1 + GameWindow.set_state("end") +end + --- Opens the DDR minigame test. --- @within MenuWindow function MenuWindow.ddr_test() @@ -171,13 +190,15 @@ function MenuWindow.refresh_menu_items() end table.insert(_menu_items, {label = "New Game", decision = MenuWindow.new_game}) - table.insert(_menu_items, {label = "Load Game", decision = MenuWindow.load_game}) table.insert(_menu_items, {label = "Controls", decision = MenuWindow.controls}) + table.insert(_menu_items, {label = "Credits", decision = MenuWindow.credits}) if Context.test_mode then 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 = "DDR Test", decision = MenuWindow.ddr_test}) + table.insert(_menu_items, {label = "End Screen", decision = MenuWindow.end_screen}) + table.insert(_menu_items, {label = "Player Name", decision = MenuWindow.player_name}) end table.insert(_menu_items, {label = "Exit", decision = MenuWindow.exit}) diff --git a/inc/window/window.player_name.lua b/inc/window/window.player_name.lua new file mode 100644 index 0000000..4748ee1 --- /dev/null +++ b/inc/window/window.player_name.lua @@ -0,0 +1,115 @@ +--- @section PlayerNameWindow + +local _frame = 0 +local _on_confirm = nil +local MAX_LEN = 3 +local BOX_W = 24 +local BOX_H = 24 +local BOX_GAP = 12 +local BOX_Y = 50 +local WARN_Y = 104 + +local function box_start_x() + return math.floor((Config.screen.width - (MAX_LEN * BOX_W + (MAX_LEN - 1) * BOX_GAP)) / 2) +end + +local function box_x(i) + return box_start_x() + (i - 1) * (BOX_W + BOX_GAP) +end + +--- Initialises the player name window. +--- @within PlayerNameWindow +--- @param on_confirm function Called with the entered name when the player saves. +function PlayerNameWindow.init(on_confirm) + _frame = 0 + _on_confirm = on_confirm + TextInput.init(MAX_LEN) +end + +local function draw_boxes() + local cursor = TextInput.get_position() + local blink = math.floor(_frame / 18) % 2 == 0 + + for i = 1, MAX_LEN do + local x = box_x(i) + local is_cur = (i == cursor) + local done = TextInput.is_done() + + local fill = (is_cur and not done) and Config.colors.blue or Config.colors.black + local border = (is_cur and not done) and Config.colors.white + or done and Config.colors.light_blue + or Config.colors.dark_grey + + rect (x, BOX_Y, BOX_W, BOX_H, fill) + rectb(x, BOX_Y, BOX_W, BOX_H, border) + + local show = not (is_cur and blink and not done) + if show then + local ch = TextInput.get_letter(i) + local cw = print(ch, 0, -100, 0, false, 2) + local cx = x + math.floor((BOX_W - cw) / 2) + local cy = BOX_Y + math.floor((BOX_H - 11) / 2) + local col = (is_cur and not done) and Config.colors.white or Config.colors.light_grey + print(ch, cx, cy, col, false, 2) + end + end + + -- caret arrow below active box + if not TextInput.is_done() then + local cx = box_x(cursor) + math.floor(BOX_W / 2) + local ay = BOX_Y + BOX_H + 4 + line(cx - 4, ay, cx, ay + 4, Config.colors.white) + line(cx + 4, ay, cx, ay + 4, Config.colors.white) + end +end + +--- Draws the player name window. +--- @within PlayerNameWindow +function PlayerNameWindow.draw() + cls(Config.colors.black) + + Print.text_center("Player Name", Config.screen.width / 2, 14, Config.colors.white, false, 2) + + draw_boxes() + + if TextInput.is_done() then + Print.text_center("Z: save name B: edit", Config.screen.width / 2, BOX_Y + BOX_H + 12, Config.colors.light_blue) + else + Print.text_center("Up/Dn: letter Lft/Rgt: move Z: ok", Config.screen.width / 2, BOX_Y + BOX_H + 12, Config.colors.dark_grey) + end + + -- Warning section + rect(0, WARN_Y, Config.screen.width, Config.screen.height - WARN_Y, Config.colors.blue) + rectb(0, WARN_Y, Config.screen.width, Config.screen.height - WARN_Y, Config.colors.light_blue) + Print.text_center("Remember your name!", Config.screen.width / 2, WARN_Y + 8, Config.colors.white) + Print.text_center("You will need it to load the game.", Config.screen.width / 2, WARN_Y + 20, Config.colors.light_grey) +end + +--- Updates player name window logic. +--- @within PlayerNameWindow +function PlayerNameWindow.update() + _frame = _frame + 1 + + if TextInput.is_done() then + if Input.select() then + Context.player_name = TextInput.get_name() + if _on_confirm then _on_confirm() else Window.set_current("menu") end + elseif Input.back() then + TextInput.prev_position() + end + return + end + + if Input.up_repeat() then TextInput.next_letter() end + if Input.down_repeat() then TextInput.prev_letter() end + if Input.right() then TextInput.next_position() end + if Input.select() then TextInput.select_letter() end + + if Input.left() or Input.back() then + if TextInput.get_position() > 1 then + TextInput.prev_position() + else + Window.set_current("menu") + end + end +end diff --git a/inc/window/window.register.lua b/inc/window/window.register.lua index a3388ac..afc6fd6 100644 --- a/inc/window/window.register.lua +++ b/inc/window/window.register.lua @@ -39,3 +39,9 @@ Window.register("discussion", DiscussionWindow) ContinuedWindow = {} Window.register("continued", ContinuedWindow) + +CreditsWindow = {} +Window.register("credits", CreditsWindow) + +PlayerNameWindow = {} +Window.register("player_name", PlayerNameWindow) From 73b2a8843793cd25a6311b5b649fcd2afc26dcaf Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Mon, 27 Apr 2026 23:33:07 +0200 Subject: [PATCH 5/9] alphabetize luacheck config --- .luacheckrc | 115 ++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/.luacheckrc b/.luacheckrc index 727ce76..088b512 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -2,79 +2,78 @@ -- Configuration for luacheck globals = { - "Focus", - "Day", - "Timer", - "Glitch", - "Trigger", - "Discussion", - "Util", - "Decision", - "Screen", - "Sprite", - "UI", - "Print", - "Input", - "Audio", "AsciiArt", "Ascension", + "Audio", + "AudioTestWindow", + "BriefIntroWindow", + "CodeGenerator", "Config", "Context", - "Meter", - "Minigame", - "Window", "ContinuedWindow", - "CreditsWindow", - "TTGIntroWindow", - "BriefIntroWindow", - "TitleIntroWindow", - "MenuWindow", - "GameWindow", - "PopupWindow", "ControlsWindow", - "AudioTestWindow", - "MinigameButtonMashWindow", - "MinigameRhythmWindow", - "MinigameDDRWindow", - "MysteriousManScreen", + "CreditsWindow", + "Day", + "Decision", + "Discussion", "DiscussionWindow", "EndWindow", - "PlayerNameWindow", - "TextInput", - "CodeGenerator", - "CreditsWindow", + "Focus", "GameOverWindow", - "mset", - "mget", + "GameWindow", + "Glitch", + "Input", + "Map", + "MapBedroom", + "MenuWindow", + "Meter", + "Minigame", + "MinigameButtonMashWindow", + "MinigameDDRWindow", + "MinigameRhythmWindow", + "Mouse", + "MysteriousManScreen", + "PlayerNameWindow", + "PopupWindow", + "Print", + "RLE", + "Screen", + "Songs", + "Sprite", + "TIC", + "TTGIntroWindow", + "TextInput", + "Timer", + "TitleIntroWindow", + "Trigger", + "UI", + "Util", + "Window", + "beats_to_pattern", "btnp", + "circb", + "circ", + "cls", + "exit", + "frame_from_beat", + "index_menu", "keyp", + "line", + "map", + "mouse", + "mget", + "mset", "music", - "sfx", - "spr", + "musicator_generate_pattern", + "pix", + "print", "rect", "rectb", - "circ", - "circb", - "cls", - "tri", - "pix", - "line", - "Songs", - "frame_from_beat", - "beats_to_pattern", - "MapBedroom", - "TIC", - "exit", - "trace", - "index_menu", - "Map", - "map", + "sfx", + "spr", "time", - "RLE", - "mouse", - "Mouse", - "print", - "musicator_generate_pattern", + "trace", + "tri", } From efe903110bd0164cc454e1f574f7d69b0a7127ab Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Tue, 28 Apr 2026 00:51:42 +0200 Subject: [PATCH 6/9] feat: added ascension logic 4-7, added new decision (eating fast food), indicating meter changes better, added discussions (needs more work, but meh ... fine like this) --- impostor.inc | 2 + inc/decision/decision.do_work.lua | 5 +- inc/decision/decision.eating_fast_food.lua | 11 + inc/decision/decision.have_a_coffee.lua | 11 +- inc/decision/decision.sumphore_discussion.lua | 9 +- inc/discussion/discussion.coworker.lua | 223 ++++++++++++++++++ inc/discussion/discussion.pizza_vendor.lua | 28 +++ inc/discussion/discussion.sumphore.lua | 119 ++++++++++ inc/init/init.ascension.lua | 2 +- inc/init/init.context.lua | 4 + inc/logic/logic.day.lua | 2 + inc/logic/logic.meter.lua | 49 +++- inc/screen/screen.mysterious_man.lua | 48 +++- inc/screen/screen.walking_to_home.lua | 11 +- inc/screen/screen.walking_to_office.lua | 19 +- 15 files changed, 522 insertions(+), 21 deletions(-) create mode 100644 inc/decision/decision.eating_fast_food.lua create mode 100644 inc/discussion/discussion.pizza_vendor.lua diff --git a/impostor.inc b/impostor.inc index dcffdf2..aa7d97d 100644 --- a/impostor.inc +++ b/impostor.inc @@ -50,8 +50,10 @@ decision/decision.go_to_sleep.lua decision/decision.do_work.lua decision/decision.have_a_coffee.lua decision/decision.sumphore_discussion.lua +decision/decision.eating_fast_food.lua discussion/discussion.sumphore.lua discussion/discussion.coworker.lua +discussion/discussion.pizza_vendor.lua map/map.manager.lua map/map.bedroom.lua map/map.street.lua diff --git a/inc/decision/decision.do_work.lua b/inc/decision/decision.do_work.lua index 827707b..196f6ed 100644 --- a/inc/decision/decision.do_work.lua +++ b/inc/decision/decision.do_work.lua @@ -10,6 +10,7 @@ Decision.register({ modes_for_ascension_levels[1] = "only_special" modes_for_ascension_levels[2] = "only_left" modes_for_ascension_levels[3] = "only_nothing" + modes_for_ascension_levels[4] = "normal" MinigameDDRWindow.start("game", "generated", { on_win = function(game_context) @@ -19,8 +20,6 @@ Decision.register({ Context.should_ascend = true elseif (game_context.special_mode_condition and Context.ascension.level == 3) then Context.should_ascend = true - elseif (game_context.special_mode_condition and Context.ascension.level == 4) then - Context.should_ascend = true end Meter.show() @@ -28,7 +27,7 @@ Decision.register({ Window.set_current("game") Context.have_done_work_today = true end, - special_mode = modes_for_ascension_levels[Ascension.get_level()] + special_mode = modes_for_ascension_levels[Ascension.get_level()] or "normal" }) end, }) diff --git a/inc/decision/decision.eating_fast_food.lua b/inc/decision/decision.eating_fast_food.lua new file mode 100644 index 0000000..12ec7f4 --- /dev/null +++ b/inc/decision/decision.eating_fast_food.lua @@ -0,0 +1,11 @@ +Decision.register({ + id = "eating_fast_food", + label = "Eat Fast Food", + condition = function() + return Context.fast_food_eaten_today < 3 + end, + handle = function() + Context.fast_food_approaching = true + Discussion.start("pizza_vendor_disc", "game") + end, +}) diff --git a/inc/decision/decision.have_a_coffee.lua b/inc/decision/decision.have_a_coffee.lua index 985f3c9..cde5d49 100644 --- a/inc/decision/decision.have_a_coffee.lua +++ b/inc/decision/decision.have_a_coffee.lua @@ -4,10 +4,17 @@ Decision.register({ handle = function() local level = Ascension.get_level() 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 <= 5 then local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level) disc_id = "coworker_disc" .. suffix + elseif level == 6 then + if not Context.glitch_conversation_done_today and Context.glitch_conversation_count < 6 then + Context.glitch_conversation_done_today = true + Context.glitch_conversation_count = Context.glitch_conversation_count + 1 + Glitch.show() + Discussion.start("coworker_disc_asc_6_" .. Context.glitch_conversation_count, "game") + return + end end Discussion.start(disc_id, "game") end, diff --git a/inc/decision/decision.sumphore_discussion.lua b/inc/decision/decision.sumphore_discussion.lua index a968ddb..e29d959 100644 --- a/inc/decision/decision.sumphore_discussion.lua +++ b/inc/decision/decision.sumphore_discussion.lua @@ -13,9 +13,14 @@ Decision.register({ end 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 <= 5 then Discussion.start("sumphore_disc_asc_" .. level, "game") + elseif level == 6 then + if Context.glitch_conversation_count >= 6 then + Discussion.start("sumphore_disc_asc_6", "game") + else + Discussion.start("sumphore_disc_asc_6_waiting", "game") + end else Discussion.start("homeless_guy", "game", 4) end diff --git a/inc/discussion/discussion.coworker.lua b/inc/discussion/discussion.coworker.lua index a7c4c1c..aacf6a0 100644 --- a/inc/discussion/discussion.coworker.lua +++ b/inc/discussion/discussion.coworker.lua @@ -141,4 +141,227 @@ Discussion.register({ }, }, }, +}) + +Discussion.register({ + id = "coworker_disc_4", + on_end = Meter.apply_coworker_discussion_reward, + steps = { + { + question = "Normaaan! Same spot, same cup, same time. You're like a statue that drinks coffee!", + answers = { + { label = "I'm a person.", next_step = 2 }, + { label = "Yep. Still here.", next_step = 2 }, + }, + }, + { + question = "I love that about you! So reliable! If the coffee machine breaks we still have Norman!", + answers = { + { label = "Please don't.", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_4", + on_end = Meter.apply_coworker_discussion_reward, + steps = { + { + question = "Norman, you were seriously locked in today. What on earth were you building?", + answers = { + { label = "Just some things.", next_step = 2 }, + { label = "Nothing important.", next_step = 2 }, + }, + }, + { + question = "So modest! I heard the seniors literally whispering your name!", + answers = { + { label = "Concerning.", next_step = 3 }, + { label = "That's... fine.", next_step = 3 }, + }, + }, + { + question = "You should celebrate! Coffee's on me tomorrow!", + answers = { + { label = "Already have one.", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_5", + on_end = Meter.apply_coworker_discussion_reward, + steps = { + { + question = "Morning! Funny thought — I feel like we do this exact same thing every single day!", + answers = { + { label = "We do.", next_step = 2 }, + { label = "Yes. We do.", next_step = 2 }, + }, + }, + { + question = "Ha! Routine is such a comfort, right? Same coffee, same smile, same everything!", + answers = { + { label = "Sure. Very comforting.", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_5", + on_end = Meter.apply_coworker_discussion_reward, + steps = { + { + question = "Norman! You were staring right THROUGH your screen today. What was going on up there?", + answers = { + { label = "Coffee was cold.", next_step = 2 }, + { label = "Maybe I was.", next_step = 2 }, + }, + }, + { + question = "Were you meditating? Or doing your intense bug-stare thing?", + answers = { + { label = "Something like that.", next_step = 3 }, + { label = "Bug stare thing?", next_step = 3 }, + }, + }, + { + question = "You're always somewhere else in your head, Norman. Must be nice up there!", + answers = { + { label = "It's complicated.", next_step = nil }, + }, + }, + }, +}) + +local function _glitch_on_end() + Meter.apply_coworker_discussion_reward() + Context.glitch_conversation_count = Context.glitch_conversation_count + 1 + Glitch.hide() +end + +Discussion.register({ + id = "coworker_disc_asc_6_1", + on_end = _glitch_on_end, + steps = { + { + question = "Hey Norman, good morning! Good morning! Good morning! ...Sorry. I don't know why I keep saying that.", + answers = { + { label = "Are you feeling ok?", next_step = 2 }, + }, + }, + { + question = "Good morning. Good morning. Good— I can't stop. Why can't I stop?", + answers = { + { label = "...", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_6_2", + on_end = _glitch_on_end, + steps = { + { + question = "Hey... Marcus. How's it going?", + answers = { + { label = "My name is Norman.", next_step = 2 }, + }, + }, + { + question = "Right, sorry. Marcus. You look tired today.", + answers = { + { label = "Norman. It's Norman.", next_step = 3 }, + }, + }, + { + question = "Sure, sure. You should get some rest, Marcus.", + answers = { + { label = "...", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_6_3", + on_end = _glitch_on_end, + steps = { + { + question = "We've had this conversation before, haven't we? Exact same words. Same coffee. Same spot.", + answers = { + { label = "I don't think so.", next_step = 2 }, + { label = "Maybe.", next_step = 2 }, + }, + }, + { + question = "Every day. I always say the same thing and you always say that. It's very strange.", + answers = { + { label = "That's just routine.", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_6_4", + on_end = _glitch_on_end, + steps = { + { + question = "Do you ever look at the walls here? Really look? Sometimes they don't feel... solid.", + answers = { + { label = "They're just walls.", next_step = 2 }, + { label = "I know what you mean.", next_step = 2 }, + }, + }, + { + question = "Like they're only there because we expect them to be. Like set dressing. Does any of this feel load-bearing to you?", + answers = { + { label = "I need more coffee.", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_6_5", + on_end = _glitch_on_end, + steps = { + { + question = "Norman, I'm not supposed to— I mean. How are you doing today?", + answers = { + { label = "What weren't you supposed to say?", next_step = 2 }, + }, + }, + { + question = "...", + answers = { + { label = "Hello?", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "coworker_disc_asc_6_6", + on_end = _glitch_on_end, + steps = { + { + question = "Forget it. You won't remember this conversation anyway. None of us do.", + answers = { + { label = "What do you mean?", next_step = 2 }, + { label = "That's a strange thing to say.", next_step = 2 }, + }, + }, + { + question = "Tomorrow you'll come back and it'll be like this never happened. Same coffee. Same office. Same Norman.", + answers = { + { label = "...", next_step = nil }, + }, + }, + }, }) \ No newline at end of file diff --git a/inc/discussion/discussion.pizza_vendor.lua b/inc/discussion/discussion.pizza_vendor.lua new file mode 100644 index 0000000..238ec18 --- /dev/null +++ b/inc/discussion/discussion.pizza_vendor.lua @@ -0,0 +1,28 @@ +Discussion.register({ + id = "pizza_vendor_disc", + on_end = function() + Context.fast_food_approaching = false + end, + steps = { + { + question = "Hey friend! Hot slice, fresh out of the oven. You look like a man who knows good food!", + answers = { + { + label = "Devour a juicy one", + next_step = nil, + on_select = function() + local max = Meter.get_max() + Meter.add("wpm", math.floor(max * 0.05)) + Meter.add("ism", math.floor(max * -0.05)) + Meter.add("bm", math.floor(max * -0.05)) + Context.fast_food_eaten_today = Context.fast_food_eaten_today + 1 + end, + }, + { + label = "Stay lean and sharp", + next_step = nil, + }, + }, + }, + }, +}) diff --git a/inc/discussion/discussion.sumphore.lua b/inc/discussion/discussion.sumphore.lua index 340c8d1..fa27351 100644 --- a/inc/discussion/discussion.sumphore.lua +++ b/inc/discussion/discussion.sumphore.lua @@ -89,6 +89,125 @@ Discussion.register({ }, }) +Discussion.register({ + id = "sumphore_disc_asc_4", + on_end = Meter.apply_sumphore_discussion_reward, + steps = { + { + question = "The alarm wakes you every morning without fail, I bet.", + answers = { + { label = "It's how it works.", next_step = 2 }, + { label = "Sometimes I wish it didn't.", next_step = 2 }, + }, + }, + { + question = "What if the alarm is part of the problem?", + answers = { + { label = "That doesn't make any sense.", next_step = 3 }, + { label = "Go on.", next_step = 3 }, + }, + }, + { + question = "One morning, Norman. When the choice comes, choose the bed. See what the world does without you in it.", + answers = { + { label = "You're drunk.", next_step = nil }, + { label = "What choice?", next_step = nil, on_select = function() + Meter.add("ism", 5) + end }, + }, + }, + }, +}) + +Discussion.register({ + id = "sumphore_disc_asc_5", + on_end = Meter.apply_sumphore_discussion_reward, + steps = { + { + question = "You saw something you weren't supposed to, didn't you.", + answers = { + { label = "I don't know what you mean.", next_step = 2 }, + { label = "Maybe.", next_step = 2 }, + }, + }, + { + question = "The world around you has seams. Your coworkers slip sometimes. Say things that don't quite fit.", + answers = { + { label = "They seem fine to me.", next_step = nil }, + { label = "I've noticed something odd.", next_step = 3 }, + }, + }, + { + question = "Count those moments. Six of them should be enough to see the whole picture.", + answers = { + { label = "Six of what, exactly?", next_step = nil, on_select = function() + Meter.add("ism", 5) + end }, + { label = "How would you know any of this?", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "sumphore_disc_asc_6_waiting", + on_end = Meter.apply_sumphore_discussion_reward, + steps = { + { + question = "You look like a man who has seen something he can't explain.", + answers = { + { label = "I'm hearing things.", next_step = 2 }, + { label = "Maybe.", next_step = 2 }, + }, + }, + { + question = "Keep looking. The world has a way of showing you what you need to see. Talk to people. You're almost there.", + answers = { + { label = "Almost where?", next_step = nil }, + }, + }, + }, +}) + +Discussion.register({ + id = "sumphore_disc_asc_6", + on_end = function() + Meter.apply_sumphore_discussion_reward() + Context.should_ascend = true + Day.increase() + MysteriousManScreen.start({ + text = MysteriousManScreen.get_text_for_level(Ascension.get_level()) + }) + end, + steps = { + { + question = "You've been seeing the cracks, haven't you? The repetition. The loops. The coworkers who can't quite remember.", + answers = { + { label = "How do you know that?", next_step = 2 }, + }, + }, + { + question = "Because I was you, once. This isn't a workplace, Norman. It never was. You're in a system. And you've just become aware of it.", + answers = { + { label = "That can't be true.", next_step = 3 }, + { label = "I knew something was wrong.", next_step = 3 }, + }, + }, + { + question = "It doesn't matter if you believe it. You already know. That's why the cracks are showing. That's why you're still here.", + answers = { + { label = "What do I do now?", next_step = 4 }, + }, + }, + { + question = "Oh look, is that a squirrel ?", + answers = { + { label = "Alcoholic ...", next_step = nil }, + }, + }, + }, +}) + Discussion.register({ id = "homeless_guy", on_end = Meter.apply_sumphore_discussion_reward, diff --git a/inc/init/init.ascension.lua b/inc/init/init.ascension.lua index e343b90..84017e0 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, + level = 0, -- FYI: change this to test ascension levels without having to play through them } end diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index d13c6fb..a5d4d7d 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -53,8 +53,12 @@ function Context.initial_data() toilet_meters_today_evening = false, coworker_discussion_meter_applied_today = false, sumphore_discussion_meter_applied_today = false, + glitch_conversation_count = 0, + glitch_conversation_done_today = false, should_ascend = false, have_met_sumphore = false, + fast_food_approaching = false, + fast_food_eaten_today = 0, office_sprites = {}, walking_to_office_sprites = {}, game = { diff --git a/inc/logic/logic.day.lua b/inc/logic/logic.day.lua index 1be8991..59d5a72 100644 --- a/inc/logic/logic.day.lua +++ b/inc/logic/logic.day.lua @@ -36,6 +36,8 @@ Day.register_handler(function() Context.toilet_meters_today_evening = false Context.coworker_discussion_meter_applied_today = false Context.sumphore_discussion_meter_applied_today = false + Context.glitch_conversation_done_today = false + Context.fast_food_eaten_today = 0 end) Day.register_handler(function() diff --git a/inc/logic/logic.meter.lua b/inc/logic/logic.meter.lua index 552ddd0..1e22d24 100644 --- a/inc/logic/logic.meter.lua +++ b/inc/logic/logic.meter.lua @@ -8,6 +8,8 @@ local METER_DECAY_PER_DAY = 20 local COMBO_BASE_BONUS = 0.02 local COMBO_MAX_BONUS = 0.16 local COMBO_TIMEOUT_FRAMES = 600 +local FLASH_DURATION = 2.0 +local FLASH_COLOR = 4 -- Internal meters for tracking game progress and player stats. Meter.COLOR_ISM = Config.colors.orange @@ -16,6 +18,12 @@ Meter.COLOR_BM = Config.colors.red Meter.COLOR_BG = Config.colors.meter_bg Meter.COLOR_CONTOUR = Config.colors.white +local _flash = { + wpm = { timer = 0, delta = 0 }, + ism = { timer = 0, delta = 0 }, + bm = { timer = 0, delta = 0 }, +} + --- Gets initial meter values. --- @within Meter --- @return result table Initial meter values.
@@ -95,6 +103,12 @@ function Meter.update() end end end + local dt = Context.delta_time or 0 + for _, key in ipairs({ "wpm", "ism", "bm" }) do + if _flash[key].timer > 0 then + _flash[key].timer = _flash[key].timer - dt + end + end end --- Adds amount to a meter. @@ -109,7 +123,18 @@ function Meter.add(key, amount) GameOverWindow.show(key) return end + local prev_wpm = (key == "wpm") and m.wpm or nil + local old_val = m[key] m[key] = math.max(0, math.min(METER_MAX, m[key] + amount)) + local actual_delta = m[key] - old_val + if actual_delta ~= 0 and _flash[key] then + _flash[key].delta = actual_delta + _flash[key].timer = FLASH_DURATION + end + if prev_wpm and prev_wpm > 0 and m.wpm == 0 and Context.game_in_progress + and Ascension.get_level() == 5 then + Context.should_ascend = true + end end end @@ -258,12 +283,32 @@ function Meter.draw() 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) - if fill_w > 0 then + local flash = _flash[meter.key] + if flash and flash.timer > 0 then + local old_val = m[meter.key] - flash.delta + local old_fill_w = math.max(0, math.floor((old_val / max) * bar_w)) + local stable_w = math.min(fill_w, old_fill_w) + if stable_w > 0 then + rect(bar_x, bar_y, stable_w, bar_h, meter.color) + end + if flash.delta > 0 then + local hi_w = fill_w - stable_w + if hi_w > 0 then + rect(bar_x + stable_w, bar_y, hi_w, bar_h, FLASH_COLOR) + end + else + local hi_w = old_fill_w - fill_w + if hi_w > 0 then + rect(bar_x + fill_w, bar_y, hi_w, bar_h, FLASH_COLOR) + end + end + elseif fill_w > 0 then rect(bar_x, bar_y, fill_w, bar_h, meter.color) end ---print(meter.label, label_x, label_y, meter.color, false, 1, true) end local ascension_y = start_y + 3 * line_h + 1 - Ascension.draw(bar_x, ascension_y, { spacing = 8 }) + Ascension.draw(bar_x - 4, ascension_y, { spacing = 8 }) end + diff --git a/inc/screen/screen.mysterious_man.lua b/inc/screen/screen.mysterious_man.lua index e55d11f..880be12 100644 --- a/inc/screen/screen.mysterious_man.lua +++ b/inc/screen/screen.mysterious_man.lua @@ -54,12 +54,40 @@ local ASC_45_TEXT = [[ ]] +local ASC_56_TEXT = [[ + Norman is not as productive as he should be. + + Can we distract him? + + We need to keep him busy. + + We need + + More + + Time +]] + +local ASC_67_TEXT = [[ + He knows. + + Norman has broken through the first veil. + + The simulation is compromised. + + This was not supposed to happen. + + Not yet. +]] + local ascension_texts = { [1] = ASC_01_TEXT, [2] = ASC_12_TEXT, [3] = ASC_23_TEXT, [4] = ASC_34_TEXT, [5] = ASC_45_TEXT, + [6] = ASC_56_TEXT, + [7] = ASC_67_TEXT, } function MysteriousManScreen.get_text_for_level(level) @@ -146,11 +174,25 @@ function MysteriousManScreen.wake_up() end -- Norman chooses to stay in bed, skipping the minigame and flash, and going straight to the next day. +-- At ascension level 4, staying in bed triggers 4->5: shows the ascension text then wakes with flash. -- @within MysteriousManScreen function MysteriousManScreen.stay_in_bed() - Day.increase() - state = STATE_DAY - day_timer = day_display_seconds + if Ascension.get_level() == 4 then + Context.should_ascend = true + Day.increase() + Ascension.consume_increase() + trigger_flash_on_wake = true + show_mysterious_screen = true + text = MysteriousManScreen.get_text_for_level(Ascension.get_level()) + text_y = Config.screen.height + text_done = false + text_done_timer = 0 + state = STATE_TEXT + else + Day.increase() + state = STATE_DAY + day_timer = day_display_seconds + end end --- Starts the mysterious man screen. diff --git a/inc/screen/screen.walking_to_home.lua b/inc/screen/screen.walking_to_home.lua index b9c9ad2..73000c0 100644 --- a/inc/screen/screen.walking_to_home.lua +++ b/inc/screen/screen.walking_to_home.lua @@ -4,15 +4,20 @@ Screen.register({ decisions = { "go_to_home", "go_to_office", + "eating_fast_food", }, init = function() Audio.music_play_room_work() end, background = "street", draw = function() - if Window.get_current_id() == "game" then - Sprite.draw_at("norman", 7 * 8, 3 * 8) - Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8) + local w = Window.get_current_id() + if w == "game" or w == "discussion" then + local norman_x = Context.fast_food_approaching and (19 * 8) or (7 * 8) + Sprite.draw_at("norman", norman_x, 3 * 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, 2 * 8) end end diff --git a/inc/screen/screen.walking_to_office.lua b/inc/screen/screen.walking_to_office.lua index af578d4..cdc39f2 100644 --- a/inc/screen/screen.walking_to_office.lua +++ b/inc/screen/screen.walking_to_office.lua @@ -1,3 +1,5 @@ +WalkingToOfficeScreen = {} + Screen.register({ id = "walking_to_office", name = "Walking to office", @@ -5,6 +7,7 @@ Screen.register({ "go_to_home", "go_to_office", "sumphore_discussion", + "eating_fast_food", }, init = function() Audio.music_play_room_work() @@ -32,12 +35,18 @@ Screen.register({ Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions) end, background = "street", + update = function() + end, draw = function() - if Window.get_current_id() == "game" then - Sprite.draw_at("norman", 7 * 8, 3 * 8) - Sprite.draw_at("sumphore", 9 * 8, 2 * 8) - Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8) - Sprite.draw_at("dev_guard", 22 * 8, 2 * 8) + local w = Window.get_current_id() + if w == "game" or w == "discussion" then + 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) end From 9fc659c8191b7f4dbaa6684ca5d3bce77f8be766 Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Tue, 28 Apr 2026 01:00:23 +0200 Subject: [PATCH 7/9] lint fix --- .luacheckrc | 1 + inc/screen/screen.mysterious_man.lua | 2 +- inc/screen/screen.walking_to_office.lua | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.luacheckrc b/.luacheckrc index 088b512..6c2efe3 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -48,6 +48,7 @@ globals = { "Trigger", "UI", "Util", + "WalkingToOfficeScreen", "Window", "beats_to_pattern", "btnp", diff --git a/inc/screen/screen.mysterious_man.lua b/inc/screen/screen.mysterious_man.lua index 880be12..f086ae5 100644 --- a/inc/screen/screen.mysterious_man.lua +++ b/inc/screen/screen.mysterious_man.lua @@ -59,7 +59,7 @@ local ASC_56_TEXT = [[ Can we distract him? - We need to keep him busy. + We need to keep him busy. We need diff --git a/inc/screen/screen.walking_to_office.lua b/inc/screen/screen.walking_to_office.lua index cdc39f2..44cd7d1 100644 --- a/inc/screen/screen.walking_to_office.lua +++ b/inc/screen/screen.walking_to_office.lua @@ -1,5 +1,3 @@ -WalkingToOfficeScreen = {} - Screen.register({ id = "walking_to_office", name = "Walking to office", From 53c95bf17d4025673fb031f3f51546048289d6e0 Mon Sep 17 00:00:00 2001 From: Zoltan Timar Date: Tue, 28 Apr 2026 01:01:15 +0200 Subject: [PATCH 8/9] lint fix --- inc/logic/logic.meter.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/logic/logic.meter.lua b/inc/logic/logic.meter.lua index 1e22d24..aa114f1 100644 --- a/inc/logic/logic.meter.lua +++ b/inc/logic/logic.meter.lua @@ -8,7 +8,7 @@ local METER_DECAY_PER_DAY = 20 local COMBO_BASE_BONUS = 0.02 local COMBO_MAX_BONUS = 0.16 local COMBO_TIMEOUT_FRAMES = 600 -local FLASH_DURATION = 2.0 +local METER_FLASH_DURATION = 2.0 local FLASH_COLOR = 4 -- Internal meters for tracking game progress and player stats. @@ -129,7 +129,7 @@ function Meter.add(key, amount) local actual_delta = m[key] - old_val if actual_delta ~= 0 and _flash[key] then _flash[key].delta = actual_delta - _flash[key].timer = FLASH_DURATION + _flash[key].timer = METER_FLASH_DURATION end if prev_wpm and prev_wpm > 0 and m.wpm == 0 and Context.game_in_progress and Ascension.get_level() == 5 then From 0ea538ce2402a8a7c92124db107b9bb26628005b Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Tue, 28 Apr 2026 01:07:30 +0200 Subject: [PATCH 9/9] new end window --- inc/window/window.end.lua | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/inc/window/window.end.lua b/inc/window/window.end.lua index 63f5e4e..25c56fe 100644 --- a/inc/window/window.end.lua +++ b/inc/window/window.end.lua @@ -33,12 +33,22 @@ function EndWindow.draw() local cx = Config.screen.width / 2 local name = Context.player_name or "AAA" local code = CodeGenerator.encrypt(name) - Print.text_center("Game over -- good ending.", cx, 40, Config.colors.light_blue) - Print.text_center("Congrats " .. name .. "!", cx, 54, Config.colors.white) - Print.text_center("Your personal code:", cx, 72, Config.colors.light_grey) - Print.text_center(code, cx, 84, Config.colors.white, false, 3) - Print.text_center("Write it down!", cx, 112, Config.colors.item) - Print.text_center("Press Z to return to menu", cx, 126, 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 end