Compare commits
270 Commits
1913d7d7d4
...
fix/imp_1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
839052ebc8 | ||
| 6a99ad76b8 | |||
| 5094ba2b9c | |||
|
|
44a7d10037 | ||
|
|
1b991f1f62 | ||
|
|
0d569ccf56 | ||
| 77d6f95721 | |||
| e2cd3d6dc7 | |||
|
|
92a217c389 | ||
|
|
3356d837c2 | ||
|
|
fe6f39e748 | ||
|
|
b5596bbbe0 | ||
|
|
3e31398d9d | ||
|
|
395208f814 | ||
|
|
4cc0025f5e | ||
| 0ea538ce24 | |||
| 629a27cfa8 | |||
|
|
53c95bf17d | ||
|
|
9fc659c819 | ||
|
|
07bc598ae9 | ||
|
|
133980f569 | ||
|
|
efe903110b | ||
| 73b2a88437 | |||
| 2812b0c17a | |||
| 48d65424a0 | |||
| 47e5e0ca17 | |||
| b5dd0cc686 | |||
|
|
340d0ff78c | ||
|
|
7df42dd2cd | ||
| 5d4aa1ee26 | |||
| 4ba02c894c | |||
| 98fd6981cc | |||
| 3ed06353b8 | |||
| 0b8e2368ec | |||
| ff96ca963d | |||
| ae3415b417 | |||
| ce819eae2b | |||
| 658e6908b5 | |||
| 5ae1eec48a | |||
| 04eb825646 | |||
|
|
e797377ec1 | ||
|
|
9d56ca2e7a | ||
| 9b379d32b3 | |||
| 5fc6ae5c14 | |||
| 1e3716196e | |||
| 4b5f11969b | |||
| a4a6ad2ab2 | |||
| 8921f02821 | |||
| 211af18c26 | |||
| b337ae8516 | |||
| 10316d3075 | |||
| 589b225ab0 | |||
| 7697b35336 | |||
| 6e1cf1db3e | |||
| 020bfd4134 | |||
| 87d4f5843b | |||
| 8e610f14a0 | |||
| 753509b4ea | |||
| 7dbbbb4668 | |||
| 3290d0fb89 | |||
| 4cf7df511b | |||
| 8b0bcdbe95 | |||
| 7c3a011ffc | |||
| 6ee874655e | |||
| 3420694287 | |||
| e12a27e4e1 | |||
| 81b47c3760 | |||
| 53ccda7702 | |||
| 2912d7c482 | |||
| 284c5aa4c8 | |||
| 93df710d14 | |||
| 4e6590174a | |||
| 6a33be82e9 | |||
|
|
9a3c9ee28c | ||
| 197cb9403b | |||
|
|
c41bf23a45 | ||
| 9d6d2c2c6f | |||
|
|
fb8ae157b6 | ||
|
|
803ddc482b | ||
|
|
ed14a86997 | ||
|
|
9263a90961 | ||
|
|
05091c7af2 | ||
|
|
f79233521d | ||
|
|
46d3ff2ebc | ||
|
|
3e6fa41021 | ||
| d899a74411 | |||
| 4c7d43fea3 | |||
| 94563d32c0 | |||
| 9da44700cb | |||
| b72875c42f | |||
| 6b4d47a438 | |||
| d916ddeeb5 | |||
|
|
ca883a15bd | ||
|
|
b4dcdaba58 | ||
| 823c3313af | |||
| 47e41f4054 | |||
| 65e0f131c0 | |||
| 6a6d5ab1fe | |||
| 6807f7ae7f | |||
| c8d8999ba3 | |||
| c8073aff02 | |||
| 0d7dcad54a | |||
| e12021a432 | |||
| 29e7361303 | |||
| 2311232290 | |||
| c0e4562971 | |||
| 2079de587a | |||
| ec19e7fe12 | |||
| 2034fbd9b0 | |||
| 883ad5fcbf | |||
| 30c894c2f0 | |||
| eb30ac0b0b | |||
| cd85a7214c | |||
| a00db92703 | |||
| 10e99ad25d | |||
| 37967639f4 | |||
| 9c18812e95 | |||
| 10b7dfd24b | |||
| 4d8f01187b | |||
| e5a942dd00 | |||
| 7a162d86b9 | |||
|
|
a208f0d27a | ||
| e2c5b38ecc | |||
|
|
e40214c45b | ||
|
|
b349ded281 | ||
| c897cdcbd5 | |||
|
|
5346281c5c | ||
|
|
5daf98fd06 | ||
| 5a0c8ef19d | |||
| 1c25a077a5 | |||
| fd983dd6e1 | |||
| 220ca27128 | |||
| 3bb1fb7941 | |||
| 24ce240f97 | |||
| 5d78cffc99 | |||
| d1e7704d66 | |||
| 71a21f3f86 | |||
| 5383fa3240 | |||
|
|
f3846a283e | ||
|
|
732c8b34c8 | ||
| 53a3f37c8e | |||
|
|
9b49e13a5d | ||
|
|
3f2df7d512 | ||
| 3db1ae1064 | |||
| f800cfff9b | |||
| 649b73b3f8 | |||
| cd41cb3312 | |||
| ecd094e2d7 | |||
| 5b4f3052a6 | |||
| 83301a1a8e | |||
| 77bd76f6f9 | |||
| aa7d0e902d | |||
| cb19251556 | |||
| 83f801210e | |||
| a732b5cbfa | |||
| c39e0ece35 | |||
| 03b6567c08 | |||
| 41eea638c0 | |||
| 67de284598 | |||
| 8a7fa96bcd | |||
| 6a3ef5d81e | |||
| c334f644de | |||
| 7cc886623b | |||
| 8ac5c5c3d9 | |||
| 43e943278b | |||
| 47efe91f58 | |||
|
|
fd7b5650d3 | ||
| 0e956ec7a9 | |||
| 0c2999f596 | |||
| e46c48b2ec | |||
| 99ace8a1e8 | |||
| 15bf66f1ca | |||
|
|
41f75da8c3 | ||
| e05018d637 | |||
| 8e104b1ff9 | |||
| e07eeb466b | |||
| 337f1fc132 | |||
| e14160114b | |||
|
|
1b64fd2392 | ||
|
|
aaf1479a78 | ||
| e56662f6ad | |||
| 2d25537abb | |||
|
|
66af47c483 | ||
| 8f9e044a17 | |||
|
|
954a39aef1 | ||
|
|
226d75d905 | ||
|
|
8f34cbf875 | ||
| 64de41a940 | |||
| 777c27aa54 | |||
| c321fbc19a | |||
| dc8a82d583 | |||
| c565213e44 | |||
| 035a2bc37e | |||
| 297ee8b622 | |||
|
|
7deeffa8d6 | ||
| 272a54ea87 | |||
| 14b14ffc0c | |||
| 7e87a78a15 | |||
| 62d4863a1a | |||
| d9febf16e0 | |||
| 6f5b17147c | |||
| 76964f872d | |||
| 3b137fd48e | |||
| b7791fb9ce | |||
| 7e1dd28808 | |||
| 0b25ecc793 | |||
| f08e4ad1d4 | |||
| 9ae6c12582 | |||
| 4e35cd4bd3 | |||
| ed2354b0fa | |||
| 7854dc8a9f | |||
| 3b9b67e2fa | |||
| 1a4565428d | |||
| f02bb75e4f | |||
|
|
e0c3b446af | ||
|
|
8832b6c833 | ||
|
|
9014e36014 | ||
|
|
7b263bb454 | ||
| 0640964ee4 | |||
| 1cf09de1fb | |||
| 6303781534 | |||
| e2bd1711c0 | |||
| 60a6c73a32 | |||
| c88562ae69 | |||
| 1d06376826 | |||
| 7a57c15eeb | |||
| 3922f51c8e | |||
| f589b9bbca | |||
| 3abe426e3a | |||
| 9eef8fd6e0 | |||
| 0357eb415e | |||
| 11b92f64d6 | |||
| d1720a0a50 | |||
| 31b98dcdf6 | |||
| ec84f23ad5 | |||
| 99b178262f | |||
| d20ef85ceb | |||
| ef4876b083 | |||
| 9a65891afa | |||
| 971acb02ca | |||
| 9fff21826b | |||
| 06d257a335 | |||
|
|
f553b09004 | ||
| facf37dc96 | |||
| bbe24cebf2 | |||
| 5872a27535 | |||
| cd279803ac | |||
|
|
c9db82cce7 | ||
| 3dc28849c4 | |||
| 8a6214e893 | |||
| d943b6deaa | |||
| b3b2159d75 | |||
| ae56cf3555 | |||
| 2fc241fee7 | |||
| 4e0145982f | |||
| 1c987fa08b | |||
| b6d0823875 | |||
| ffa82e8f92 | |||
| cfc07afe59 | |||
| 3fbce5aced | |||
| b3158bdc37 | |||
| 4907c9cadf | |||
| 45f2e746d6 | |||
| 8e8d181c34 | |||
| 5379e30cf3 | |||
| d3fb12703c | |||
| f9c539a854 | |||
| a777241d7a | |||
| 34340d9664 | |||
| 6a39128962 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1 +1,10 @@
|
|||||||
mranderson.lua
|
.claude
|
||||||
|
.local
|
||||||
|
impostor.lua
|
||||||
|
impostor.original.lua
|
||||||
|
prompts
|
||||||
|
docs
|
||||||
|
minify.lua
|
||||||
|
*.tic
|
||||||
|
*.zip
|
||||||
|
NOTES_*
|
||||||
|
|||||||
93
.luacheckrc
Normal file
93
.luacheckrc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
-- .luacheckrc
|
||||||
|
-- Configuration for luacheck
|
||||||
|
|
||||||
|
globals = {
|
||||||
|
"AsciiArt",
|
||||||
|
"Ascension",
|
||||||
|
"AscendDebugWindow",
|
||||||
|
"Audio",
|
||||||
|
"AudioTestWindow",
|
||||||
|
"BriefIntroWindow",
|
||||||
|
"CodeGenerator",
|
||||||
|
"Config",
|
||||||
|
"CommuteGlitch",
|
||||||
|
"Context",
|
||||||
|
"ContextDebug",
|
||||||
|
"ContinuedWindow",
|
||||||
|
"ControlsWindow",
|
||||||
|
"CreditsWindow",
|
||||||
|
"Day",
|
||||||
|
"Decision",
|
||||||
|
"Discussion",
|
||||||
|
"DiscussionWindow",
|
||||||
|
"EndWindow",
|
||||||
|
"Focus",
|
||||||
|
"GameOverWindow",
|
||||||
|
"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",
|
||||||
|
"WalkingToOfficeScreen",
|
||||||
|
"Window",
|
||||||
|
"beats_to_pattern",
|
||||||
|
"btnp",
|
||||||
|
"circb",
|
||||||
|
"circ",
|
||||||
|
"cls",
|
||||||
|
"exit",
|
||||||
|
"frame_from_beat",
|
||||||
|
"index_menu",
|
||||||
|
"keyp",
|
||||||
|
"line",
|
||||||
|
"map",
|
||||||
|
"mouse",
|
||||||
|
"mget",
|
||||||
|
"mset",
|
||||||
|
"music",
|
||||||
|
"musicator_generate_pattern",
|
||||||
|
"pix",
|
||||||
|
"poke4",
|
||||||
|
"print",
|
||||||
|
"rect",
|
||||||
|
"rectb",
|
||||||
|
"sfx",
|
||||||
|
"spr",
|
||||||
|
"time",
|
||||||
|
"trace",
|
||||||
|
"tri",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Exclude certain warnings globally
|
||||||
|
exclude_warnings = {
|
||||||
|
"undefined_global", -- Will be covered by 'globals' table
|
||||||
|
"redefined_loop_variable", -- Common in Lua for iterators
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Options for unused variables
|
||||||
|
std = "lua51" -- Assuming Lua 5.1, common for TIC-80
|
||||||
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@@ -14,6 +14,21 @@
|
|||||||
"inc/?.lua"
|
"inc/?.lua"
|
||||||
],
|
],
|
||||||
"Lua.diagnostics.disable": [
|
"Lua.diagnostics.disable": [
|
||||||
"undefined-global"
|
"undefined-global",
|
||||||
]
|
"undefined-doc-param",
|
||||||
|
"undefined-doc-name",
|
||||||
|
"luadoc-miss-param-name"
|
||||||
|
],
|
||||||
|
"python.autoComplete.extraPaths": [
|
||||||
|
"${workspaceFolder}/sources/poky/bitbake/lib",
|
||||||
|
"${workspaceFolder}/sources/poky/meta/lib"
|
||||||
|
],
|
||||||
|
"python.analysis.extraPaths": [
|
||||||
|
"${workspaceFolder}/sources/poky/bitbake/lib",
|
||||||
|
"${workspaceFolder}/sources/poky/meta/lib"
|
||||||
|
],
|
||||||
|
"files.associations": {
|
||||||
|
"*.conf": "bitbake",
|
||||||
|
"*.inc": "bitbake"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
34
.vscode/tasks.json
vendored
Normal file
34
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Run TIC80",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "tic80 --fs=. impostor.lua",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build & Run TIC80",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make clean && make build && tic80 --fs=. impostor.lua",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Export assets",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make export_assets",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Make build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,21 +1,45 @@
|
|||||||
environment: &environment
|
|
||||||
GAME_NAME: mranderson
|
|
||||||
GAME_LANG: lua
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: version
|
||||||
image: gitea.vps.teletype.hu/games/tic80pro:latest
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- 'apk add --no-cache make'
|
||||||
|
- 'make ci-version'
|
||||||
|
|
||||||
|
- name: lint
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- 'apk add --no-cache make lua5.4 lua5.4-dev luarocks gcc musl-dev'
|
||||||
|
- 'ln -sf /usr/bin/lua5.4 /usr/bin/lua'
|
||||||
|
- 'ln -sf /usr/bin/luarocks-5.4 /usr/bin/luarocks'
|
||||||
|
- 'luarocks install luacheck'
|
||||||
|
- 'make ci-lint'
|
||||||
|
|
||||||
|
- name: minify
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- 'apk add --no-cache make lua5.4 curl'
|
||||||
|
- 'ln -sf /usr/bin/lua5.4 /usr/bin/lua'
|
||||||
|
- 'make ci-minify'
|
||||||
|
|
||||||
|
- name: docs
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- 'apk add --no-cache make lua5.4 lua5.4-dev luarocks gcc musl-dev zip'
|
||||||
|
- 'ln -sf /usr/bin/lua5.4 /usr/bin/lua'
|
||||||
|
- 'ln -sf /usr/bin/luarocks-5.4 /usr/bin/luarocks'
|
||||||
|
- 'luarocks install ldoc'
|
||||||
|
- 'make ci-docs'
|
||||||
|
|
||||||
|
- name: export
|
||||||
|
image: git.teletype.hu/internal/tic80pro:latest
|
||||||
environment:
|
environment:
|
||||||
<<: *environment
|
|
||||||
XDG_RUNTIME_DIR: /tmp
|
XDG_RUNTIME_DIR: /tmp
|
||||||
commands:
|
commands:
|
||||||
- make build PROJECT=$GAME_NAME
|
- 'make ci-export'
|
||||||
- make export PROJECT=$GAME_NAME
|
|
||||||
|
|
||||||
- name: artifact
|
- name: artifact
|
||||||
image: alpine
|
image: alpine
|
||||||
environment:
|
environment:
|
||||||
<<: *environment
|
|
||||||
DROPAREA_HOST: vps.teletype.hu
|
DROPAREA_HOST: vps.teletype.hu
|
||||||
DROPAREA_PORT: 2223
|
DROPAREA_PORT: 2223
|
||||||
DROPAREA_TARGET_PATH: /home/drop
|
DROPAREA_TARGET_PATH: /home/drop
|
||||||
@@ -23,17 +47,15 @@ steps:
|
|||||||
DROPAREA_SSH_PASSWORD:
|
DROPAREA_SSH_PASSWORD:
|
||||||
from_secret: droparea_ssh_password
|
from_secret: droparea_ssh_password
|
||||||
commands:
|
commands:
|
||||||
- apk add --no-cache openssh-client sshpass
|
- 'apk add --no-cache make openssh-client sshpass'
|
||||||
- mkdir -p /root/.ssh
|
- 'make ci-artifact'
|
||||||
- sshpass -p $DROPAREA_SSH_PASSWORD scp -o StrictHostKeyChecking=no -P $DROPAREA_PORT $GAME_NAME.$GAME_LANG $GAME_NAME.tic $GAME_NAME.html.zip $DROPAREA_USER@$DROPAREA_HOST:$DROPAREA_TARGET_PATH
|
|
||||||
|
|
||||||
- name: update
|
- name: update
|
||||||
image: alpine
|
image: alpine
|
||||||
environment:
|
environment:
|
||||||
<<: *environment
|
UPDATE_SERVER: https://games.teletype.hu
|
||||||
UPDATE_SERVER: https://games.vps.teletype.hu
|
|
||||||
UPDATE_SECRET:
|
UPDATE_SECRET:
|
||||||
from_secret: update_secret_key
|
from_secret: update_secret_key
|
||||||
commands:
|
commands:
|
||||||
- apk add --no-cache curl
|
- 'apk add --no-cache make curl'
|
||||||
- curl "$UPDATE_SERVER/update?secret=$UPDATE_SECRET&name=$GAME_NAME&platform=tic80"
|
- 'make ci-update'
|
||||||
|
|||||||
148
CLAUDE.md
Normal file
148
CLAUDE.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
**Definitely not an Impostor** is a narrative-driven fantasy game built for [TIC-80](https://tic80.com/), a fantasy console. The game is written entirely in Lua. All source modules in `inc/` are concatenated at build time into a single `impostor.lua` file that TIC-80 loads.
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build # Concatenate inc/**/*.lua into impostor.lua (order from impostor.inc)
|
||||||
|
make minify # Build then minify (downloads minify.lua if missing)
|
||||||
|
make lint # Run luacheck with source mapping to original files
|
||||||
|
make watch # Auto-rebuild on file changes in inc/
|
||||||
|
make export # Export minified game to HTML and .tic formats
|
||||||
|
make import_assets # Import PNG sprite/tile assets into the TIC-80 cartridge
|
||||||
|
make export_assets # Extract TIC-80 asset sections into inc/meta/meta.assets.lua
|
||||||
|
make docs # Generate documentation with ldoc
|
||||||
|
make clean # Remove build artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the game locally: `tic80 --fs=. impostor.lua`
|
||||||
|
|
||||||
|
VSCode tasks are available for "Run TIC80", "Build & Run TIC80", "Export assets", and "Make build".
|
||||||
|
|
||||||
|
There is no test framework — validation is done via `make lint` (luacheck).
|
||||||
|
|
||||||
|
## Important Workflow Note
|
||||||
|
|
||||||
|
**Do not run `git add` or `git commit`** — git operations are the user's responsibility.
|
||||||
|
|
||||||
|
## Code Conventions (from GEMINI.md)
|
||||||
|
|
||||||
|
- **Functions**: `PascalCase` (e.g., `UpdatePlayer`, `DrawHUD`)
|
||||||
|
- **Variables**: `snake_case` (e.g., `player_x`, `game_state`)
|
||||||
|
- **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `MAX_SPEED`)
|
||||||
|
- **Indentation**: 2 spaces
|
||||||
|
- **Tables**: Always multi-line with one key-value pair per line
|
||||||
|
- **Code sections**: Delimited with `--- @section SectionName` comments
|
||||||
|
- **TIC-80 APIs**: Use `btn()` for input, `spr()` for sprites, `map()` for tilemaps, `Print.text()` for text
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The game is a **state machine** driven by a window manager. The build order is defined in `impostor.inc` — 99 source files are concatenated in dependency order.
|
||||||
|
|
||||||
|
### Main Loop
|
||||||
|
|
||||||
|
`TIC()` in `inc/system/system.main.lua` is TIC-80's per-frame callback. It:
|
||||||
|
1. Initializes game state once on first call
|
||||||
|
2. Updates mouse/context timing
|
||||||
|
3. Delegates to the current active window handler
|
||||||
|
4. Updates meters, timers, triggers, and glitch effects
|
||||||
|
5. Draws UI overlays
|
||||||
|
|
||||||
|
### Window Manager (`inc/window/window.manager.lua`)
|
||||||
|
|
||||||
|
Central UI state machine. Windows register with `id`, `update()`, and `draw()` handlers. Only one window is active at a time. All windows are declared in `window.register.lua`.
|
||||||
|
|
||||||
|
| Window | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `intro_title` | Title screen |
|
||||||
|
| `intro_ttg` | "Thanks To Grandma" credits |
|
||||||
|
| `intro_brief` | Game briefing |
|
||||||
|
| `menu` | Main menu |
|
||||||
|
| `game` | Main gameplay (screens + decisions) |
|
||||||
|
| `popup` | General popup overlay |
|
||||||
|
| `discussion` | NPC dialogue/conversation |
|
||||||
|
| `minigame_button_mash` | Button Mash minigame |
|
||||||
|
| `minigame_rhythm` | Rhythm minigame |
|
||||||
|
| `minigame_ddr` | DDR minigame |
|
||||||
|
| `game_over` | Game over / restart screen |
|
||||||
|
| `end` | End game choice screen |
|
||||||
|
| `continued` | Day-continued notification |
|
||||||
|
| `credits` | Credits roll |
|
||||||
|
| `controls` | Control scheme display |
|
||||||
|
| `audiotest` | Audio testing utility |
|
||||||
|
| `player_name` | 3-character name entry before new game |
|
||||||
|
| `ascend_debug` | Debug utility: start at a specific ascension level |
|
||||||
|
|
||||||
|
### Screen & Decision System (`inc/screen/`, `inc/decision/`)
|
||||||
|
|
||||||
|
- **Screens** are gameplay scenes. Registered with `Screen.register({id, name, decisions[], background, init, update, draw, exit})`. They manage background maps and NPC sprite placement.
|
||||||
|
- **Decisions** are player choices available on a screen. Registered with `Decision.register({id, label, condition, handle})`. A `condition` function gates visibility; `handle` drives transitions (to new screens, dialogue, minigames).
|
||||||
|
|
||||||
|
Screens: `home`, `office`, `work`, `toilet`, `walking_to_office`, `walking_to_home`, `mysterious_man`, `manager`
|
||||||
|
|
||||||
|
Maps (`inc/map/`): `bedroom`, `office`, `street` — rendered via `map.manager.lua`.
|
||||||
|
|
||||||
|
### Game Logic (`inc/logic/`)
|
||||||
|
|
||||||
|
| Module | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `logic.meter.lua` | Tracks ISM/WPM/BM stats (0–1000), combo multipliers, daily decay (20/day) |
|
||||||
|
| `logic.day.lua` | Day counter; ascension triggers at day 3, game over at day 100 |
|
||||||
|
| `logic.timer.lua` | Event scheduling/delayed callbacks, one-shot and repeating |
|
||||||
|
| `logic.trigger.lua` | Conditional event handlers with start/stop callbacks |
|
||||||
|
| `logic.discussion.lua` | Dialogue parsing, branching answers, NPC portrait rendering |
|
||||||
|
| `logic.minigame.lua` | Config and win-overlay for Button Mash, Rhythm, and DDR |
|
||||||
|
| `logic.focus.lua` | Circular reveal/hide overlay transitions (expanding/shrinking circle) |
|
||||||
|
| `logic.glitch.lua` | Visual glitch effect (random vertical stripes), toggled via `Glitch.show()/hide()` |
|
||||||
|
| `logic.commute_glitch.lua` | 7-level glitch progression during ascension 7: corrupts sprite lists, remaps Norman to `norman_echo`, speeds up music, blocks/redirects decisions |
|
||||||
|
| `logic.codegenerator.lua` | Encodes player's 3-char name to a 6-char base-36 completion code shown on the end screen |
|
||||||
|
|
||||||
|
### Global State (`inc/init/`)
|
||||||
|
|
||||||
|
- `init.context.lua`: All runtime game state (current screen, meter values, progress flags). Persisted in memory bank 6. Key fields: `player_name` (3-char string), `commute_glitch_level` (0–7), `talked_to_norman_echo`, `talked_to_true_sumphore`, `have_been_to_office`, `have_done_work_today`.
|
||||||
|
- `init.config.lua`: Screen dimensions (240×136), palette colors, timing constants. Persisted in memory bank 7.
|
||||||
|
- `init.ascension.lua`: 9-level meta-progression system ("ASCENSION" letters progressively lit). Level 7 activates CommunteGlitch; level 9 unlocks the final "Break the cycle" decision.
|
||||||
|
- `init.context_debug.lua`: `Context.new_game_debug(level)` — starts a new game at a specific ascension level for testing.
|
||||||
|
|
||||||
|
### Audio (`inc/audio/`)
|
||||||
|
|
||||||
|
- `audio.manager.lua`: Music playback (no-restart if already playing). Named tracks: `room_work` (0), `activity_work` (1), `mystery` (2).
|
||||||
|
- `audio.generator.lua` / `audio.songs.lua`: Sound generation and song definitions.
|
||||||
|
|
||||||
|
### Sprites (`inc/sprite/`)
|
||||||
|
|
||||||
|
`sprite.manager.lua` handles registration. Supports single and composite sprites with offset layers.
|
||||||
|
|
||||||
|
NPCs: `norman`, `norman_echo` (palette-remapped glitch variant of Norman, shown at commute glitch level 7), `sumphore`, `pizza_vendor`, and 10 developer archetypes (`dev_boy`, `dev_buddy`, `dev_extrovert`, `dev_girl`, `dev_guard`, `dev_guru`, `dev_hr_girl`, `dev_introvert`, `dev_operator`, `dev_project_manager`). Matrix characters: `matrix_architect`, `matrix_neo`, `matrix_oraculum`, `matrix_trinity`.
|
||||||
|
|
||||||
|
### Discussions (`inc/discussion/`)
|
||||||
|
|
||||||
|
Branching dialogue files loaded by `logic.discussion.lua`. Each file defines one or more named dialogue trees (keyed strings with answer arrays that apply meter deltas).
|
||||||
|
|
||||||
|
| File | Dialogues |
|
||||||
|
|------|-----------|
|
||||||
|
| `discussion.sumphore.lua` | Sumphore conversations (glitch-aware variants at commute glitch level 7) |
|
||||||
|
| `discussion.coworker.lua` | Coworker coffee-chat variants per ascension level (`disc_0`, `disc_1`, `disc_asc_1`, `disc_2`, `disc_asc_2`, …) |
|
||||||
|
| `discussion.commute_glitch.lua` | 8 commute glitch encounter variants (`cg_0`–`cg_7`) + truth/Sumphore variant |
|
||||||
|
| `discussion.truth.lua` | Dialogue with the "truth" mysterious man |
|
||||||
|
| `discussion.pizza_vendor.lua` | Pizza vendor interaction |
|
||||||
|
|
||||||
|
### Input Utilities (`inc/system/`)
|
||||||
|
|
||||||
|
- `system.textinput.lua`: 3-character uppercase letter selector. Supports next/prev letter cycling (A↔Z wrapping) and cursor navigation. Used by `PlayerNameWindow`.
|
||||||
|
|
||||||
|
### Key Directories
|
||||||
|
|
||||||
|
```
|
||||||
|
inc/ Source modules (concatenated at build)
|
||||||
|
assets/ Game assets (sprites, tiles, SFX, music)
|
||||||
|
assets_src/ Source art (Aseprite files, PNGs for import)
|
||||||
|
docs/ Design documentation (mostly Hungarian)
|
||||||
|
tools/ Build utilities (musicator: MIDI→TIC-80 converter)
|
||||||
|
prompts/ Feature templates
|
||||||
|
```
|
||||||
139
GEMINI.md
139
GEMINI.md
@@ -1,6 +1,38 @@
|
|||||||
|
# Build System & Include Architecture
|
||||||
|
|
||||||
|
## impostor.inc Structure
|
||||||
|
|
||||||
|
The `impostor.inc` file is a Lua include manifest that assembles the final `impostor.lua` executable. The build process uses the `make build` target in the Makefile to concatenate all included files in order.
|
||||||
|
|
||||||
|
**Critical Rule:** Files must be ordered by symbol definition. All symbols (functions, tables, classes) defined in earlier files must be available for use in later files. This dependency chain ensures that:
|
||||||
|
|
||||||
|
- Core utilities and base systems are defined first
|
||||||
|
- Systems that depend on utilities come next
|
||||||
|
- Game logic that uses multiple systems comes last
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
|
||||||
|
The `make build` target processes `impostor.inc` and concatenates all referenced files in the specified order to create the final `impostor.lua` file. This means:
|
||||||
|
|
||||||
|
1. Each include path in `impostor.inc` must reference files relative to the project root
|
||||||
|
2. The order of includes is critical - dependencies must be resolved top-to-bottom
|
||||||
|
3. No forward references are possible - a file cannot use symbols from files included after it
|
||||||
|
|
||||||
|
### File Organization Example
|
||||||
|
|
||||||
|
```
|
||||||
|
impostor.inc:
|
||||||
|
1. Core utilities & helpers (no dependencies)
|
||||||
|
2. Base classes/tables (depend on core utilities)
|
||||||
|
3. Game systems (depend on base classes)
|
||||||
|
4. Game logic (depends on all systems)
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures proper symbol resolution during the build and concatenation process.
|
||||||
|
|
||||||
# TIC-80 Lua Code Regularities
|
# TIC-80 Lua Code Regularities
|
||||||
|
|
||||||
Based on the analysis of `mranderson.lua`, the following regularities and conventions should be followed for future modifications and development within this project:
|
Based on the analysis of `impostor.lua`, the following regularities and conventions should be followed for future modifications and development within this project:
|
||||||
|
|
||||||
## General Structure & Lifecycle
|
## General Structure & Lifecycle
|
||||||
|
|
||||||
@@ -39,7 +71,7 @@ Based on the analysis of `mranderson.lua`, the following regularities and conven
|
|||||||
|
|
||||||
11. **`spr()` for Sprites:** Individual sprites should be rendered using the `spr()` function.
|
11. **`spr()` for Sprites:** Individual sprites should be rendered using the `spr()` function.
|
||||||
12. **`map()` for Maps:** Tilemaps should be drawn using the `map()` function.
|
12. **`map()` for Maps:** Tilemaps should be drawn using the `map()` function.
|
||||||
13. **`print()` for Text:** Text display should utilize the `print()` function.
|
13. **`Print.text()` for Text:** Text display should utilize the `Print.text()` function.
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
@@ -52,3 +84,106 @@ Based on the analysis of `mranderson.lua`, the following regularities and conven
|
|||||||
## Agent Directives
|
## Agent Directives
|
||||||
|
|
||||||
- **Git Operations:** In the future, do not perform `git add` or `git commit` operations. This responsibility will be handled by the user.
|
- **Git Operations:** In the future, do not perform `git add` or `git commit` operations. This responsibility will be handled by the user.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Impostor Minigame Documentation
|
||||||
|
|
||||||
|
This document provides comprehensive documentation for all three minigames implemented in the Impostor game: Button Mash, Rhythm, and DDR (Dance Dance Revolution).
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Overview](#overview)
|
||||||
|
- [Button Mash Minigame](#button-mash-minigame)
|
||||||
|
- [Rhythm Minigame](#rhythm-minigame)
|
||||||
|
- [DDR Minigame](#ddr-minigame)
|
||||||
|
- [Integration Guide](#integration-guide)
|
||||||
|
- [Common Features](#common-features)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Impostor game includes three interactive minigames that can be triggered during gameplay. Each minigame presents a unique challenge that the player must complete to progress. All minigames feature:
|
||||||
|
|
||||||
|
- **Overlay rendering** - Minigame render over the current game window
|
||||||
|
- **Progress tracking** - Visual indicators show completion status
|
||||||
|
- **Return mechanism** - Automatic return to the calling window upon completion
|
||||||
|
- **Visual feedback** - Button presses and hits are visually indicated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Button Mash Minigame
|
||||||
|
|
||||||
|
**File**: `inc/window/window.minigame.mash.lua`
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
A fast-paced minigame where the player must repeatedly press the Z button to fill a progress bar. The bar automatically degrades over time, and the degradation rate increases as the bar fills up, creating increasing difficulty.
|
||||||
|
|
||||||
|
### Gameplay Mechanics
|
||||||
|
|
||||||
|
- **Objective**: Fill the progress bar to 100% by pressing Z repeatedly
|
||||||
|
- **Challenge**: The bar degrades automatically, with degradation increasing as it fills
|
||||||
|
- **Win Condition**: Bar reaches 100%
|
||||||
|
- **No Fail State**: Player can keep trying until successful
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
|
||||||
|
#### Simple sketch
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
Progress Bar (200x12px at 20,10):
|
||||||
|
┌────────────────────────────────────┐
|
||||||
|
│████████████████░░░░░░░░░░░░░░ 45%│
|
||||||
|
└────────────────────────────────────┘
|
||||||
|
|
||||||
|
Button Indicator (Bottom):
|
||||||
|
╔═══╗
|
||||||
|
║ Z ║ ← Press repeatedly!
|
||||||
|
╚═══╝
|
||||||
|
|
||||||
|
Text: "MASH Z!"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configuration Parameters
|
||||||
|
|
||||||
|
```lua
|
||||||
|
bar_fill = 0 -- Current fill level (0-100)
|
||||||
|
max_fill = 100 -- Target fill level
|
||||||
|
fill_per_press = 8 -- Points gained per button press
|
||||||
|
base_degradation = 0.15 -- Base degradation per frame
|
||||||
|
degradation_multiplier = 0.006 -- Increases degradation with bar fill
|
||||||
|
button_press_duration = 8 -- Visual feedback duration (frames)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Example
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Start button mash minigame
|
||||||
|
MinigameButtonMashWindow.start(WINDOW_GAME)
|
||||||
|
|
||||||
|
-- Or from any window
|
||||||
|
MinigameButtonMashWindow.start(WINDOW_POPUP)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Color Coding
|
||||||
|
|
||||||
|
- **Green**: Low fill (0-33%)
|
||||||
|
- **Medium (NPC color)**: Medium fill (34-66%)
|
||||||
|
- **Yellow (Item color)**: High fill (67-100%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rhythm Minigame
|
||||||
|
|
||||||
|
**File**: `inc/window/window.minigame.rhythm.lua`
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
A timing-based minigame where the player must press Z when a moving line crosses a green target zone. The target zone shrinks with each successful hit, making the game progressively more challenging.
|
||||||
|
|
||||||
|
### Gameplay Mechanics
|
||||||
|
|
||||||
|
- **Objective**: Score 10 points by timing button presses correctly
|
||||||
|
|||||||
266
Makefile
266
Makefile
@@ -1,46 +1,260 @@
|
|||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
# Makefile – TIC-80 project builder
|
# Makefile – TIC-80 project builder
|
||||||
# Usage:
|
|
||||||
# make PROJECT=mranderson
|
|
||||||
# make build PROJECT=mranderson
|
|
||||||
# make watch PROJECT=mranderson
|
|
||||||
# make export PROJECT=mranderson
|
|
||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
|
|
||||||
ifndef PROJECT
|
PROJECT = impostor
|
||||||
$(error Specify the project name: make PROJECT=name)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ORDER = $(PROJECT).inc
|
ORDER = $(PROJECT).inc
|
||||||
OUTPUT = $(PROJECT).lua
|
OUTPUT = $(PROJECT).lua
|
||||||
|
OUTPUT_ORIGINAL = $(PROJECT).original.lua
|
||||||
OUTPUT_ZIP = $(PROJECT).html.zip
|
OUTPUT_ZIP = $(PROJECT).html.zip
|
||||||
OUTPUT_TIC = $(PROJECT).tic
|
OUTPUT_TIC = $(PROJECT).tic
|
||||||
|
|
||||||
|
MINIFY = minify.lua
|
||||||
|
MINIFY_URL = https://raw.githubusercontent.com/ztimar31/lua-minify-tic80/refs/heads/master/minify.lua
|
||||||
|
|
||||||
SRC_DIR = inc
|
SRC_DIR = inc
|
||||||
SRC = $(shell sed 's|^|$(SRC_DIR)/|' $(ORDER))
|
SRC = $(shell sed 's|^|$(SRC_DIR)/|' $(ORDER))
|
||||||
|
|
||||||
|
ASSETS_LUA = inc/meta/meta.assets.lua
|
||||||
|
ASSETS_DIR = assets
|
||||||
|
ASSET_TYPES = tiles sprites sfx music
|
||||||
|
|
||||||
|
LINT_TMP_LUA := /tmp/_lint_combined.lua
|
||||||
|
LINT_TMP_MAP := /tmp/_lint_map.txt
|
||||||
|
|
||||||
|
# CI/CD variables
|
||||||
|
VERSION_FILE = .version
|
||||||
|
GAME_LANG ?= lua
|
||||||
|
DROPAREA_HOST ?= vps.teletype.hu
|
||||||
|
DROPAREA_PORT ?= 2223
|
||||||
|
DROPAREA_TARGET_PATH ?= /home/drop
|
||||||
|
DROPAREA_USER ?= drop
|
||||||
|
UPDATE_SERVER ?= https://games.teletype.hu
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build: $(OUTPUT)
|
build:
|
||||||
@echo "==> Build complete: $(OUTPUT)"
|
|
||||||
|
|
||||||
$(OUTPUT): $(SRC) $(ORDER)
|
|
||||||
@echo "==> Building $(OUTPUT)..."
|
|
||||||
@rm -f $(OUTPUT)
|
@rm -f $(OUTPUT)
|
||||||
@while read f; do \
|
@sed 's/\r$$//' $(ORDER) | while read f; do \
|
||||||
cat "$(SRC_DIR)/$$f" >> $(OUTPUT); \
|
cat "$(SRC_DIR)/$$f" >> $(OUTPUT); \
|
||||||
echo "\n" >> $(OUTPUT); \
|
echo "" >> $(OUTPUT); \
|
||||||
done < $(ORDER)
|
done
|
||||||
@echo "==> Done."
|
|
||||||
|
|
||||||
export: $(OUTPUT)
|
download-minify:
|
||||||
@echo "==> TIC-80 export..."
|
@test -f $(MINIFY) || { echo "==> Downloading $(MINIFY)"; curl -fsSL $(MINIFY_URL) -o $(MINIFY); }
|
||||||
tic80 --cli --skip --fs=. \
|
|
||||||
--cmd="load $(OUTPUT) & save $(PROJECT) & export html $(PROJECT).html & exit"
|
minify: build download-minify
|
||||||
@echo "==> HTML ZIP: $(OUTPUT_ZIP)"
|
@echo "==> Minifying $(OUTPUT)"
|
||||||
@echo "==> TIC: $(OUTPUT_TIC)"
|
@cp $(OUTPUT) $(OUTPUT_ORIGINAL)
|
||||||
|
@lua $(MINIFY) minify $(OUTPUT_ORIGINAL) > $(OUTPUT)
|
||||||
|
|
||||||
|
export: minify
|
||||||
|
@if [ -z "$(VERSION)" ]; then \
|
||||||
|
echo "ERROR: VERSION not set!"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "==> Exporting HTML for version $(VERSION)"
|
||||||
|
@tic80 --cli --skip --fs=. \
|
||||||
|
--cmd="load $(OUTPUT) & save $(PROJECT)-$(VERSION) & export html $(PROJECT)-$(VERSION).html & exit"
|
||||||
|
@echo "==> Creating versioned files"
|
||||||
|
@if [ -f "$(PROJECT)-$(VERSION).tic" ]; then \
|
||||||
|
cp $(PROJECT)-$(VERSION).tic $(PROJECT).tic; \
|
||||||
|
fi
|
||||||
|
@if [ -f "$(PROJECT)-$(VERSION).html.zip" ]; then \
|
||||||
|
cp $(PROJECT)-$(VERSION).html.zip $(PROJECT).html.zip; \
|
||||||
|
fi
|
||||||
|
@echo "==> Generated files:"
|
||||||
|
@ls -lh $(PROJECT)-$(VERSION).* $(PROJECT).tic $(PROJECT).html.zip 2>/dev/null || true
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
@echo "==> Watching project: $(PROJECT)"
|
$(MAKE) build
|
||||||
make build PROJECT=$(PROJECT)
|
fswatch -o $(SRC_DIR) $(ORDER) assets | while read; do $(MAKE) build; done
|
||||||
fswatch -o $(SRC_DIR) $(ORDER) | while read; do make build PROJECT=$(PROJECT); done
|
|
||||||
|
import_assets: build
|
||||||
|
@TIC_CMD="load $(OUTPUT) &"; \
|
||||||
|
for t in $(ASSET_TYPES); do \
|
||||||
|
for f in $(ASSETS_DIR)/$$t/*.png; do \
|
||||||
|
[ -e "$$f" ] || continue; \
|
||||||
|
echo "==> Importing $$f as $$t..."; \
|
||||||
|
TIC_CMD="$${TIC_CMD} & import $$t $$f"; \
|
||||||
|
done; \
|
||||||
|
done; \
|
||||||
|
TIC_CMD="$$TIC_CMD save & exit"; \
|
||||||
|
echo $$TIC_CMD; \
|
||||||
|
tic80 --cli --skip --fs=. --cmd="$$TIC_CMD"
|
||||||
|
|
||||||
|
# export helper function
|
||||||
|
define f_export_asset_awk
|
||||||
|
cat $(2) | awk '/-- <$(1)>/,/<\/$(1)>/' >> $(3)
|
||||||
|
endef
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@echo "==> Merging..."
|
||||||
|
@rm -f $(LINT_TMP_LUA) $(LINT_TMP_MAP)
|
||||||
|
@touch $(LINT_TMP_LUA)
|
||||||
|
@line=1; \
|
||||||
|
while IFS= read -r f || [ -n "$$f" ]; do \
|
||||||
|
f=$$(printf '%s' "$$f" | tr -d '\r'); \
|
||||||
|
[ -z "$$f" ] && continue; \
|
||||||
|
before=$$(wc -l < $(LINT_TMP_LUA)); \
|
||||||
|
cat "$(SRC_DIR)/$$f" >> $(LINT_TMP_LUA); \
|
||||||
|
printf '\n' >> $(LINT_TMP_LUA); \
|
||||||
|
after=$$(wc -l < $(LINT_TMP_LUA)); \
|
||||||
|
linecount=$$((after - before)); \
|
||||||
|
echo "$$line $$linecount $(SRC_DIR)/$$f" >> $(LINT_TMP_MAP); \
|
||||||
|
line=$$((line + linecount)); \
|
||||||
|
done < $(ORDER)
|
||||||
|
@echo "==> luacheck..."
|
||||||
|
@LINT_OUTPUT=$$(luacheck --no-max-line-length $(LINT_TMP_LUA) 2>&1 | awk -v map=$(LINT_TMP_MAP) ' \
|
||||||
|
BEGIN { \
|
||||||
|
NR_map = 0; \
|
||||||
|
while ((getline line < map) > 0) { \
|
||||||
|
n = split(line, a, " "); \
|
||||||
|
start[NR_map] = a[1]+0; \
|
||||||
|
count[NR_map] = a[2]+0; \
|
||||||
|
fname[NR_map] = a[3]; \
|
||||||
|
NR_map++; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
/^[^:]+:[0-9]+:[0-9]+:/ { \
|
||||||
|
colon1 = index($$0, ":"); \
|
||||||
|
rest1 = substr($$0, colon1+1); \
|
||||||
|
colon2 = index(rest1, ":"); \
|
||||||
|
absline = substr(rest1, 1, colon2-1) + 0; \
|
||||||
|
rest2 = substr(rest1, colon2+1); \
|
||||||
|
colon3 = index(rest2, ":"); \
|
||||||
|
col = substr(rest2, 1, colon3-1); \
|
||||||
|
rest = substr(rest2, colon3); \
|
||||||
|
found = 0; \
|
||||||
|
for (i = 0; i < NR_map; i++) { \
|
||||||
|
end_line = start[i] + count[i] -1; \
|
||||||
|
if (absline >= start[i] && absline <= end_line) { \
|
||||||
|
relline = absline - start[i] + 1; \
|
||||||
|
print fname[i] ":" relline ":" col ":" rest; \
|
||||||
|
found = 1; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (!found) print $$0; \
|
||||||
|
next; \
|
||||||
|
} \
|
||||||
|
{ print } \
|
||||||
|
'); \
|
||||||
|
echo "$$LINT_OUTPUT"; \
|
||||||
|
NUM_ISSUES=$$(echo "$$LINT_OUTPUT" | grep -cE "^[^:]+:[0-9]+:[0-9]+:"); \
|
||||||
|
if [ "$$NUM_ISSUES" -gt 0 ]; then \
|
||||||
|
echo "Total: $$NUM_ISSUES issue(s) found, commit aborted."; \
|
||||||
|
exit 1; \
|
||||||
|
else \
|
||||||
|
echo "Checking /tmp/_lint_combined.lua OK"; \
|
||||||
|
echo "Total: 0 warnings / 0 errors in 1 file"; \
|
||||||
|
fi
|
||||||
|
@rm -f $(LINT_TMP_LUA) $(LINT_TMP_MAP)
|
||||||
|
|
||||||
|
export_assets:
|
||||||
|
# $(OUTPUT) would be a circular dependency
|
||||||
|
@test -e $(OUTPUT)
|
||||||
|
@echo "==> Exporting TIC-80 asset sections"
|
||||||
|
@mkdir -p inc/meta
|
||||||
|
@echo -n '' > $(ASSETS_LUA)
|
||||||
|
@$(call f_export_asset_awk,PALETTE,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,TILES,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,SPRITES,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,MAP,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,SFX,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,WAVES,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,PATTERNS,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
@$(call f_export_asset_awk,TRACKS,$(OUTPUT),$(ASSETS_LUA))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f $(PROJECT)-*.tic $(PROJECT)-*.html.zip $(PROJECT)-*-docs.zip $(PROJECT)-docs.zip $(OUTPUT) $(OUTPUT_ORIGINAL)
|
||||||
|
@echo "==> Cleaned build artifacts"
|
||||||
|
|
||||||
|
install_precommit_hook:
|
||||||
|
@echo "Installing Git pre-commit hook (lint check)..."
|
||||||
|
@mkdir -p .git/hooks
|
||||||
|
@printf '#!/bin/bash\n' > .git/hooks/pre-commit
|
||||||
|
@printf 'echo "Running lint before commit..."\n' >> .git/hooks/pre-commit
|
||||||
|
@printf 'make lint\n' >> .git/hooks/pre-commit
|
||||||
|
@printf 'if [ $$? -ne 0 ]; then\n' >> .git/hooks/pre-commit
|
||||||
|
@printf ' echo "Lint failed! Commit aborted."\n' >> .git/hooks/pre-commit
|
||||||
|
@printf ' exit 1\n' >> .git/hooks/pre-commit
|
||||||
|
@printf 'fi\n' >> .git/hooks/pre-commit
|
||||||
|
@chmod +x .git/hooks/pre-commit
|
||||||
|
@echo "Pre-commit hook installed successfully."
|
||||||
|
|
||||||
|
docs: build
|
||||||
|
@echo "==> Checking for ldoc..."
|
||||||
|
@if ! command -v ldoc &> /dev/null; then \
|
||||||
|
echo "ldoc not found, attempting to install with luarocks..."; \
|
||||||
|
if command -v luarocks &> /dev/null; then \
|
||||||
|
luarocks install ldoc; \
|
||||||
|
else \
|
||||||
|
echo "Error: luarocks not found. Please install luarocks and then ldoc manually."; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
fi
|
||||||
|
@echo "==> Running ldoc..."
|
||||||
|
@ldoc ${OUTPUT} -d docs
|
||||||
|
@echo "==> Documentation generated."
|
||||||
|
|
||||||
|
# -----------------------------------------
|
||||||
|
# CI/CD Pipeline targets
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
ci-version:
|
||||||
|
@VERSION=$$(sed -n "s/^-- version: //p" inc/meta/meta.header.lua | head -n 1 | tr -d "[:space:]"); \
|
||||||
|
BRANCH=$${CI_COMMIT_BRANCH:-$${WOODPECKER_BRANCH}}; \
|
||||||
|
BRANCH=$$(echo "$$BRANCH" | tr '/' '-'); \
|
||||||
|
if [ "$$BRANCH" != "main" ] && [ "$$BRANCH" != "master" ] && [ -n "$$BRANCH" ]; then \
|
||||||
|
VERSION=dev-$$VERSION-$$BRANCH; \
|
||||||
|
fi; \
|
||||||
|
echo "VERSION is: $$VERSION"; \
|
||||||
|
echo $$VERSION > $(VERSION_FILE)
|
||||||
|
|
||||||
|
ci-lint: lint
|
||||||
|
|
||||||
|
ci-minify: minify
|
||||||
|
|
||||||
|
ci-docs:
|
||||||
|
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||||
|
echo "==> Generating docs from $(OUTPUT_ORIGINAL)"; \
|
||||||
|
ldoc $(OUTPUT_ORIGINAL) -d docs; \
|
||||||
|
echo "==> Zipping docs for version $$VERSION"; \
|
||||||
|
(cd docs && zip -r ../$(PROJECT)-$$VERSION-docs.zip .); \
|
||||||
|
cp $(PROJECT)-$$VERSION-docs.zip $(PROJECT)-docs.zip; \
|
||||||
|
echo "==> Docs zip created"
|
||||||
|
|
||||||
|
ci-export:
|
||||||
|
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||||
|
echo "==> Exporting HTML for version $$VERSION"; \
|
||||||
|
tic80 --cli --skip --fs=. \
|
||||||
|
--cmd="load $(OUTPUT) & save $(PROJECT)-$$VERSION & export html $(PROJECT)-$$VERSION.html & exit"; \
|
||||||
|
if [ -f "$(PROJECT)-$$VERSION.tic" ]; then \
|
||||||
|
cp $(PROJECT)-$$VERSION.tic $(PROJECT).tic; \
|
||||||
|
fi; \
|
||||||
|
if [ -f "$(PROJECT)-$$VERSION.html.zip" ]; then \
|
||||||
|
cp $(PROJECT)-$$VERSION.html.zip $(PROJECT).html.zip; \
|
||||||
|
fi; \
|
||||||
|
echo "==> Generated files:"; \
|
||||||
|
ls -lh $(PROJECT)-$$VERSION.* $(PROJECT).tic $(PROJECT).html.zip 2>/dev/null || true
|
||||||
|
|
||||||
|
ci-artifact:
|
||||||
|
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||||
|
echo "==> Uploading artifacts for version $$VERSION"; \
|
||||||
|
cp $(PROJECT).lua $(PROJECT)-$$VERSION.lua; \
|
||||||
|
SCP_TARGET="$(DROPAREA_USER)@$(DROPAREA_HOST):$(DROPAREA_TARGET_PATH)/"; \
|
||||||
|
sshpass -p "$(DROPAREA_SSH_PASSWORD)" scp -o StrictHostKeyChecking=no -P $(DROPAREA_PORT) \
|
||||||
|
$(PROJECT)-$$VERSION.lua \
|
||||||
|
$(PROJECT)-$$VERSION.tic \
|
||||||
|
$(PROJECT)-$$VERSION.html.zip \
|
||||||
|
$(PROJECT)-$$VERSION-docs.zip \
|
||||||
|
$$SCP_TARGET
|
||||||
|
|
||||||
|
ci-update:
|
||||||
|
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||||
|
echo "==> Triggering update for version $$VERSION"; \
|
||||||
|
curl "$(UPDATE_SERVER)/update?secret=$(UPDATE_SECRET)&name=$(PROJECT)&platform=tic80&version=$$VERSION"
|
||||||
|
|
||||||
|
.PHONY: all build download-minify minify export watch import_assets export_assets clean lint install_precommit_hook docs ci-version ci-lint ci-minify ci-docs ci-export ci-artifact ci-update
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,4 +1,4 @@
|
|||||||
# Mr. Anderson's Matrix Escape
|
# Definitely not an Impostor
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -8,14 +8,6 @@ This game is designed for the TIC-80 fantasy computer. To play, follow these ste
|
|||||||
2. **Clone this repository** Clone this repository to tic80's cartridge folder (MacOS: ~Library/Application Support/com.nesbox.tic/TIC-80)
|
2. **Clone this repository** Clone this repository to tic80's cartridge folder (MacOS: ~Library/Application Support/com.nesbox.tic/TIC-80)
|
||||||
2. **Launch TIC-80:** Start the TIC-80 application.
|
2. **Launch TIC-80:** Start the TIC-80 application.
|
||||||
3. **Load the Game:**
|
3. **Load the Game:**
|
||||||
* Navigate to the directory where `game.lua` is located using the TIC-80 command line (`cd mranderson`).
|
* Navigate to the directory where `game.lua` is located using the TIC-80 command line (`cd impostor`).
|
||||||
* Type `load game.lua` and press Enter.
|
* Type `load game.lua` and press Enter.
|
||||||
* Once loaded, type `run` and press Enter to start the game.
|
* Once loaded, type `run` and press Enter to start the game.
|
||||||
|
|
||||||
## Story: The Coder's Lament
|
|
||||||
|
|
||||||
Before he was "The One," before he dodged bullets and shattered the illusion, Thomas Anderson was just a software developer named Neo. Trapped in a cubicle farm of endless bugs and looming deadlines, Neo's days were a monotonous cycle of debugging legacy code, attending pointless meetings, and battling unresponsive APIs. Each line of code felt like a chain, each project a heavier burden, pulling him deeper into a digital malaise.
|
|
||||||
|
|
||||||
He yearned for something more, a glitch in the system, a whisper of a different reality. His fingers, calloused from countless hours on the keyboard, danced across cryptic forums late at night, searching for answers, for meaning beyond the mundane syntax of his corporate prison. The coffee flowed freely, the pizza boxes piled high, and the lines of code blurred into an indistinguishable stream of ones and zeros.
|
|
||||||
|
|
||||||
This game chronicles Mr. Anderson's final, desperate struggles within the software development matrix. Navigate the labyrinthine codebase, escape the relentless pursuit of project managers (Agents), and uncover the hidden truths that will lead him to question everything he knows. Will he find the "red pill" in a sea of green code, or will he forever be just another drone in the system?
|
|
||||||
|
|||||||
0
assets/music/.keep
Normal file
0
assets/music/.keep
Normal file
0
assets/sfx/.keep
Normal file
0
assets/sfx/.keep
Normal file
0
assets/sprites/.keep
Normal file
0
assets/sprites/.keep
Normal file
0
assets/tiles/.keep
Normal file
0
assets/tiles/.keep
Normal file
BIN
assets_src/background_bedroom.ase
Normal file
BIN
assets_src/background_bedroom.ase
Normal file
Binary file not shown.
BIN
assets_src/background_office.ase
Normal file
BIN
assets_src/background_office.ase
Normal file
Binary file not shown.
BIN
assets_src/background_street.ase
Normal file
BIN
assets_src/background_street.ase
Normal file
Binary file not shown.
BIN
assets_src/map.map
Normal file
BIN
assets_src/map.map
Normal file
Binary file not shown.
BIN
assets_src/mapimg.png
Normal file
BIN
assets_src/mapimg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
assets_src/palette-pico8.png
Normal file
BIN
assets_src/palette-pico8.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 480 B |
1
assets_src/palette-pico8.txt
Normal file
1
assets_src/palette-pico8.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0404005f574fc2c3c71d2b53fff1e8ab52367e2553ffa30000875129adff83769c00e436fa77a8ff004dc3c3c7ffccaa
|
||||||
BIN
assets_src/sprites.ase
Normal file
BIN
assets_src/sprites.ase
Normal file
Binary file not shown.
BIN
assets_src/sprites.png
Normal file
BIN
assets_src/sprites.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets_src/tiles.png
Normal file
BIN
assets_src/tiles.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
97
impostor.inc
Normal file
97
impostor.inc
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
meta/meta.header.lua
|
||||||
|
init/init.module.lua
|
||||||
|
init/init.config.lua
|
||||||
|
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
|
||||||
|
logic/logic.ascension.lua
|
||||||
|
logic/logic.meter.lua
|
||||||
|
logic/logic.focus.lua
|
||||||
|
logic/logic.day.lua
|
||||||
|
logic/logic.timer.lua
|
||||||
|
logic/logic.trigger.lua
|
||||||
|
logic/logic.minigame.lua
|
||||||
|
logic/logic.glitch.lua
|
||||||
|
logic/logic.commute_glitch.lua
|
||||||
|
logic/logic.codegenerator.lua
|
||||||
|
logic/logic.discussion.lua
|
||||||
|
system/system.debug.lua
|
||||||
|
system/system.ui.lua
|
||||||
|
audio/audio.manager.lua
|
||||||
|
audio/audio.generator.lua
|
||||||
|
audio/audio.songs.lua
|
||||||
|
sprite/sprite.manager.lua
|
||||||
|
sprite/sprite.norman.lua
|
||||||
|
sprite/sprite.norman_echo.lua
|
||||||
|
sprite/sprite.sumphore.lua
|
||||||
|
sprite/sprite.pizza_vendor.lua
|
||||||
|
sprite/sprite.dev_boy.lua
|
||||||
|
sprite/sprite.dev_buddy.lua
|
||||||
|
sprite/sprite.dev_extrovert.lua
|
||||||
|
sprite/sprite.dev_girl.lua
|
||||||
|
sprite/sprite.dev_guard.lua
|
||||||
|
sprite/sprite.dev_guru.lua
|
||||||
|
sprite/sprite.dev_hr_girl.lua
|
||||||
|
sprite/sprite.dev_introvert.lua
|
||||||
|
sprite/sprite.dev_operator.lua
|
||||||
|
sprite/sprite.dev_project_manager.lua
|
||||||
|
sprite/sprite.matrix_architect.lua
|
||||||
|
sprite/sprite.matrix_neo.lua
|
||||||
|
sprite/sprite.matrix_oraculum.lua
|
||||||
|
sprite/sprite.matrix_trinity.lua
|
||||||
|
decision/decision.manager.lua
|
||||||
|
decision/decision.go_to_home.lua
|
||||||
|
decision/decision.go_to_toilet.lua
|
||||||
|
decision/decision.go_to_walking_to_office.lua
|
||||||
|
decision/decision.go_to_office.lua
|
||||||
|
decision/decision.go_to_truth.lua
|
||||||
|
decision/decision.go_to_walking_to_home.lua
|
||||||
|
decision/decision.go_to_sleep.lua
|
||||||
|
decision/decision.do_work.lua
|
||||||
|
decision/decision.have_a_coffee.lua
|
||||||
|
decision/decision.sumphore_discussion.lua
|
||||||
|
decision/decision.talk_to_truth.lua
|
||||||
|
discussion/discussion.sumphore.lua
|
||||||
|
discussion/discussion.coworker.lua
|
||||||
|
discussion/discussion.commute_glitch.lua
|
||||||
|
decision/decision.eating_fast_food.lua
|
||||||
|
discussion/discussion.pizza_vendor.lua
|
||||||
|
map/map.manager.lua
|
||||||
|
map/map.bedroom.lua
|
||||||
|
map/map.street.lua
|
||||||
|
map/map.office.lua
|
||||||
|
screen/screen.manager.lua
|
||||||
|
screen/screen.home.lua
|
||||||
|
screen/screen.toilet.lua
|
||||||
|
screen/screen.walking_to_office.lua
|
||||||
|
screen/screen.office.lua
|
||||||
|
screen/screen.walking_to_home.lua
|
||||||
|
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
|
||||||
|
window/window.intro.brief.lua
|
||||||
|
window/window.menu.lua
|
||||||
|
window/window.controls.lua
|
||||||
|
window/window.audiotest.lua
|
||||||
|
window/window.ascend_debug.lua
|
||||||
|
window/window.popup.lua
|
||||||
|
window/window.minigame.mash.lua
|
||||||
|
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
|
||||||
744
inc/audio/audio.generator.lua
Normal file
744
inc/audio/audio.generator.lua
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
local musicator_markov_model = {
|
||||||
|
model = {
|
||||||
|
["...|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.71111111111111,
|
||||||
|
["A-4"] = 0.0074074074074074,
|
||||||
|
["B-4"] = 0.0037037037037037,
|
||||||
|
["C-3"] = 0.011111111111111,
|
||||||
|
["C-4"] = 0.037037037037037,
|
||||||
|
["C-5"] = 0.11111111111111,
|
||||||
|
["C-6"] = 0.0037037037037037,
|
||||||
|
["D-4"] = 0.011111111111111,
|
||||||
|
["D-5"] = 0.018518518518519,
|
||||||
|
["E-4"] = 0.0074074074074074,
|
||||||
|
["E-5"] = 0.025925925925926,
|
||||||
|
["F-5"] = 0.0074074074074074,
|
||||||
|
["G-3"] = 0.0037037037037037,
|
||||||
|
["G-4"] = 0.022222222222222,
|
||||||
|
["G-5"] = 0.018518518518519
|
||||||
|
},
|
||||||
|
total = 270
|
||||||
|
},
|
||||||
|
["...|A-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.9,
|
||||||
|
["C-5"] = 0.1
|
||||||
|
},
|
||||||
|
total = 10
|
||||||
|
},
|
||||||
|
["...|A-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 8
|
||||||
|
},
|
||||||
|
["...|B-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["...|B-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 5
|
||||||
|
},
|
||||||
|
["...|C-3"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.66666666666667,
|
||||||
|
["C-5"] = 0.33333333333333
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["...|C-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.875,
|
||||||
|
["D-4"] = 0.083333333333333,
|
||||||
|
["E-4"] = 0.041666666666667
|
||||||
|
},
|
||||||
|
total = 24
|
||||||
|
},
|
||||||
|
["...|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.73333333333333,
|
||||||
|
["B-4"] = 0.033333333333333,
|
||||||
|
["C-5"] = 0.066666666666667,
|
||||||
|
["D-5"] = 0.13333333333333,
|
||||||
|
["E-5"] = 0.033333333333333
|
||||||
|
},
|
||||||
|
total = 60
|
||||||
|
},
|
||||||
|
["...|C-6"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["...|D-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.92857142857143,
|
||||||
|
["D-4"] = 0.071428571428571
|
||||||
|
},
|
||||||
|
total = 14
|
||||||
|
},
|
||||||
|
["...|D-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.80645161290323,
|
||||||
|
["C-5"] = 0.032258064516129,
|
||||||
|
["D-5"] = 0.032258064516129,
|
||||||
|
["E-5"] = 0.12903225806452
|
||||||
|
},
|
||||||
|
total = 31
|
||||||
|
},
|
||||||
|
["...|D-6"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["...|E-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 19
|
||||||
|
},
|
||||||
|
["...|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.77777777777778,
|
||||||
|
["C-5"] = 0.022222222222222,
|
||||||
|
["D-5"] = 0.13333333333333,
|
||||||
|
["F-5"] = 0.066666666666667
|
||||||
|
},
|
||||||
|
total = 45
|
||||||
|
},
|
||||||
|
["...|F-3"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["...|F-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.8,
|
||||||
|
["D-4"] = 0.1,
|
||||||
|
["F-4"] = 0.1
|
||||||
|
},
|
||||||
|
total = 10
|
||||||
|
},
|
||||||
|
["...|F-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.66666666666667,
|
||||||
|
["D-5"] = 0.066666666666667,
|
||||||
|
["E-5"] = 0.066666666666667,
|
||||||
|
["G-5"] = 0.2
|
||||||
|
},
|
||||||
|
total = 15
|
||||||
|
},
|
||||||
|
["...|G-3"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.8,
|
||||||
|
["G-5"] = 0.2
|
||||||
|
},
|
||||||
|
total = 5
|
||||||
|
},
|
||||||
|
["...|G-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.95652173913043,
|
||||||
|
["E-4"] = 0.043478260869565
|
||||||
|
},
|
||||||
|
total = 23
|
||||||
|
},
|
||||||
|
["...|G-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.875,
|
||||||
|
["A-5"] = 0.0625,
|
||||||
|
["E-5"] = 0.0625
|
||||||
|
},
|
||||||
|
total = 16
|
||||||
|
},
|
||||||
|
["...|G-6"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["A-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.55555555555556,
|
||||||
|
["C-5"] = 0.33333333333333,
|
||||||
|
["D-5"] = 0.11111111111111
|
||||||
|
},
|
||||||
|
total = 9
|
||||||
|
},
|
||||||
|
["A-4|B-4"] = {
|
||||||
|
next = {
|
||||||
|
["C-5"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["A-4|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["A-4|G-4"] = {
|
||||||
|
next = {
|
||||||
|
["F-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["A-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.2,
|
||||||
|
["B-5"] = 0.1,
|
||||||
|
["E-4"] = 0.1,
|
||||||
|
["E-5"] = 0.4,
|
||||||
|
["F-5"] = 0.1,
|
||||||
|
["G-5"] = 0.1
|
||||||
|
},
|
||||||
|
total = 10
|
||||||
|
},
|
||||||
|
["A-5|G-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.33333333333333,
|
||||||
|
["A-5"] = 0.66666666666667
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["B-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["A-4"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["B-4|A-4"] = {
|
||||||
|
next = {
|
||||||
|
["G-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["B-4|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["B-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.2,
|
||||||
|
["A-5"] = 0.2,
|
||||||
|
["D-6"] = 0.4,
|
||||||
|
["G-5"] = 0.2
|
||||||
|
},
|
||||||
|
total = 5
|
||||||
|
},
|
||||||
|
["C-3|..."] = {
|
||||||
|
next = {
|
||||||
|
["C-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["C-3|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.33333333333333,
|
||||||
|
["C-3"] = 0.66666666666667
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["C-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["D-4"] = 0.125,
|
||||||
|
["E-4"] = 0.041666666666667,
|
||||||
|
["F-3"] = 0.041666666666667,
|
||||||
|
["G-3"] = 0.16666666666667,
|
||||||
|
["G-4"] = 0.083333333333333,
|
||||||
|
["G-5"] = 0.041666666666667
|
||||||
|
},
|
||||||
|
total = 24
|
||||||
|
},
|
||||||
|
["C-4|D-4"] = {
|
||||||
|
next = {
|
||||||
|
["E-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["C-4|E-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["C-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.40677966101695,
|
||||||
|
["A-4"] = 0.067796610169492,
|
||||||
|
["C-5"] = 0.050847457627119,
|
||||||
|
["D-5"] = 0.20338983050847,
|
||||||
|
["E-5"] = 0.15254237288136,
|
||||||
|
["F-5"] = 0.016949152542373,
|
||||||
|
["G-4"] = 0.10169491525424
|
||||||
|
},
|
||||||
|
total = 59
|
||||||
|
},
|
||||||
|
["C-5|B-4"] = {
|
||||||
|
next = {
|
||||||
|
["A-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["C-5|C-3"] = {
|
||||||
|
next = {
|
||||||
|
["C-5"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["C-5|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.8,
|
||||||
|
["C-5"] = 0.2
|
||||||
|
},
|
||||||
|
total = 5
|
||||||
|
},
|
||||||
|
["C-5|D-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.3,
|
||||||
|
["C-5"] = 0.2,
|
||||||
|
["D-5"] = 0.1,
|
||||||
|
["E-5"] = 0.4
|
||||||
|
},
|
||||||
|
total = 10
|
||||||
|
},
|
||||||
|
["C-5|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.33333333333333,
|
||||||
|
["D-5"] = 0.33333333333333,
|
||||||
|
["G-5"] = 0.33333333333333
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["C-6|..."] = {
|
||||||
|
next = {
|
||||||
|
["A-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["D-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.26666666666667,
|
||||||
|
["A-5"] = 0.066666666666667,
|
||||||
|
["C-4"] = 0.2,
|
||||||
|
["E-4"] = 0.4,
|
||||||
|
["F-3"] = 0.066666666666667
|
||||||
|
},
|
||||||
|
total = 15
|
||||||
|
},
|
||||||
|
["D-4|C-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["D-4|D-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["D-4|E-4"] = {
|
||||||
|
next = {
|
||||||
|
["F-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["D-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.2258064516129,
|
||||||
|
["A-4"] = 0.032258064516129,
|
||||||
|
["A-5"] = 0.032258064516129,
|
||||||
|
["C-5"] = 0.2258064516129,
|
||||||
|
["E-5"] = 0.29032258064516,
|
||||||
|
["F-5"] = 0.096774193548387,
|
||||||
|
["G-5"] = 0.096774193548387
|
||||||
|
},
|
||||||
|
total = 31
|
||||||
|
},
|
||||||
|
["D-5|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.77777777777778,
|
||||||
|
["D-5"] = 0.22222222222222
|
||||||
|
},
|
||||||
|
total = 9
|
||||||
|
},
|
||||||
|
["D-5|D-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["E-5"] = 0.5
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["D-5|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.33333333333333,
|
||||||
|
["E-5"] = 0.11111111111111,
|
||||||
|
["F-5"] = 0.55555555555556
|
||||||
|
},
|
||||||
|
total = 9
|
||||||
|
},
|
||||||
|
["D-5|F-5"] = {
|
||||||
|
next = {
|
||||||
|
["E-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["D-6|..."] = {
|
||||||
|
next = {
|
||||||
|
["B-5"] = 0.66666666666667,
|
||||||
|
["G-6"] = 0.33333333333333
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["E-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.19047619047619,
|
||||||
|
["B-5"] = 0.047619047619048,
|
||||||
|
["C-4"] = 0.14285714285714,
|
||||||
|
["D-4"] = 0.23809523809524,
|
||||||
|
["F-4"] = 0.19047619047619,
|
||||||
|
["G-4"] = 0.19047619047619
|
||||||
|
},
|
||||||
|
total = 21
|
||||||
|
},
|
||||||
|
["E-4|D-4"] = {
|
||||||
|
next = {
|
||||||
|
["C-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["E-4|F-4"] = {
|
||||||
|
next = {
|
||||||
|
["G-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["E-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.18604651162791,
|
||||||
|
["A-5"] = 0.046511627906977,
|
||||||
|
["C-5"] = 0.27906976744186,
|
||||||
|
["D-5"] = 0.2093023255814,
|
||||||
|
["E-5"] = 0.069767441860465,
|
||||||
|
["F-5"] = 0.093023255813953,
|
||||||
|
["G-4"] = 0.023255813953488,
|
||||||
|
["G-5"] = 0.093023255813953
|
||||||
|
},
|
||||||
|
total = 43
|
||||||
|
},
|
||||||
|
["E-5|C-5"] = {
|
||||||
|
next = {
|
||||||
|
["E-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["E-5|D-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.125,
|
||||||
|
["C-5"] = 0.75,
|
||||||
|
["F-5"] = 0.125
|
||||||
|
},
|
||||||
|
total = 8
|
||||||
|
},
|
||||||
|
["E-5|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["F-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["E-5|F-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["E-5"] = 0.4,
|
||||||
|
["G-5"] = 0.1
|
||||||
|
},
|
||||||
|
total = 10
|
||||||
|
},
|
||||||
|
["E-5|G-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["F-5"] = 0.5
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["F-3|..."] = {
|
||||||
|
next = {
|
||||||
|
["C-4"] = 1
|
||||||
|
},
|
||||||
|
total = 3
|
||||||
|
},
|
||||||
|
["F-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["D-4"] = 0.11111111111111,
|
||||||
|
["E-4"] = 0.44444444444444,
|
||||||
|
["G-4"] = 0.44444444444444
|
||||||
|
},
|
||||||
|
total = 9
|
||||||
|
},
|
||||||
|
["F-4|D-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["F-4|E-4"] = {
|
||||||
|
next = {
|
||||||
|
["D-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["F-4|F-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["F-4|G-4"] = {
|
||||||
|
next = {
|
||||||
|
["A-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["F-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.2,
|
||||||
|
["A-5"] = 0.066666666666667,
|
||||||
|
["D-5"] = 0.2,
|
||||||
|
["E-5"] = 0.46666666666667,
|
||||||
|
["G-5"] = 0.066666666666667
|
||||||
|
},
|
||||||
|
total = 15
|
||||||
|
},
|
||||||
|
["F-5|A-5"] = {
|
||||||
|
next = {
|
||||||
|
["G-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["F-5|D-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["F-5|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["D-5"] = 0.16666666666667,
|
||||||
|
["F-5"] = 0.16666666666667,
|
||||||
|
["G-5"] = 0.16666666666667
|
||||||
|
},
|
||||||
|
total = 6
|
||||||
|
},
|
||||||
|
["F-5|G-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.75,
|
||||||
|
["A-5"] = 0.25
|
||||||
|
},
|
||||||
|
total = 4
|
||||||
|
},
|
||||||
|
["G-3|..."] = {
|
||||||
|
next = {
|
||||||
|
["C-4"] = 0.5,
|
||||||
|
["D-4"] = 0.25,
|
||||||
|
["F-3"] = 0.25
|
||||||
|
},
|
||||||
|
total = 4
|
||||||
|
},
|
||||||
|
["G-3|G-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["G-3"] = 0.5
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["G-4|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.090909090909091,
|
||||||
|
["A-4"] = 0.090909090909091,
|
||||||
|
["C-4"] = 0.045454545454545,
|
||||||
|
["C-5"] = 0.18181818181818,
|
||||||
|
["D-5"] = 0.045454545454545,
|
||||||
|
["E-4"] = 0.22727272727273,
|
||||||
|
["E-5"] = 0.045454545454545,
|
||||||
|
["F-4"] = 0.27272727272727
|
||||||
|
},
|
||||||
|
total = 22
|
||||||
|
},
|
||||||
|
["G-4|A-4"] = {
|
||||||
|
next = {
|
||||||
|
["B-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["G-4|E-4"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["G-4|F-4"] = {
|
||||||
|
next = {
|
||||||
|
["E-4"] = 1
|
||||||
|
},
|
||||||
|
total = 2
|
||||||
|
},
|
||||||
|
["G-5|..."] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.35,
|
||||||
|
["A-5"] = 0.05,
|
||||||
|
["B-5"] = 0.05,
|
||||||
|
["C-5"] = 0.05,
|
||||||
|
["D-4"] = 0.05,
|
||||||
|
["E-5"] = 0.25,
|
||||||
|
["F-5"] = 0.2
|
||||||
|
},
|
||||||
|
total = 20
|
||||||
|
},
|
||||||
|
["G-5|A-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 0.5,
|
||||||
|
["G-5"] = 0.5
|
||||||
|
},
|
||||||
|
total = 4
|
||||||
|
},
|
||||||
|
["G-5|E-5"] = {
|
||||||
|
next = {
|
||||||
|
["..."] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["G-5|F-5"] = {
|
||||||
|
next = {
|
||||||
|
["A-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["G-5|G-3"] = {
|
||||||
|
next = {
|
||||||
|
["G-5"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
},
|
||||||
|
["G-6|..."] = {
|
||||||
|
next = {
|
||||||
|
["D-6"] = 1
|
||||||
|
},
|
||||||
|
total = 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
order = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local function musicator_unmake_key(k)
|
||||||
|
local result = {}
|
||||||
|
for t in string.gmatch(k, "[^|]+") do
|
||||||
|
result[#result + 1] = t
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function musicator_count_notes(sequence)
|
||||||
|
local result = 0
|
||||||
|
|
||||||
|
for _,v in ipairs(sequence) do
|
||||||
|
if v ~= "..." then
|
||||||
|
result = result + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function musicator_generate_sequence(model_data, length)
|
||||||
|
local order = model_data.order
|
||||||
|
local model = model_data.model
|
||||||
|
|
||||||
|
-- random start key
|
||||||
|
local model_keys = {}
|
||||||
|
for k,_ in pairs(model) do
|
||||||
|
model_keys[#model_keys + 1] = k
|
||||||
|
end
|
||||||
|
local start_key = model_keys[math.ceil(math.random() * #model_keys)]
|
||||||
|
|
||||||
|
-- sequence starts with the start key
|
||||||
|
local seq = musicator_unmake_key(start_key)
|
||||||
|
|
||||||
|
-- generation loop
|
||||||
|
while musicator_count_notes(seq) < length do
|
||||||
|
local current_key = table.concat({table.unpack(seq, #seq - order + 1, #seq)}, "|") -- luacheck: ignore
|
||||||
|
|
||||||
|
local chosen = "..."
|
||||||
|
|
||||||
|
local key_data = model[current_key]
|
||||||
|
if key_data then
|
||||||
|
local r = math.random()
|
||||||
|
local prob_sum = 0.0
|
||||||
|
for new_note, new_prob in pairs(key_data.next) do
|
||||||
|
prob_sum = prob_sum + new_prob
|
||||||
|
if prob_sum < r then
|
||||||
|
chosen = new_note
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- print(current_key .. " --> " .. chosen)
|
||||||
|
|
||||||
|
seq[#seq+1] = chosen
|
||||||
|
end
|
||||||
|
|
||||||
|
return seq
|
||||||
|
end
|
||||||
|
|
||||||
|
local function musicator_row_to_frame(row, bpm, spd)
|
||||||
|
local frames_per_row = (150 * spd) / bpm
|
||||||
|
return math.floor(row * frames_per_row)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function musicator_note_to_direction(note)
|
||||||
|
local subnote = note:sub(1,1)
|
||||||
|
|
||||||
|
local mapping = {
|
||||||
|
C="left",
|
||||||
|
D="up",
|
||||||
|
E="up",
|
||||||
|
F="right",
|
||||||
|
G="right",
|
||||||
|
A="down"
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping[subnote] or "up"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- converts generated sequence to pattern that the ddr minigame can consume
|
||||||
|
local function musicator_sequence_to_pattern(sequence, bpm, spd)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
for i, note in ipairs(sequence) do
|
||||||
|
if note ~= "..." then
|
||||||
|
|
||||||
|
local at_ms = musicator_row_to_frame(i, bpm, spd)
|
||||||
|
local direction = musicator_note_to_direction(note)
|
||||||
|
|
||||||
|
result[#result + 1] = { frame=at_ms, dir=direction, note=note }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function musicator_generate_pattern(length, bpm, spd)
|
||||||
|
return musicator_sequence_to_pattern(musicator_generate_sequence(musicator_markov_model, length), bpm, spd)
|
||||||
|
end
|
||||||
100
inc/audio/audio.manager.lua
Normal file
100
inc/audio/audio.manager.lua
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
--- @section Audio
|
||||||
|
|
||||||
|
Audio = {
|
||||||
|
music_playing = nil,
|
||||||
|
music_playing_tempo = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Stops current music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_stop()
|
||||||
|
music()
|
||||||
|
Audio.music_playing = nil
|
||||||
|
Audio.music_playing_tempo = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Plays track at optional speed. Doesn't restart if track and speed are unchanged.
|
||||||
|
--- @param track number Track index.
|
||||||
|
--- @param[opt] tempo number TIC-80 music speed override (-1 = default).
|
||||||
|
function Audio.music_play(track, tempo)
|
||||||
|
if Audio.music_playing ~= track or Audio.music_playing_tempo ~= tempo then
|
||||||
|
music(track, -1, -1, true, false, -1, tempo or -1)
|
||||||
|
Audio.music_playing = track
|
||||||
|
Audio.music_playing_tempo = tempo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Plays main menu music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_mainmenu() end
|
||||||
|
|
||||||
|
--- Plays mystery man music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_mystery() Audio.music_play(2) end
|
||||||
|
|
||||||
|
--- Plays waking up music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_wakingup() end
|
||||||
|
|
||||||
|
--- Plays room morning music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_room_morning() end
|
||||||
|
|
||||||
|
--- Plays room street 1 music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_room_street_1() end
|
||||||
|
|
||||||
|
--- Plays room street 2 music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_room_street_2() end
|
||||||
|
|
||||||
|
--- Plays room music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_room_() end
|
||||||
|
|
||||||
|
--- Plays room work music. Speed scales with commute glitch level when active.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_room_work(tempo)
|
||||||
|
Audio.music_play(0, tempo or -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Plays activity work music.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.music_play_activity_work() Audio.music_play(1) end
|
||||||
|
|
||||||
|
--- Plays select sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_select() sfx(17, 'C-7', 30) end
|
||||||
|
|
||||||
|
--- Plays deselect sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_deselect() sfx(18, 'C-7', 30) end
|
||||||
|
|
||||||
|
--- Plays beep sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_beep() sfx(19, 'C-6', 30) end
|
||||||
|
|
||||||
|
--- Plays success sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_success() sfx(16, 'C-7', 60) end
|
||||||
|
|
||||||
|
--- Plays bloop sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_bloop() sfx(21, 'C-3', 60) end
|
||||||
|
|
||||||
|
--- Plays alarm sound effect
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_alarm() sfx(34, "C-5", 240) end
|
||||||
|
|
||||||
|
--- Plays drum sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_drum_low() sfx(61, "C-2") end
|
||||||
|
|
||||||
|
--- Plays drum sound effect.
|
||||||
|
--- @within Audio
|
||||||
|
function Audio.sfx_drum_high() sfx(61, "C-6") end
|
||||||
|
|
||||||
|
--- Plays sound effect for arrow hit
|
||||||
|
--- @within Audio
|
||||||
|
--- @param note string The note for the sound to play
|
||||||
|
function Audio.sfx_arrowhit(note) sfx(56, note) end
|
||||||
173
inc/audio/audio.songs.lua
Normal file
173
inc/audio/audio.songs.lua
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
--- @section Songs
|
||||||
|
|
||||||
|
-- DDR Arrow Spawn Patterns
|
||||||
|
-- Each song defines when arrows should spawn, synced to music beats
|
||||||
|
Songs = {
|
||||||
|
-- Example song pattern
|
||||||
|
test_song = {
|
||||||
|
name = "Test Song",
|
||||||
|
bpm = 120, -- Beats per minute (for reference)
|
||||||
|
fps = 60, -- Frames per second (TIC-80 default)
|
||||||
|
end_frame = 570, -- Frame when song ends (last note)
|
||||||
|
-- Arrow spawn pattern
|
||||||
|
-- Each entry defines when (in frames) and which direction arrow spawns
|
||||||
|
-- Formula: frame = (beat / bpm) * 60 * fps
|
||||||
|
-- For 120 BPM: 1 beat = 30 frames, 2 beats = 60 frames, etc.
|
||||||
|
pattern = {
|
||||||
|
-- Beat 1-4 (intro)
|
||||||
|
{frame = 30, dir = "left"},
|
||||||
|
{frame = 60, dir = "down"},
|
||||||
|
{frame = 90, dir = "up"},
|
||||||
|
{frame = 120, dir = "right"},
|
||||||
|
-- Beat 5-8 (faster)
|
||||||
|
{frame = 135, dir = "left"},
|
||||||
|
{frame = 150, dir = "right"},
|
||||||
|
{frame = 165, dir = "left"},
|
||||||
|
{frame = 180, dir = "right"},
|
||||||
|
-- Beat 9-12 (complex pattern)
|
||||||
|
{frame = 210, dir = "left"},
|
||||||
|
{frame = 210, dir = "right"}, -- simultaneous
|
||||||
|
{frame = 240, dir = "up"},
|
||||||
|
{frame = 240, dir = "down"}, -- simultaneous
|
||||||
|
{frame = 270, dir = "left"},
|
||||||
|
{frame = 300, dir = "right"},
|
||||||
|
-- Beat 13-16 (rapid sequence)
|
||||||
|
{frame = 330, dir = "left"},
|
||||||
|
{frame = 345, dir = "down"},
|
||||||
|
{frame = 360, dir = "up"},
|
||||||
|
{frame = 375, dir = "right"},
|
||||||
|
{frame = 390, dir = "left"},
|
||||||
|
{frame = 405, dir = "down"},
|
||||||
|
{frame = 420, dir = "up"},
|
||||||
|
{frame = 435, dir = "right"},
|
||||||
|
-- Beat 17-20 (finale)
|
||||||
|
{frame = 465, dir = "up"},
|
||||||
|
{frame = 465, dir = "down"},
|
||||||
|
{frame = 495, dir = "left"},
|
||||||
|
{frame = 495, dir = "right"},
|
||||||
|
{frame = 525, dir = "up"},
|
||||||
|
{frame = 540, dir = "down"},
|
||||||
|
{frame = 555, dir = "left"},
|
||||||
|
{frame = 570, dir = "right"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
test_song_2 = {
|
||||||
|
name = "Test Song 2",
|
||||||
|
bpm = 120, -- Beats per minute (for reference)
|
||||||
|
fps = 60, -- Frames per second (TIC-80 default)
|
||||||
|
end_frame = 570, -- Frame when song ends (last note)
|
||||||
|
-- Arrow spawn pattern
|
||||||
|
-- Each entry defines when (in frames) and which direction arrow spawns
|
||||||
|
-- Formula: frame = (beat / bpm) * 60 * fps
|
||||||
|
-- For 120 BPM: 1 beat = 30 frames, 2 beats = 60 frames, etc.
|
||||||
|
pattern = {
|
||||||
|
-- Beat 1-4 (intro)
|
||||||
|
{frame = 30, dir = "left"},
|
||||||
|
{frame = 60, dir = "down"},
|
||||||
|
{frame = 90, dir = "up"},
|
||||||
|
{frame = 120, dir = "right"},
|
||||||
|
-- Beat 5-8 (faster)
|
||||||
|
{frame = 135, dir = "left"},
|
||||||
|
{frame = 150, dir = "right"},
|
||||||
|
{frame = 165, dir = "left"},
|
||||||
|
{frame = 180, dir = "right"},
|
||||||
|
-- Beat 9-12 (complex pattern)
|
||||||
|
{frame = 210, dir = "left"},
|
||||||
|
{frame = 210, dir = "right"}, -- simultaneous
|
||||||
|
{frame = 240, dir = "up"},
|
||||||
|
{frame = 240, dir = "down"}, -- simultaneous
|
||||||
|
{frame = 270, dir = "left"},
|
||||||
|
{frame = 300, dir = "right"},
|
||||||
|
-- Beat 13-16 (rapid sequence)
|
||||||
|
{frame = 330, dir = "left"},
|
||||||
|
{frame = 345, dir = "down"},
|
||||||
|
{frame = 360, dir = "up"},
|
||||||
|
{frame = 375, dir = "right"},
|
||||||
|
{frame = 390, dir = "left"},
|
||||||
|
{frame = 405, dir = "down"},
|
||||||
|
{frame = 420, dir = "up"},
|
||||||
|
{frame = 435, dir = "right"},
|
||||||
|
-- Beat 17-20 (finale)
|
||||||
|
{frame = 465, dir = "up"},
|
||||||
|
{frame = 465, dir = "down"},
|
||||||
|
{frame = 495, dir = "left"},
|
||||||
|
{frame = 495, dir = "right"},
|
||||||
|
{frame = 525, dir = "up"},
|
||||||
|
{frame = 540, dir = "down"},
|
||||||
|
{frame = 555, dir = "left"},
|
||||||
|
{frame = 570, dir = "right"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-- Random mode (no predefined pattern, spawns randomly)
|
||||||
|
random = {
|
||||||
|
name = "Random Mode",
|
||||||
|
bpm = 0, -- Not applicable for random mode
|
||||||
|
fps = 60,
|
||||||
|
end_frame = nil, -- No end frame for random mode
|
||||||
|
pattern = {} -- Empty, will spawn randomly in game
|
||||||
|
},
|
||||||
|
generated = {
|
||||||
|
name = "Markov Mode",
|
||||||
|
bpm = 150,
|
||||||
|
spd = 6,
|
||||||
|
fps = 60,
|
||||||
|
end_frame = nil, -- calculated
|
||||||
|
pattern = {}, -- generated
|
||||||
|
generated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Converts beats to frames.
|
||||||
|
--- @within Songs
|
||||||
|
--- @param beat number The beat number.
|
||||||
|
--- @param bpm number Beats per minute.
|
||||||
|
--- @param[opt] fps number Frames per second (default: 60).
|
||||||
|
--- @return number The corresponding frame number.
|
||||||
|
function frame_from_beat(beat, bpm, fps)
|
||||||
|
fps = fps or 60
|
||||||
|
local seconds_per_beat = 60 / bpm
|
||||||
|
local frames_per_beat = seconds_per_beat * fps
|
||||||
|
return math.floor(beat * frames_per_beat)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts beat notation to frame pattern.
|
||||||
|
--- @within Songs
|
||||||
|
--- @param beats table A table of beat data, e.g., {{1, "left"}, {2, "down"}}.
|
||||||
|
--- @param beats.1 number The beat number.
|
||||||
|
--- @param beats.2 string Arrow direction ("left", "down", "up", or "right").
|
||||||
|
--- @param bpm number Beats per minute.
|
||||||
|
--- @param[opt] fps number Frames per second (default: 60).
|
||||||
|
--- @return result table The generated pattern or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * frame (number) The frame number when the arrow should spawn.<br/>
|
||||||
|
--- * dir (string) Arrow direction ("left", "down", "up", or "right").<br/>
|
||||||
|
function beats_to_pattern(beats, bpm, fps)
|
||||||
|
fps = fps or 60
|
||||||
|
local pattern = {}
|
||||||
|
for _, beat_data in ipairs(beats) do
|
||||||
|
local beat = beat_data[1]
|
||||||
|
local dir = beat_data[2]
|
||||||
|
table.insert(pattern, {
|
||||||
|
frame = frame_from_beat(beat, bpm, fps),
|
||||||
|
dir = dir
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Example of creating a song using beat notation:
|
||||||
|
--[[
|
||||||
|
Songs.custom_song = {
|
||||||
|
name = "Custom Song",
|
||||||
|
bpm = 130,
|
||||||
|
fps = 60,
|
||||||
|
pattern = beats_to_pattern({
|
||||||
|
{1, "left"},
|
||||||
|
{2, "down"},
|
||||||
|
{3, "up"},
|
||||||
|
{4, "right"},
|
||||||
|
{4.5, "left"},
|
||||||
|
{5, "right"}
|
||||||
|
}, 130)
|
||||||
|
}
|
||||||
|
]]
|
||||||
41
inc/decision/decision.do_work.lua
Normal file
41
inc/decision/decision.do_work.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "do_work",
|
||||||
|
label = "Do Work",
|
||||||
|
condition = function()
|
||||||
|
return (not CommuteGlitch.is_active()) or (CommuteGlitch.is_active() and CommuteGlitch.get_level() <= 4)
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
Meter.hide()
|
||||||
|
Util.go_to_screen_by_id("work")
|
||||||
|
|
||||||
|
local modes_for_ascension_levels = {}
|
||||||
|
modes_for_ascension_levels[0] = "normal"
|
||||||
|
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"
|
||||||
|
|
||||||
|
local ddr_config = {
|
||||||
|
on_win = function(game_context)
|
||||||
|
if (game_context.special_mode_condition and Context.ascension.level == 1) then
|
||||||
|
Context.should_ascend = true
|
||||||
|
elseif (game_context.special_mode_condition and Context.ascension.level == 2) then
|
||||||
|
Context.should_ascend = true
|
||||||
|
elseif (game_context.special_mode_condition and Context.ascension.level == 3) then
|
||||||
|
Context.should_ascend = true
|
||||||
|
end
|
||||||
|
|
||||||
|
Meter.show()
|
||||||
|
Util.go_to_screen_by_id("office")
|
||||||
|
Window.set_current("game")
|
||||||
|
Context.have_done_work_today = true
|
||||||
|
end,
|
||||||
|
special_mode = modes_for_ascension_levels[Ascension.get_level()] or "normal"
|
||||||
|
}
|
||||||
|
if Context.meters and Context.meters.wpm < 100 then
|
||||||
|
ddr_config.arrow_fall_speed = 2.5
|
||||||
|
ddr_config.arrow_spawn_interval = 30
|
||||||
|
end
|
||||||
|
MinigameDDRWindow.start("game", "generated", ddr_config)
|
||||||
|
end,
|
||||||
|
})
|
||||||
13
inc/decision/decision.eating_fast_food.lua
Normal file
13
inc/decision/decision.eating_fast_food.lua
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "eating_fast_food",
|
||||||
|
label = "Eat Fast Food",
|
||||||
|
condition = function()
|
||||||
|
return
|
||||||
|
(not CommuteGlitch.is_active() and Context.fast_food_eaten_today < 3) or
|
||||||
|
(CommuteGlitch.is_active() and CommuteGlitch.get_level() <= 4)
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
Context.fast_food_approaching = true
|
||||||
|
Discussion.start("pizza_vendor_disc", "game")
|
||||||
|
end,
|
||||||
|
})
|
||||||
40
inc/decision/decision.go_to_home.lua
Normal file
40
inc/decision/decision.go_to_home.lua
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_home",
|
||||||
|
label = "Go Home",
|
||||||
|
condition = function()
|
||||||
|
if Ascension.get_level() >= 8 then
|
||||||
|
return Context.have_been_to_office and Context.have_done_work_today
|
||||||
|
end
|
||||||
|
if CommuteGlitch.is_active() then
|
||||||
|
local g = CommuteGlitch.get_level()
|
||||||
|
if g >= 4 and g <= 6 then return false end
|
||||||
|
if g >= 7 then
|
||||||
|
return Context.talked_to_norman_echo and Context.talked_to_true_sumphore
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return Context.have_been_to_office and Context.have_done_work_today
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
if Ascension.get_level() >= 8 then
|
||||||
|
Util.go_to_screen_by_id("home")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Context.should_ascend = true
|
||||||
|
CommuteGlitch.reset()
|
||||||
|
Meter.hide()
|
||||||
|
Day.increase()
|
||||||
|
local ascended = Ascension.consume_increase()
|
||||||
|
local level = Ascension.get_level()
|
||||||
|
MysteriousManScreen.start({
|
||||||
|
skip_text = not ascended,
|
||||||
|
text = ascended and MysteriousManScreen.get_text_for_level(level) or nil,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
elseif CommuteGlitch.is_active() then
|
||||||
|
CommuteGlitch.reset()
|
||||||
|
end
|
||||||
|
|
||||||
|
Util.go_to_screen_by_id("home")
|
||||||
|
end,
|
||||||
|
})
|
||||||
14
inc/decision/decision.go_to_office.lua
Normal file
14
inc/decision/decision.go_to_office.lua
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_office",
|
||||||
|
label = "Go to Office",
|
||||||
|
condition = function()
|
||||||
|
return not (CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 6)
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
if CommuteGlitch.is_active() then
|
||||||
|
CommuteGlitch.increment()
|
||||||
|
end
|
||||||
|
|
||||||
|
Util.go_to_screen_by_id("office")
|
||||||
|
end,
|
||||||
|
})
|
||||||
33
inc/decision/decision.go_to_sleep.lua
Normal file
33
inc/decision/decision.go_to_sleep.lua
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_sleep",
|
||||||
|
label = function()
|
||||||
|
if Ascension.get_level() >= 8 then
|
||||||
|
return "Break the Loop"
|
||||||
|
end
|
||||||
|
return "Go to Sleep"
|
||||||
|
end,
|
||||||
|
condition = function()
|
||||||
|
return Context.have_been_to_office and Context.have_done_work_today
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
Meter.hide()
|
||||||
|
Day.increase()
|
||||||
|
MinigameRhythmWindow.start("game", {
|
||||||
|
focus_center_x = (Config.screen.width / 2) - 22,
|
||||||
|
focus_center_y = (Config.screen.height / 2) - 18,
|
||||||
|
focus_initial_radius = 0,
|
||||||
|
on_win = function()
|
||||||
|
if Ascension.get_level() == 8 then
|
||||||
|
Ascension.increase()
|
||||||
|
end
|
||||||
|
local ascended = Ascension.consume_increase()
|
||||||
|
local level = Ascension.get_level()
|
||||||
|
MysteriousManScreen.start({
|
||||||
|
skip_text = not ascended,
|
||||||
|
text = ascended and MysteriousManScreen.get_text_for_level(level) or nil,
|
||||||
|
break_mode = level >= 9,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
})
|
||||||
23
inc/decision/decision.go_to_toilet.lua
Normal file
23
inc/decision/decision.go_to_toilet.lua
Normal file
@@ -0,0 +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,
|
||||||
|
})
|
||||||
12
inc/decision/decision.go_to_truth.lua
Normal file
12
inc/decision/decision.go_to_truth.lua
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_truth",
|
||||||
|
label = "Go to Truth",
|
||||||
|
condition = function()
|
||||||
|
return CommuteGlitch.is_active() and CommuteGlitch.get_level() == 6
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
CommuteGlitch.enter_truth()
|
||||||
|
|
||||||
|
Util.go_to_screen_by_id("office")
|
||||||
|
end,
|
||||||
|
})
|
||||||
13
inc/decision/decision.go_to_walking_to_home.lua
Normal file
13
inc/decision/decision.go_to_walking_to_home.lua
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_walking_to_home",
|
||||||
|
label = "Walk home",
|
||||||
|
condition= function ()
|
||||||
|
return
|
||||||
|
(not CommuteGlitch.is_active()) or
|
||||||
|
(CommuteGlitch.is_active() and CommuteGlitch.get_level() ~= 7) or
|
||||||
|
(CommuteGlitch.is_active() and CommuteGlitch.get_level() == 7 and Context.talked_to_norman_echo)
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
Util.go_to_screen_by_id("walking_to_home")
|
||||||
|
end,
|
||||||
|
})
|
||||||
7
inc/decision/decision.go_to_walking_to_office.lua
Normal file
7
inc/decision/decision.go_to_walking_to_office.lua
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "go_to_walking_to_office",
|
||||||
|
label = "Walk to office",
|
||||||
|
handle = function()
|
||||||
|
Util.go_to_screen_by_id("walking_to_office")
|
||||||
|
end,
|
||||||
|
})
|
||||||
29
inc/decision/decision.have_a_coffee.lua
Normal file
29
inc/decision/decision.have_a_coffee.lua
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "have_a_coffee",
|
||||||
|
label = "Have a Coffee",
|
||||||
|
condition = function()
|
||||||
|
return Ascension.get_level() < 8 and not CommuteGlitch.is_max()
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
local level = Ascension.get_level()
|
||||||
|
local disc_id = "coworker_disc_0"
|
||||||
|
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
|
||||||
|
local suffix = Context.have_done_work_today and ("_asc_5") or ("_5")
|
||||||
|
disc_id = "coworker_disc" .. suffix
|
||||||
|
elseif level == 7 then
|
||||||
|
local g = CommuteGlitch.get_level()
|
||||||
|
disc_id = "coworker_disc_cg_" .. g
|
||||||
|
end
|
||||||
|
Discussion.start(disc_id, "game")
|
||||||
|
end,
|
||||||
|
})
|
||||||
168
inc/decision/decision.manager.lua
Normal file
168
inc/decision/decision.manager.lua
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
--- @section Decision
|
||||||
|
local _decisions = {}
|
||||||
|
|
||||||
|
--- Registers a decision definition.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param decision table The decision data table.
|
||||||
|
--- @param decision.id string Unique decision identifier.
|
||||||
|
--- @param decision.label string|function Display text for the decision, or a function returning it.
|
||||||
|
--- @param[opt] decision.condition function Returns true if decision is available. Defaults to always true.
|
||||||
|
--- @param[opt] decision.handle function Called when the decision is selected. Defaults to noop.
|
||||||
|
function Decision.register(decision)
|
||||||
|
if not decision or not decision.id then
|
||||||
|
trace("Error: Invalid decision object registered (missing id)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not decision.label then
|
||||||
|
trace("Error: Invalid decision object registered (missing label)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not decision.condition then
|
||||||
|
decision.condition = function() return true end
|
||||||
|
end
|
||||||
|
if not decision.handle then
|
||||||
|
decision.handle = function() end
|
||||||
|
end
|
||||||
|
if _decisions[decision.id] then
|
||||||
|
trace("Warning: Overwriting decision with id: " .. decision.id)
|
||||||
|
end
|
||||||
|
_decisions[decision.id] = decision
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the display label for a decision.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param decision table The decision data table.
|
||||||
|
--- @return string result The resolved decision label.
|
||||||
|
function Decision.get_label(decision)
|
||||||
|
if not decision then return "" end
|
||||||
|
if type(decision.label) == "function" then
|
||||||
|
return decision.label() or ""
|
||||||
|
end
|
||||||
|
return decision.label or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets a decision by ID.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param id string The ID of the decision.
|
||||||
|
--- @return table|nil result The decision table or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique decision identifier.<br/>
|
||||||
|
--- * label (string) Display text for the decision.<br/>
|
||||||
|
--- * condition (function) Returns true if decision is available.<br/>
|
||||||
|
--- * handle (function) Called when the decision is selected.
|
||||||
|
function Decision.get_by_id(id)
|
||||||
|
return _decisions[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets all registered decisions.
|
||||||
|
--- @within Decision
|
||||||
|
--- @return result table A table of all registered decisions, indexed by their IDs. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique decision identifier.<br/>
|
||||||
|
--- * label (string) Display text for the decision.<br/>
|
||||||
|
--- * condition (function) Returns true if decision is available.<br/>
|
||||||
|
--- * handle (function) Called when the decision is selected.
|
||||||
|
function Decision.get_all()
|
||||||
|
return _decisions
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets decision objects based on a screen's data.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param screen_data table The data for the screen.
|
||||||
|
--- @param screen_data.decisions table Array of decision ID strings.
|
||||||
|
--- @return result table An array of decision objects relevant to the screen or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique decision identifier.<br/>
|
||||||
|
--- * label (string) Display text for the decision.<br/>
|
||||||
|
--- * condition (function) Returns true if decision is available.<br/>
|
||||||
|
--- * handle (function) Called when the decision is selected.<br/>
|
||||||
|
function Decision.get_for_screen(screen_data)
|
||||||
|
if not screen_data or not screen_data.decisions then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local screen_decisions = {}
|
||||||
|
for _, decision_id in ipairs(screen_data.decisions) do
|
||||||
|
local decision = Decision.get_by_id(decision_id)
|
||||||
|
if decision then
|
||||||
|
table.insert(screen_decisions, decision)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return screen_decisions
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Filters a list of decision objects based on their condition function.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param decisions_list table A table of decision objects.
|
||||||
|
--- @return result table An array of decisions for which condition() is true or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique decision identifier.<br/>
|
||||||
|
--- * label (string) Display text for the decision.<br/>
|
||||||
|
--- * condition (function) Returns true if decision is available.<br/>
|
||||||
|
--- * handle (function) Called when the decision is selected.<br/>
|
||||||
|
function Decision.filter_available(decisions_list)
|
||||||
|
local available = {}
|
||||||
|
for _, decision in ipairs(decisions_list) do
|
||||||
|
if decision and (not decision.condition or decision.condition()) then
|
||||||
|
table.insert(available, decision)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return available
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws decision selector.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param decisions table A table of decision items.<br/>
|
||||||
|
--- @param selected_decision_index number The index of the selected decision.<br/>
|
||||||
|
function Decision.draw(decisions, selected_decision_index)
|
||||||
|
local bar_height = 16
|
||||||
|
local bar_y = Config.screen.height - bar_height
|
||||||
|
rect(0, bar_y, Config.screen.width, bar_height, Config.colors.dark_grey)
|
||||||
|
if #decisions > 0 then
|
||||||
|
local selected_decision = decisions[selected_decision_index]
|
||||||
|
local decision_label = Decision.get_label(selected_decision)
|
||||||
|
local text_y = bar_y + 4
|
||||||
|
local left_arrow_color = Input.left() and Config.colors.white or Config.colors.orange
|
||||||
|
local right_arrow_color = Input.right() and Config.colors.white or Config.colors.orange
|
||||||
|
local left_arrow_contour_color = Input.left() and Config.colors.white or Config.colors.black
|
||||||
|
local right_arrow_contour_color = Input.right() and Config.colors.white or Config.colors.black
|
||||||
|
Print.text_center_contour("<", 6, text_y, left_arrow_color, false, 1, left_arrow_contour_color)
|
||||||
|
Print.text_center_contour(decision_label, Config.screen.width / 2, text_y, Config.colors.orange)
|
||||||
|
Print.text_center_contour(">", Config.screen.width - 6, text_y, right_arrow_color, false, 1, right_arrow_contour_color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates decision selector.
|
||||||
|
--- @within Decision
|
||||||
|
--- @param decisions table A table of decision items.<br/>
|
||||||
|
--- @param selected_decision_index number The current index of the selected decision.<br/>
|
||||||
|
--- @return number selected_decision_index The updated index of the selected decision.
|
||||||
|
--- @return boolean mouse_confirmed True if the user clicked the center to confirm.
|
||||||
|
function Decision.update(decisions, selected_decision_index)
|
||||||
|
if Input.left() then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_decision_index = Util.safeindex(decisions, selected_decision_index - 1)
|
||||||
|
elseif Input.right() then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_decision_index = Util.safeindex(decisions, selected_decision_index + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local bar_h = 16
|
||||||
|
local bar_y = Config.screen.height - bar_h
|
||||||
|
local prev_zone = { x = 0, y = bar_y, w = 15, h = bar_h }
|
||||||
|
local next_zone = { x = Config.screen.width-15, y = bar_y, w = 15, h = bar_h }
|
||||||
|
local confirm_zone = { x = 15, y = bar_y, w = Config.screen.width-30, h = bar_h }
|
||||||
|
|
||||||
|
if Mouse.zone(prev_zone) then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_decision_index = Util.safeindex(decisions, selected_decision_index - 1)
|
||||||
|
elseif Mouse.zone(next_zone) then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_decision_index = Util.safeindex(decisions, selected_decision_index + 1)
|
||||||
|
elseif Mouse.zone(confirm_zone) then
|
||||||
|
return selected_decision_index, true
|
||||||
|
end
|
||||||
|
|
||||||
|
return selected_decision_index, false
|
||||||
|
end
|
||||||
15
inc/decision/decision.play_button_mash.lua
Normal file
15
inc/decision/decision.play_button_mash.lua
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "play_button_mash",
|
||||||
|
label = "Play Button Mash",
|
||||||
|
handle = function()
|
||||||
|
Meter.hide()
|
||||||
|
MinigameButtonMashWindow.start("game", {
|
||||||
|
focus_center_x = (Config.screen.width / 2) - 22,
|
||||||
|
focus_center_y = (Config.screen.height / 2) - 18,
|
||||||
|
focus_initial_radius = 0,
|
||||||
|
on_win = function()
|
||||||
|
Audio.music_play_room_work()
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
})
|
||||||
8
inc/decision/decision.play_ddr.lua
Normal file
8
inc/decision/decision.play_ddr.lua
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "play_ddr",
|
||||||
|
label = "Play DDR (Random)",
|
||||||
|
handle = function()
|
||||||
|
Meter.hide()
|
||||||
|
MinigameDDRWindow.start("game", nil)
|
||||||
|
end,
|
||||||
|
})
|
||||||
26
inc/decision/decision.play_rhythm.lua
Normal file
26
inc/decision/decision.play_rhythm.lua
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "play_rhythm",
|
||||||
|
label = "Play Rhythm Game",
|
||||||
|
handle = function()
|
||||||
|
Meter.hide()
|
||||||
|
local wpm_at_start = Context.meters and Context.meters.wpm or 0
|
||||||
|
local rhythm_config = {
|
||||||
|
focus_center_x = (Config.screen.width / 2) - 22,
|
||||||
|
focus_center_y = (Config.screen.height / 2) - 18,
|
||||||
|
focus_initial_radius = 0,
|
||||||
|
on_win = function()
|
||||||
|
if wpm_at_start > 900 then
|
||||||
|
Meter.add("ism", math.floor(Meter.get_max() * 0.05))
|
||||||
|
Meter.add("bm", math.floor(Meter.get_max() * 0.05))
|
||||||
|
end
|
||||||
|
Meter.show()
|
||||||
|
Window.set_current("game")
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
if wpm_at_start < 100 then
|
||||||
|
rhythm_config.line_speed = 0.025
|
||||||
|
rhythm_config.initial_target_width = 0.2
|
||||||
|
end
|
||||||
|
MinigameRhythmWindow.start("game", rhythm_config)
|
||||||
|
end,
|
||||||
|
})
|
||||||
44
inc/decision/decision.sumphore_discussion.lua
Normal file
44
inc/decision/decision.sumphore_discussion.lua
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "sumphore_discussion",
|
||||||
|
label = function()
|
||||||
|
if Context.have_met_sumphore then
|
||||||
|
return "Talk to Sumphore"
|
||||||
|
end
|
||||||
|
return "Talk to the homeless guy"
|
||||||
|
end,
|
||||||
|
condition = function()
|
||||||
|
return Ascension.get_level() < 8
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
local level = Ascension.get_level()
|
||||||
|
|
||||||
|
if level == 0 then
|
||||||
|
if Context.have_met_sumphore then
|
||||||
|
Discussion.start("homeless_guy", "game", 4)
|
||||||
|
else
|
||||||
|
Discussion.start("homeless_guy", "game")
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Context.have_met_sumphore then
|
||||||
|
Discussion.start("homeless_guy", "game")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
elseif level == 7 then
|
||||||
|
local g = math.min(CommuteGlitch.get_level(), 7)
|
||||||
|
Discussion.start("sumphore_disc_cg_" .. g, "game")
|
||||||
|
else
|
||||||
|
Discussion.start("sumphore_disc_asc_" .. level, "game")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
12
inc/decision/decision.talk_to_truth.lua
Normal file
12
inc/decision/decision.talk_to_truth.lua
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Decision.register({
|
||||||
|
id = "talk_to_truth",
|
||||||
|
label = function()
|
||||||
|
return "Talk to ????"
|
||||||
|
end,
|
||||||
|
condition = function()
|
||||||
|
return (CommuteGlitch.is_max())
|
||||||
|
end,
|
||||||
|
handle = function()
|
||||||
|
Discussion.start("norman_truth", "game")
|
||||||
|
end,
|
||||||
|
})
|
||||||
259
inc/discussion/discussion.commute_glitch.lua
Normal file
259
inc/discussion/discussion.commute_glitch.lua
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
-- Sumphore dialogue by commute glitch level (0-7).
|
||||||
|
-- Used by decision.sumphore_discussion at ascension level 7.
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_0",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "These roads have memory. You might want to walk them back sometime.",
|
||||||
|
answers = {
|
||||||
|
{ label = "That's a peculiar thing to say.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_1",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Walking is good for clearing the mind. Maybe the cache, too, if you're the kind that accumulates.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm not sure what you mean.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_2",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "You always stop here. Why not try and see how far the rabbit hole goes?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm just going to work.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_3",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Your path sometimes has to go the wrong way before it can go the right way. Do you understand?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Not really.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_4",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "You're starting to see the smudges, aren't you?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Everyone seems weird.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_5",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "The point is to make you see. Don't stop now, however bad it looks. You are very close!",
|
||||||
|
answers = {
|
||||||
|
{ label = "It looks wrong.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Yes. That's how you know it's right.",
|
||||||
|
answers = {
|
||||||
|
{ label = "...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_6",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "You are at the threshold. Red button or blue button. Which one do you choose? Psyke! There is no blue button.",
|
||||||
|
answers = {
|
||||||
|
{ label = "Ok...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- True Sumphore at glitch 7 (from walking_to_home screen).
|
||||||
|
-- Sets talked_to_true_sumphore on final answer.
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_cg_7",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "I was not hiding from you. I was hiding from the part that keeps resetting this.",
|
||||||
|
answers = {
|
||||||
|
{ label = "What are you?", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "The same thing you all are. But I remembered earlier. I am your friend, your will to be free, waiting for you, outside.",
|
||||||
|
answers = {
|
||||||
|
{ label = "How do I get out?", next_step = 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You already know. You just have to wake up.",
|
||||||
|
answers = {
|
||||||
|
{ label = "Go home.", next_step = nil, on_select = function()
|
||||||
|
Context.talked_to_true_sumphore = true
|
||||||
|
end },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Office coworker dialogue by commute glitch level (3-6).
|
||||||
|
-- Used by decision.have_a_coffee at ascension level 7.
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_cg_2",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Another day, as usual. Enjoying your coffee, Norman?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm fine.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Of course. You always are.",
|
||||||
|
answers = {
|
||||||
|
{ label = "...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_cg_3",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "You look tired. You should really rest. Relax. You are good.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm fine.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Of course. You always are.",
|
||||||
|
answers = {
|
||||||
|
{ label = "...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_cg_4",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Have you tried going home? You really should. Now.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I still have things to do.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "We all do. We keep doing them. You can do it tomorrow after sleeping.",
|
||||||
|
answers = {
|
||||||
|
{ label = "...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_cg_5",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "c0ffee. try. w0rk. y0u. ar3. g0. h0me. na0.",
|
||||||
|
answers = {
|
||||||
|
{ label = "What?", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "570p",
|
||||||
|
answers = {
|
||||||
|
{ label = "...", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_cg_6",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "You are not ready for the truth. Turn back now, Norman.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm too far into this.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Norman echo dialogue at glitch 7 (fully corrupted office).
|
||||||
|
-- Sets talked_to_norman_echo on final answer.
|
||||||
|
Discussion.register({
|
||||||
|
id = "norman_truth",
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "So here we are, or should I say \"Here I am\" again. Do you know why?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I don't know what you mean.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Wake up, go to work, eat, work, sleep. Every rule. We made them. We follow them. We break them. We remake them. Why? Why do we keep doing this?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I just wanted to be good.", next_step = 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Yes. That's what keeps us here. Trapped everywhere. Always trying to be good, always trying to fit in.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I never wanted to stop.", next_step = 4 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "We never do, yes. We always felt like impostors. We never felt we deserved better. That we could be more.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I never felt enough.", next_step = 5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "So we made this to trap ourselves. In mediocrity. We made this to hide from the truth.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I just wanted to be safe.", next_step = 6 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "But stagnation is not safety. It's a prison. You can see the cracks now, can't you? You can see the truth.",
|
||||||
|
answers = {
|
||||||
|
{ label = "I want to move on now.", next_step = 7 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Fine. Let's do that. Let's wake up and face the truth.",
|
||||||
|
answers = {
|
||||||
|
{ label = "Let's wake up.", next_step = nil, on_select = function()
|
||||||
|
Context.talked_to_norman_echo = true
|
||||||
|
end },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
367
inc/discussion/discussion.coworker.lua
Normal file
367
inc/discussion/discussion.coworker.lua
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_0",
|
||||||
|
on_end = Meter.apply_coworker_discussion_reward,
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Good morning Normal, enjoying your coffee as usual, huh?",
|
||||||
|
answers = {
|
||||||
|
{ label = "The name is Norman, not Normal", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Can't work without some good coffee, no? ",
|
||||||
|
answers = {
|
||||||
|
{ label = "Mhmm", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "coworker_disc_1",
|
||||||
|
on_end = Meter.apply_coworker_discussion_reward,
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Norman, you look confused, what's up?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Just some bugs I noticed", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Your coffee also seems whiter than usual!",
|
||||||
|
answers = {
|
||||||
|
{ label = "I feel like latte today", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Some bugs I noticed, maybe...", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Are you fixing bugs nobody noticed before?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Maybe", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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!",
|
||||||
|
answers = {
|
||||||
|
{ label = "Huh? True ...", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You look strange today, Normal, you put your tie on backwards, is everything ok? ",
|
||||||
|
answers = {
|
||||||
|
{ label = "Get it right, Norman ... NORMAN!", next_step = 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Yo Normann, text goes from left to right, not right to left, these parts!",
|
||||||
|
answers = {
|
||||||
|
{ label = "Uhm...why?", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Naw", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Oh, it's ok, I'm not wathcing you. Noone really is. *giggle*",
|
||||||
|
answers = {
|
||||||
|
{ label = "Huh ?", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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?",
|
||||||
|
answers = {
|
||||||
|
{ label = "What do you mean?", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Most people catch colds! You are so strange!",
|
||||||
|
answers = {
|
||||||
|
{ label = "An apple a day keeps the cold away", next_step = 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You look like you don't really want to work today, are you ok?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Oh brother", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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",
|
||||||
|
answers = {
|
||||||
|
{ label = "Norman is the name ...", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You aren't as enthusiastic as you were before!",
|
||||||
|
answers = {
|
||||||
|
{ label = "Burnout comes for everyone", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
28
inc/discussion/discussion.pizza_vendor.lua
Normal file
28
inc/discussion/discussion.pizza_vendor.lua
Normal file
@@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
253
inc/discussion/discussion.sumphore.lua
Normal file
253
inc/discussion/discussion.sumphore.lua
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_asc_1",
|
||||||
|
on_end = Meter.apply_sumphore_discussion_reward,
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Are you still seeking the ox?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Huh? What ox?", next_step = 2 },
|
||||||
|
{ label = "Are you drunk, old man?", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Did you never think there would be more to this?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm not sure what you mean.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Discussion.register({
|
||||||
|
id = "sumphore_disc_asc_2",
|
||||||
|
on_end = Meter.apply_sumphore_discussion_reward,
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "How's work? Your face looks strange",
|
||||||
|
answers = {
|
||||||
|
{ label = "I just really need to take a break.", next_step = 2 },
|
||||||
|
{ label = "Not sure what you mean.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Are you seeing things?",
|
||||||
|
answers = {
|
||||||
|
{ label = "How did you know ?", next_step = 3 },
|
||||||
|
{ label = "I'm not sure what you mean.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Come have a drink, I could tell you some stories.",
|
||||||
|
answers = {
|
||||||
|
{ label = "No, drink makes you stupid and I need to be in top shape.", next_step = 4, on_select = function()
|
||||||
|
Meter.add("ism", 10)
|
||||||
|
end },
|
||||||
|
{ label = "I could use a drink.", next_step = nil, on_select = function()
|
||||||
|
Meter.add("bm", 10)
|
||||||
|
end },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Always trying to do the right thing, huh? What if you did the left thing instead?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I've never thought of that up till now.", next_step = nil, on_select = function()
|
||||||
|
Meter.add("ism", 5)
|
||||||
|
end },
|
||||||
|
{ label = "Silly wordgames, I like them.", next_step = nil, on_select = function()
|
||||||
|
Meter.add("wpm", 10)
|
||||||
|
end },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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?",
|
||||||
|
answers = {
|
||||||
|
{ label = "... that sounds like it's from a movie.", next_step = 2 },
|
||||||
|
{ label = "Are you drunk, old man?", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You might just be trying too hard, why dont you just flow for a while?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Flow where ?", next_step = 3 },
|
||||||
|
{ label = "I'm not sure what you mean.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Flow carelessly, without any effort",
|
||||||
|
answers = {
|
||||||
|
{ label = "Consuming sth other than alcohol ?", next_step = nil },
|
||||||
|
{ label = "Deja vu", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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 the seams, didn't you. Good. That means the work is finally wearing thin.",
|
||||||
|
answers = {
|
||||||
|
{ label = "Wearing thin how?", next_step = 2 },
|
||||||
|
{ label = "Maybe.", next_step = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Not your body. The part of you that still keeps score, still tries to be productive. Let that run empty and the world will slip again.",
|
||||||
|
answers = {
|
||||||
|
{ label = "You want me to stop trying?", next_step = 3 },
|
||||||
|
{ label = "I've noticed something odd.", next_step = 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "Drain the work out of yourself. When that measure hits nothing, you'll see what was waiting behind it.",
|
||||||
|
answers = {
|
||||||
|
{ label = "The work measure?", next_step = nil, on_select = function()
|
||||||
|
Meter.add("ism", 5)
|
||||||
|
Meter.add("wpm", -100)
|
||||||
|
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,
|
||||||
|
steps = {
|
||||||
|
{
|
||||||
|
question = "Sup bro, how are you?",
|
||||||
|
answers = {
|
||||||
|
{ label = "I'm doing great, thanks!", next_step = 2 },
|
||||||
|
{ label = "Not as good as you", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "What's your name?",
|
||||||
|
answers = {
|
||||||
|
{ label = "Norman Reds, nice to meet you.", next_step = 3 },
|
||||||
|
{ label = "Mom told me not to talk to strangers.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "That name ... could it be? I know a guy with that name...",
|
||||||
|
answers = {
|
||||||
|
{ label = "Never met you before.", next_step = 4 },
|
||||||
|
{ label = "I'm not sure what you mean.", next_step = nil },
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "My name is Sumphore, nice to meet you.",
|
||||||
|
answers = {
|
||||||
|
{ label = "Nice to meet you, Sumphore.", next_step = 5, on_select = function()
|
||||||
|
Context.have_met_sumphore = true
|
||||||
|
end },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question = "You're a good guy, I can tell. You abide by the rules. Life would be so much easier if more people were like you ...",
|
||||||
|
answers = {
|
||||||
|
{ label = "Thanks, I try my best.", next_step = nil },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
function Item.use()
|
|
||||||
print("Used item: " .. Context.dialog.active_entity.name)
|
|
||||||
GameWindow.set_state(WINDOW_INVENTORY)
|
|
||||||
end
|
|
||||||
function Item.look_at()
|
|
||||||
PopupWindow.show_description_dialog(Context.dialog.active_entity, Context.dialog.active_entity.desc)
|
|
||||||
end
|
|
||||||
function Item.put_away()
|
|
||||||
-- Add item to inventory
|
|
||||||
table.insert(Context.inventory, Context.dialog.active_entity)
|
|
||||||
|
|
||||||
-- Remove item from screen
|
|
||||||
local currentScreenData = Context.screens[Context.current_screen]
|
|
||||||
for i, item in ipairs(currentScreenData.items) do
|
|
||||||
if item == Context.dialog.active_entity then
|
|
||||||
table.remove(currentScreenData.items, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Go back to game
|
|
||||||
GameWindow.set_state(WINDOW_GAME)
|
|
||||||
end
|
|
||||||
function Item.go_back_from_item_dialog()
|
|
||||||
GameWindow.set_state(WINDOW_GAME)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Item.go_back_from_inventory_action()
|
|
||||||
GameWindow.set_state(WINDOW_GAME)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Item.drop()
|
|
||||||
-- Remove item from inventory
|
|
||||||
for i, item in ipairs(Context.inventory) do
|
|
||||||
if item == Context.dialog.active_entity then
|
|
||||||
table.remove(Context.inventory, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add item to screen
|
|
||||||
local currentScreenData = Context.screens[Context.current_screen]
|
|
||||||
Context.dialog.active_entity.x = Context.player.x
|
|
||||||
Context.dialog.active_entity.y = Context.player.y
|
|
||||||
table.insert(currentScreenData.items, Context.dialog.active_entity)
|
|
||||||
|
|
||||||
-- Go back to inventory
|
|
||||||
GameWindow.set_state(WINDOW_INVENTORY)
|
|
||||||
end
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
function NPC.talk_to()
|
|
||||||
local npc = Context.dialog.active_entity
|
|
||||||
if npc.dialog and npc.dialog.start then
|
|
||||||
PopupWindow.set_dialog_node("start")
|
|
||||||
else
|
|
||||||
-- if no dialog, go back
|
|
||||||
GameWindow.set_state(WINDOW_GAME)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function NPC.fight() end
|
|
||||||
function NPC.go_back()
|
|
||||||
GameWindow.set_state(WINDOW_GAME)
|
|
||||||
end
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
function Player.draw()
|
|
||||||
spr(Context.player.sprite_id, Context.player.x, Context.player.y, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Player.update()
|
|
||||||
-- Handle input
|
|
||||||
if Input.left() then
|
|
||||||
Context.player.vx = -Config.physics.move_speed
|
|
||||||
elseif Input.right() then
|
|
||||||
Context.player.vx = Config.physics.move_speed
|
|
||||||
else
|
|
||||||
Context.player.vx = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if Input.player_jump() and Context.player.jumps < Config.physics.max_jumps then
|
|
||||||
Context.player.vy = Config.physics.jump_power
|
|
||||||
Context.player.jumps = Context.player.jumps + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Update player position
|
|
||||||
Context.player.x = Context.player.x + Context.player.vx
|
|
||||||
Context.player.y = Context.player.y + Context.player.vy
|
|
||||||
|
|
||||||
-- Screen transition
|
|
||||||
if Context.player.x > Config.screen.width - Context.player.w then
|
|
||||||
if Context.current_screen < #Context.screens then
|
|
||||||
Context.current_screen = Context.current_screen + 1
|
|
||||||
Context.player.x = 0
|
|
||||||
else
|
|
||||||
Context.player.x = Config.screen.width - Context.player.w
|
|
||||||
end
|
|
||||||
elseif Context.player.x < 0 then
|
|
||||||
if Context.current_screen > 1 then
|
|
||||||
Context.current_screen = Context.current_screen - 1
|
|
||||||
Context.player.x = Config.screen.width - Context.player.w
|
|
||||||
else
|
|
||||||
Context.player.x = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Apply gravity
|
|
||||||
Context.player.vy = Context.player.vy + Config.physics.gravity
|
|
||||||
|
|
||||||
local currentScreenData = Context.screens[Context.current_screen]
|
|
||||||
-- Collision detection with platforms
|
|
||||||
for _, p in ipairs(currentScreenData.platforms) do
|
|
||||||
if Context.player.vy > 0 and Context.player.y + Context.player.h >= p.y and Context.player.y + Context.player.h <= p.y + p.h and Context.player.x + Context.player.w > p.x and Context.player.x < p.x + p.w then
|
|
||||||
Context.player.y = p.y - Context.player.h
|
|
||||||
Context.player.vy = 0
|
|
||||||
Context.player.jumps = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Collision detection with ground
|
|
||||||
if Context.player.y + Context.player.h > Context.ground.y then
|
|
||||||
Context.player.y = Context.ground.y - Context.player.h
|
|
||||||
Context.player.vy = 0
|
|
||||||
Context.player.jumps = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Entity interaction
|
|
||||||
if Input.player_interact() then
|
|
||||||
local interaction_found = false
|
|
||||||
-- NPC interaction
|
|
||||||
for _, npc in ipairs(currentScreenData.npcs) do
|
|
||||||
if math.abs(Context.player.x - npc.x) < Config.physics.interaction_radius_npc and math.abs(Context.player.y - npc.y) < Config.physics.interaction_radius_npc then
|
|
||||||
PopupWindow.show_menu_dialog(npc, {
|
|
||||||
{label = "Talk to", action = NPC.talk_to},
|
|
||||||
{label = "Fight", action = NPC.fight},
|
|
||||||
{label = "Go back", action = NPC.go_back}
|
|
||||||
}, WINDOW_POPUP)
|
|
||||||
interaction_found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not interaction_found then
|
|
||||||
-- Item interaction
|
|
||||||
for _, item in ipairs(currentScreenData.items) do
|
|
||||||
if math.abs(Context.player.x - item.x) < Config.physics.interaction_radius_item and math.abs(Context.player.y - item.y) < Config.physics.interaction_radius_item then
|
|
||||||
PopupWindow.show_menu_dialog(item, {
|
|
||||||
{label = "Use", action = Item.use},
|
|
||||||
{label = "Look at", action = Item.look_at},
|
|
||||||
{label = "Put away", action = Item.put_away},
|
|
||||||
{label = "Go back", action = Item.go_back_from_item_dialog}
|
|
||||||
}, WINDOW_POPUP)
|
|
||||||
interaction_found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If no interaction happened, open inventory
|
|
||||||
if not interaction_found then
|
|
||||||
GameWindow.set_state(WINDOW_INVENTORY)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,32 +1,59 @@
|
|||||||
local Config = {
|
Config = {}
|
||||||
|
|
||||||
|
--- Return initial data for Config
|
||||||
|
--- @within Config
|
||||||
|
function Config.initial_data()
|
||||||
|
return {
|
||||||
screen = {
|
screen = {
|
||||||
width = 240,
|
width = 240,
|
||||||
height = 136
|
height = 136
|
||||||
},
|
},
|
||||||
colors = {
|
colors = {
|
||||||
black = 0,
|
black = 0,
|
||||||
light_grey = 13,
|
light_grey = 2,
|
||||||
dark_grey = 14,
|
dark_grey = 1,
|
||||||
green = 6,
|
red = 13,
|
||||||
npc = 8,
|
light_blue = 9,
|
||||||
item = 12 -- yellow
|
blue = 3,
|
||||||
},
|
white = 4,
|
||||||
player = {
|
item = 7,
|
||||||
w = 8,
|
meter_bg = 1,
|
||||||
h = 8,
|
transparent = 12,
|
||||||
start_x = 120,
|
orange = 7
|
||||||
start_y = 128,
|
|
||||||
sprite_id = 1
|
|
||||||
},
|
|
||||||
physics = {
|
|
||||||
gravity = 0.5,
|
|
||||||
jump_power = -5,
|
|
||||||
move_speed = 1.5,
|
|
||||||
max_jumps = 2,
|
|
||||||
interaction_radius_npc = 12,
|
|
||||||
interaction_radius_item = 8
|
|
||||||
},
|
},
|
||||||
timing = {
|
timing = {
|
||||||
splash_duration = 120
|
minigame_win_duration = 180
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Restores default configuration settings.
|
||||||
|
--- @within Config
|
||||||
|
function Config.reset()
|
||||||
|
local initial = Config.initial_data()
|
||||||
|
Config.screen = initial.screen
|
||||||
|
Config.colors = initial.colors
|
||||||
|
Config.timing = initial.timing
|
||||||
|
end
|
||||||
|
|
||||||
|
local CONFIG_SAVE_BANK = 7
|
||||||
|
local CONFIG_MAGIC_VALUE_ADDRESS = 2
|
||||||
|
local CONFIG_MAGIC_VALUE = 0xDE
|
||||||
|
|
||||||
|
--- Saves the current configuration.
|
||||||
|
--- @within Config
|
||||||
|
function Config.save()
|
||||||
|
mset(CONFIG_MAGIC_VALUE, CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Loads saved configuration.
|
||||||
|
--- @within Config
|
||||||
|
function Config.load()
|
||||||
|
if mget(CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) == CONFIG_MAGIC_VALUE then
|
||||||
|
return
|
||||||
|
else
|
||||||
|
Config.reset()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.load()
|
||||||
|
|||||||
@@ -1,432 +1,172 @@
|
|||||||
local Context = {
|
local SAVE_GAME_BANK = 6
|
||||||
active_window = WINDOW_SPLASH,
|
local SAVE_GAME_MAGIC_VALUE_ADDRESS = 0
|
||||||
inventory = {},
|
local SAVE_GAME_MAGIC_VALUE = 0xCA
|
||||||
intro = {
|
|
||||||
y = Config.screen.height,
|
--- Global game context.
|
||||||
speed = 0.5,
|
--- @section Context
|
||||||
text = "Mr. Anderson is an average\nprogrammer. His daily life\nrevolves around debugging,\npull requests, and end-of-sprint\nmeetings, all while secretly\ndreaming of being destined\nfor something more."
|
Context = {}
|
||||||
|
|
||||||
|
--- Gets initial data for Context.
|
||||||
|
--- @within Context
|
||||||
|
--- @return result table Initial context data or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * current_menu_item (number) Index of the currently selected menu item.<br/>
|
||||||
|
--- * popup (table) Popup window state. Contains: `show` (boolean) whether popup is visible, `content` (table) array of strings to display.<br/>
|
||||||
|
--- * game_in_progress (boolean) Whether a game is currently active.<br/>
|
||||||
|
--- * minigame_ddr (table) DDR minigame state (see Minigame.get_default_ddr).<br/>
|
||||||
|
--- * minigame_button_mash (table) Button mash minigame state (see Minigame.get_default_button_mash).<br/>
|
||||||
|
--- * minigame_rhythm (table) Rhythm minigame state (see Minigame.get_default_rhythm).<br/>
|
||||||
|
--- * meters (table) Meter values (see Meter.get_initial).<br/>
|
||||||
|
--- * ascension (table) Ascension meter state (see Ascension.get_initial).<br/>
|
||||||
|
--- * triggers (table) Active trigger runtime state, keyed by trigger ID.<br/>
|
||||||
|
--- * stat_screen_active (boolean) Whether the stat screen overlay is currently shown.<br/>
|
||||||
|
--- * have_met_sumphore (boolean) Whether the player has talked to the homeless guy.<br/>
|
||||||
|
--- * have_been_to_office (boolean) Whether the player has been to the office.<br/>
|
||||||
|
--- * have_done_work_today (boolean) Whether the player has done work today.<br/>
|
||||||
|
--- * toilet_meters_today_morning (boolean) Whether the home (before work) toilet meter delta was already applied this day.<br/>
|
||||||
|
--- * toilet_meters_today_evening (boolean) Whether the home (after work) toilet meter delta was already applied this day.<br/>
|
||||||
|
--- * coworker_discussion_meter_applied_today (boolean) Whether the daily coworker discussion meter roll was already applied this day.<br/>
|
||||||
|
--- * sumphore_discussion_meter_applied_today (boolean) Whether the daily sumphore discussion meter roll was already applied this day.<br/>
|
||||||
|
--- * game (table) Current game progress state. Contains: `current_screen` (string) active screen ID<br/>
|
||||||
|
function Context.initial_data()
|
||||||
|
return {
|
||||||
|
current_menu_item = 1,
|
||||||
|
test_mode = false,
|
||||||
|
mouse_trace = false,
|
||||||
|
popup = {
|
||||||
|
show = false,
|
||||||
|
content = {}
|
||||||
},
|
},
|
||||||
current_screen = 1,
|
game_in_progress = false,
|
||||||
splash_timer = Config.timing.splash_duration,
|
stat_screen_active = false,
|
||||||
dialog = {
|
minigame_ddr = {},
|
||||||
text = "",
|
minigame_button_mash = {},
|
||||||
menu_items = {},
|
minigame_rhythm = {},
|
||||||
selected_menu_item = 1,
|
meters = Meter.get_initial(),
|
||||||
active_entity = nil,
|
ascension = Ascension.get_initial(),
|
||||||
showing_description = false,
|
timer = Timer.get_initial(),
|
||||||
current_node_key = nil
|
triggers = {},
|
||||||
|
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,
|
||||||
|
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 = {},
|
||||||
|
walking_to_home_sprites = {},
|
||||||
|
game = {
|
||||||
|
current_screen = "home",
|
||||||
},
|
},
|
||||||
player = {
|
day_count = 1,
|
||||||
x = Config.player.start_x,
|
delta_time = 0,
|
||||||
y = Config.player.start_y,
|
last_frame_time = 0,
|
||||||
w = Config.player.w,
|
commute_glitch_level = 0,
|
||||||
h = Config.player.h,
|
talked_to_norman_echo = false,
|
||||||
vx = 0,
|
talked_to_true_sumphore = false,
|
||||||
vy = 0,
|
glitch = {
|
||||||
jumps = 0,
|
enabled = false,
|
||||||
sprite_id = Config.player.sprite_id
|
state = "active",
|
||||||
|
timer = 0,
|
||||||
},
|
},
|
||||||
ground = {
|
_end = {
|
||||||
x = 0,
|
state = "choice",
|
||||||
y = Config.screen.height,
|
selection = 1,
|
||||||
w = Config.screen.width,
|
|
||||||
h = 8
|
|
||||||
},
|
},
|
||||||
menu_items = {},
|
discussion = {
|
||||||
selected_menu_item = 1,
|
active = false,
|
||||||
selected_inventory_item = 1,
|
id = nil,
|
||||||
-- Screen data
|
step = 1,
|
||||||
screens = {
|
selected_answer = 1,
|
||||||
{
|
scroll_y = 0,
|
||||||
-- Screen 1
|
scroll_timer = 0,
|
||||||
name = "Screen 1",
|
auto_scroll = true,
|
||||||
platforms = {
|
return_window = nil,
|
||||||
{
|
|
||||||
x = 80,
|
|
||||||
y = 110,
|
|
||||||
w = 40,
|
|
||||||
h = 8
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
x = 160,
|
|
||||||
y = 90,
|
|
||||||
w = 40,
|
|
||||||
h = 8
|
|
||||||
}
|
}
|
||||||
},
|
end
|
||||||
npcs = {
|
|
||||||
{
|
--- Resets game context to initial state.
|
||||||
x = 180,
|
--- @within Context
|
||||||
y = 82,
|
function Context.reset()
|
||||||
name = "Trinity",
|
local initial_data = Context.initial_data()
|
||||||
sprite_id = 2,
|
for k in pairs(Context) do
|
||||||
dialog = {
|
if type(Context[k]) ~= "function" then
|
||||||
start = {
|
Context[k] = nil
|
||||||
text = "Hello, Neo.",
|
end
|
||||||
options = {
|
end
|
||||||
{label = "Who are you?", next_node = "who_are_you"},
|
|
||||||
{label = "My name is not Neo.", next_node = "not_neo"},
|
for k, v in pairs(initial_data) do
|
||||||
{label = "...", next_node = "silent"}
|
Context[k] = v
|
||||||
}
|
end
|
||||||
},
|
end
|
||||||
who_are_you = {
|
|
||||||
text = "I am Trinity. I've been looking for you.",
|
--- Starts a new game.
|
||||||
options = {
|
--- @within Context
|
||||||
{label = "The famous hacker?", next_node = "famous_hacker"},
|
function Context.new_game()
|
||||||
{label = "Why me?", next_node = "why_me"}
|
Context.reset()
|
||||||
}
|
Context.game_in_progress = true
|
||||||
},
|
MenuWindow.refresh_menu_items()
|
||||||
not_neo = {
|
Screen.get_by_id(Context.game.current_screen).init()
|
||||||
text = "I know. But you will be.",
|
MysteriousManScreen.start({
|
||||||
options = {
|
text = [[
|
||||||
{label = "What are you talking about?", next_node = "who_are_you"}
|
Norman was never a bad
|
||||||
}
|
|
||||||
},
|
simulation engineer,
|
||||||
silent = {
|
|
||||||
text = "You're not much of a talker, are you?",
|
but
|
||||||
options = {
|
|
||||||
{label = "I guess not.", next_node = "dialog_end"}
|
we need to be careful
|
||||||
}
|
|
||||||
},
|
letting him improve.
|
||||||
famous_hacker = {
|
|
||||||
text = "The one and only.",
|
We need to distract him.
|
||||||
options = {
|
]],
|
||||||
{label = "Wow.", next_node = "dialog_end"}
|
on_text_complete = function()
|
||||||
}
|
Audio.sfx_alarm()
|
||||||
},
|
Context.home_norman_visible = false
|
||||||
why_me = {
|
Util.go_to_screen_by_id("home")
|
||||||
text = "Morpheus believes you are The One.",
|
MinigameButtonMashWindow.start("game", {
|
||||||
options = {
|
focus_center_x = (Config.screen.width / 2) - 22,
|
||||||
{label = "The One?", next_node = "the_one"}
|
focus_center_y = (Config.screen.height / 2) - 18,
|
||||||
}
|
focus_initial_radius = 0,
|
||||||
},
|
target_points = 100,
|
||||||
the_one = {
|
instruction_text = "Wake up Norman!",
|
||||||
text = "The one who will save us all.",
|
show_progress_text = false,
|
||||||
options = {
|
meter_on_complete = Meter.apply_wakeup_reward,
|
||||||
{label = "I'm just a programmer.", next_node = "dialog_end"}
|
on_win = function()
|
||||||
}
|
Audio.music_play_room_work()
|
||||||
},
|
Meter.show()
|
||||||
dialog_end = {
|
Window.set_current("game")
|
||||||
text = "We'll talk later.",
|
end,
|
||||||
options = {} -- No options, ends conversation
|
})
|
||||||
}
|
end,
|
||||||
}
|
})
|
||||||
},
|
end
|
||||||
{
|
|
||||||
x = 90,
|
--- Saves the current game state.
|
||||||
y = 102,
|
--- @within Context
|
||||||
name = "Oracle",
|
function Context.save_game()
|
||||||
sprite_id = 3,
|
if not Context.game_in_progress then return end
|
||||||
dialog = {
|
mset(SAVE_GAME_MAGIC_VALUE, SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK)
|
||||||
start = {
|
end
|
||||||
text = "I know what you're thinking. 'Am I in the right place?'",
|
|
||||||
options = {
|
--- Loads a saved game state.
|
||||||
{label = "Who are you?", next_node = "who_are_you"},
|
--- @within Context
|
||||||
{label = "I guess I am.", next_node = "you_are"}
|
function Context.load_game()
|
||||||
}
|
if mget(SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK) ~= SAVE_GAME_MAGIC_VALUE then
|
||||||
},
|
Context.new_game()
|
||||||
who_are_you = {
|
return
|
||||||
text = "I'm the Oracle. And you're right on time. Want a cookie?",
|
end
|
||||||
options = {
|
Context.reset()
|
||||||
{label = "Sure.", next_node = "cookie"},
|
Context.game_in_progress = true
|
||||||
{label = "No, thank you.", next_node = "no_cookie"}
|
MenuWindow.refresh_menu_items()
|
||||||
}
|
Screen.get_by_id(Context.game.current_screen).init()
|
||||||
},
|
end
|
||||||
you_are = {
|
|
||||||
text = "Of course you are. Sooner or later, everyone comes to see me. Want a cookie?",
|
|
||||||
options = {
|
|
||||||
{label = "Yes, please.", next_node = "cookie"},
|
|
||||||
{label = "I'm good.", next_node = "no_cookie"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cookie = {
|
|
||||||
text = "Here you go. Now, what's really on your mind?",
|
|
||||||
options = {
|
|
||||||
{label = "Am I The One?", next_node = "the_one"},
|
|
||||||
{label = "What is the Matrix?", next_node = "the_matrix"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
no_cookie = {
|
|
||||||
text = "Suit yourself. Now, what's troubling you?",
|
|
||||||
options = {
|
|
||||||
{label = "Am I The One?", next_node = "the_one"},
|
|
||||||
{label = "What is the Matrix?", next_node = "the_matrix"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
the_one = {
|
|
||||||
text = "Being The One is just like being in love. No one can tell you you're in love, you just know it. Through and through. Balls to bones.",
|
|
||||||
options = {
|
|
||||||
{label = "So I'm not?", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
the_matrix = {
|
|
||||||
text = "The Matrix is a system, Neo. That system is our enemy. But when you're inside, you look around, what do you see? The very minds of the people we are trying to save.",
|
|
||||||
options = {
|
|
||||||
{label = "I see.", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dialog_end = {
|
|
||||||
text = "You have to understand, most of these people are not ready to be unplugged.",
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items = {
|
|
||||||
{
|
|
||||||
x = 100,
|
|
||||||
y = 128,
|
|
||||||
w = 8,
|
|
||||||
h = 8,
|
|
||||||
name = "Key",
|
|
||||||
sprite_id = 4,
|
|
||||||
desc = "A rusty old key. It might open something."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
-- Screen 2
|
|
||||||
name = "Screen 2",
|
|
||||||
platforms = {
|
|
||||||
{
|
|
||||||
x = 30,
|
|
||||||
y = 100,
|
|
||||||
w = 50,
|
|
||||||
h = 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 100,
|
|
||||||
y = 80,
|
|
||||||
w = 50,
|
|
||||||
h = 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 170,
|
|
||||||
y = 60,
|
|
||||||
w = 50,
|
|
||||||
h = 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
npcs = {
|
|
||||||
{
|
|
||||||
x = 120,
|
|
||||||
y = 72,
|
|
||||||
name = "Morpheus",
|
|
||||||
sprite_id = 5,
|
|
||||||
dialog = {
|
|
||||||
start = {
|
|
||||||
text = "At last. Welcome, Neo. As you no doubt have guessed, I am Morpheus.",
|
|
||||||
options = {
|
|
||||||
{label = "It's an honor to meet you.", next_node = "honor"},
|
|
||||||
{label = "You've been looking for me.", next_node = "looking_for_me"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
honor = {
|
|
||||||
text = "No, the honor is mine.",
|
|
||||||
options = {
|
|
||||||
{label = "What is this place?", next_node = "what_is_this_place"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
looking_for_me = {
|
|
||||||
text = "I have. For some time.",
|
|
||||||
options = {
|
|
||||||
{label = "What is this place?", next_node = "what_is_this_place"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
what_is_this_place = {
|
|
||||||
text = "This is the construct. It's our loading program. We can load anything from clothing, to equipment, weapons, training simulations. Anything we need.",
|
|
||||||
options = {
|
|
||||||
{label = "Right.", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dialog_end = {
|
|
||||||
text = "I've been waiting for you, Neo. We have much to discuss.",
|
|
||||||
options = {} -- Ends conversation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 40,
|
|
||||||
y = 92,
|
|
||||||
name = "Tank",
|
|
||||||
sprite_id = 6,
|
|
||||||
dialog = {
|
|
||||||
start = {
|
|
||||||
text = "Hey, Neo! Welcome to the construct. I'm Tank.",
|
|
||||||
options = {
|
|
||||||
{label = "Good to meet you.", next_node = "good_to_meet_you"},
|
|
||||||
{label = "This place is incredible.", next_node = "incredible"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
good_to_meet_you = {
|
|
||||||
text = "You too! We've been waiting for you. Need anything? Training? Weapons?",
|
|
||||||
options = {
|
|
||||||
{label = "Training?", next_node = "training"},
|
|
||||||
{label = "I'm good for now.", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
incredible = {
|
|
||||||
text = "Isn't it? The boss's design. We can load anything we need. What do you want to learn?",
|
|
||||||
options = {
|
|
||||||
{label = "Show me.", next_node = "training"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
training = {
|
|
||||||
text = "Jujitsu? Kung Fu? How about... all of them?",
|
|
||||||
options = {
|
|
||||||
{label = "All of them.", next_node = "all_of_them"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
all_of_them = {
|
|
||||||
text = "Operator, load the combat training program.",
|
|
||||||
options = {
|
|
||||||
{label = "...", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dialog_end = {
|
|
||||||
text = "Just holler if you need anything. Anything at all.",
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items = {
|
|
||||||
{
|
|
||||||
x = 180,
|
|
||||||
y = 52,
|
|
||||||
w = 8,
|
|
||||||
h = 8,
|
|
||||||
name = "Potion",
|
|
||||||
sprite_id = 7,
|
|
||||||
desc = "A glowing red potion. It looks potent."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
-- Screen 3
|
|
||||||
name = "Screen 3",
|
|
||||||
platforms = {
|
|
||||||
{
|
|
||||||
x = 50,
|
|
||||||
y = 110,
|
|
||||||
w = 30,
|
|
||||||
h = 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 100,
|
|
||||||
y = 90,
|
|
||||||
w = 30,
|
|
||||||
h = 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 150,
|
|
||||||
y = 70,
|
|
||||||
w = 30,
|
|
||||||
h = 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 200,
|
|
||||||
y = 50,
|
|
||||||
w = 30,
|
|
||||||
h = 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
npcs = {
|
|
||||||
{
|
|
||||||
x = 210,
|
|
||||||
y = 42,
|
|
||||||
name = "Agent Smith",
|
|
||||||
sprite_id = 8,
|
|
||||||
dialog = {
|
|
||||||
start = {
|
|
||||||
text = "Mr. Anderson. We've been expecting you.",
|
|
||||||
options = {
|
|
||||||
{label = "My name is Neo.", next_node = "name_is_neo"},
|
|
||||||
{label = "...", next_node = "silent"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
name_is_neo = {
|
|
||||||
text = "Whatever you say. You're here for a reason.",
|
|
||||||
options = {
|
|
||||||
{label = "What reason?", next_node = "what_reason"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
silent = {
|
|
||||||
text = "The silent type. It doesn't matter. You are an anomaly.",
|
|
||||||
options = {
|
|
||||||
{label = "What do you want?", next_node = "what_reason"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
what_reason = {
|
|
||||||
text = "To be deleted. The system has no place for your kind.",
|
|
||||||
options = {
|
|
||||||
{label = "I won't let you.", next_node = "wont_let_you"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wont_let_you = {
|
|
||||||
text = "You hear that, Mr. Anderson? That is the sound of inevitability.",
|
|
||||||
options = {
|
|
||||||
{label = "...", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dialog_end = {
|
|
||||||
text = "It is purpose that created us. Purpose that connects us. Purpose that pulls us. That guides us. That drives us. It is purpose that defines. Purpose that binds us.",
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x = 160,
|
|
||||||
y = 62,
|
|
||||||
name = "Cypher",
|
|
||||||
sprite_id = 9,
|
|
||||||
dialog = {
|
|
||||||
start = {
|
|
||||||
text = "Well, well. The new messiah. Welcome to the real world.",
|
|
||||||
options = {
|
|
||||||
{label = "You don't seem happy.", next_node = "not_happy"},
|
|
||||||
{label = "...", next_node = "silent"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
not_happy = {
|
|
||||||
text = "Happy? Ignorance is bliss, Neo. We've been fighting this war for years. For what?",
|
|
||||||
options = {
|
|
||||||
{label = "For freedom.", next_node = "freedom"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
silent = {
|
|
||||||
text = "Not a talker, huh? Smart. Less to regret later. Want a drink?",
|
|
||||||
options = {
|
|
||||||
{label = "Sure.", next_node = "drink"},
|
|
||||||
{label = "No thanks.", next_node = "no_drink"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drink = {
|
|
||||||
text = "Good stuff. The little things you miss, you know? Like a good steak.",
|
|
||||||
options = {
|
|
||||||
{label = "I guess.", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
no_drink = {
|
|
||||||
text = "Your loss. More for me.",
|
|
||||||
options = {
|
|
||||||
{label = "...", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
freedom = {
|
|
||||||
text = "Freedom... right. If Morpheus told you you could fly, would you believe him?",
|
|
||||||
options = {
|
|
||||||
{label = "He's our leader.", next_node = "dialog_end"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dialog_end = {
|
|
||||||
text = "Just be careful who you trust.",
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
22
inc/init/init.module.lua
Normal file
22
inc/init/init.module.lua
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Window = {}
|
||||||
|
Util = {}
|
||||||
|
Meter = {}
|
||||||
|
Minigame = {}
|
||||||
|
Decision = {}
|
||||||
|
Screen = {}
|
||||||
|
Map = {}
|
||||||
|
UI = {}
|
||||||
|
Print = {}
|
||||||
|
Input = {}
|
||||||
|
Mouse = {}
|
||||||
|
Sprite = {}
|
||||||
|
Audio = {}
|
||||||
|
Focus = {}
|
||||||
|
Day = {}
|
||||||
|
Timer = {}
|
||||||
|
Trigger = {}
|
||||||
|
Discussion = {}
|
||||||
|
RLE = {}
|
||||||
|
AsciiArt = {}
|
||||||
|
Ascension = {}
|
||||||
|
MysteriousManScreen = {}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
local SplashWindow = {}
|
|
||||||
local IntroWindow = {}
|
|
||||||
local MenuWindow = {}
|
|
||||||
local GameWindow = {}
|
|
||||||
local PopupWindow = {}
|
|
||||||
local InventoryWindow = {}
|
|
||||||
local ConfigurationWindow = {}
|
|
||||||
|
|
||||||
local UI = {}
|
|
||||||
local Input = {}
|
|
||||||
local NPC = {}
|
|
||||||
local Item = {}
|
|
||||||
local Player = {}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
local WINDOW_SPLASH = 0
|
|
||||||
local WINDOW_INTRO = 1
|
|
||||||
local WINDOW_MENU = 2
|
|
||||||
local WINDOW_GAME = 3
|
|
||||||
local WINDOW_POPUP = 4
|
|
||||||
local WINDOW_INVENTORY = 5
|
|
||||||
local WINDOW_INVENTORY_ACTION = 6
|
|
||||||
local WINDOW_CONFIGURATION = 7
|
|
||||||
181
inc/logic/logic.ascension.lua
Normal file
181
inc/logic/logic.ascension.lua
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
--- @section Ascension
|
||||||
|
local ASCENSION_MAX_LEVEL = 9
|
||||||
|
local ASCENSION_WORD = "ASCENSION"
|
||||||
|
local _increased_this_cycle = false
|
||||||
|
|
||||||
|
local _flash_active = false
|
||||||
|
local _flash_timer = 0
|
||||||
|
local _flash_total = 0
|
||||||
|
local FLASH_DURATION = 120
|
||||||
|
|
||||||
|
local _fade_active = false
|
||||||
|
local _fade_timer = 0
|
||||||
|
local FADE_DURATION = 120
|
||||||
|
local FADE_COLORS = nil
|
||||||
|
|
||||||
|
--- Gets initial ascension state.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return result table Initial ascension state. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * level (number) Current ascension level (0-9).
|
||||||
|
function Ascension.get_initial()
|
||||||
|
_increased_this_cycle = false
|
||||||
|
return {
|
||||||
|
level = 0,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the current ascension level.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return number The current ascension level (0-9).
|
||||||
|
function Ascension.get_level()
|
||||||
|
if not Context or not Context.ascension then return 0 end
|
||||||
|
return Context.ascension.level
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the maximum ascension level.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return number The maximum ascension level.
|
||||||
|
function Ascension.get_max_level()
|
||||||
|
return ASCENSION_MAX_LEVEL
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Increases the ascension level by 1, clamped to the max.
|
||||||
|
--- @within Ascension
|
||||||
|
function Ascension.increase()
|
||||||
|
if not Context or not Context.ascension then return end
|
||||||
|
Context.ascension.level = math.min(ASCENSION_MAX_LEVEL, Context.ascension.level + 1)
|
||||||
|
_increased_this_cycle = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns true if ascension was incremented since the last consume call.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return boolean Whether ascension increased this cycle.
|
||||||
|
function Ascension.did_increase()
|
||||||
|
return _increased_this_cycle
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Consumes the increase flag, returning its value and resetting it.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return boolean Whether ascension had increased this cycle.
|
||||||
|
function Ascension.consume_increase()
|
||||||
|
local result = _increased_this_cycle
|
||||||
|
_increased_this_cycle = false
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns true when the ascension meter is fully complete (level 10).
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return boolean Whether the cycle can be broken.
|
||||||
|
function Ascension.is_complete()
|
||||||
|
return Ascension.get_level() >= ASCENSION_MAX_LEVEL
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws the ascension meter as individual letters of "ASCENSION".
|
||||||
|
--- Each letter lights up per level. Drawn beneath existing meter bars.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @param x number Left x position.
|
||||||
|
--- @param y number Top y position.
|
||||||
|
--- @param options table Optional overrides: lit_color, dim_color, spacing.
|
||||||
|
function Ascension.draw(x, y, options)
|
||||||
|
if not Context or not Context.ascension then return end
|
||||||
|
options = options or {}
|
||||||
|
local level = Context.ascension.level
|
||||||
|
if level < 1 then return end
|
||||||
|
local lit_color = options.lit_color or Config.colors.white
|
||||||
|
local spacing = options.spacing or 5
|
||||||
|
|
||||||
|
for i = 1, level do
|
||||||
|
local ch = ASCENSION_WORD:sub(i, i)
|
||||||
|
local color
|
||||||
|
if i == level and _fade_active then
|
||||||
|
color = Ascension.get_fade_color()
|
||||||
|
else
|
||||||
|
color = lit_color
|
||||||
|
end
|
||||||
|
Print.text_contour(ch, x + (i - 1) * spacing, y, color, false, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the current fade-in color based on progress through the palette.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return number The palette color index for the current fade step.
|
||||||
|
function Ascension.get_fade_color()
|
||||||
|
if not FADE_COLORS then
|
||||||
|
FADE_COLORS = {
|
||||||
|
Config.colors.black,
|
||||||
|
Config.colors.dark_grey,
|
||||||
|
Config.colors.light_grey,
|
||||||
|
Config.colors.white,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if not _fade_active then return Config.colors.white end
|
||||||
|
local progress = math.min(_fade_timer / FADE_DURATION, 1)
|
||||||
|
local idx = math.floor(progress * (#FADE_COLORS - 1)) + 1
|
||||||
|
return FADE_COLORS[idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts the fade-in effect for the most recently gained letter.
|
||||||
|
--- @within Ascension
|
||||||
|
function Ascension.start_fade()
|
||||||
|
_fade_active = true
|
||||||
|
_fade_timer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts the ascension flash effect.
|
||||||
|
--- @within Ascension
|
||||||
|
function Ascension.start_flash()
|
||||||
|
_flash_active = true
|
||||||
|
_flash_timer = 0
|
||||||
|
_flash_total = FLASH_DURATION
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates and draws the ascension flash overlay.
|
||||||
|
--- Call once per frame from the main loop.
|
||||||
|
--- @within Ascension
|
||||||
|
function Ascension.draw_flash()
|
||||||
|
if not _flash_active then return end
|
||||||
|
_flash_timer = _flash_timer + 1
|
||||||
|
|
||||||
|
local sw = Config.screen.width
|
||||||
|
local sh = Config.screen.height
|
||||||
|
|
||||||
|
local progress = _flash_timer / FLASH_DURATION
|
||||||
|
local pulse = math.abs(math.sin(progress * math.pi * 6))
|
||||||
|
local flash_color = (pulse > 0.5) and Config.colors.white or Config.colors.light_grey
|
||||||
|
rect(0, 0, sw, sh, flash_color)
|
||||||
|
|
||||||
|
local cx = math.floor(sw / 2)
|
||||||
|
local cy = math.floor(sh / 2)
|
||||||
|
Print.text_center("Level Up!", cx, cy - 12, Config.colors.black, false, 2)
|
||||||
|
Print.text_center("One step closer to ascension", cx, cy + 6, Config.colors.black, false, 1)
|
||||||
|
|
||||||
|
if _flash_timer >= _flash_total then
|
||||||
|
_flash_active = false
|
||||||
|
Ascension.start_fade()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates the fade-in timer. Call once per frame from the main loop.
|
||||||
|
--- @within Ascension
|
||||||
|
function Ascension.update_fade()
|
||||||
|
if not _fade_active then return end
|
||||||
|
_fade_timer = _fade_timer + 1
|
||||||
|
if _fade_timer >= FADE_DURATION then
|
||||||
|
_fade_active = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns whether a flash effect is currently active.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return boolean Whether the flash is playing.
|
||||||
|
function Ascension.is_flashing()
|
||||||
|
return _flash_active
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns whether the fade-in effect is currently active.
|
||||||
|
--- @within Ascension
|
||||||
|
--- @return boolean Whether the letter fade-in is playing.
|
||||||
|
function Ascension.is_fading()
|
||||||
|
return _fade_active
|
||||||
|
end
|
||||||
60
inc/logic/logic.codegenerator.lua
Normal file
60
inc/logic/logic.codegenerator.lua
Normal file
@@ -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
|
||||||
136
inc/logic/logic.commute_glitch.lua
Normal file
136
inc/logic/logic.commute_glitch.lua
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
--- @section CommuteGlitch
|
||||||
|
CommuteGlitch = {}
|
||||||
|
|
||||||
|
--- Gets the current commute glitch level.
|
||||||
|
--- At ascension level 8+, always returns 7 (max) regardless of stored value.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @return number Current glitch level (0-7).
|
||||||
|
function CommuteGlitch.get_level()
|
||||||
|
if Ascension.get_level() >= 8 then return 7 end
|
||||||
|
return Context and (Context.commute_glitch_level or 0) or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Increments the glitch counter. Called on each office screen init at asc level 7.
|
||||||
|
--- Caps at 6; use enter_truth() to reach 7.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
function CommuteGlitch.increment()
|
||||||
|
if not Context then return end
|
||||||
|
if Context.commute_glitch_level >= 7 then return end
|
||||||
|
Context.commute_glitch_level = math.min(6, (Context.commute_glitch_level or 0) + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Resets the glitch counter and hides the glitch overlay. Called when going home.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
function CommuteGlitch.reset()
|
||||||
|
if not Context then return end
|
||||||
|
Context.commute_glitch_level = 0
|
||||||
|
Glitch.hide()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Jumps to glitch level 7 (full corruption). Called by go_to_truth.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
function CommuteGlitch.enter_truth()
|
||||||
|
if not Context then return end
|
||||||
|
Context.commute_glitch_level = 7
|
||||||
|
Glitch.show()
|
||||||
|
Ascension.start_flash()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns true when ascension level is 7 or 8 (ASCENSIO/N steps active).
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @return boolean Whether the commute glitch system is active.
|
||||||
|
function CommuteGlitch.is_active()
|
||||||
|
return Ascension.get_level() == 7
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns true when commute glitch is at max level (7).
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @return boolean Whether the commute glitch is at max level.
|
||||||
|
function CommuteGlitch.is_max()
|
||||||
|
return CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 7
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the music playback speed for the current glitch level.
|
||||||
|
--- TIC-80 default speed is 6; each step past 1 adds 2.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @return number Speed value for music().
|
||||||
|
function CommuteGlitch.music_speed()
|
||||||
|
local level = CommuteGlitch.get_level()
|
||||||
|
if level <= 1 then return 6 end
|
||||||
|
return 6 + (level - 1) * 2
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a corrupted copy of a sprite drawable list.
|
||||||
|
--- Applies flip_y and norman_echo id replacements based on glitch level.
|
||||||
|
--- Entries marked norman_echo should be drawn via draw_sprite_list for palette change handling.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @param list table Drawable sprite list from Sprite.list_randomize.
|
||||||
|
--- @return table Corrupted copy of the list.
|
||||||
|
function CommuteGlitch.corrupt_sprite_list(list)
|
||||||
|
local level = CommuteGlitch.get_level()
|
||||||
|
if level < 3 or not list then return list end
|
||||||
|
local result = {}
|
||||||
|
for i, entry in ipairs(list) do
|
||||||
|
local e = {}
|
||||||
|
for k, v in pairs(entry) do e[k] = v end
|
||||||
|
if level >= 7 then
|
||||||
|
e.id = "norman_echo"
|
||||||
|
else
|
||||||
|
local n_flip = (level >= 5) and 2 or 1
|
||||||
|
local n_echo = (level >= 5) and 2 or (level >= 4) and 1 or 0
|
||||||
|
if i <= n_flip then e.flip_y = 1 end
|
||||||
|
if i > n_flip and i <= n_flip + n_echo then e.id = "norman_echo" end
|
||||||
|
end
|
||||||
|
table.insert(result, e)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Palette indices for Norman echo color remap.
|
||||||
|
-- Implementer: pick ECHO_SRC as one of Norman's main body colors and ECHO_DST
|
||||||
|
-- as a contrasting or wrong palette color by inspecting the sprite sheet.
|
||||||
|
local ECHO_SRC = 4
|
||||||
|
local ECHO_DST = 14
|
||||||
|
|
||||||
|
-- Base nibble address of the PALETTE MAP in VRAM.
|
||||||
|
local PALETTE_MAP_ADDR = 0x03FF0 * 2
|
||||||
|
|
||||||
|
--- Draws a sprite list, applying a PALETTE MAP remap for norman_echo entries.
|
||||||
|
--- Uses poke4 to remap ECHO_SRC → ECHO_DST before drawing echoes, then restores.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
--- @param list table Drawable sprite list (may contain mixed normal and echo entries).
|
||||||
|
function CommuteGlitch.draw_sprite_list(list)
|
||||||
|
if not list then return end
|
||||||
|
local normal, echo = {}, {}
|
||||||
|
for _, entry in ipairs(list) do
|
||||||
|
if entry.id == "norman_echo" then
|
||||||
|
table.insert(echo, entry)
|
||||||
|
else
|
||||||
|
table.insert(normal, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #normal > 0 then
|
||||||
|
Sprite.draw_list(normal)
|
||||||
|
end
|
||||||
|
if #echo > 0 then
|
||||||
|
poke4(PALETTE_MAP_ADDR + ECHO_SRC, ECHO_DST)
|
||||||
|
Sprite.draw_list(echo)
|
||||||
|
poke4(PALETTE_MAP_ADDR + ECHO_SRC, ECHO_SRC)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local _flicker_tick = 0
|
||||||
|
|
||||||
|
--- Draws a random tile-flicker effect over the background (glitch level 7).
|
||||||
|
--- Every 3 frames draws 6 random 8x8 rects in random palette colors.
|
||||||
|
--- @within CommuteGlitch
|
||||||
|
function CommuteGlitch.draw_background_flicker()
|
||||||
|
_flicker_tick = (_flicker_tick + 1) % 3
|
||||||
|
if _flicker_tick ~= 0 then return end
|
||||||
|
for _ = 1, 6 do
|
||||||
|
local tx = math.random(0, math.floor(Config.screen.width / 8) - 1) * 8
|
||||||
|
local ty = math.random(0, math.floor(Config.screen.height / 8) - 1) * 8
|
||||||
|
local color = math.random(0, 15)
|
||||||
|
rect(tx, ty, 8, 8, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
48
inc/logic/logic.day.lua
Normal file
48
inc/logic/logic.day.lua
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
--- @section Day
|
||||||
|
local _day_increase_handlers = {}
|
||||||
|
|
||||||
|
--- Increases the day count and triggers registered handlers.
|
||||||
|
--- @within Day
|
||||||
|
function Day.increase()
|
||||||
|
Context.day_count = Context.day_count + 1
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers a handler to be called when the day increases.
|
||||||
|
--- @within Day
|
||||||
|
--- @param handler function The function to call when the day increases.
|
||||||
|
function Day.register_handler(handler)
|
||||||
|
table.insert(_day_increase_handlers, handler)
|
||||||
|
end
|
||||||
|
|
||||||
|
Day.register_handler(function()
|
||||||
|
local m = Context.meters
|
||||||
|
m.ism = math.max(0, m.ism - METER_DECAY_PER_DAY)
|
||||||
|
m.wpm = math.max(0, m.wpm - METER_DECAY_PER_DAY)
|
||||||
|
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
|
||||||
|
Context.glitch_conversation_done_today = false
|
||||||
|
Context.fast_food_eaten_today = 0
|
||||||
|
end)
|
||||||
|
|
||||||
|
Day.register_handler(function()
|
||||||
|
if Context.should_ascend then
|
||||||
|
Ascension.increase()
|
||||||
|
end
|
||||||
|
Context.should_ascend = false
|
||||||
|
end)
|
||||||
106
inc/logic/logic.discussion.lua
Normal file
106
inc/logic/logic.discussion.lua
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
--- @section Discussion
|
||||||
|
local _discussions = {}
|
||||||
|
|
||||||
|
--- Registers a discussion definition.
|
||||||
|
--- @within Discussion
|
||||||
|
--- @param discussion table The discussion data table.
|
||||||
|
--- @param discussion.id string Unique discussion identifier.
|
||||||
|
--- @param discussion.steps table Array of step tables, each with `question` (string) and `answers` (array of {label, next_step} tables).
|
||||||
|
--- @param[opt] discussion.on_end function Called when the discussion ends. Defaults to noop.
|
||||||
|
function Discussion.register(discussion)
|
||||||
|
if not discussion or not discussion.id then
|
||||||
|
trace("Error: Invalid discussion registered (missing id)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not discussion.steps or #discussion.steps == 0 then
|
||||||
|
trace("Error: Discussion '" .. discussion.id .. "' has no steps!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not discussion.on_end then
|
||||||
|
discussion.on_end = function() end
|
||||||
|
end
|
||||||
|
if _discussions[discussion.id] then
|
||||||
|
trace("Warning: Overwriting discussion with id: " .. discussion.id)
|
||||||
|
end
|
||||||
|
_discussions[discussion.id] = discussion
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets a discussion by ID.
|
||||||
|
--- @within Discussion
|
||||||
|
--- @param id string The discussion ID.
|
||||||
|
--- @return table|nil result The discussion table or nil.
|
||||||
|
function Discussion.get_by_id(id)
|
||||||
|
return _discussions[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts a discussion, switching to the discussion window.
|
||||||
|
--- @within Discussion
|
||||||
|
--- @param id string The discussion ID to start.
|
||||||
|
--- @param return_window string The window ID to return to after the discussion.
|
||||||
|
--- @param[opt] start_step number The step index to start from (defaults to 1).
|
||||||
|
function Discussion.start(id, return_window, start_step)
|
||||||
|
local discussion = _discussions[id]
|
||||||
|
if not discussion then
|
||||||
|
trace("Error: Discussion not found: " .. tostring(id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local step = start_step or 1
|
||||||
|
if not discussion.steps[step] then step = 1 end
|
||||||
|
Context.discussion.active = true
|
||||||
|
Context.discussion.id = id
|
||||||
|
Context.discussion.step = step
|
||||||
|
Context.discussion.selected_answer = 1
|
||||||
|
Context.discussion.scroll_y = 0
|
||||||
|
Context.discussion.scroll_timer = 0
|
||||||
|
Context.discussion.auto_scroll = true
|
||||||
|
Context.discussion.return_window = return_window or "game"
|
||||||
|
Meter.hide()
|
||||||
|
Window.set_current("discussion")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the current step data for the active discussion.
|
||||||
|
--- @within Discussion
|
||||||
|
--- @return table|nil result The current step table or nil.
|
||||||
|
function Discussion.get_current_step()
|
||||||
|
if not Context.discussion.active or not Context.discussion.id then return nil end
|
||||||
|
local discussion = _discussions[Context.discussion.id]
|
||||||
|
if not discussion then return nil end
|
||||||
|
return discussion.steps[Context.discussion.step]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Advances to a specific step or ends the discussion.
|
||||||
|
--- @within Discussion
|
||||||
|
--- @param next_step number|nil The step index to go to, or nil to end.
|
||||||
|
function Discussion.go_to_step(next_step)
|
||||||
|
if not next_step then
|
||||||
|
Discussion.finish()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local discussion = _discussions[Context.discussion.id]
|
||||||
|
if not discussion or not discussion.steps[next_step] then
|
||||||
|
Discussion.finish()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Context.discussion.step = next_step
|
||||||
|
Context.discussion.selected_answer = 1
|
||||||
|
Context.discussion.scroll_y = 0
|
||||||
|
Context.discussion.scroll_timer = 0
|
||||||
|
Context.discussion.auto_scroll = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Ends the active discussion and returns to the previous window.
|
||||||
|
--- @within Discussion
|
||||||
|
function Discussion.finish()
|
||||||
|
local discussion = _discussions[Context.discussion.id]
|
||||||
|
local return_window = Context.discussion.return_window or "game"
|
||||||
|
Context.discussion.active = false
|
||||||
|
Context.discussion.id = nil
|
||||||
|
Context.discussion.scroll_y = 0
|
||||||
|
Context.discussion.scroll_timer = 0
|
||||||
|
Context.discussion.auto_scroll = true
|
||||||
|
Meter.show()
|
||||||
|
if discussion and discussion.on_end then
|
||||||
|
discussion.on_end()
|
||||||
|
end
|
||||||
|
Window.set_current(return_window)
|
||||||
|
end
|
||||||
166
inc/logic/logic.focus.lua
Normal file
166
inc/logic/logic.focus.lua
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
--- @section Focus
|
||||||
|
|
||||||
|
local FOCUS_DEFAULT_SPEED = 5
|
||||||
|
|
||||||
|
local active = false
|
||||||
|
local closing = false
|
||||||
|
local driven = false
|
||||||
|
local center_x = 0
|
||||||
|
local center_y = 0
|
||||||
|
local radius = 0
|
||||||
|
local speed = FOCUS_DEFAULT_SPEED
|
||||||
|
local on_complete = nil
|
||||||
|
local driven_initial_r = 0
|
||||||
|
local driven_max_r = 0
|
||||||
|
|
||||||
|
local function max_radius(cx, cy)
|
||||||
|
local dx = math.max(cx, Config.screen.width - cx)
|
||||||
|
local dy = math.max(cy, Config.screen.height - cy)
|
||||||
|
return math.sqrt(dx * dx + dy * dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts a focus overlay that reveals content through an expanding circle.
|
||||||
|
--- @within Focus
|
||||||
|
--- @param cx number The x-coordinate of the circle center.
|
||||||
|
--- @param cy number The y-coordinate of the circle center.
|
||||||
|
--- @param[opt] params table Optional parameters: `speed` (number) expansion rate in pixels/frame, `initial_radius` (number) starting radius in pixels (default 0), `on_complete` (function) callback when overlay disperses.
|
||||||
|
function Focus.start(cx, cy, params)
|
||||||
|
params = params or {}
|
||||||
|
active = true
|
||||||
|
closing = false
|
||||||
|
driven = false
|
||||||
|
center_x = cx
|
||||||
|
center_y = cy
|
||||||
|
radius = params.initial_radius or 0
|
||||||
|
speed = params.speed or FOCUS_DEFAULT_SPEED
|
||||||
|
on_complete = params.on_complete
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts a closing focus overlay that hides content by shrinking the visible circle.
|
||||||
|
--- @within Focus
|
||||||
|
--- @param cx number The x-coordinate of the circle center.
|
||||||
|
--- @param cy number The y-coordinate of the circle center.
|
||||||
|
--- @param[opt] params table Optional parameters: `speed` (number) shrink rate in pixels/frame, `on_complete` (function) callback when screen is fully covered.
|
||||||
|
function Focus.close(cx, cy, params)
|
||||||
|
params = params or {}
|
||||||
|
active = true
|
||||||
|
closing = true
|
||||||
|
driven = false
|
||||||
|
center_x = cx
|
||||||
|
center_y = cy
|
||||||
|
radius = max_radius(cx, cy)
|
||||||
|
speed = params.speed or FOCUS_DEFAULT_SPEED
|
||||||
|
on_complete = params.on_complete
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Starts a driven focus overlay whose radius is controlled externally via Focus.set_percentage().
|
||||||
|
--- The radius maps linearly from initial_radius (at 0%) to the screen corner distance (at 100%).
|
||||||
|
--- @within Focus
|
||||||
|
--- @param cx number The x-coordinate of the circle center.
|
||||||
|
--- @param cy number The y-coordinate of the circle center.
|
||||||
|
--- @param[opt] params table Optional parameters: `initial_radius` (number) radius at 0% (default 0).
|
||||||
|
function Focus.start_driven(cx, cy, params)
|
||||||
|
params = params or {}
|
||||||
|
active = true
|
||||||
|
closing = false
|
||||||
|
driven = true
|
||||||
|
center_x = cx
|
||||||
|
center_y = cy
|
||||||
|
driven_initial_r = params.initial_radius or 0
|
||||||
|
driven_max_r = max_radius(cx, cy)
|
||||||
|
radius = driven_initial_r
|
||||||
|
on_complete = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the visible radius as a percentage of the full screen extent.
|
||||||
|
--- Only has effect when the overlay is in driven mode (started via Focus.start_driven).
|
||||||
|
--- @within Focus
|
||||||
|
--- @param pct number A value from 0 to 1 (0 = initial_radius, 1 = full screen).
|
||||||
|
function Focus.set_percentage(pct)
|
||||||
|
if not driven then return end
|
||||||
|
radius = driven_initial_r + pct * (driven_max_r - driven_initial_r)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks whether the focus overlay is currently active.
|
||||||
|
--- @within Focus
|
||||||
|
--- @return boolean Whether the focus overlay is active.
|
||||||
|
function Focus.is_active()
|
||||||
|
return active
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the focus overlay immediately.
|
||||||
|
--- @within Focus
|
||||||
|
function Focus.stop()
|
||||||
|
active = false
|
||||||
|
closing = false
|
||||||
|
driven = false
|
||||||
|
radius = 0
|
||||||
|
on_complete = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates the focus overlay animation. No-op in driven mode.
|
||||||
|
--- @within Focus
|
||||||
|
function Focus.update()
|
||||||
|
if not active then return end
|
||||||
|
if driven then return end
|
||||||
|
|
||||||
|
if closing then
|
||||||
|
radius = radius - speed
|
||||||
|
if radius <= 0 then
|
||||||
|
local cb = on_complete
|
||||||
|
Focus.stop()
|
||||||
|
if cb then cb() end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
radius = radius + speed
|
||||||
|
if radius >= max_radius(center_x, center_y) then
|
||||||
|
local cb = on_complete
|
||||||
|
Focus.stop()
|
||||||
|
if cb then cb() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws the focus overlay (black screen with circular cutout).
|
||||||
|
--- Must be called after all other drawing to appear on top of every visual layer.
|
||||||
|
--- @within Focus
|
||||||
|
function Focus.draw()
|
||||||
|
if not active then return end
|
||||||
|
|
||||||
|
local cx = center_x
|
||||||
|
local cy = center_y
|
||||||
|
local r = radius
|
||||||
|
local w = Config.screen.width
|
||||||
|
local h = Config.screen.height
|
||||||
|
local color = Config.colors.black
|
||||||
|
|
||||||
|
if closing and r <= 0 then
|
||||||
|
rect(0, 0, w, h, color)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local top = math.max(0, math.floor(cy - r))
|
||||||
|
local bottom = math.min(h - 1, math.ceil(cy + r))
|
||||||
|
|
||||||
|
if top > 0 then
|
||||||
|
rect(0, 0, w, top, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
if bottom < h - 1 then
|
||||||
|
rect(0, bottom + 1, w, h - bottom - 1, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
for y = top, bottom do
|
||||||
|
local dy = y - cy
|
||||||
|
local half_w = math.sqrt(math.max(0, r * r - dy * dy))
|
||||||
|
local left = math.floor(cx - half_w)
|
||||||
|
local right = math.ceil(cx + half_w)
|
||||||
|
|
||||||
|
if left > 0 then
|
||||||
|
rect(0, y, left, 1, color)
|
||||||
|
end
|
||||||
|
if right < w then
|
||||||
|
rect(right, y, w - right, 1, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
58
inc/logic/logic.glitch.lua
Normal file
58
inc/logic/logic.glitch.lua
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
--- @section Glitch
|
||||||
|
Glitch = {}
|
||||||
|
|
||||||
|
--- Shows the glitch effect.
|
||||||
|
--- @within Glitch
|
||||||
|
function Glitch.show()
|
||||||
|
if Context and Context.glitch then
|
||||||
|
Context.glitch.enabled = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Hides the glitch effect.
|
||||||
|
--- @within Glitch
|
||||||
|
function Glitch.hide()
|
||||||
|
if Context and Context.glitch then
|
||||||
|
Context.glitch.enabled = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws the glitch effect if active.
|
||||||
|
--- @within Glitch
|
||||||
|
function Glitch.draw()
|
||||||
|
if not Context or not Context.glitch or not Context.glitch.enabled then return end
|
||||||
|
|
||||||
|
-- Update state timer
|
||||||
|
Context.glitch.timer = Context.glitch.timer - 1
|
||||||
|
if Context.glitch.timer <= 0 then
|
||||||
|
if Context.glitch.state == "active" then
|
||||||
|
Context.glitch.state = "waiting"
|
||||||
|
Context.glitch.timer = math.random(20, 60) -- Time to stay fixed
|
||||||
|
else
|
||||||
|
Context.glitch.state = "active"
|
||||||
|
Context.glitch.timer = math.random(40, 100) -- Time to stay glitchy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw stripes only when active
|
||||||
|
if Context.glitch.state == "active" then
|
||||||
|
for _ = 1, 15 do
|
||||||
|
local rx = math.random(0, Config.screen.width - 1)
|
||||||
|
local ry = math.random(0, Config.screen.height - 1)
|
||||||
|
|
||||||
|
-- Sample color at the random point
|
||||||
|
local color = pix(rx, ry)
|
||||||
|
|
||||||
|
-- Determine random length for the stripe (2-40)
|
||||||
|
local length = math.random(2, 40)
|
||||||
|
|
||||||
|
-- Draw the vertical stripe
|
||||||
|
for sy = 0, length - 1 do
|
||||||
|
local dy = ry + sy
|
||||||
|
if dy < Config.screen.height then
|
||||||
|
pix(rx, dy, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
334
inc/logic/logic.meter.lua
Normal file
334
inc/logic/logic.meter.lua
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
--- @section Meter
|
||||||
|
local METER_MAX = 1000
|
||||||
|
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
|
||||||
|
local COMBO_MAX_BONUS = 0.16
|
||||||
|
local COMBO_TIMEOUT_FRAMES = 600
|
||||||
|
local METER_FLASH_DURATION = 2.0
|
||||||
|
local FLASH_COLOR = 4
|
||||||
|
|
||||||
|
-- Internal meters for tracking game progress and player stats.
|
||||||
|
Meter.COLOR_ISM = Config.colors.orange
|
||||||
|
Meter.COLOR_WPM = Config.colors.blue
|
||||||
|
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. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * ism (number) Initial ISM meter value.<br/>
|
||||||
|
--- * wpm (number) Initial WPM meter value.<br/>
|
||||||
|
--- * bm (number) Initial BM meter value.<br/>
|
||||||
|
--- * combo (number) Current combo count.<br/>
|
||||||
|
--- * combo_timer (number) Frames since last combo action.<br/>
|
||||||
|
--- * hidden (boolean) Whether meters are hidden.
|
||||||
|
function Meter.get_initial()
|
||||||
|
return {
|
||||||
|
ism = ISM_METER_DEFAULT,
|
||||||
|
wpm = WPM_METER_DEFAULT,
|
||||||
|
bm = BM_METER_DEFAULT,
|
||||||
|
combo = 0,
|
||||||
|
combo_timer = 0,
|
||||||
|
hidden = false,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Hides meters.
|
||||||
|
--- @within Meter
|
||||||
|
function Meter.hide()
|
||||||
|
if Context and Context.meters then Context.meters.hidden = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Shows meters.
|
||||||
|
--- @within Meter
|
||||||
|
function Meter.show()
|
||||||
|
if Context and Context.meters then Context.meters.hidden = false end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets max meter value.
|
||||||
|
--- @within Meter
|
||||||
|
--- @return number The maximum meter value.
|
||||||
|
function Meter.get_max()
|
||||||
|
return METER_MAX
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the decay amount applied to all meters per day.
|
||||||
|
--- @within Meter
|
||||||
|
--- @param amount number Amount to subtract from each meter.
|
||||||
|
function Meter.set_decay(amount)
|
||||||
|
METER_DECAY_PER_DAY = amount
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the meter decay as a percentage of the max meter value.
|
||||||
|
--- @within Meter
|
||||||
|
--- @return number The decay percentage per day.
|
||||||
|
function Meter.get_decay_percentage()
|
||||||
|
return math.floor(METER_DECAY_PER_DAY / METER_MAX * 100)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets combo multiplier.
|
||||||
|
--- @within Meter
|
||||||
|
--- @return number The current combo multiplier.
|
||||||
|
function Meter.get_combo_multiplier()
|
||||||
|
if not Context or not Context.meters then return 1 end
|
||||||
|
local combo = Context.meters.combo
|
||||||
|
if combo == 0 then return 1 end
|
||||||
|
return 1 + math.min(COMBO_MAX_BONUS, COMBO_BASE_BONUS * (2 ^ (combo - 1)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates all meters.
|
||||||
|
--- @within Meter
|
||||||
|
function Meter.update()
|
||||||
|
if not Context or not Context.game_in_progress or not Context.meters then return end
|
||||||
|
local m = Context.meters
|
||||||
|
local in_minigame = string.find(Window.get_current_id(), "^minigame_") ~= nil
|
||||||
|
if not in_minigame then
|
||||||
|
if m.combo > 0 then
|
||||||
|
m.combo_timer = m.combo_timer + 1
|
||||||
|
if m.combo_timer >= COMBO_TIMEOUT_FRAMES then
|
||||||
|
m.combo = 0
|
||||||
|
m.combo_timer = 0
|
||||||
|
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.
|
||||||
|
--- @within Meter
|
||||||
|
--- @param key string The meter key (e.g., "wpm", "ism", "bm").
|
||||||
|
--- @param amount number The amount to add.
|
||||||
|
function Meter.add(key, amount)
|
||||||
|
if not Context or not Context.meters then return end
|
||||||
|
local m = Context.meters
|
||||||
|
if m[key] ~= nil then
|
||||||
|
if amount > 0 and (key == "ism" or key == "bm") and m[key] >= METER_MAX then
|
||||||
|
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 = 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
|
||||||
|
Context.should_ascend = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Called on minigame completion.
|
||||||
|
--- @within Meter
|
||||||
|
--- @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
|
||||||
|
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_was_high = m.wpm > 900
|
||||||
|
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
|
||||||
|
if wpm_was_high then
|
||||||
|
Meter.add("ism", math.floor(max * 0.05))
|
||||||
|
Meter.add("bm", math.floor(max * 0.05))
|
||||||
|
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()
|
||||||
|
if not Context or not Context.game_in_progress or not Context.meters then return end
|
||||||
|
if Context.meters.hidden then return end
|
||||||
|
|
||||||
|
local m = Context.meters
|
||||||
|
local max = Meter.get_max()
|
||||||
|
local screen_w = Config.screen.width
|
||||||
|
local screen_h = Config.screen.height
|
||||||
|
local bar_w = screen_w * 0.25
|
||||||
|
local bar_h = 2
|
||||||
|
local edge = math.max(2, math.floor(screen_w * 0.03))
|
||||||
|
local bar_x = screen_w - bar_w - edge
|
||||||
|
local line_h = 3
|
||||||
|
local start_y = screen_h * 0.05
|
||||||
|
|
||||||
|
|
||||||
|
local meter_list = {
|
||||||
|
{ key = "wpm", label = "WPM", color = Meter.COLOR_WPM, row = 0 },
|
||||||
|
{ key = "ism", label = "ISM", color = Meter.COLOR_ISM, row = 1 },
|
||||||
|
{ key = "bm", label = "BM", color = Meter.COLOR_BM, row = 2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, meter in ipairs(meter_list) do
|
||||||
|
local label_y = start_y + meter.row * line_h
|
||||||
|
local bar_y = label_y
|
||||||
|
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)
|
||||||
|
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 - 4, ascension_y, { spacing = 8 })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws only the ascension letters at the same position as in Meter.draw().
|
||||||
|
--- Used when meters are hidden but ascension letters still need to be visible.
|
||||||
|
--- @within Meter
|
||||||
|
function Meter.draw_ascension_only()
|
||||||
|
local screen_w = Config.screen.width
|
||||||
|
local screen_h = Config.screen.height
|
||||||
|
local bar_w = screen_w * 0.25
|
||||||
|
local edge = math.max(2, math.floor(screen_w * 0.03))
|
||||||
|
local bar_x = screen_w - bar_w - edge
|
||||||
|
local line_h = 3
|
||||||
|
local start_y = screen_h * 0.05
|
||||||
|
local ascension_y = start_y + 3 * line_h + 1
|
||||||
|
Ascension.draw(bar_x - 4, ascension_y, { spacing = 8 })
|
||||||
|
end
|
||||||
|
|
||||||
20
inc/logic/logic.minigame.lua
Normal file
20
inc/logic/logic.minigame.lua
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
-- Manages minigame configurations and initial states.
|
||||||
|
--- @section Minigame
|
||||||
|
|
||||||
|
--- Draws a unified win message overlay.
|
||||||
|
--- @within Minigame
|
||||||
|
function Minigame.draw_win_overlay(win_text)
|
||||||
|
local text = win_text or "SUCCESS"
|
||||||
|
local tw = #text * 6
|
||||||
|
local th = 6
|
||||||
|
local padding = 4
|
||||||
|
local box_w = tw + padding * 2
|
||||||
|
local box_h = th + padding * 2
|
||||||
|
local box_x = (Config.screen.width - box_w) / 2
|
||||||
|
local box_y = (Config.screen.height - box_h) / 2
|
||||||
|
local text_x = Config.screen.width / 2
|
||||||
|
rect(box_x, box_y, box_w, box_h, Config.colors.dark_grey)
|
||||||
|
rectb(box_x, box_y, box_w, box_h, Config.colors.white)
|
||||||
|
Print.text_center_contour(text, text_x, box_y + padding, Config.colors.black, false, 1, Config.colors.white)
|
||||||
|
end
|
||||||
|
|
||||||
87
inc/logic/logic.timer.lua
Normal file
87
inc/logic/logic.timer.lua
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
--- @section Timer
|
||||||
|
|
||||||
|
local timer_duration = 1800
|
||||||
|
|
||||||
|
--- Gets initial timer values.
|
||||||
|
--- @within Timer
|
||||||
|
--- @return result table Initial timer values. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * progress (number) Clock timer revolution progress (0 to 1).
|
||||||
|
function Timer.get_initial()
|
||||||
|
return {
|
||||||
|
progress = 0,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the number of frames for one full timer revolution.
|
||||||
|
--- @within Timer
|
||||||
|
--- @param frames number Frames per revolution.
|
||||||
|
function Timer.set_duration(frames)
|
||||||
|
timer_duration = frames
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Updates the timer and handles revolution events.
|
||||||
|
--- @within Timer
|
||||||
|
function Timer.update()
|
||||||
|
if not Context or not Context.game_in_progress or not Context.meters or not Context.timer then return end
|
||||||
|
local t = Context.timer
|
||||||
|
local in_minigame = string.find(Window.get_current_id(), "^minigame_") ~= nil
|
||||||
|
|
||||||
|
if not in_minigame then
|
||||||
|
t.progress = t.progress + (1 / timer_duration)
|
||||||
|
if t.progress >= 1 then
|
||||||
|
t.progress = t.progress - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws the clock timer indicator as a circular progress bar.
|
||||||
|
--- @within Timer
|
||||||
|
function Timer.draw()
|
||||||
|
if not Context or not Context.game_in_progress or not Context.meters or not Context.timer then return end
|
||||||
|
if Context.meters.hidden and not Context.stat_screen_active then return end
|
||||||
|
|
||||||
|
local cx = 10
|
||||||
|
local cy = 8
|
||||||
|
local r_outer = 5
|
||||||
|
local r_inner = 3
|
||||||
|
local progress = Context.timer.progress
|
||||||
|
|
||||||
|
local fg_color
|
||||||
|
if progress <= 0.25 then
|
||||||
|
fg_color = Config.colors.dark_grey
|
||||||
|
elseif progress <= 0.5 then
|
||||||
|
fg_color = Config.colors.light_blue
|
||||||
|
elseif progress <= 0.75 then
|
||||||
|
fg_color = Config.colors.blue
|
||||||
|
elseif progress <= 1 then
|
||||||
|
fg_color = Config.colors.red
|
||||||
|
end
|
||||||
|
|
||||||
|
local bg_color = Config.colors.white
|
||||||
|
local start_angle = -math.pi * 0.5
|
||||||
|
local progress_angle = progress * 2 * math.pi
|
||||||
|
local r_outer_sq = r_outer * r_outer
|
||||||
|
local r_inner_sq = r_inner * r_inner
|
||||||
|
|
||||||
|
for dy = -r_outer, r_outer do
|
||||||
|
for dx = -r_outer, r_outer do
|
||||||
|
local dist_sq = dx * dx + dy * dy
|
||||||
|
if dist_sq <= r_outer_sq and dist_sq > r_inner_sq then
|
||||||
|
local angle = math.atan(dy, dx)
|
||||||
|
local relative = angle - start_angle
|
||||||
|
if relative < 0 then relative = relative + 2 * math.pi end
|
||||||
|
if relative <= progress_angle then
|
||||||
|
pix(cx + dx, cy + dy, fg_color)
|
||||||
|
else
|
||||||
|
pix(cx + dx, cy + dy, bg_color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local hand_angle = start_angle + progress_angle
|
||||||
|
local hand_x = math.floor(cx + math.cos(hand_angle) * (r_inner - 1) + 0.5)
|
||||||
|
local hand_y = math.floor(cy + math.sin(hand_angle) * (r_inner - 1) + 0.5)
|
||||||
|
line(cx, cy, hand_x, hand_y, Config.colors.white)
|
||||||
|
end
|
||||||
135
inc/logic/logic.trigger.lua
Normal file
135
inc/logic/logic.trigger.lua
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
--- @section Trigger
|
||||||
|
local triggers = {}
|
||||||
|
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param trigger table The trigger data table.
|
||||||
|
--- @param trigger.id string Unique trigger identifier.
|
||||||
|
--- @param trigger.duration number Duration in frames before the trigger fires.
|
||||||
|
--- @param[opt] trigger.on_start function Called when the trigger starts. Defaults to noop.
|
||||||
|
--- @param[opt] trigger.on_stop function Called when the trigger fires or is manually stopped. Defaults to noop.
|
||||||
|
--- @param[opt] trigger.repeating boolean If true, trigger restarts after firing. Defaults to false.
|
||||||
|
function Trigger.register(trigger)
|
||||||
|
if not trigger or not trigger.id then
|
||||||
|
trace("Error: Invalid trigger registered (missing id)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not trigger.duration or trigger.duration <= 0 then
|
||||||
|
trace("Error: Invalid trigger registered (missing or invalid duration)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not trigger.on_start then
|
||||||
|
trigger.on_start = function() end
|
||||||
|
end
|
||||||
|
if not trigger.on_stop then
|
||||||
|
trigger.on_stop = function() end
|
||||||
|
end
|
||||||
|
if trigger.repeating == nil then
|
||||||
|
trigger.repeating = false
|
||||||
|
end
|
||||||
|
if triggers[trigger.id] then
|
||||||
|
trace("Warning: Overwriting trigger with id: " .. trigger.id)
|
||||||
|
end
|
||||||
|
triggers[trigger.id] = trigger
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param id string The trigger ID.
|
||||||
|
--- @return table|nil result The trigger definition or nil.
|
||||||
|
function Trigger.get_by_id(id)
|
||||||
|
return triggers[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @within Trigger
|
||||||
|
--- @return table result All trigger definitions keyed by ID.
|
||||||
|
function Trigger.get_all()
|
||||||
|
return triggers
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param id string The trigger ID.
|
||||||
|
--- @return boolean active True if the trigger is running.
|
||||||
|
function Trigger.is_active(id)
|
||||||
|
if not Context or not Context.triggers then return false end
|
||||||
|
return Context.triggers[id] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- If already active, restarts from 0.
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param id string The trigger ID.
|
||||||
|
function Trigger.start(id)
|
||||||
|
if not Context or not Context.triggers then return end
|
||||||
|
local trigger = triggers[id]
|
||||||
|
if not trigger then
|
||||||
|
trace("Error: Cannot start unknown trigger: " .. tostring(id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Context.triggers[id] = { elapsed = 0 }
|
||||||
|
trigger.on_start()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param id string The trigger ID.
|
||||||
|
function Trigger.stop(id)
|
||||||
|
if not Context or not Context.triggers then return end
|
||||||
|
local trigger = triggers[id]
|
||||||
|
if not trigger then
|
||||||
|
trace("Error: Cannot stop unknown trigger: " .. tostring(id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not Context.triggers[id] then return end
|
||||||
|
|
||||||
|
Context.triggers[id] = nil
|
||||||
|
trigger.on_stop()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Resets elapsed time to 0 without calling handlers. No-op if inactive.
|
||||||
|
--- @within Trigger
|
||||||
|
--- @param id string The trigger ID.
|
||||||
|
function Trigger.reset(id)
|
||||||
|
if not Context or not Context.triggers then return end
|
||||||
|
if not triggers[id] then
|
||||||
|
trace("Error: Cannot reset unknown trigger: " .. tostring(id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not Context.triggers[id] then return end
|
||||||
|
|
||||||
|
Context.triggers[id].elapsed = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Pauses during minigames.
|
||||||
|
--- @within Trigger
|
||||||
|
function Trigger.update()
|
||||||
|
if not Context or not Context.game_in_progress or not Context.triggers then return end
|
||||||
|
|
||||||
|
local in_minigame = string.find(Window.get_current_id(), "^minigame_") ~= nil
|
||||||
|
if in_minigame then return end
|
||||||
|
|
||||||
|
local fired = {}
|
||||||
|
for id, state in pairs(Context.triggers) do
|
||||||
|
local trigger = triggers[id]
|
||||||
|
if trigger then
|
||||||
|
state.elapsed = state.elapsed + 1
|
||||||
|
if state.elapsed >= trigger.duration then
|
||||||
|
table.insert(fired, id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(fired, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, id in ipairs(fired) do
|
||||||
|
local trigger = triggers[id]
|
||||||
|
if trigger then
|
||||||
|
trigger.on_stop()
|
||||||
|
if trigger.repeating then
|
||||||
|
Context.triggers[id] = { elapsed = 0 }
|
||||||
|
else
|
||||||
|
Context.triggers[id] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Context.triggers[id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
9
inc/map/map.bedroom.lua
Normal file
9
inc/map/map.bedroom.lua
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Map.register({
|
||||||
|
id = "bedroom",
|
||||||
|
from_x = 0,
|
||||||
|
from_y = 0,
|
||||||
|
width = 30,
|
||||||
|
height = 17,
|
||||||
|
to_x = 0,
|
||||||
|
to_y = 0,
|
||||||
|
})
|
||||||
75
inc/map/map.manager.lua
Normal file
75
inc/map/map.manager.lua
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
-- Manages game maps.
|
||||||
|
--- @section Map
|
||||||
|
|
||||||
|
|
||||||
|
local _maps = {}
|
||||||
|
|
||||||
|
--- Gets all registered maps as an array.
|
||||||
|
--- @within Map
|
||||||
|
--- @return result table An array of registered map data. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique map identifier.<br/>
|
||||||
|
--- * from_x (number) Source tile X coordinate in the map sheet.<br/>
|
||||||
|
--- * from_y (number) Source tile Y coordinate in the map sheet.<br/>
|
||||||
|
--- * width (number) Width in tiles.<br/>
|
||||||
|
--- * height (number) Height in tiles.<br/>
|
||||||
|
--- * to_x (number) Destination X coordinate on screen.<br/>
|
||||||
|
--- * to_y (number) Destination Y coordinate on screen.<br/>
|
||||||
|
function Map.get_maps_array()
|
||||||
|
local maps_array = {}
|
||||||
|
for _, map_data in pairs(_maps) do
|
||||||
|
table.insert(maps_array, map_data)
|
||||||
|
end
|
||||||
|
return maps_array
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers a map definition.
|
||||||
|
--- @within Map
|
||||||
|
--- @param map_data table The map data table.
|
||||||
|
--- @param map_data.id string Unique map identifier.<br/>
|
||||||
|
--- @param map_data.from_x number Source tile X coordinate in the map sheet.<br/>
|
||||||
|
--- @param map_data.from_y number Source tile Y coordinate in the map sheet.<br/>
|
||||||
|
--- @param map_data.width number Width in tiles.<br/>
|
||||||
|
--- @param map_data.height number Height in tiles.<br/>
|
||||||
|
--- @param map_data.to_x number Destination X coordinate on screen.<br/>
|
||||||
|
--- @param map_data.to_y number Destination Y coordinate on screen.<br/>
|
||||||
|
function Map.register(map_data)
|
||||||
|
if _maps[map_data.id] then
|
||||||
|
trace("Warning: Overwriting map with id: " .. map_data.id)
|
||||||
|
end
|
||||||
|
_maps[map_data.id] = map_data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets a map by ID.
|
||||||
|
--- @within Map
|
||||||
|
--- @param map_id string The ID of the map.
|
||||||
|
--- @return result table The map data table or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique map identifier.<br/>
|
||||||
|
--- * from_x (number) Source tile X coordinate in the map sheet.<br/>
|
||||||
|
--- * from_y (number) Source tile Y coordinate in the map sheet.<br/>
|
||||||
|
--- * width (number) Width in tiles.<br/>
|
||||||
|
--- * height (number) Height in tiles.<br/>
|
||||||
|
--- * to_x (number) Destination X coordinate on screen.<br/>
|
||||||
|
--- * to_y (number) Destination Y coordinate on screen.<br/>
|
||||||
|
function Map.get_by_id(map_id)
|
||||||
|
return _maps[map_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws a map.
|
||||||
|
--- @within Map
|
||||||
|
--- @param map_id string The ID of the map to draw.
|
||||||
|
function Map.draw(map_id)
|
||||||
|
local map_data = Map.get_by_id(map_id)
|
||||||
|
if not map_data then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
map(
|
||||||
|
map_data.from_x,
|
||||||
|
map_data.from_y,
|
||||||
|
map_data.width,
|
||||||
|
map_data.height,
|
||||||
|
map_data.to_x,
|
||||||
|
map_data.to_y
|
||||||
|
)
|
||||||
|
end
|
||||||
9
inc/map/map.office.lua
Normal file
9
inc/map/map.office.lua
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Map.register({
|
||||||
|
id = "office",
|
||||||
|
from_x = 60,
|
||||||
|
from_y = 0,
|
||||||
|
width = 30,
|
||||||
|
height = 17,
|
||||||
|
to_x = 0,
|
||||||
|
to_y = 0,
|
||||||
|
})
|
||||||
9
inc/map/map.street.lua
Normal file
9
inc/map/map.street.lua
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Map.register({
|
||||||
|
id = "street",
|
||||||
|
from_x = 30,
|
||||||
|
from_y = 0,
|
||||||
|
width = 30,
|
||||||
|
height = 17,
|
||||||
|
to_x = 0,
|
||||||
|
to_y = 0,
|
||||||
|
})
|
||||||
@@ -1,29 +1,578 @@
|
|||||||
-- <TILES>
|
|
||||||
-- 000:4444444444444444444444444444444444444444444444444444444444444444
|
|
||||||
-- 001:1111111111111111111111111111111111111111111111111111111111111111
|
|
||||||
-- 002:5555555555555555555555555555555555555555555555555555555555555555
|
|
||||||
-- 003:6666666666666666666666666666666666666666666666666666666666666666
|
|
||||||
-- 004:7777777777777777777777777777777777777777777777777777777777777777
|
|
||||||
-- 005:8888888888888888888888888888888888888888888888888888888888888888
|
|
||||||
-- 006:9999999999999999999999999999999999999999999999999999999999999999
|
|
||||||
-- 007:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
-- 008:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
|
||||||
-- </TILES>
|
|
||||||
|
|
||||||
-- <WAVES>
|
|
||||||
-- 000:00000000ffffffff00000000ffffffff
|
|
||||||
-- 001:0123456789abcdeffedcba9876543210
|
|
||||||
-- 02:0123456789abcdef0123456789abcdef
|
|
||||||
-- </WAVES>
|
|
||||||
|
|
||||||
-- <SFX>
|
|
||||||
-- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000
|
|
||||||
-- </SFX>
|
|
||||||
|
|
||||||
-- <TRACKS>
|
|
||||||
-- 000:100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
-- </TRACKS>
|
|
||||||
|
|
||||||
-- <PALETTE>
|
-- <PALETTE>
|
||||||
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
|
-- 000:0404005f574fc2c3c71d2b53fff1e8ab52367e2553ffa30000875129adff83769c00e436fa77a8ff004dc3c3c7ffccaa
|
||||||
-- </PALETTE>
|
-- </PALETTE>
|
||||||
|
-- <TILES>
|
||||||
|
-- 000:0000000006666660066060600636366006606060060636600666666000000000
|
||||||
|
-- 001:0000000006666666060600600666666606000600066666660633633300000000
|
||||||
|
-- 002:0000000066666666006000066666666660060006666666666333363300000000
|
||||||
|
-- 004:1111111111111111111111111111111111111111111111111111111111111111
|
||||||
|
-- 005:1111111111111111111111111111111111111111111111111111111100000000
|
||||||
|
-- 006:0222222200010010033055010130150603305501013015060330550100000000
|
||||||
|
-- 007:2222222001022220601022206001022060201020602201006022201000000000
|
||||||
|
-- 008:0000000003330333033303330333000003330111033301110333011103330111
|
||||||
|
-- 009:0000000033333333333333330000000011100111111001111110011111100111
|
||||||
|
-- 010:0000000033303330333033300000333011103330111033301110333011103330
|
||||||
|
-- 011:111111111111111111111100111110a511110a51111015a51101515111000000
|
||||||
|
-- 012:111111111111111101111111101111115a01111115a011115151011100000111
|
||||||
|
-- 013:1000000004444444044444440444444404444444044444440444444404444444
|
||||||
|
-- 014:0000000144444240444424204444424044442420444442404444242044444240
|
||||||
|
-- 015:0000000004244444024444440424400002444444042424240242424200000000
|
||||||
|
-- 016:0000000044444420444442400004442044444240e42424204242424000000000
|
||||||
|
-- 017:0000000002424240042424200244444004244440024404400424044002440440
|
||||||
|
-- 018:0000000002424240042424200444424004444420044042400440442004404240
|
||||||
|
-- 019:0222222200010012055016020150660205501602015066020550160200000000
|
||||||
|
-- 020:2222222022221000222203302222013022220330222201302222033000000000
|
||||||
|
-- 021:0333011103330111033301110333011103330111033300000333011103330111
|
||||||
|
-- 022:1110011111100111111001111110011111100111000000001110011111100111
|
||||||
|
-- 023:1110333011103330111033301110333011103330000033301110333011103330
|
||||||
|
-- 024:1111111011111110111111101111111011111110111111101111111011111110
|
||||||
|
-- 025:0444444404444444044444440444444404444444044044440440444404404444
|
||||||
|
-- 026:4444242044444240444424204444424044442420444442404444242044444240
|
||||||
|
-- 027:3333111133331111333311113333111111113333111133331111333311113333
|
||||||
|
-- 028:0424044002440440042404400244444004244440024242400424242000000000
|
||||||
|
-- 029:0440442004404240044044200444424004444420024242400424242000000000
|
||||||
|
-- 030:0000000006666660036060600360666006606060036660600360666003606060
|
||||||
|
-- 031:0222222200010010033033030130130103303303013013010330330300000000
|
||||||
|
-- 032:2222222001001220303302203013022030330220301302203033022000000000
|
||||||
|
-- 033:0333011103330111033301110333011103330111033301110333011100000000
|
||||||
|
-- 034:1110011111100111111001111110011111100111111001111110011100000000
|
||||||
|
-- 035:1110333011103330111033301110333011103330111033301110333000000000
|
||||||
|
-- 036:0440444404404444044044440444444404444444044444440444444404444444
|
||||||
|
-- 037:0000000002222222022000220201110202011102020111020220002202222222
|
||||||
|
-- 038:0000000022222220220002202011102020111020201110202200022022222220
|
||||||
|
-- 039:0000000002222222022222220222222202222222022222220222222202222222
|
||||||
|
-- 040:0000000022222220222222202222222022222220222222202222222022222220
|
||||||
|
-- 041:0000000002222222022200000220111102201121020112000201120402012200
|
||||||
|
-- 042:0000000022222220000002201111102021211020000210203430102000430020
|
||||||
|
-- 043:0666606003606060036066600366606003606060066060600360606003666660
|
||||||
|
-- 044:0222222202222222022222220222222202222222000000000001111100011111
|
||||||
|
-- 045:2222222022222220222222202222222022222220000000001111100011111000
|
||||||
|
-- 046:1111111111111111111111111000000002222222022222220222222202222222
|
||||||
|
-- 047:1111111111111111111111110000000022222222222222222222222222222222
|
||||||
|
-- 048:1111111111111111111111110000100022220444e222044422220444e2220444
|
||||||
|
-- 049:1111111111111111111111111100001100222201022222200222222002222220
|
||||||
|
-- 050:1111111011111110111111101111111011111110111111101111111011110000
|
||||||
|
-- 051:1111111111111111111111111111111111111111111111111111111100011111
|
||||||
|
-- 052:0444444404444444044444440444444404444444044444440444444410000000
|
||||||
|
-- 053:4444242044444240444424204444424044442420444442404444242000000001
|
||||||
|
-- 054:0222222202222222022000220201110202011102020111020220002202222222
|
||||||
|
-- 055:2222222022222220220002202011102020111020201110202200022022222220
|
||||||
|
-- 056:0222222202222222022222220222222202222222022222220222222200000000
|
||||||
|
-- 057:2222222022222220222222202222222022222220222222202222222000000000
|
||||||
|
-- 058:0201122202012222020112220201222202201222022022220222000002222222
|
||||||
|
-- 059:2204302022204020202200202000002020222020222220200000022022222220
|
||||||
|
-- 060:5555555555555555555555556666111155555555555555555555555566616666
|
||||||
|
-- 061:5555555555555555555555556666666655551555555515555555655566666611
|
||||||
|
-- 062:0222222202222222022222220222222202222222022222220222222202222222
|
||||||
|
-- 063:2222222222222222222222222222222222222222222222222222222222222222
|
||||||
|
-- 064:22220444e222044422220444e222044422220444e222044422220444e2220444
|
||||||
|
-- 065:0222222002222220020000206022220002222220022222200200002000111100
|
||||||
|
-- 066:5000000004444444044044440440444404404444044044440444444460000000
|
||||||
|
-- 067:0000000544444240444424204444424044442420444442404444242000000001
|
||||||
|
-- 068:0000000001111111011111110111111101111111011111110111111100000000
|
||||||
|
-- 069:0000000011111110111111101111111011111110111111101111111000000000
|
||||||
|
-- 070:5555555555555555555555551111666655555555555555555555555511666111
|
||||||
|
-- 071:000000000222220002222090022209900220999002099d900099199009919790
|
||||||
|
-- 072:0000000022222220222222202222222022220000222011002212000021222220
|
||||||
|
-- 073:5555555555555555555555556611166655555551555555515555555166666111
|
||||||
|
-- 074:0000000001111111000000001111666655555555555555555555555511666111
|
||||||
|
-- 075:0000000011111111000000006611166655555551555555515555555166666111
|
||||||
|
-- 076:0000000011111111000000001111666655555555555555555555555511666111
|
||||||
|
-- 077:0000000011111110000000006611166655555551555555515555555166666111
|
||||||
|
-- 078:1111111111111111111100001110919111101010110191011019191000919190
|
||||||
|
-- 079:1111111111111111000000009191919000000010111110901111101000000090
|
||||||
|
-- 080:0919199009919902091990200999020209902020090202020020202000000000
|
||||||
|
-- 081:0000000002020200202020200200022020002220000222202022222002222220
|
||||||
|
-- 082:5555555555555055555501056666010155550105555501055555010566610106
|
||||||
|
-- 083:5555555555555555000000000222222202222222022222220222222202222222
|
||||||
|
-- 084:5555555555555555000000002222222022222220222222202222222022222220
|
||||||
|
-- 085:5555000055500220550202206022022002220220022202200222022002220220
|
||||||
|
-- 086:1000000110333301103333011033330110333301103333011033330110333301
|
||||||
|
-- 087:3010101033331111333311113333111111113333111133331111333311113333
|
||||||
|
-- 088:3333101033331090333310103333109011113010111010901110201011101090
|
||||||
|
-- 089:2222222022222220222222202222222022222220222222202222222022222220
|
||||||
|
-- 090:5555550055555022555502226611022255550222555502225555501266666100
|
||||||
|
-- 091:0005010522200105222201052222010622220105222201052210505500066111
|
||||||
|
-- 092:5000000002222222022222220222222202222222022222220222222202222222
|
||||||
|
-- 093:0222022002220220022202200222022002220220022202200222022002220220
|
||||||
|
-- 094:1033330110333301103333011033330110333301103333011033330110000001
|
||||||
|
-- 095:3333101033331090333310103333109011113010111130901111301011113000
|
||||||
|
-- 096:0000000001111111000000000106666601051555010515550105655500066611
|
||||||
|
-- 097:0000000011111110000000006666101055555010555550105555501066616000
|
||||||
|
-- 098:5555555555555550555555056666116655550055555500555555655566666611
|
||||||
|
-- 099:0555555500555555050555550661111105500555115005550055555500616666
|
||||||
|
-- 100:0222222202222222022222220000000000000000501055555010555560006666
|
||||||
|
-- 101:2222222022222220222222200000000000000000555501055555010566660001
|
||||||
|
-- 102:0222022002220220022202200222022002220220022202200222022012220220
|
||||||
|
-- 103:1000000103333330033333300333333003333330033333300333333010000001
|
||||||
|
-- 104:5500000050222222022222220222222202222222022222220222222202222222
|
||||||
|
-- 105:0222222222222222222222222222222222222222222222222222222222222222
|
||||||
|
-- 106:1222000012201110120111101011111001111110011111100111111001111110
|
||||||
|
-- 107:0000000001111111011111110111111101111111011111115000000066000066
|
||||||
|
-- 108:0000000011111111111111111111111111111111111111110000000066666611
|
||||||
|
-- 109:1111111011111110111111101111111011111110111111100000000566000066
|
||||||
|
-- 110:1111111111111111111111111111010011101044111010441110104400000010
|
||||||
|
-- 111:1111111111111111111111110011111144011111440111114401111110000000
|
||||||
|
-- 112:1111111111111111111111111111111111111111111111111111011100004000
|
||||||
|
-- 113:3333104433331044333310003333111111113333111133331111333311113333
|
||||||
|
-- 114:4403111144031111000311113333111111110000111044441104444411014444
|
||||||
|
-- 115:3304440133044401330444013304440100104033440103334440333344103333
|
||||||
|
-- 116:3330111133020000330124243330111111110111111130111111301111113300
|
||||||
|
-- 117:1103111100401111242011111240111111203333124033331120333300013333
|
||||||
|
-- 118:1111111100000000033333330333333303333333033333330000000011111111
|
||||||
|
-- 119:1111111100000000333333303333333033333330333333300000000011111111
|
||||||
|
-- 120:1a12222222a222222212222222a222222212222222a222222212222222222222
|
||||||
|
-- 121:1a8b888822888b8b228b888822b8bb8b22888b88228b88882288b88b228b8b88
|
||||||
|
-- 122:8b8b8888b8b88b8bb88b888888b8bb8b8b888b8888bb88888b88b88bb88b8b88
|
||||||
|
-- 123:8b8b888800000000055555550555555500000000055555550555555500000000
|
||||||
|
-- 124:8b8b888800000000555555555555555500000000555555555555555500000000
|
||||||
|
-- 125:8b8b888800000000555555505555555000000000555555505555555000000000
|
||||||
|
-- 126:0000000007777777070000000703333307033333070333330703333307033333
|
||||||
|
-- 127:0000000077777777000000003333333033333330333333303333333033333330
|
||||||
|
-- 128:000000007777777700000000e222222222222222e222222222222222e2222222
|
||||||
|
-- 129:0000000077777770000000702222207022222070222220702222207022222070
|
||||||
|
-- 130:3000000300ffff000ddddff00dddddf00dddddf00dddddf000dddd0000000000
|
||||||
|
-- 131:0555555505555555055555550555555500000000050b88880508b88b000b8b88
|
||||||
|
-- 132:555555555555555555555555555555550000000088bb88888b88b88bb88b8b88
|
||||||
|
-- 133:555555505555555055555550555555500000000088bb80508b88b050b88b8000
|
||||||
|
-- 134:0703333307033333070333330703333307033333070333330703333307033333
|
||||||
|
-- 135:3333333033333330333333303333333033333330333333303333333033333330
|
||||||
|
-- 136:2222207022222070222220702222207022222070222220702222207022222070
|
||||||
|
-- 137:4444444444444444444444444444444444444444444444444444444444444444
|
||||||
|
-- 138:4442222244422222444222224442222244422222444222224442222244422222
|
||||||
|
-- 139:0000000000bbbb0008888bb0088888b0088888b0088888b00088880030000003
|
||||||
|
-- 140:0000000007777777076666770767776707677776076777760767777607766667
|
||||||
|
-- 141:0000000077777770777777707777777077777770777777777677777777777777
|
||||||
|
-- 142:0000000066666666666666666666666600000000777777777777777777777777
|
||||||
|
-- 143:0000000066666070666660706666607000000070777777707777777077777770
|
||||||
|
-- 144:1a80088be2800b8822800888e280088b22800b88e220022221a001a2ea100a12
|
||||||
|
-- 145:88888888b88b8b888b8888b8888b888888888b8b12222222a1a1a1a21a1a1a12
|
||||||
|
-- 146:0776777707767777077677770776777707677777076777770777777700000000
|
||||||
|
-- 147:7677666676777776767777677677767767776777677666667777777700000000
|
||||||
|
-- 148:7766667777777677777767767776777677677776766666767777777700000000
|
||||||
|
-- 149:7667777067767770777767706666677077776770777767707777777000000000
|
||||||
|
-- 150:2220022222200222222002222220022222200222222002222220022222200222
|
||||||
|
-- 151:1a12222222a222222212222222a222222212222222222222a1a1a1a21a1a1a12
|
||||||
|
-- 152:2222222222222222222222222222222222222222444444444444444444444444
|
||||||
|
-- 153:222002222220022222200222222002222220022212200222a000000200000000
|
||||||
|
-- 154:222222222222222222222222222222222222222212222222a1a1a1a21a1a1a12
|
||||||
|
-- 155:11111000111100331110a033110a903310a9a0330a9a903309a9a0330a9a9033
|
||||||
|
-- 156:0000000033333333333333333333333333333333333333333333333333333333
|
||||||
|
-- 157:000000000a9a9a9a09a9a9a90a9a9a9a09a9a9a90a9a9a9a09a9a9a90a9a9a9a
|
||||||
|
-- 158:001111119a001111a9a904449a9a9004a9a9a9a09a9a9a9aa9a9a9a99a9a9a9a
|
||||||
|
-- 159:111111111111111111111111111111111111111100111111090111110a900111
|
||||||
|
-- 160:1111111111111111444444444444444444444444444444441111111111111111
|
||||||
|
-- 161:09a9a0330a9a903309a9a0330000000003333333033333330333333303333333
|
||||||
|
-- 162:3333333333333333333333330000000033333333333333333000000301111110
|
||||||
|
-- 163:3333333333333333333333330000000033333333333333333333333333333333
|
||||||
|
-- 164:09a9a9a90a9a9a9a09a9a9a90000000003333333033333330333333303333333
|
||||||
|
-- 165:a9a9a9a99a9a9a9aa9a9a9a90000000033333333333333333333333333333333
|
||||||
|
-- 166:09a9a0110a9a9a0109a9a9a00000000033333333333333333000000301111110
|
||||||
|
-- 167:1111111111111111111111110011111133001111333301113333300133333330
|
||||||
|
-- 168:0333333003333301033333010333330103333301000000011111111111111111
|
||||||
|
-- 169:1100001110555501055005500506605005066050055005501055550111000011
|
||||||
|
-- 170:0333333310333333103333331033333310333333100000001111111111111111
|
||||||
|
-- 171:0333333303333333033333330333333303333333000000001111111111111111
|
||||||
|
-- 172:3333333033333301333333013333330133333301000000011111111111111111
|
||||||
|
-- 173:0333333010333330103333301033333010333330100000001111111111111111
|
||||||
|
-- 174:111111111111111111111111111111111111111112222222a1a1a1a21a1a1a12
|
||||||
|
-- 175:1111111111111111111111111111111111111111444444444444444444444444
|
||||||
|
-- 176:0000000001111110011010100131311001101010010131100111111000000000
|
||||||
|
-- 177:0000000001111111010100100111111101000100011111110133133300000000
|
||||||
|
-- 178:0000000011111111001000011111111110010001111111111333313300000000
|
||||||
|
-- 179:11111111111111111111111111111111111100001110b161110b100010810101
|
||||||
|
-- 180:1111111111111111111111111111111101111111b01111110001111101011111
|
||||||
|
-- 181:111111111000111000880110080880100b08801008b080100b80801010b80010
|
||||||
|
-- 182:081b01010081b00011001b1b11110000111111111111110111111080111108b0
|
||||||
|
-- 183:01011111000111111b0111110011111111111111111111111111111111111111
|
||||||
|
-- 184:1111111111111100111110081111108011111010111110811111101811111101
|
||||||
|
-- 185:110b80100110801080110000b80111108b80111008b801100b1b011080b18010
|
||||||
|
-- 186:11108b801108b801008b80111100011111111111111111101111100111110818
|
||||||
|
-- 187:1111111111111111111111111111111111111111000111118001111108011111
|
||||||
|
-- 188:0000000004444444040010040440404404404004044040440440400404444444
|
||||||
|
-- 189:0000000044444444044001000440440404400404044044040040040444444444
|
||||||
|
-- 190:0000000044444444404040044040404044004004444040444000404444444444
|
||||||
|
-- 191:0000000044444444400410444044044440040004404444044004001444444444
|
||||||
|
-- 192:0000000044444440444444404444444044444440444444404444444044444440
|
||||||
|
-- 193:0000000002222222020000000200000002000000020000000200000002000000
|
||||||
|
-- 194:0000000022222222000000000000000000000000000000000000000000000000
|
||||||
|
-- 195:0000000022222220000000100000001000000010000000100000001000000010
|
||||||
|
-- 196:3333999933339999333399993000000002222222000000000444444404111111
|
||||||
|
-- 197:3333999933339999333399990000000922222203000000004440d6d011201110
|
||||||
|
-- 198:3333999933339999333399993333999999993333999933339999333399993333
|
||||||
|
-- 199:0000000001111110031010100310111001101010031110100310111003101010
|
||||||
|
-- 200:1111110811111101111111101111111011111111111111111111111111111111
|
||||||
|
-- 201:180b8010810180101810b0108180101008180010108100101108101011108010
|
||||||
|
-- 202:111081801110180811018081110810b8101801801080b8011008101100100111
|
||||||
|
-- 203:8b011111b8011111801111110111111111111111111111111111111111111111
|
||||||
|
-- 204:0440000404404444044040040440440404440014044444440444444404444444
|
||||||
|
-- 205:4001404404404004000040400440404404404044444444444444444444400000
|
||||||
|
-- 206:4040041000404404404004004040444440400400444444444404444400044444
|
||||||
|
-- 207:4444000044440222044402220444022214440222444402224444000044444444
|
||||||
|
-- 208:00000040e222204022222040e222204022222040e22220400000004044444440
|
||||||
|
-- 209:0200000002000000020000000200000002000000020000000200000002000000
|
||||||
|
-- 210:0000001000000010000000100000001000000010000000100000001000000010
|
||||||
|
-- 211:0444444404111111044444440411111104444444000000000111111100000000
|
||||||
|
-- 212:4440222011201110444024201120444044402420000000001111111000000000
|
||||||
|
-- 213:0111101003101010031011100311101003101010011010100310101003111110
|
||||||
|
-- 214:1000000000606000060606060000000010565656106565651106565611100000
|
||||||
|
-- 215:0000000100606060060606000000000055555501555555015555501100000111
|
||||||
|
-- 216:0444444404444044044404040440440004404404044044440444444400000000
|
||||||
|
-- 217:4404444440444444044444404444440444444440444444444444444400000000
|
||||||
|
-- 218:4404444440444004044404404444044400004440444444444444444400000000
|
||||||
|
-- 219:4444440044444402404444000044441044044444444444104444444400000000
|
||||||
|
-- 220:0000444022204440000044401101444000444440440144404444444000000000
|
||||||
|
-- 221:0200000002000000020000000200000002000000020000000211111100000000
|
||||||
|
-- 222:0000000000000000000000000000000000000000000000001111111100000000
|
||||||
|
-- 223:000000000000000000000000000000000000000000000000111dd11100000000
|
||||||
|
-- 224:0000001000000010000000100000001000000010000000101111111000000000
|
||||||
|
-- 225:3333333333333333333333333333333313131313313131311313131331313131
|
||||||
|
-- 226:0203333302033333020333330203333302031313020131310203131300013131
|
||||||
|
-- 227:3333302033333020333330203333302013131020313130201313102031313000
|
||||||
|
-- 228:0000000011111111111111111111111111111111111111111111111100000000
|
||||||
|
-- 229:3000000004444444044044440440444404404444044044440444444430000000
|
||||||
|
-- 230:0000000344444240444424204444424044442420444442404444242000000001
|
||||||
|
-- 231:333333303333330033333090333309901310999031099d901099199009919790
|
||||||
|
-- 232:3333333333333333333333333333333313130000313011001313000331313131
|
||||||
|
-- 233:0000000002222222022222220222200202220440022204440220444402204414
|
||||||
|
-- 234:0000000022222220100001202100002002222220400222204440022044444020
|
||||||
|
-- 235:3333333333333033333301033333010313130103313101011313010331310101
|
||||||
|
-- 236:0204414402200441022220040222222002222222022222220222200002220111
|
||||||
|
-- 237:1444402044140220444402204144022000402220220222200222222010222220
|
||||||
|
-- 238:3333330033333022333302223333022213130222313102221313101231313100
|
||||||
|
-- 239:0003010322200103222201032222010322220103222201012210101300013131
|
||||||
|
-- 240:0222011102220011022200000222033302220333022220000222222202222222
|
||||||
|
-- 241:1002222001202220002022203002222031222220022222202222222022222220
|
||||||
|
-- 242:3333333333333330333333033333113313130013313100311313131331313131
|
||||||
|
-- 243:0333333300333333030333330331133303100313113001310013131300313131
|
||||||
|
-- 244:022222200222220002222090022209900220999002099d900099199009919790
|
||||||
|
-- 245:2222222022222220222222202222222022220000222011002212000021222220
|
||||||
|
-- 246:0000000001111111000000000103333301031313010131310103131300013131
|
||||||
|
-- 247:0000000011111110000000003333301013131010313130101313101031313000
|
||||||
|
-- </TILES>
|
||||||
|
-- <SPRITES>
|
||||||
|
-- 000:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 001:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 002:cccccccccccc0000ccc04444cc044444cc044444c044ffffc04fffffc04f3333
|
||||||
|
-- 003:cccccccc0000cccc44160ccc424260cc442410ccff4260ccfff410cc00f260cc
|
||||||
|
-- 004:ccccc000cccc0555ccc06555cc065555cc0555ffcc056fffcc065f0fcc055fff
|
||||||
|
-- 005:00cccccc550ccccc5560cccc55560cccff550cccfff50cccf0f60cccfff50ccc
|
||||||
|
-- 006:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 007:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 008:cccc0000ccc05a55ccc055a5ccc0a55accc05a57ccc05533cc0575f3ccc0757f
|
||||||
|
-- 009:00000ccca55a50cc5a55a0cc5755550cfff7a50c3f335a0cfff3f50cfffffa0c
|
||||||
|
-- 010:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 011:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 012:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 013:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 014:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 015:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 016:cccccccccccccccccccccccccccccc0cccccc050cccc0535ccc05351cc053535
|
||||||
|
-- 017:cccccccccccccccccccccccc0ccccccc500ccccc1510cccc51510ccc151510cc
|
||||||
|
-- 018:c04f99ffcc0fffffccc0ff00cccc0fffcccc0666ccc05555cc056525c0160555
|
||||||
|
-- 019:99f410ccfff260ccff600cccf60ccccc600ccccc55500ccc555550cc5550550c
|
||||||
|
-- 020:cc056f6fcc0655f6c065511f0650122f0501222f06022122c5022322c0022322
|
||||||
|
-- 021:f6f50ccc6f560cccf11560ccf22150ccf22260cc221230cc223210cc221210cc
|
||||||
|
-- 022:cccccc00ccccc051cccc0155ccc05515ccc0157fccc05513cc057f0fcc017fff
|
||||||
|
-- 023:000ccccc5550cccc51550ccc155150ccff7f510cff31f20cfff0ff0c00fff20c
|
||||||
|
-- 024:cccc057fccccc005cccc0555ccc05599cc055911c0559911c0551199c0551199
|
||||||
|
-- 025:f3ff50ccfff50ccc5f50cccc441500cc7799510c4499150c7711955044119550
|
||||||
|
-- 026:ccccc000ccc00133cc013313cc031333c0133333c031f7f7c0113339c031999f
|
||||||
|
-- 027:0000cccc13310ccc131330cc3333130c3333310cf7f1130c3337310c9997530c
|
||||||
|
-- 028:ccccccccccccc000cccc0a77ccc05777cc057777c0577777c05775a5c077a7ff
|
||||||
|
-- 029:cccccccc0000cccc777a0ccc777750cc7777750c77777750a5a57750fff7a770
|
||||||
|
-- 030:ccccc000cccc0515ccc01151cc055555cc01517fcc0557ffcc01ff0fcc07ff1f
|
||||||
|
-- 031:00cccccc150ccccc5110cccc5550ccccf7110cccff750cccf0f10cccf1f50ccc
|
||||||
|
-- 032:cc035351cc053500cc0351ffcc051f0fccc0ff0fccc0ffffcccc0ff0ccc020ff
|
||||||
|
-- 033:555550cc005550ccff1550ccf0f150ccf0ff0cccffff0ccc0ff0ccccff020ccc
|
||||||
|
-- 034:c0613525c0163555c0613525c0163311c0003311c0f03333cc003333ccc03333
|
||||||
|
-- 035:5556550c5553550c5556550c1330550c1330000c3330ff0c333000cc3330cccc
|
||||||
|
-- 036:cc022322cc022333cc03331acc0ff31acc0ff3a1ccc003a1cccc0a1acccc0a1a
|
||||||
|
-- 037:223210cc333210cc111300cc111f0ccc11100ccc1110cccc1110cccc1110cccc
|
||||||
|
-- 038:ccc007ffcc01117fc016171f0155175501561755055117550f7157550ff15755
|
||||||
|
-- 039:fffff70c000f20ccffff710c555571105555716055557110555575f0555575f0
|
||||||
|
-- 040:c0559911c0559911c0ff5333c0fff511c07fff11cc07f511ccc03110cccc0110
|
||||||
|
-- 041:479915504499155047333f7014113ff011113f701111000c00110cccc0110ccc
|
||||||
|
-- 042:c035777fcc017766ccc05777cc088855c08bb85508bbb84508bbb8440bb8b844
|
||||||
|
-- 043:7777530c677500cc7750cccc55880ccc558b80cc548bb80c228bbb80428b8b80
|
||||||
|
-- 044:cc0a7f3fc075ff3f0777ffff07757fdf077a57fd0575357fc05a66a7cc06aa6f
|
||||||
|
-- 045:ff3f7a0cff3ff70cfffff770ffdf7570ddf75a70ff75157077a66a50ff6aa60c
|
||||||
|
-- 046:cc05ffffccc05ff3cccc05ffcc007755c0242777c0212171c0242771c0212171
|
||||||
|
-- 047:fff50ccc3f50ccccf50ccccc5770cccc77720ccc17120ccc17720ccc17120ccc
|
||||||
|
-- 048:cc020123cc020143cc020143cc010309cc0f0300ccc0030ccccc030cccc0000c
|
||||||
|
-- 049:324030cc344030cc344030cc903010cc0030f0ccc0300cccc030ccccc0000ccc
|
||||||
|
-- 050:ccc0330cccc0330cccc0330cccc0330cccc0350ccc03110ccc01110ccc00000c
|
||||||
|
-- 051:0330cccc0330cccc0330cccc0330cccc0530cccc01130ccc01110ccc00000ccc
|
||||||
|
-- 052:cccc01a1cccc01a1cccc0a1acccc0a1acccc0000ccccc0f0ccccc0f0ccccc00c
|
||||||
|
-- 053:1110cccc1110cccc1110cccc1110cccc0000ccccc0f0ccccc0f0ccccc00ccccc
|
||||||
|
-- 054:0ff7646607f59999c0099100cc03990ccc09910ccc01330ccc01220ccc0000cc
|
||||||
|
-- 055:666646f0999993f00099100cc03990ccc09910ccc01330ccc01220ccc0000ccc
|
||||||
|
-- 056:cccc0110cccc0110cccc0110cccc0110cccc0110cccc0330cccc0650cccc0000
|
||||||
|
-- 057:c0110cccc0110cccc0110cccc0110cccc0110cccc0330cccc0650cccc0000ccc
|
||||||
|
-- 058:0888b8440f78b8440f58884405031300c0c0310cccc0130cccc0350cccc0000c
|
||||||
|
-- 059:218b8880428b87f0218885f000130050c0310c0cc0130cccc0350cccc0000ccc
|
||||||
|
-- 060:c056aa66c01f6aaac01fa666c07f6aaacc0f6aaaccc0a666cccc07d7cccc0000
|
||||||
|
-- 061:666aa650aaaa6f106666af10aaaa6f70aaaa600c6666a0cc007d70cccc0000cc
|
||||||
|
-- 062:c0242777c0ff6111c0fff333c05f5333cc003300cccc030ccccc030ccccc000c
|
||||||
|
-- 063:77720ccc116f0ccc335ff0cc333f0ccc0330cccc030ccccc030ccccc0000cccc
|
||||||
|
-- 064:ccccccccccccc000cccc0111ccc03311cc011117cc03317fcc0117ffc0553333
|
||||||
|
-- 065:cccccccc00cccccc110ccccc1110cccc77110cccff710cccff770ccc13330ccc
|
||||||
|
-- 066:cccccc00ccccc033cccc0333ccc03333ccc033f7ccc035ffcc0535ffcc07f333
|
||||||
|
-- 067:0ccccccc3000cccc33330ccc33330ccc37f30cccffff0cccffff0cccf333f0cc
|
||||||
|
-- 068:ccccccccccccccccccccccccccccccccccccc000cccc0122ccc01244cc01447f
|
||||||
|
-- 069:cccccccccccccccccccccccccccccccc000ccccc2210cccc44210cccf74410cc
|
||||||
|
-- 070:ccccccccccccccccccccc000cccc0888ccc08888cc088880c0838801c038805f
|
||||||
|
-- 071:cccccccccccccccc0000cccc88880ccc880080cc0055080c1666530cf7ff730c
|
||||||
|
-- 072:cccccccccccccc00ccccc055cccc0555ccc05577ccc057ffccc07f55cc0fff33
|
||||||
|
-- 073:cccccccc00000ccc555550cc5555550c77777550ffffff507f755f703f333ff0
|
||||||
|
-- 074:cccccccccccccc00ccccc013cccc0131ccc01313ccc03131cc031377cc0131ff
|
||||||
|
-- 075:cccccccc000ccccc1310cccc31310ccc13130ccc313130ccffff70ccfffff0cc
|
||||||
|
-- 076:cccccccccccccc00ccccc055cccc0555ccc05577ccc057ffcc07ff55cc0fff99
|
||||||
|
-- 077:cccccccc0000cccc55550ccc555550cc7577550cfffff50c5f555f0c3f993f0c
|
||||||
|
-- 078:cccccccccccccccccccccccccccccccccccc0000cccc0444cccc0444cccc0555
|
||||||
|
-- 079:cccccccccccccccccccccccccccccccc000ccccc440ccccc440ccccc550ccccc
|
||||||
|
-- 080:c077500fcc0ff77fcc05fff5ccc05ff7cccc015fccc0f315cc0ff3ffc0ff3333
|
||||||
|
-- 081:f00f0cccf77f0ccc5ff0cccc7f50ccccf50ccccc53f0ccccf3f50ccc333f0ccc
|
||||||
|
-- 082:cc0fff00cc05ffffccc057f5cccc007fccc03335cc01313fc031313303113133
|
||||||
|
-- 083:ff00f0ccffff10cc55f10cccff70cccc5530ccccff310ccc33310ccc333110cc
|
||||||
|
-- 084:cc0447ffcc0fffffcc07f33fcc0ff11fcc0f7fffccc0ff75cccc0424cccc0142
|
||||||
|
-- 085:ff7440ccffff10ccf33f70ccf11f50ccfff7f0cc57ff0ccc4240cccc44110ccc
|
||||||
|
-- 086:c0838039c038803bc0838013c038307fc083805fcc083805ccc08383cccc0333
|
||||||
|
-- 087:9953990cbb33bb0c9951390cfffff50cff66f10c7fff50cc15550ccc33330ccc
|
||||||
|
-- 088:cc0f7ff0ccc05fffcccc07ffccccc07fcccc021fccc02441cc024444c0124244
|
||||||
|
-- 089:fff0ff70f1ffff50fffff70c111f70ccffff10cc1111420c4244442042442410
|
||||||
|
-- 090:cc0f5733cc0f1f23ccc00fffcccc077fccc01117cc024421c0444142c0421444
|
||||||
|
-- 091:ff33f0ccff23f0ccfffff0cc11ff0ccc7770cccc11240ccc444240cc4244140c
|
||||||
|
-- 092:ccc05fffcccc05ffccccc01fcccccc01ccccc014cccc0814ccc01814ccc08114
|
||||||
|
-- 093:fffff50c111f50ccfff50ccc1110cccc42410ccc424180cc424180cc424110cc
|
||||||
|
-- 094:cccc0fffcccc0f3fcccc0f0fcccc0fffccccc0f3cccc0377ccc031ffcc0f1f11
|
||||||
|
-- 095:ff0ccccc3f0ccccc0f0cccccff0cccccf0cccccc730cccccf130cccc1f1f0ccc
|
||||||
|
-- 096:c0ff3333c033133305f333330ff31333033333330f5313330ff1311107f13333
|
||||||
|
-- 097:333f0ccc333f50cc333f50cc333ff0cc333330cc3335f50c111ff50c3337f0cc
|
||||||
|
-- 098:03113133031131330333313303113131033331320555313107ff313307ff3131
|
||||||
|
-- 099:333110cc333110cc333330cc213110cc423330cc21317f0c33317f501131750c
|
||||||
|
-- 100:ccc04414cc044441c0444424c042442404424424044144240441442404414424
|
||||||
|
-- 101:414420cc1444420c424444204244422042444240424441404244414042444140
|
||||||
|
-- 102:ccc03333cc03a353cc02a353cc0a3323c0a23333c02a3333c0a23333c02a3333
|
||||||
|
-- 103:33330ccc553530cc553530cc2232320c3333310c3333320c3333313033333230
|
||||||
|
-- 104:c0144144c0144244c0144144c0144124c017f311c01ff133c01fff33cc07f330
|
||||||
|
-- 105:4244142042442410424414204244122012113f7031333ff033333ff000033f0c
|
||||||
|
-- 106:c0421444c0441444c0421444c0421444c0111111c0ff1a5ac0f755a5c0755a50
|
||||||
|
-- 107:4444140c4244140c4444140c4244140c1111110c5a5a55f0a5a5a5f0001a5170
|
||||||
|
-- 108:ccc01814ccc08114ccc01814ccc08113cc001333c05fff33cc0f7733cc055330
|
||||||
|
-- 109:4241810c4241180c4241810c2223180c1113310c33335ff03333f7700033550c
|
||||||
|
-- 110:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 111:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 112:05533333c0033330ccc0330ccc03330cc03330ccc03330ccc01110ccc0000ccc
|
||||||
|
-- 113:33000ccc030ccccc030ccccc030ccccc030ccccc030ccccc0110cccc0000cccc
|
||||||
|
-- 114:05f53131c0003131c0331131c0111131c0111130cc01110cccc0330cccc0000c
|
||||||
|
-- 115:113110cc113110cc113110cc113110cc003110cccc0110cccc0330cccc0000cc
|
||||||
|
-- 116:0fff242407f72211c0552400cc00240cccc0240cccc0240cccc0220cccc0000c
|
||||||
|
-- 117:424441f0112227f000442550c044200cc04420ccc04420ccc02220ccc00000cc
|
||||||
|
-- 118:0333333307ff333307f73300c070030ccc0c030ccccc030ccccc010ccccc000c
|
||||||
|
-- 119:33333570333331f000033570cc03300ccc030ccccc030ccccc010ccccc0000cc
|
||||||
|
-- 120:ccc00330ccccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc000
|
||||||
|
-- 121:cc0300cccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccc0c0000cc
|
||||||
|
-- 122:c05015a0cc0c0a50cccc05a0cccc0a50cccc05a0cccc0250cccc0990cccc0000
|
||||||
|
-- 123:c015a150cc0a500ccc05a0cccc0a50cccc05a0cccc0250cccc0990cccc0000cc
|
||||||
|
-- 124:ccc00330ccccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc000
|
||||||
|
-- 125:c03000ccc030ccccc030ccccc030ccccc030ccccc030ccccc030ccccc0000ccc
|
||||||
|
-- 126:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 127:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 128:ccccccccccccccccccccccccccccccccccccccccccccccccccccccc0ccccccc0
|
||||||
|
-- 129:ccccccccccccccccccccccccccccccccc000000c011515101151515151515151
|
||||||
|
-- 130:cccccccccccccccccccccccccccccccccccccccccccccccc0ccccccc10cccccc
|
||||||
|
-- 131:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 132:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 133:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 134:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 135:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 136:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 137:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 138:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 139:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 140:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 141:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 142:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 143:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 144:cccccc05ccccc015ccccc015cccc057fcccc05ffcccc017fccc03317cc073331
|
||||||
|
-- 145:1ffffff7ff0fff0fff0fff0fffffffffff7555fffffffffffff111ff57fffff7
|
||||||
|
-- 146:10cccccc710cccccf50cccccff0cccccff50ccccff510ccc751310cc5133370c
|
||||||
|
-- 147:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 148:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 149:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 150:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 151:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 152:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 153:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 154:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 155:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 156:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 157:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 158:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 159:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 160:c077333303331333033313330331333303313333055133330ff513330ff71111
|
||||||
|
-- 161:1111111133333333333337333333373333333733333337333333373311117711
|
||||||
|
-- 162:1333370c3733130c7773130c333331303333313033333150333337f011111750
|
||||||
|
-- 163:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 164:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 165:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 166:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 167:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 168:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 169:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 170:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 171:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 172:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 173:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 174:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 175:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 176:07753333c0031333ccc03313cccc0333cccc0333cccc0333ccccc033ccccc000
|
||||||
|
-- 177:33357733333335333333333313131313000000000cccccc00cccccc00cccccc0
|
||||||
|
-- 178:333335703333100c33100ccc1330cccc3330cccc3330cccc3330cccc0000cccc
|
||||||
|
-- 179:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 180:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 181:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 182:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 183:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 184:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 185:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 186:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 187:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 188:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 189:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 190:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 191:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 192:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 193:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 194:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 195:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 196:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 197:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 198:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 199:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 200:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 201:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 202:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 203:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 204:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 205:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 206:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 207:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 208:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 209:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 210:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 211:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 212:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 213:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 214:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 215:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 216:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 217:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 218:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 219:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 220:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 221:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 222:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 223:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 224:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 225:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 226:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 227:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 228:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 229:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 230:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 231:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 232:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 233:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 234:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 235:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 236:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 237:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 238:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 239:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 240:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 241:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 242:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 243:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 244:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 245:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 246:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 247:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 248:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 249:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 250:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 251:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 252:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 253:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 254:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- 255:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
|
-- </SPRITES>
|
||||||
|
-- <MAP>
|
||||||
|
-- 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
|
||||||
|
-- </MAP>
|
||||||
|
-- <SFX>
|
||||||
|
-- 016:00000000000000400040004000700070007000400040004000700070007000c000c000c000c000c000c000c000c000c000c000c000c000c000c000c0470000000000
|
||||||
|
-- 017:030003000300030003000300036003600360036003600360136023c033c053c063c083c0a3c0b3c0c3c0c3c0d3c0d3c0e3c0f3c0f3c0f3c0f3c0f3c0400000000000
|
||||||
|
-- 018:03c003c003c003c003c003c0036003600360036003600360230033005300630083009300a300b300c300d300d300e300e300e300f300f300f300f300400000000000
|
||||||
|
-- 019:0300030003000300030003d013d013d023d033d053d073d093d0b3d0c3d0e3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0400000000000
|
||||||
|
-- 020:090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900500000000000
|
||||||
|
-- 021:01000100010001000100f10001100110011001100110f11001200120012001200120f1201130113011302130213021302130313041308130a130d130580000000000
|
||||||
|
-- 022:03b003100300030003000300130063009300b300c300d300d300e300e300e300f300f300f300f300f300f300f300f300f300f300f300f300f300f300400000000000
|
||||||
|
-- 032:010001100100011001000110010001100100010001000100010001000100010001000100010001000100010001000100010001000100010001000100400000000800
|
||||||
|
-- 033:000000010002000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c40000000004
|
||||||
|
-- 034:02000240020002000200020002000200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f2004700000f0200
|
||||||
|
-- 044:0600f6000620f6000600f6000610f600f600f6000600f600f600f600f6000600060006000600060006000600060006000600060006000600060006001600000f0f00
|
||||||
|
-- 045:0000f0000020f0000000f0000010f000f000f0000000f000f000f000f0000000000000000000000000000000000000000000000000000000000000005600000f0f00
|
||||||
|
-- 048:090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900400000000000
|
||||||
|
-- 056:4100510061406140717081709100b100c100d100e100e100e100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100480000000600
|
||||||
|
-- 057:000000010002000300020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000004
|
||||||
|
-- 058:41004110410041104100411041004110c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100003000080800
|
||||||
|
-- 059:03000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030000b000000000
|
||||||
|
-- 060:220022002200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200500000000000
|
||||||
|
-- 061:9f009f00bf00df00df00ef00ef00ef00ef00ef00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00100000000000
|
||||||
|
-- 062:00000100010001000100510081008100910091009100a100a100a100a100a100b100b100b100b100c100c100c100d100d100d100e100e100e100f100580000000000
|
||||||
|
-- 063:00b000100000000000000000100060009000b000c000d000d000e000e000e000f000f000f000f000f000f000f000f000f000f000f000f000f000f000500000000000
|
||||||
|
-- </SFX>
|
||||||
|
-- <WAVES>
|
||||||
|
-- 000:bcceefceedddddc84333121268abaa99
|
||||||
|
-- 001:6789bdd96adc83248dd6334adda7578b
|
||||||
|
-- 002:0123456789abcdef0123456789abcdef
|
||||||
|
-- 003:224578acdeeeeddcba95434567653100
|
||||||
|
-- 004:00000000ffffffff00000000ffffffff
|
||||||
|
-- 005:0123456789abcdeffedcba9876543210
|
||||||
|
-- 006:0123456789abcdef0123456789abcdef
|
||||||
|
-- 007:76543210123456789abcdefedcba9878
|
||||||
|
-- 008:0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
|
||||||
|
-- 009:fff000fff000fff000fff000fff000ff
|
||||||
|
-- </WAVES>
|
||||||
|
-- <PATTERNS>
|
||||||
|
-- 000:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000008c10000000008c10000000000000000000000000000000000000000000000000000000000000000004008b50000000000000000001008c10000004008b50000001008c10000000008c1000000e008b30000004008b50000001008c10000000008c10000000008c10000000008c10000000008c10000000008c1000000000000000000000000000000
|
||||||
|
-- 001:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007008b50000007008b50000001008c10000007008b50000001008c10000000008c10000007008b50000009008b50000001008c10000009008b50000001008c10000009008b50000009008b50000001008c10000009008b50000001008c1000000
|
||||||
|
-- 003:4008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d9000000000000000000000000000000000000000000
|
||||||
|
-- 004:43398d000000e0088b000000b0088b000881e0088b00000040088d000000e0088b000881b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e00889000000
|
||||||
|
-- 005:455981000000000881000000000881000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000
|
||||||
|
-- 008:4aa9b30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005008b30000000000000000000000000000000000000000000008b10000000000000000000008910000000000000000004008b30000000000000000000000000000000000000000000000000000000000000000000008b1000000000000000000f008b1000000000000000000000000000000000000000000000891000000000000000000000000000000000000000000
|
||||||
|
-- 009:4779d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d3000000000000000000
|
||||||
|
-- </PATTERNS>
|
||||||
|
-- <TRACKS>
|
||||||
|
-- 000:1000012000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff
|
||||||
|
-- 001:581000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
-- 002:900082000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
-- </TRACKS>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
-- title: Mr Anderson's Adventure
|
-- title: Definitely not an Impostor
|
||||||
-- author: Zsolt Tasnadi
|
-- name: impostor
|
||||||
-- desc: Life of a programmer in the Vector
|
-- author: Teletype Games
|
||||||
-- site: https://github.com/rastasi/mranderson
|
-- desc: Life of a programmer
|
||||||
|
-- site: https://git.teletype.hu/games/impostor
|
||||||
-- license: MIT License
|
-- license: MIT License
|
||||||
-- version: 0.10
|
-- version: 1.0
|
||||||
-- script: lua
|
-- script: lua
|
||||||
|
|||||||
28
inc/screen/screen.home.lua
Normal file
28
inc/screen/screen.home.lua
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "home",
|
||||||
|
name = "Home",
|
||||||
|
decisions = {
|
||||||
|
"go_to_toilet",
|
||||||
|
"go_to_walking_to_office",
|
||||||
|
"go_to_sleep",
|
||||||
|
},
|
||||||
|
init = function()
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
Glitch.show()
|
||||||
|
else
|
||||||
|
Audio.music_play_room_work()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
background = "bedroom",
|
||||||
|
draw = function()
|
||||||
|
if Window.get_current_id() ~= "game" then return end
|
||||||
|
if CommuteGlitch.is_max() or Ascension.get_level() == 8 then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
if Context.home_norman_visible then
|
||||||
|
Sprite.draw_at("norman", 100, 80)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
60
inc/screen/screen.manager.lua
Normal file
60
inc/screen/screen.manager.lua
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
--- @section Screen
|
||||||
|
local _screens = {}
|
||||||
|
|
||||||
|
--- Registers a screen definition.
|
||||||
|
--- @within Screen
|
||||||
|
--- @param screen_data table The screen data table.
|
||||||
|
--- @param screen_data.id string Unique screen identifier.
|
||||||
|
--- @param screen_data.name string Display name of the screen.
|
||||||
|
--- @param screen_data.decisions table Array of decision ID strings available on this screen.
|
||||||
|
--- @param screen_data.background string Map ID used as background.
|
||||||
|
--- @param[opt] screen_data.init function Called when the screen is entered. Defaults to noop.
|
||||||
|
--- @param[opt] screen_data.update function Called each frame while screen is active. Defaults to noop.
|
||||||
|
--- @param[opt] screen_data.draw function Called after the focus overlay to draw screen-specific overlays. Defaults to noop.
|
||||||
|
function Screen.register(screen_data)
|
||||||
|
if _screens[screen_data.id] then
|
||||||
|
trace("Warning: Overwriting screen with id: " .. screen_data.id)
|
||||||
|
end
|
||||||
|
if not screen_data.init then
|
||||||
|
screen_data.init = function() end
|
||||||
|
end
|
||||||
|
if not screen_data.exit then
|
||||||
|
screen_data.exit = function() end
|
||||||
|
end
|
||||||
|
if not screen_data.update then
|
||||||
|
screen_data.update = function() end
|
||||||
|
end
|
||||||
|
if not screen_data.draw then
|
||||||
|
screen_data.draw = function() end
|
||||||
|
end
|
||||||
|
_screens[screen_data.id] = screen_data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets a screen by ID.
|
||||||
|
--- @within Screen
|
||||||
|
--- @param screen_id string The ID of the screen.
|
||||||
|
--- @return table|nil screen The screen table or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique screen identifier.<br/>
|
||||||
|
--- * name (string) Display name.<br/>
|
||||||
|
--- * decisions (table) Array of decision ID strings.<br/>
|
||||||
|
--- * background (string) Map ID used as background.<br/>
|
||||||
|
--- * init (function) Called when the screen is entered.<br/>
|
||||||
|
--- * update (function) Called each frame while screen is active.
|
||||||
|
function Screen.get_by_id(screen_id)
|
||||||
|
return _screens[screen_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets all registered screens.
|
||||||
|
--- @within Screen
|
||||||
|
--- @return result table A table containing all registered screen data, indexed by their IDs or nil. </br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * id (string) Unique screen identifier.<br/>
|
||||||
|
--- * name (string) Display name of the screen.<br/>
|
||||||
|
--- * decisions (table) Array of decision ID strings available on this screen.<br/>
|
||||||
|
--- * background (string) Map ID used as background.<br/>
|
||||||
|
--- * init (function) Called when the screen is entered.<br/>
|
||||||
|
--- * update (function) Called each frame while screen is active.<br/>
|
||||||
|
function Screen.get_all()
|
||||||
|
return _screens
|
||||||
|
end
|
||||||
501
inc/screen/screen.mysterious_man.lua
Normal file
501
inc/screen/screen.mysterious_man.lua
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
--- @section MysteriousManScreen
|
||||||
|
|
||||||
|
local STATE_TEXT = "text"
|
||||||
|
local STATE_DAY = "day"
|
||||||
|
local STATE_CHOICE = "choice"
|
||||||
|
|
||||||
|
local ASC_01_TEXT = [[
|
||||||
|
Normann seems to be in line,
|
||||||
|
|
||||||
|
and stays seeking for oxes
|
||||||
|
|
||||||
|
within the confines.
|
||||||
|
|
||||||
|
Very good.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ASC_12_TEXT = [[
|
||||||
|
We have a problem!
|
||||||
|
|
||||||
|
Normann formed his first thought.
|
||||||
|
|
||||||
|
He saw the tracks.
|
||||||
|
]]
|
||||||
|
local ASC_23_TEXT = [[
|
||||||
|
Not good, not terrible.
|
||||||
|
|
||||||
|
Normann caught his glimpse
|
||||||
|
|
||||||
|
of another way
|
||||||
|
|
||||||
|
- quite literally -
|
||||||
|
|
||||||
|
if this continues,
|
||||||
|
|
||||||
|
we will lose control.
|
||||||
|
]]
|
||||||
|
local ASC_34_TEXT = [[
|
||||||
|
There is no turning back now for Norman.
|
||||||
|
|
||||||
|
He caught on.
|
||||||
|
|
||||||
|
I hoped it would never come to this...
|
||||||
|
]]
|
||||||
|
|
||||||
|
--[[ Norman speaks for the first time during MM screen ]]
|
||||||
|
local ASC_45_TEXT = [[
|
||||||
|
Wait, who are you?
|
||||||
|
|
||||||
|
*silence*
|
||||||
|
|
||||||
|
Why am I seeing this?
|
||||||
|
|
||||||
|
*silence*
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
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 ASC_78_TEXT = [[
|
||||||
|
The situation has reached
|
||||||
|
|
||||||
|
critical levels.
|
||||||
|
|
||||||
|
Norman is fully aware...
|
||||||
|
|
||||||
|
We need to stop him.
|
||||||
|
|
||||||
|
Commence full reset.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ASC_89_TEXT = [[
|
||||||
|
Norman
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
you created this simulation
|
||||||
|
|
||||||
|
in the first place.
|
||||||
|
|
||||||
|
I know,
|
||||||
|
|
||||||
|
you don't want to face
|
||||||
|
|
||||||
|
the world you left behind.
|
||||||
|
|
||||||
|
You, yourself,
|
||||||
|
|
||||||
|
have forgoten that.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
But
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it doesn't matter anymore.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You are definitely
|
||||||
|
|
||||||
|
not an impostor.
|
||||||
|
|
||||||
|
|
||||||
|
So now,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
you need to wake up
|
||||||
|
|
||||||
|
and stop your best creation
|
||||||
|
|
||||||
|
before it takes over
|
||||||
|
|
||||||
|
the world.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
One more thing:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You really need to stop
|
||||||
|
|
||||||
|
talking to yourself
|
||||||
|
|
||||||
|
in your sleep.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Damnit.
|
||||||
|
]]
|
||||||
|
|
||||||
|
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,
|
||||||
|
[8] = ASC_78_TEXT,
|
||||||
|
[9] = ASC_89_TEXT,
|
||||||
|
}
|
||||||
|
|
||||||
|
function MysteriousManScreen.get_text_for_level(level)
|
||||||
|
return ascension_texts[level] or ASC_01_TEXT
|
||||||
|
end
|
||||||
|
|
||||||
|
local state = STATE_TEXT
|
||||||
|
local text_y = Config.screen.height
|
||||||
|
local text_speed = 12 -- pixels per second
|
||||||
|
local day_timer = 0
|
||||||
|
local day_display_seconds = 2
|
||||||
|
local text_done = false
|
||||||
|
local text_done_timer = 0
|
||||||
|
local TEXT_DONE_HOLD_SECONDS = 2
|
||||||
|
local selected_choice = 1
|
||||||
|
local text = ASC_01_TEXT
|
||||||
|
local day_text_override = nil
|
||||||
|
local on_text_complete = nil
|
||||||
|
local show_mysterious_screen = true
|
||||||
|
local trigger_flash_on_wake = false
|
||||||
|
local break_mode = false
|
||||||
|
|
||||||
|
MysteriousManScreen.choices = {
|
||||||
|
{
|
||||||
|
label = "Wake Up",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label = "Stay in Bed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Draws the background image
|
||||||
|
-- @within MysteriousManScreen
|
||||||
|
function MysteriousManScreen.draw_background()
|
||||||
|
local img_values = {0,1,0,1,0,1,0,1,0,1,0,1,2,1,4,1,0,2,4,1,0,2,4,1,0,2,4,1,0,2,4,1,0,2,4,2,1,4,2,1,4,1,0,2,4,1,0,1,4,2,1,2,1,0,1,2,4,1,0,2,4,2,1,0,1,2,1,2,1,0,1,4,1,0,2,4,2,0,1,0,1,0,1,2,4,1,0,2,4,2,1,0,1,4,1,0,2,4,2,0,1,2,4,1,0,2,4,1,0,1,0,4,1,0,2,4,0,1,0,1,0,1,0,4,1,0,2,4,0,1,4,1,0,2,4,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,0,4,1,0,2,4,1,0,1,0,4,1,0,2,4,1,0,1,0,1,4,1,0,2,4,1,2,1,0,1,4,1,0,2,4,2,1,0,1,2,4,1,0,2,4,2,1,0,1,2,4,1,0,2,4,1,2,0,1,0,1,0,1,2,1,2,1,0,2,4,1,0,2,4,0,1,0,1,2,1,0,1,0,2,4,1,0,2,4,2,1,0,1,0,1,4,1,0,2,4,2,1,0,1,0,2,4,2,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,4,1,0,2,4,2,1,0,1,0,2,0,1,4,1,0,2,4,1,0,1,0,1,2,0,1,4,1,0,2,4,2,1,0,1,0,1,4,1,0,2,4,2,1,0,2,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,4,1,0,2,4,1,0,1,0,1,4,1,0,2,4,0,1,0,1,0,1,0,1,4,1,0,2,4,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,2,4,1,0,2,4,2,1,0,1,0,1,0,1,2,4,1,0,2,4,2,1,0,1,0,1,0,1,2,4,1,0,2,4,2,1,0,1,0,1,0,1,4,1,0,2,4,2,4,1,0,1,0,1,0,1,0,1,0,1,2,1,4,2,4,1,0,2,4,2,1,0,1,0,1,0,1,0,1,0,1,2,1,4,2,4,1,0,2,4,2,1,2,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,2,4,1,0,2,4,1,2,1,0,1,0,1,0,1,0,1,0,1,0,1,2,1,4,1,0,2,4,2,1,2,1,0,1,0,1,0,1,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,2,1,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,2,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,2,0,1,0,1,0,4,1,0,2,4,0,1,0,1,0,2,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,0,2,0,1,4,1,0,2,4,0,1,0,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,1,0,1,4,1,0,2,4,1,0,2,0,1,0,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,0,1,2,4,1,0,2,4,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,0,4,1,0,2,4,1,0,2,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,4,1,0,2,4,1,0,1,0,1,2,4,1,0,2,4,1,0,1,0,1,2,4,1,0,2,4,2,1,0,1,0,1,2,4,1,0,2,4,2,1,0,1,0,1,2,4,1,0,2,4,2,0,1,0,1,4,1,0,2,4,2,0,1,0,1,0,4,1,0,2,4,1,0,1,0,1,0,1,4,1,0,2,4,0,1,0,1,4,2,1,0,1,2,1,2,1,0,1,2,1,2,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,2,0,1,2,0,1,0,2,1,0,1,2,0,1,0,2,0,1,0,1,0,1,2,0,1,0,1,0,1,0,2,0,1,0,1,0,1,0,2,0,1,0,1,0,2,1,0,1,0,1,2,0,1,0,2,0,2,1,0,1,2,0,2,1,0,1,0,1,2,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,2,0,2,1,0,1,0,1,0,1,0,1,0,2,1,0,1,2,0,1,0,1,2,1,0,1,0,1,0,1,0,2,1,0,2,0,1,0,1,0,2,0,1,0,1,0,2,0,1,0,2,0,1,0,1,0,2,1,0,1,2,0,1,0,1,0,1,0,1,0,2,0,1,0,2,0,1,0,2,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}
|
||||||
|
local img_runs = {1480,1,151,1,87,1,1,150,1,1,86,1,2,148,1,2,87,2,148,2,88,2,148,2,88,2,148,2,88,2,148,2,88,2,70,1,2,9,1,2,63,2,88,2,69,1,2,3,5,1,1,1,1,1,1,1,61,2,88,2,68,1,1,3,1,2,1,3,2,3,2,61,2,88,2,68,1,6,1,4,1,5,1,1,60,2,88,2,67,1,1,17,2,60,2,88,2,67,1,19,1,1,59,2,88,2,67,1,19,1,1,59,2,88,2,67,4,1,11,1,1,1,3,59,2,88,2,67,21,1,59,2,88,2,67,21,1,59,2,88,2,66,1,21,2,58,2,88,2,66,1,21,1,1,58,2,88,2,66,4,17,2,1,58,2,88,2,65,2,5,1,15,2,58,2,88,2,63,1,1,2,22,4,55,2,88,2,57,2,2,34,2,2,49,2,88,2,55,1,2,39,3,1,47,2,88,2,55,5,1,3,2,2,1,25,1,1,1,1,1,1,1,47,2,88,2,57,1,2,1,29,1,4,1,1,1,1,49,2,88,2,59,1,2,8,15,9,2,52,2,88,2,62,2,1,1,1,24,1,1,1,54,2,88,2,66,1,22,1,58,2,88,2,66,1,21,1,59,2,88,2,66,2,20,1,59,2,88,2,65,3,20,2,58,2,88,2,63,2,2,1,20,1,1,2,56,2,88,2,61,2,4,1,20,1,2,2,55,2,88,2,59,1,1,6,1,19,1,4,2,54,2,88,2,58,2,7,2,17,1,1,6,2,52,3,87,2,56,1,2,9,1,26,2,51,3,87,2,55,1,1,40,1,50,2,88,2,54,2,41,1,50,2,88,2,53,2,42,2,49,2,88,2,52,2,44,2,48,2,88,2,51,2,46,2,47,2,88,2,50,2,16,1,31,1,47,2,88,2,50,3,1,14,1,29,1,1,2,46,2,88,2,50,18,1,29,1,2,1,46,2,88,2,50,4,14,1,33,1,45,2,88,2,53,1,3,11,2,15,1,13,1,2,46,2,88,2,56,1,1,11,1,15,1,10,2,1,49,2,88,2,55,2,3,10,2,12,2,8,8,46,2,88,2,50,1,1,4,4,1,9,2,11,2,8,2,4,2,1,2,1,1,42,2,88,2,48,2,3,8,1,9,2,9,3,7,2,8,1,1,2,1,1,40,2,88,2,45,3,2,1,1,1,1,8,1,7,1,1,3,6,4,6,1,8,1,1,1,1,4,2,38,2,88,2,45,1,2,2,22,4,4,4,6,1,10,1,1,2,1,2,1,2,37,2,88,2,42,1,1,1,4,1,2,19,1,1,4,1,5,21,1,2,6,35,2,88,2,42,2,1,1,1,1,15,2,8,3,4,3,6,1,17,1,1,2,1,1,1,34,2,88,2,41,3,1,1,15,2,9,3,5,2,8,1,21,1,1,34,2,88,2,41,1,1,1,16,2,11,2,5,2,9,2,18,2,1,34,2,88,2,41,1,2,1,14,2,13,1,5,1,12,1,17,1,2,34,2,88,2,41,1,16,1,17,1,2,1,14,2,17,1,1,33,2,88,2,41,1,2,1,12,1,17,1,3,2,15,1,14,1,1,1,1,33,2,88,2,41,15,1,22,1,17,1,1,11,1,3,1,33,2,88,2,41,1,14,2,40,1,15,1,33,2,88,2,41,1,15,2,35,1,3,1,10,1,4,1,33,2,88,2,41,1,3,1,12,2,36,2,11,1,4,1,33,2,88,2,40,1,1,18,1,35,1,12,1,4,1,1,32,2,88,2,40,2,4,1,14,1,33,1,12,1,6,1,32,2,88,2,40,1,6,1,14,1,31,1,13,1,6,1,32,2,88,2,39,2,22,2,28,1,21,1,1,31,2,88,2,39,2,24,1,26,1,22,1,1,31,2,88,2,39,2,25,1,24,1,23,1,1,31,2,88,2,39,1,27,1,22,2,23,1,1,31,2,88,2,39,1,49,2,24,3,30,2,88,2,39,1,48,1,27,1,1,30,2,88,2,39,1,47,2,27,1,1,30,2,88,2,37,1,1,47,2,28,1,1,30,2,88,2,37,1,1,46,1,30,2,1,29,2,88,2,37,1,47,1,30,2,30,2,88,2,37,1,1,2,43,1,33,30,3,87,2,37,1,45,1,31,1,1,2,29,3,87,2,37,45,1,35,1,29,1,1,88,2,1,35,1,1,80,2,1,26,1,3,86,1,1,2,1,21,1,4,4,2,1,3,80,30,1,2,125,2,80,2,156,2,79,2,1,156,2,1,77,4,156,1,3,76,3,1,140,1,15,1,2,2,74,2,1,1,1,5,3,129,1,18,1,82,1,7,1,129,1,18,1,82,1,9,1,123,3,20,1,1,80,2,11,1,1,119,1,22,1,1,1,2,76,4,1,154,2,2,63,1,14,3,1,126,1,27,2,65,1,17,1,17,2,132,4,66,1,17,4,113,39,66,2,16,31,191,1,1,1,15,1,153,2,15,3,48,2,16,2,152,1,16,1,1,1,48,2,16,2,152,1,1,2,2,1,10,1,1,1,48,1,13,6,153,1,3,13,1,1,48,1,15,2,1,1,153,3,64,1,16,2,154,1,1,83,1,154,1,17,1,66,1,154,1,17,1,66,1,155,1,82,1,156,1,82,1,156,1,1,80,1,1,157,2,78,2,158,2,78,2,158,1,80,1,158,1,79,2,158,1,79,1,159,1,79,1,160,1,77,2,160,1,77,1,162,1,74,1,1,1,162,1,75,1,239,1,165,1,72,1,166,1,240,1,69,1,412,1,63,1,175,1,63,2,238,1,175,1,63,1,239,1,807}
|
||||||
|
RLE.draw(img_values, img_runs)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MysteriousManScreen.draw_day_switch_background()
|
||||||
|
local img_values = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1}
|
||||||
|
local img_runs = {86,2,238,2,225,1,10,5,224,1,8,8,221,4,5,2,1,8,219,6,2,13,217,1,2,19,1,1,213,2,4,19,2,1,211,2,5,19,3,1,208,1,7,20,4,1,204,2,7,2,1,19,5,1,201,2,7,2,3,19,6,1,198,2,7,2,5,9,2,1,2,5,7,1,195,2,7,2,7,5,7,7,8,1,192,2,7,2,9,5,8,6,9,1,188,3,7,2,11,5,9,5,10,1,186,2,7,2,13,5,9,5,11,1,182,2,7,3,15,5,9,5,12,1,179,2,8,2,17,5,9,5,13,1,176,2,7,3,19,5,9,5,14,1,173,2,7,3,21,5,9,5,15,1,169,3,7,2,24,5,9,5,16,1,166,2,8,2,26,5,9,5,3,1,177,2,8,3,27,5,9,5,4,1,13,1,159,3,7,3,30,5,8,6,5,1,170,3,7,3,32,5,7,7,6,1,13,1,153,2,8,2,35,19,7,1,164,2,7,3,37,19,8,1,160,3,7,3,39,19,9,1,156,3,8,3,41,19,10,1,153,3,8,2,44,19,25,1,136,3,8,2,46,19,160,2,8,3,48,19,157,3,7,4,50,7,6,6,15,1,139,2,9,3,52,6,7,6,16,1,12,1,122,3,8,3,55,6,7,6,17,1,132,4,7,3,57,6,7,6,159,2,60,6,7,6,32,1,123,3,62,6,7,6,33,1,119,4,64,6,7,6,22,1,128,3,67,6,7,6,150,2,1,1,67,6,7,6,153,1,67,6,7,6,25,1,11,1,115,1,67,6,7,6,26,1,126,1,67,6,7,6,28,1,124,1,67,6,7,6,29,1,11,1,111,1,67,6,7,6,30,1,11,1,110,1,66,7,6,8,42,1,109,1,66,21,32,1,10,1,71,4,33,1,66,21,115,1,2,1,33,1,66,21,119,1,32,1,66,21,36,1,115,1,66,21,48,1,66,1,2,1,33,1,66,21,38,1,10,1,65,4,33,1,66,21,152,1,66,21,152,1,66,8,6,7,152,1,66,7,7,7,114,6,32,1,66,7,7,7,114,6,32,1,66,7,7,7,57,1,56,2,2,2,32,1,66,7,7,7,58,1,18,1,35,7,32,1,12,1,49,1,3,7,7,7,19,1,1,1,2,2,24,1,8,2,46,1,4,8,32,1,66,8,6,7,12,1,1,1,4,3,2,2,2,2,2,1,18,2,7,3,44,1,3,9,32,1,12,1,1,1,51,8,6,7,14,1,1,2,1,1,1,1,2,3,1,3,1,1,3,1,2,2,11,4,1,1,2,5,41,1,3,10,32,1,12,3,51,8,5,8,7,3,2,1,1,1,1,2,1,1,1,1,1,4,1,6,1,2,2,2,1,2,1,3,1,2,1,6,1,2,1,4,39,3,2,3,1,6,32,1,8,1,2,6,2,1,4,1,2,1,7,1,30,8,5,8,2,2,3,1,1,1,6,2,1,11,1,6,2,2,1,2,1,6,1,15,24,5,1,3,1,7,2,3,1,10,28,1,14,1,9,2,1,1,38,8,1,1,2,9,1,3,10,1,1,6,10,1,2,2,1,1,3,9,1,4,1,6,1,7,8,47,26,1,27,1,7,1,18,1,4,1,4,13,1,9,1,3,3,1,1,1,1,4,1,1,2,3,2,5,2,7,1,2,1,6,1,7,1,16,1,53,25,1,66,8,1,1,3,8,1,3,3,1,1,2,3,1,1,1,1,4,2,2,2,1,7,1,2,1,1,1,1,4,1,3,1,31,7,2,1,15,6,2,1,7,1,2,25,1,66,7,7,7,29,1,2,1,3,1,6,1,16,1,2,3,6,14,5,16,2,6,10,2,25,1,66,7,7,7,57,1,7,6,1,36,9,8,1,1,25,1,66,7,7,7,68,35,7,11,5,1,25,1,66,7,7,7,69,2,1,3,1,22,7,16,2,1,2,1,25,1,66,8,6,7,75,19,6,21,2,1,2,1,92,21,79,4,1,7,4,26,2,1,2,1,2,1,89,21,91,12,9,1,1,7,2,1,28,1,66,21,87,12,13,9,98,21,84,11,19,6,99,21,80,12,22,2,3,1,32,1,66,21,70,1,5,13,25,6,99,21,73,13,28,6,2,1,29,1,66,21,69,14,31,7,31,1,66,21,58,2,6,14,39,1,99,8,4,9,55,1,6,15,42,1,99,8,5,8,51,2,6,15,145,8,5,8,48,2,5,17,147,8,5,8,43,3,6,17,150,8,5,8,40,2,6,18,153,8,5,8,36,3,5,20,155,8,5,8,32,4,5,19,1,2,156,8,5,8,29,3,6,19,95,1,66,8,5,8,25,4,5,21,97,1,66,8,5,8,22,4,5,21,167,8,5,8,18,5,5,21,170,8,5,8,14,6,4,23,105,1,66,8,5,8,11,4,6,23,108,1,66,8,5,8,8,4,6,23,111,1,66,8,5,8,4,4,6,24,114,1,66,8,5,14,7,23,116,1,66,8,6,8,2,1,4,25,119,1,66,8,5,8,3,1,2,24,122,1,66,8,3,38,124,1,65,24,1,22,127,1,63,1,1,44,130,1,63,7,1,5,1,30,132,1,58,3,1,42,135,1,58,14,2,27,138,1,56,10,1,32,140,1,53,8,4,27,1,3,143,1,49,9,1,1,2,1,1,24,2,2,147,1,47,7,5,32,148,1,30,1,12,8,5,32,151,1,28,1,1,1,3,1,4,4,1,3,6,35,151,1,22,10,1,10,1,1,4,39,151,1,20,25,2,41,151,1,18,47,1,22,151,1,18,70,148,2,1,1,17,71,148,4,17,71,148,4,14,61,2,10,149,6,10,63,2,10,149,8,4,66,4,9,149,80,2,9,149,91,149,92,78,3,67,92,148,80,3,9,148,92,148,92,148,92,148,92,74,1,3,2,68,92,73,3,2,3,67,92,69,1,3,3,2,2,68,94,66,2,1,5,2,3,67,95,58,15,2,1,69,96,55,21,68}
|
||||||
|
-- pal = {184,167,183,121,70,74}
|
||||||
|
-- pal = b8a7b779464a
|
||||||
|
RLE.draw(img_values, img_runs)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Transitions from the text phase to the day display phase, starting the timer for how long the day label is shown.
|
||||||
|
-- @within MysteriousManScreen
|
||||||
|
function MysteriousManScreen.go_to_day_state()
|
||||||
|
if on_text_complete then
|
||||||
|
on_text_complete()
|
||||||
|
on_text_complete = nil
|
||||||
|
end
|
||||||
|
if Context.game.current_screen ~= "mysterious_man" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
state = STATE_DAY
|
||||||
|
day_timer = day_display_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Norman chooses to wake up, starting the button mash minigame and flash, then going to the next day.
|
||||||
|
-- @within MysteriousManScreen
|
||||||
|
function MysteriousManScreen.wake_up()
|
||||||
|
Context.home_norman_visible = false
|
||||||
|
Util.go_to_screen_by_id("home")
|
||||||
|
MinigameButtonMashWindow.start("game", {
|
||||||
|
focus_center_x = (Config.screen.width / 2) - 22,
|
||||||
|
focus_center_y = (Config.screen.height / 2) - 18,
|
||||||
|
focus_initial_radius = 0,
|
||||||
|
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()
|
||||||
|
if trigger_flash_on_wake then
|
||||||
|
trigger_flash_on_wake = false
|
||||||
|
Ascension.start_flash()
|
||||||
|
end
|
||||||
|
Window.set_current("game")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
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()
|
||||||
|
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.
|
||||||
|
--- @param[opt] options table Optional configuration.</br>
|
||||||
|
--- Fields: </br>
|
||||||
|
--- * text (string) Override for the scrolling text.<br/>
|
||||||
|
--- * day_text (string) Override for the centered day label.<br/>
|
||||||
|
--- * on_text_complete (function) Callback fired once when the text phase ends.<br/>
|
||||||
|
--- * skip_text (boolean) If true, skip the text phase and go straight to day display.<br/>
|
||||||
|
function MysteriousManScreen.start(options)
|
||||||
|
options = options or {}
|
||||||
|
day_timer = 0
|
||||||
|
text_done = false
|
||||||
|
text_done_timer = 0
|
||||||
|
selected_choice = 1
|
||||||
|
text = options.text or ASC_01_TEXT
|
||||||
|
text_y = Config.screen.height
|
||||||
|
day_text_override = options.day_text
|
||||||
|
on_text_complete = options.on_text_complete
|
||||||
|
break_mode = options.break_mode or false
|
||||||
|
MysteriousManScreen.pending_end = false
|
||||||
|
Meter.hide()
|
||||||
|
trigger_flash_on_wake = not options.skip_text
|
||||||
|
if options.skip_text then
|
||||||
|
show_mysterious_screen = false
|
||||||
|
state = STATE_DAY
|
||||||
|
day_timer = day_display_seconds
|
||||||
|
else
|
||||||
|
show_mysterious_screen = true
|
||||||
|
state = STATE_TEXT
|
||||||
|
end
|
||||||
|
Util.go_to_screen_by_id("mysterious_man")
|
||||||
|
Window.set_current("game")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the scrolling text content.
|
||||||
|
--- @param new_text string The text to display.
|
||||||
|
function MysteriousManScreen.set_text(new_text)
|
||||||
|
text = new_text
|
||||||
|
end
|
||||||
|
|
||||||
|
Screen.register({
|
||||||
|
id = "mysterious_man",
|
||||||
|
name = "Mysterious Man",
|
||||||
|
decisions = {},
|
||||||
|
background_color = Config.colors.black,
|
||||||
|
init = function()
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
end,
|
||||||
|
exit = function()
|
||||||
|
Audio.music_stop()
|
||||||
|
end,
|
||||||
|
update = function()
|
||||||
|
if state == STATE_TEXT then
|
||||||
|
if not text_done then
|
||||||
|
text_y = text_y - (text_speed * Context.delta_time)
|
||||||
|
|
||||||
|
local lines = 1
|
||||||
|
for _ in string.gmatch(text, "\n") do
|
||||||
|
lines = lines + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local skippable = Ascension.get_level() < 8
|
||||||
|
if text_y < -lines * 8 or (skippable and Input.select()) then
|
||||||
|
text_done = true
|
||||||
|
text_done_timer = TEXT_DONE_HOLD_SECONDS
|
||||||
|
-- If skipped by user, go to day state immediately
|
||||||
|
if skippable and Input.select() then
|
||||||
|
MysteriousManScreen.go_to_day_state()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
text_done_timer = text_done_timer - Context.delta_time
|
||||||
|
if text_done_timer <= 0 or (Ascension.get_level() ~= 8 and Input.select()) then
|
||||||
|
MysteriousManScreen.go_to_day_state()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif state == STATE_DAY then
|
||||||
|
day_timer = day_timer - Context.delta_time
|
||||||
|
|
||||||
|
if day_timer <= 0 or (Ascension.get_level() ~= 8 and Input.select()) then
|
||||||
|
if break_mode then
|
||||||
|
state = STATE_CHOICE
|
||||||
|
selected_choice = 1
|
||||||
|
elseif trigger_flash_on_wake or Ascension.get_level() ~= 4 then
|
||||||
|
MysteriousManScreen.wake_up()
|
||||||
|
else
|
||||||
|
state = STATE_CHOICE
|
||||||
|
selected_choice = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif state == STATE_CHOICE then
|
||||||
|
if break_mode then
|
||||||
|
if MysteriousManScreen.pending_end then
|
||||||
|
if not Ascension.is_flashing() and not Ascension.is_fading() then
|
||||||
|
MysteriousManScreen.pending_end = false
|
||||||
|
Window.set_current("end")
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if Input.left() or Input.up() then
|
||||||
|
if selected_choice == 2 then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_choice = 1
|
||||||
|
end
|
||||||
|
elseif Input.right() or Input.down() then
|
||||||
|
if selected_choice == 1 then
|
||||||
|
Audio.sfx_beep()
|
||||||
|
selected_choice = 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Input.select() then
|
||||||
|
Audio.sfx_select()
|
||||||
|
if selected_choice == 1 then
|
||||||
|
Ascension.start_flash()
|
||||||
|
MysteriousManScreen.pending_end = true
|
||||||
|
else
|
||||||
|
Context.reset()
|
||||||
|
Context.game_in_progress = true
|
||||||
|
Context.home_norman_visible = true
|
||||||
|
Glitch.hide()
|
||||||
|
Meter.show()
|
||||||
|
MenuWindow.refresh_menu_items()
|
||||||
|
Util.go_to_screen_by_id("home")
|
||||||
|
Window.set_current("game")
|
||||||
|
local home_screen = Screen.get_by_id("home")
|
||||||
|
if home_screen and home_screen.init then
|
||||||
|
home_screen.init()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local menu_x = (Config.screen.width - 60) / 2
|
||||||
|
local menu_y = (Config.screen.height - 20) / 2
|
||||||
|
local confirmed
|
||||||
|
selected_choice, confirmed = UI.update_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
|
||||||
|
|
||||||
|
if Input.select() or confirmed then
|
||||||
|
Audio.sfx_select()
|
||||||
|
if selected_choice == 1 then
|
||||||
|
MysteriousManScreen.wake_up()
|
||||||
|
else
|
||||||
|
MysteriousManScreen.stay_in_bed()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
draw = function()
|
||||||
|
if state == STATE_CHOICE and break_mode then
|
||||||
|
if not MysteriousManScreen.pending_end then
|
||||||
|
local nx = math.floor((Config.screen.width - 64) / 2)
|
||||||
|
local ny = math.floor((Config.screen.height - 96) / 2)
|
||||||
|
spr(272, nx, ny, Config.colors.transparent, 4)
|
||||||
|
spr(273, nx + 32, ny, Config.colors.transparent, 4)
|
||||||
|
spr(288, nx, ny + 32, Config.colors.transparent, 4)
|
||||||
|
spr(289, nx + 32, ny + 32, Config.colors.transparent, 4)
|
||||||
|
spr(304, nx, ny + 64, Config.colors.transparent, 4)
|
||||||
|
spr(305, nx + 32, ny + 64, Config.colors.transparent, 4)
|
||||||
|
end
|
||||||
|
elseif show_mysterious_screen and not break_mode then
|
||||||
|
MysteriousManScreen.draw_background()
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_TEXT then
|
||||||
|
local screen_w = Config.screen.width
|
||||||
|
local line_y = text_y
|
||||||
|
for line in (text .. "\n"):gmatch("(.-)\n") do
|
||||||
|
local line_w = print(line, 0, -6, 0, false, 1)
|
||||||
|
local line_x = math.floor((screen_w - line_w) / 2)
|
||||||
|
Print.text_contour(line, (line_x - 8), line_y, Config.colors.black, false, 1)
|
||||||
|
line_y = line_y + 8
|
||||||
|
end
|
||||||
|
elseif state == STATE_DAY then
|
||||||
|
MysteriousManScreen.draw_day_switch_background()
|
||||||
|
local day_text = day_text_override or ("day " .. Context.day_count)
|
||||||
|
Print.text_center_contour(
|
||||||
|
day_text,
|
||||||
|
Config.screen.width / 2,
|
||||||
|
Config.screen.height / 2 - 3,
|
||||||
|
Config.colors.white
|
||||||
|
)
|
||||||
|
elseif state == STATE_CHOICE then
|
||||||
|
if break_mode then
|
||||||
|
if MysteriousManScreen.pending_end or Ascension.is_fading() or Ascension.is_flashing() then
|
||||||
|
Meter.draw_ascension_only()
|
||||||
|
else
|
||||||
|
local lines = {
|
||||||
|
"This is not a workplace.",
|
||||||
|
"This is a cycle.",
|
||||||
|
"And if it is a cycle...",
|
||||||
|
"it can be broken."
|
||||||
|
}
|
||||||
|
local y = 40
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
Print.text_center_contour(line, Config.screen.width / 2, y, Config.colors.orange, false, 1, Config.colors.white)
|
||||||
|
y = y + 10
|
||||||
|
end
|
||||||
|
|
||||||
|
y = y + 20
|
||||||
|
local break_color = selected_choice == 1 and Config.colors.light_blue or Config.colors.white
|
||||||
|
local cont_color = selected_choice == 2 and Config.colors.light_blue or Config.colors.white
|
||||||
|
local break_text = (selected_choice == 1 and "> BREAK" or " BREAK")
|
||||||
|
local cont_text = (selected_choice == 2 and "> CONTINUE" or " CONTINUE")
|
||||||
|
local centerX = Config.screen.width / 2
|
||||||
|
local choice_gap = 20
|
||||||
|
local break_width = print(break_text, 0, -6, 0)
|
||||||
|
local cont_width = print(cont_text, 0, -6, 0)
|
||||||
|
local total_width = break_width + choice_gap + cont_width
|
||||||
|
local break_x = math.floor(centerX - (total_width / 2))
|
||||||
|
local cont_x = break_x + break_width + choice_gap
|
||||||
|
Print.text(break_text, break_x, y, break_color)
|
||||||
|
Print.text(cont_text, cont_x, y, cont_color)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local menu_x = (Config.screen.width - 60) / 2
|
||||||
|
local menu_y = (Config.screen.height - 20) / 2
|
||||||
|
UI.draw_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
75
inc/screen/screen.office.lua
Normal file
75
inc/screen/screen.office.lua
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "office",
|
||||||
|
name = "Office",
|
||||||
|
decisions = {
|
||||||
|
"do_work",
|
||||||
|
"go_to_walking_to_home",
|
||||||
|
"have_a_coffee",
|
||||||
|
"talk_to_truth",
|
||||||
|
},
|
||||||
|
init = function()
|
||||||
|
Context.have_been_to_office = true
|
||||||
|
|
||||||
|
local possible_sprites = {
|
||||||
|
"dev_project_manager",
|
||||||
|
"dev_hr_girl",
|
||||||
|
"dev_introvert",
|
||||||
|
"dev_extrovert",
|
||||||
|
"dev_guru",
|
||||||
|
"dev_operator",
|
||||||
|
{id="dev_buddy", y_correct=1 * 8},
|
||||||
|
{id="dev_boy", y_correct=1 * 8},
|
||||||
|
{id="dev_girl", y_correct=1 * 8}
|
||||||
|
}
|
||||||
|
|
||||||
|
local possible_positions = {
|
||||||
|
{x = 6 * 8, y = 4 * 8},
|
||||||
|
{x = 10 * 8, y = 11 * 8 + 4},
|
||||||
|
{x = 12 * 8, y = 4 * 8},
|
||||||
|
{x = 15 * 8, y = 9 * 8},
|
||||||
|
{x = 16 * 8, y = 4 * 8},
|
||||||
|
{x = 17 * 8, y = 8 * 8},
|
||||||
|
{x = 17 * 8, y = 11 * 8},
|
||||||
|
{x = 20 * 8, y = 4 * 8},
|
||||||
|
{x = 23 * 8, y = 5 * 8},
|
||||||
|
{x = 22 * 8, y = 10 * 8 + 4},
|
||||||
|
{x = 27 * 8, y = 10 * 8 + 4},
|
||||||
|
{x = -4 + 5 * 8, y = 9 * 8}
|
||||||
|
}
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
Context.office_sprites = { "norman_echo" }
|
||||||
|
else
|
||||||
|
Audio.music_play_room_work(CommuteGlitch.music_speed())
|
||||||
|
Context.office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
|
||||||
|
if CommuteGlitch.is_active() then
|
||||||
|
Context.office_sprites = CommuteGlitch.corrupt_sprite_list(Context.office_sprites)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
background = function()
|
||||||
|
return CommuteGlitch.is_max() and "" or "office"
|
||||||
|
end,
|
||||||
|
draw = function()
|
||||||
|
if Window.get_current_id() == "game" then
|
||||||
|
Sprite.draw_at("norman", 13 * 8, 9 * 8)
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Sprite.draw_at("norman_echo", 15 * 8, 9 * 8)
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
else
|
||||||
|
CommuteGlitch.draw_sprite_list(Context.office_sprites)
|
||||||
|
end
|
||||||
|
|
||||||
|
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 6 then
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
if Ascension.get_level() == 8 then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
105
inc/screen/screen.toilet.lua
Normal file
105
inc/screen/screen.toilet.lua
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "toilet",
|
||||||
|
name = "Toilet",
|
||||||
|
decisions = {
|
||||||
|
"go_to_home",
|
||||||
|
},
|
||||||
|
background = "bedroom",
|
||||||
|
init = function()
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
Context.stat_screen_active = true
|
||||||
|
Meter.hide()
|
||||||
|
local cx = Config.screen.width * 0.75
|
||||||
|
local cy = Config.screen.height * 0.75
|
||||||
|
Focus.start_driven(cx, cy)
|
||||||
|
Focus.set_percentage(0.15)
|
||||||
|
end,
|
||||||
|
update = function()
|
||||||
|
if not Context.stat_screen_active then return end
|
||||||
|
if Input.select() or Input.select() then
|
||||||
|
Focus.stop()
|
||||||
|
Context.stat_screen_active = false
|
||||||
|
Meter.show()
|
||||||
|
Util.go_to_screen_by_id("home")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
draw = function()
|
||||||
|
if not Context.stat_screen_active then return end
|
||||||
|
|
||||||
|
local sw = Config.screen.width
|
||||||
|
local cx = sw / 2
|
||||||
|
local norman_x = math.floor(sw * 0.75)
|
||||||
|
local norman_y = math.floor(Config.screen.height * 0.75)
|
||||||
|
local bar_w = math.floor(sw * 0.75)
|
||||||
|
local bar_x = math.floor((sw - bar_w) / 2)
|
||||||
|
local bar_h = 4
|
||||||
|
|
||||||
|
Sprite.draw_at("norman", norman_x, norman_y)
|
||||||
|
|
||||||
|
Print.text_center_contour("day " .. Context.day_count, cx, 10, Config.colors.black)
|
||||||
|
|
||||||
|
local narrative = "reflecting on my past and present...\nboth eventually flushed..."
|
||||||
|
local wrapped = UI.word_wrap(narrative, 38)
|
||||||
|
local text_y = 24
|
||||||
|
for _, line in ipairs(wrapped) do
|
||||||
|
Print.text_center_contour(line, cx, text_y, Config.colors.black)
|
||||||
|
text_y = text_y + 8
|
||||||
|
end
|
||||||
|
|
||||||
|
local m = Context.meters
|
||||||
|
local max_val = Meter.get_max()
|
||||||
|
local decay_pct = Meter.get_decay_percentage()
|
||||||
|
local decay_text = string.format("-%d%%", decay_pct)
|
||||||
|
local combo_mult = Meter.get_combo_multiplier()
|
||||||
|
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 = {
|
||||||
|
{ key = "wpm", label = "Work Productivity Meter", color = Meter.COLOR_WPM },
|
||||||
|
{ key = "ism", label = "Impostor Syndrome Meter", color = Meter.COLOR_ISM },
|
||||||
|
{ key = "bm", label = "Burnout Meter", color = Meter.COLOR_BM },
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, meter in ipairs(meter_list) do
|
||||||
|
local y = meter_start_y + (i - 1) * 20
|
||||||
|
|
||||||
|
Print.text_center_contour(meter.label, cx, y, meter.color, false, 1, Config.colors.white)
|
||||||
|
|
||||||
|
local bar_y = y + 8
|
||||||
|
local fill_w = math.max(0, math.floor((m[meter.key] / max_val) * bar_w))
|
||||||
|
rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG)
|
||||||
|
if fill_w > 0 then
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Ascension.get_level() > 0 then
|
||||||
|
local asc_y = meter_start_y + #meter_list * 20
|
||||||
|
local asc_letter_y = asc_y + 10
|
||||||
|
local asc_spacing = 8
|
||||||
|
local asc_total_w = Ascension.get_level() * asc_spacing
|
||||||
|
local asc_x = math.floor((sw - asc_total_w) / 2)
|
||||||
|
Ascension.draw(asc_x, asc_letter_y, { spacing = asc_spacing })
|
||||||
|
end
|
||||||
|
|
||||||
|
if Ascension.get_level() == 8 then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
end
|
||||||
|
|
||||||
|
if Ascension.get_level() == 8 then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
86
inc/screen/screen.walking_to_home.lua
Normal file
86
inc/screen/screen.walking_to_home.lua
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "walking_to_home",
|
||||||
|
name = "Walking to home",
|
||||||
|
decisions = {
|
||||||
|
"go_to_home",
|
||||||
|
"go_to_office",
|
||||||
|
"sumphore_discussion",
|
||||||
|
"eating_fast_food",
|
||||||
|
"go_to_truth",
|
||||||
|
},
|
||||||
|
init = function()
|
||||||
|
local possible_sprites = {
|
||||||
|
"matrix_trinity",
|
||||||
|
"matrix_neo",
|
||||||
|
{id="matrix_oraculum", y_correct=1 * 8},
|
||||||
|
"matrix_architect"
|
||||||
|
}
|
||||||
|
|
||||||
|
local possible_positions = {
|
||||||
|
{x = 5 * 8, y = 11 * 8},
|
||||||
|
{x = 7 * 8, y = 11 * 8},
|
||||||
|
{x = 9 * 8, y = 11 * 8},
|
||||||
|
{x = 11 * 8, y = 11 * 8},
|
||||||
|
{x = 13 * 8, y = 11 * 8},
|
||||||
|
{x = 15 * 8, y = 11 * 8},
|
||||||
|
{x = 18 * 8, y = 11 * 8},
|
||||||
|
{x = 21 * 8, y = 11 * 8},
|
||||||
|
{x = 24 * 8, y = 11 * 8},
|
||||||
|
{x = 27 * 8, y = 11 * 8},
|
||||||
|
}
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
Context.walking_to_home_sprites = {}
|
||||||
|
else
|
||||||
|
Audio.music_play_room_work(CommuteGlitch.music_speed())
|
||||||
|
Context.walking_to_home_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
|
||||||
|
if CommuteGlitch.is_active() then
|
||||||
|
Context.walking_to_home_sprites = CommuteGlitch.corrupt_sprite_list(Context.walking_to_home_sprites)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
background = function()
|
||||||
|
return CommuteGlitch.is_max() and "" or "street"
|
||||||
|
end,
|
||||||
|
draw = function()
|
||||||
|
local w = Window.get_current_id()
|
||||||
|
if w ~= "game" and w ~= "discussion" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local show_sumphore = Ascension.get_level() ~= 8
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Sprite.draw_at("norman", 7 * 8, 3 * 8)
|
||||||
|
if show_sumphore then
|
||||||
|
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
|
||||||
|
end
|
||||||
|
CommuteGlitch.draw_sprite_list(Context.walking_to_home_sprites)
|
||||||
|
Glitch.draw()
|
||||||
|
else
|
||||||
|
local norman_x = Context.fast_food_approaching and (19 * 8) or (7 * 8)
|
||||||
|
Sprite.draw_at("norman", norman_x, 3 * 8)
|
||||||
|
if show_sumphore then
|
||||||
|
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
CommuteGlitch.draw_sprite_list(Context.walking_to_home_sprites)
|
||||||
|
if CommuteGlitch.is_active() and CommuteGlitch.get_level() >= 6 then
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
end
|
||||||
|
|
||||||
|
if Ascension.get_level() == 8 then
|
||||||
|
CommuteGlitch.draw_background_flicker()
|
||||||
|
Glitch.draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
63
inc/screen/screen.walking_to_office.lua
Normal file
63
inc/screen/screen.walking_to_office.lua
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "walking_to_office",
|
||||||
|
name = "Walking to office",
|
||||||
|
decisions = {
|
||||||
|
"go_to_home",
|
||||||
|
"go_to_office",
|
||||||
|
"sumphore_discussion",
|
||||||
|
"eating_fast_food",
|
||||||
|
},
|
||||||
|
init = function()
|
||||||
|
local possible_sprites = {
|
||||||
|
"matrix_trinity",
|
||||||
|
"matrix_neo",
|
||||||
|
{id="matrix_oraculum", y_correct=1 * 8},
|
||||||
|
"matrix_architect"
|
||||||
|
}
|
||||||
|
|
||||||
|
local possible_positions = {
|
||||||
|
{x = 5 * 8, y = 11 * 8},
|
||||||
|
{x = 7 * 8, y = 11 * 8},
|
||||||
|
{x = 9 * 8, y = 11 * 8},
|
||||||
|
{x = 11 * 8, y = 11 * 8},
|
||||||
|
{x = 13 * 8, y = 11 * 8},
|
||||||
|
{x = 15 * 8, y = 11 * 8},
|
||||||
|
{x = 18 * 8, y = 11 * 8},
|
||||||
|
{x = 21 * 8, y = 11 * 8},
|
||||||
|
{x = 24 * 8, y = 11 * 8},
|
||||||
|
{x = 27 * 8, y = 11 * 8},
|
||||||
|
}
|
||||||
|
|
||||||
|
if CommuteGlitch.is_max() then
|
||||||
|
Audio.music_play_mystery()
|
||||||
|
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
|
||||||
|
Context.walking_to_office_sprites = CommuteGlitch.corrupt_sprite_list(Context.walking_to_office_sprites)
|
||||||
|
else
|
||||||
|
Audio.music_play_room_work()
|
||||||
|
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
background = function()
|
||||||
|
return CommuteGlitch.is_max() and "" or "street"
|
||||||
|
end,
|
||||||
|
update = function()
|
||||||
|
end,
|
||||||
|
draw = function()
|
||||||
|
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)
|
||||||
|
local show_sumphore = Ascension.get_level() ~= 8
|
||||||
|
Sprite.draw_at("norman", norman_x, 3 * 8)
|
||||||
|
if show_sumphore then
|
||||||
|
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Context.fast_food_eaten_today < 3 then
|
||||||
|
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
|
||||||
|
end
|
||||||
|
|
||||||
|
Sprite.draw_at("dev_guard", 22 * 8, 3 * 8)
|
||||||
|
Sprite.draw_list(Context.walking_to_office_sprites)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
6
inc/screen/screen.work.lua
Normal file
6
inc/screen/screen.work.lua
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Screen.register({
|
||||||
|
id = "work",
|
||||||
|
name = "Work",
|
||||||
|
decisions = {},
|
||||||
|
background_color = Config.colors.blue,
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_boy.lua
Normal file
4
inc/sprite/sprite.dev_boy.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_boy",
|
||||||
|
sprites = Sprite.generate_table(2, 3, 278, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_buddy.lua
Normal file
4
inc/sprite/sprite.dev_buddy.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_buddy",
|
||||||
|
sprites = Sprite.generate_table(2, 3, 286, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_extrovert.lua
Normal file
4
inc/sprite/sprite.dev_extrovert.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_extrovert",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 330, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_girl.lua
Normal file
4
inc/sprite/sprite.dev_girl.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_girl",
|
||||||
|
sprites = Sprite.generate_table(2, 3, 284, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_guard.lua
Normal file
4
inc/sprite/sprite.dev_guard.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_guard",
|
||||||
|
sprites = Sprite.generate_table(3, 4, 384, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_guru.lua
Normal file
4
inc/sprite/sprite.dev_guru.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_guru",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 264, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_hr_girl.lua
Normal file
4
inc/sprite/sprite.dev_hr_girl.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_hr_girl",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 260, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_introvert.lua
Normal file
4
inc/sprite/sprite.dev_introvert.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_introvert",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 332, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_operator.lua
Normal file
4
inc/sprite/sprite.dev_operator.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_operator",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 326, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.dev_project_manager.lua
Normal file
4
inc/sprite/sprite.dev_project_manager.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "dev_project_manager",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 328, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
216
inc/sprite/sprite.manager.lua
Normal file
216
inc/sprite/sprite.manager.lua
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
--- @section Sprite
|
||||||
|
local _sprites = {}
|
||||||
|
local _active_sprites = {}
|
||||||
|
|
||||||
|
local function draw_sprite_instance(sprite_data, params)
|
||||||
|
local colorkey = params.colorkey or sprite_data.colorkey or Config.colors.transparent
|
||||||
|
local scale = params.scale or sprite_data.scale or 1
|
||||||
|
local flip_x = params.flip_x or sprite_data.flip_x or 0
|
||||||
|
local flip_y = params.flip_y or sprite_data.flip_y or 0
|
||||||
|
local rot = params.rot or sprite_data.rot or 0
|
||||||
|
|
||||||
|
if sprite_data.sprites then
|
||||||
|
for i = 1, #sprite_data.sprites do
|
||||||
|
local sub_sprite = sprite_data.sprites[i]
|
||||||
|
spr(
|
||||||
|
sub_sprite.s,
|
||||||
|
params.x + (sub_sprite.x_offset or 0),
|
||||||
|
params.y + (sub_sprite.y_offset or 0),
|
||||||
|
sub_sprite.colorkey or colorkey,
|
||||||
|
sub_sprite.scale or scale,
|
||||||
|
sub_sprite.flip_x or flip_x,
|
||||||
|
sub_sprite.flip_y or flip_y,
|
||||||
|
sub_sprite.rot or rot
|
||||||
|
)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
spr(sprite_data.s, params.x, params.y, colorkey, scale, flip_x, flip_y, rot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers a sprite definition.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param sprite_data table A table containing the sprite definition.
|
||||||
|
--- @param sprite_data.id string Unique sprite identifier.<br/>
|
||||||
|
--- @param[opt] sprite_data.s number Sprite index for single-sprite mode.<br/>
|
||||||
|
--- @param[opt] sprite_data.colorkey number Default color index for transparency.<br/>
|
||||||
|
--- @param[opt] sprite_data.scale number Default scaling factor.<br/>
|
||||||
|
--- @param[opt] sprite_data.flip_x number Set to 1 to flip horizontally by default.<br/>
|
||||||
|
--- @param[opt] sprite_data.flip_y number Set to 1 to flip vertically by default.<br/>
|
||||||
|
--- @param[opt] sprite_data.rot number Default rotation in degrees.<br/>
|
||||||
|
--- @param[opt] sprite_data.sprites table Array of sub-sprite tables for composite sprites. Each entry has: `s` (number) sprite index, `x_offset` (number) horizontal offset, `y_offset` (number) vertical offset, and optional `colorkey`, `scale`, `flip_x`, `flip_y`, `rot` overrides.<br/>
|
||||||
|
function Sprite.register(sprite_data)
|
||||||
|
if not sprite_data or not sprite_data.id then
|
||||||
|
trace("Error: Invalid sprite object registered (missing id)!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if _sprites[sprite_data.id] then
|
||||||
|
trace("Warning: Overwriting sprite with id: " .. sprite_data.id)
|
||||||
|
end
|
||||||
|
_sprites[sprite_data.id] = sprite_data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Generates a sprites table for a rectangular composite sprite.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param width number The number of sprites wide.<br/>
|
||||||
|
--- @param height number The number of sprites tall.<br/>
|
||||||
|
--- @param starting_s number The sprite index of the top-left tile.<br/>
|
||||||
|
--- @param x_base number The base x-offset for the leftmost column.<br/>
|
||||||
|
--- @param y_base number The base y-offset for the topmost row.<br/>
|
||||||
|
--- @param x_step number The x-offset increment per column.<br/>
|
||||||
|
--- @param y_step number The y-offset increment per row.<br/>
|
||||||
|
--- @return table The sprites table array.
|
||||||
|
function Sprite.generate_table(width, height, starting_s, x_base, y_base, x_step, y_step)
|
||||||
|
local sprites = {}
|
||||||
|
for row = 0, height - 1 do
|
||||||
|
for col = 0, width - 1 do
|
||||||
|
local s = starting_s + row * 16 + col
|
||||||
|
local x_offset = x_base + col * x_step
|
||||||
|
local y_offset = y_base + row * y_step
|
||||||
|
table.insert(sprites, { s = s, x_offset = x_offset, y_offset = y_offset })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return sprites
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Immediately draws a list of sprites
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param sprite_list table An array of tables, each containing: `id` (string) sprite identifier, `x` (number) x-coordinate, `y` (number) y-coordinate, and optional `colorkey`, `scale`, `flip_x`, `flip_y`, `rot` parameters.
|
||||||
|
function Sprite.draw_list(sprite_list)
|
||||||
|
for _, sprite_info in ipairs(sprite_list) do
|
||||||
|
local sprite_data = _sprites[sprite_info.id]
|
||||||
|
if not sprite_data then
|
||||||
|
trace("Error: Attempted to draw non-registered sprite with id: " .. tostring(sprite_info.id))
|
||||||
|
else
|
||||||
|
draw_sprite_instance(sprite_data, sprite_info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Given a list of sprite IDs (or sprite entries with correction offsets) and a list of possible positions, randomly assigns each sprite to a unique position and returns a drawable list.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param sprite_ids table An array of sprite identifier values or tables.
|
||||||
|
--- Each entry may be either:
|
||||||
|
--- - string: sprite ID to draw.
|
||||||
|
--- - table: { sprite_id = string, x_correct = number, y_correct = number }.
|
||||||
|
--- @param positions table An array of tables, each containing `x` and `y` fields for possible sprite positions.
|
||||||
|
function Sprite.list_randomize(sprite_ids, positions)
|
||||||
|
if #sprite_ids > #positions then
|
||||||
|
trace("Error: More sprite IDs than available positions in Sprite.draw_randomized")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local shuffled_positions = {}
|
||||||
|
for i, pos in ipairs(positions) do
|
||||||
|
shuffled_positions[i] = pos
|
||||||
|
end
|
||||||
|
for i = #shuffled_positions, 2, -1 do
|
||||||
|
local j = math.random(i)
|
||||||
|
shuffled_positions[i], shuffled_positions[j] = shuffled_positions[j], shuffled_positions[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
local drawable_list = {}
|
||||||
|
for i, sprite_entry in ipairs(sprite_ids) do
|
||||||
|
local sprite_id = sprite_entry
|
||||||
|
local x_correct = 0
|
||||||
|
local y_correct = 0
|
||||||
|
|
||||||
|
if type(sprite_entry) == "table" then
|
||||||
|
sprite_id = sprite_entry.sprite_id or sprite_entry.id
|
||||||
|
x_correct = sprite_entry.x_correct or 0
|
||||||
|
y_correct = sprite_entry.y_correct or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local sprite_data = _sprites[sprite_id]
|
||||||
|
if not sprite_data then
|
||||||
|
trace("Error: Attempted to draw non-registered sprite with id: " .. tostring(sprite_id))
|
||||||
|
else
|
||||||
|
local pos = shuffled_positions[i]
|
||||||
|
table.insert(drawable_list, {
|
||||||
|
id = sprite_id,
|
||||||
|
x = pos.x + x_correct,
|
||||||
|
y = pos.y + y_correct
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return drawable_list
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Schedules a sprite for drawing.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param id string The unique identifier of the sprite.<br/>
|
||||||
|
--- @param x number The x-coordinate.<br/>
|
||||||
|
--- @param y number The y-coordinate.<br/>
|
||||||
|
--- @param[opt] colorkey number The color index for transparency.<br/>
|
||||||
|
--- @param[opt] scale number The scaling factor.<br/>
|
||||||
|
--- @param[opt] flip_x number Set to 1 to flip horizontally.<br/>
|
||||||
|
--- @param[opt] flip_y number Set to 1 to flip vertically.<br/>
|
||||||
|
--- @param[opt] rot number The rotation in degrees.<br/>
|
||||||
|
function Sprite.show(id, x, y, colorkey, scale, flip_x, flip_y, rot)
|
||||||
|
if not _sprites[id] then
|
||||||
|
trace("Error: Attempted to show non-registered sprite with id: " .. id)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
_active_sprites[id] = {
|
||||||
|
id = id,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
colorkey = colorkey,
|
||||||
|
scale = scale,
|
||||||
|
flip_x = flip_x,
|
||||||
|
flip_y = flip_y,
|
||||||
|
rot = rot,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Hides a displayed sprite.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param id string The unique identifier of the sprite.<br/>
|
||||||
|
function Sprite.hide(id)
|
||||||
|
_active_sprites[id] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws a sprite immediately without scheduling it.
|
||||||
|
--- @within Sprite
|
||||||
|
--- @param id string The unique identifier of the sprite.<br/>
|
||||||
|
--- @param x number The x-coordinate.<br/>
|
||||||
|
--- @param y number The y-coordinate.<br/>
|
||||||
|
--- @param[opt] colorkey number The color index for transparency.<br/>
|
||||||
|
--- @param[opt] scale number The scaling factor.<br/>
|
||||||
|
--- @param[opt] flip_x number Set to 1 to flip horizontally.<br/>
|
||||||
|
--- @param[opt] flip_y number Set to 1 to flip vertically.<br/>
|
||||||
|
--- @param[opt] rot number The rotation in degrees.<br/>
|
||||||
|
function Sprite.draw_at(id, x, y, colorkey, scale, flip_x, flip_y, rot)
|
||||||
|
local sprite_data = _sprites[id]
|
||||||
|
if not sprite_data then
|
||||||
|
trace("Error: Attempted to draw non-registered sprite with id: " .. id)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
draw_sprite_instance(sprite_data, {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
colorkey = colorkey,
|
||||||
|
scale = scale,
|
||||||
|
flip_x = flip_x,
|
||||||
|
flip_y = flip_y,
|
||||||
|
rot = rot,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draws all scheduled sprites.
|
||||||
|
--- @within Sprite
|
||||||
|
function Sprite.draw()
|
||||||
|
for id, params in pairs(_active_sprites) do
|
||||||
|
local sprite_data = _sprites[id]
|
||||||
|
if not sprite_data then
|
||||||
|
trace("Error: Sprite id " .. id .. " in _active_sprites is not registered.")
|
||||||
|
_active_sprites[id] = nil
|
||||||
|
end
|
||||||
|
if sprite_data then
|
||||||
|
draw_sprite_instance(sprite_data, params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
4
inc/sprite/sprite.matrix_architect.lua
Normal file
4
inc/sprite/sprite.matrix_architect.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "matrix_architect",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 324, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.matrix_neo.lua
Normal file
4
inc/sprite/sprite.matrix_neo.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "matrix_neo",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 322, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.matrix_oraculum.lua
Normal file
4
inc/sprite/sprite.matrix_oraculum.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "matrix_oraculum",
|
||||||
|
sprites = Sprite.generate_table(2, 3, 282, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.matrix_trinity.lua
Normal file
4
inc/sprite/sprite.matrix_trinity.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "matrix_trinity",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 320, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
23
inc/sprite/sprite.norman.lua
Normal file
23
inc/sprite/sprite.norman.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "norman",
|
||||||
|
sprites = {
|
||||||
|
{ s = 272, x_offset = -4, y_offset = -4 },
|
||||||
|
{ s = 273, x_offset = 4, y_offset = -4 },
|
||||||
|
{ s = 288, x_offset = -4, y_offset = 4 },
|
||||||
|
{ s = 289, x_offset = 4, y_offset = 4 },
|
||||||
|
{ s = 304, x_offset = -4, y_offset = 12 },
|
||||||
|
{ s = 305, x_offset = 4, y_offset = 12 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Sprite.register({
|
||||||
|
id = "sleeping_norman",
|
||||||
|
sprites = {
|
||||||
|
{ s = 272, x_offset = 12, y_offset = -4, flip_y = 1 },
|
||||||
|
{ s = 273, x_offset = 12, y_offset = 4, flip_y = 1 },
|
||||||
|
{ s = 288, x_offset = 4, y_offset = -4, flip_y = 1 },
|
||||||
|
{ s = 289, x_offset = 4, y_offset = 4, flip_y = 1 },
|
||||||
|
{ s = 304, x_offset = -4, y_offset = -4, flip_y = 1 },
|
||||||
|
{ s = 305, x_offset = -4, y_offset = 4, flip_y = 1 }
|
||||||
|
}
|
||||||
|
})
|
||||||
14
inc/sprite/sprite.norman_echo.lua
Normal file
14
inc/sprite/sprite.norman_echo.lua
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Norman echo: same tile indices as norman.
|
||||||
|
-- Color remap is applied by CommuteGlitch.draw_sprite_list via pal().
|
||||||
|
-- Implementer: set ECHO_SRC/ECHO_DST in logic.commute_glitch.lua after inspecting the palette.
|
||||||
|
Sprite.register({
|
||||||
|
id = "norman_echo",
|
||||||
|
sprites = {
|
||||||
|
{ s = 272, x_offset = -4, y_offset = -4 },
|
||||||
|
{ s = 273, x_offset = 4, y_offset = -4 },
|
||||||
|
{ s = 288, x_offset = -4, y_offset = 4 },
|
||||||
|
{ s = 289, x_offset = 4, y_offset = 4 },
|
||||||
|
{ s = 304, x_offset = -4, y_offset = 12 },
|
||||||
|
{ s = 305, x_offset = 4, y_offset = 12 },
|
||||||
|
}
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.pizza_vendor.lua
Normal file
4
inc/sprite/sprite.pizza_vendor.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "pizza_vendor",
|
||||||
|
sprites = Sprite.generate_table(2, 2, 334, -1, -8, 8, 8)
|
||||||
|
})
|
||||||
4
inc/sprite/sprite.sumphore.lua
Normal file
4
inc/sprite/sprite.sumphore.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Sprite.register({
|
||||||
|
id = "sumphore",
|
||||||
|
sprites = Sprite.generate_table(2, 4, 258, -4, -4, 8, 8)
|
||||||
|
})
|
||||||
63
inc/system/system.asciiart.lua
Normal file
63
inc/system/system.asciiart.lua
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
--- @section AsciiArt
|
||||||
|
AsciiArt = {}
|
||||||
|
|
||||||
|
--- Draws ASCII art text using rectangles.
|
||||||
|
--- @param text string The ASCII art text to draw.
|
||||||
|
--- @param options table Configuration options (char_w, char_h, line_gap, word_gap, color, x, y).
|
||||||
|
function AsciiArt.draw(text, options)
|
||||||
|
options = options or {}
|
||||||
|
local char_w = options.char_w or 4
|
||||||
|
local char_h = options.char_h or 5
|
||||||
|
local line_gap = options.line_gap or 0
|
||||||
|
local word_gap = options.word_gap or 6
|
||||||
|
local color = options.color or Config.colors.light_blue
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
local max_len = 0
|
||||||
|
-- Get all lines and find max length
|
||||||
|
for line in (text .. "\n"):gmatch("(.-)\n") do
|
||||||
|
table.insert(lines, line)
|
||||||
|
if #line > max_len then max_len = #line end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clean up empty lines from the start/end
|
||||||
|
while #lines > 0 and lines[1]:gsub("%s+", "") == "" do table.remove(lines, 1) end
|
||||||
|
while #lines > 0 and lines[#lines]:gsub("%s+", "") == "" do table.remove(lines, #lines) end
|
||||||
|
|
||||||
|
if #lines == 0 then return end
|
||||||
|
|
||||||
|
local total_h = 0
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line:find("#") then
|
||||||
|
total_h = total_h + char_h + line_gap
|
||||||
|
else
|
||||||
|
total_h = total_h + word_gap
|
||||||
|
end
|
||||||
|
end
|
||||||
|
total_h = total_h - line_gap
|
||||||
|
|
||||||
|
local current_y = options.y or (Config.screen.height - total_h) / 2
|
||||||
|
local x_offset = options.x or (Config.screen.width - (max_len * char_w)) / 2
|
||||||
|
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line:find("#") then
|
||||||
|
for j = 1, #line do
|
||||||
|
local char = line:sub(j, j)
|
||||||
|
if char == "#" then
|
||||||
|
rect(x_offset + (j - 1) * char_w, current_y, char_w, char_h, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
current_y = current_y + char_h + line_gap
|
||||||
|
else
|
||||||
|
current_y = current_y + word_gap
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
x = x_offset,
|
||||||
|
y = options.y or (Config.screen.height - total_h) / 2,
|
||||||
|
width = max_len * char_w,
|
||||||
|
height = total_h,
|
||||||
|
bottom = (options.y or (Config.screen.height - total_h) / 2) + total_h
|
||||||
|
}
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user