Compare commits

...

53 Commits

Author SHA1 Message Date
ae3415b417 main menu color fix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 21:44:52 +02:00
ce819eae2b Merge branch 'develop' into feature/task110_sprite_silhouette
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 21:37:23 +02:00
658e6908b5 Merge pull request '- task #134: randomize placement of characters' (#49) from feature/134-randomized-character-positions into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #49
2026-04-09 19:05:27 +00:00
5ae1eec48a - task #134: randomize placement of characters
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 20:47:59 +02:00
04eb825646 Merge pull request 'IMP-133: re-added have_a_coffee discussion, fixed text readability, fixed centering of texts, new system.ui functions for drawing in contour, fixed ddr types, fixed meter drawing' (#48) from chore/imp-133-improve-readability into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #48
2026-04-09 14:41:43 +00:00
Zoltan Timar
e797377ec1 chore: lint fixes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 16:40:19 +02:00
Zoltan Timar
9d56ca2e7a chore: re-added have_a_coffee discussion, fixed text readability, fixed centering of texts, new system.ui functions for drawing in contour, fixed ddr types, fixed meter drawing
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-09 16:36:19 +02:00
9b379d32b3 Corrects the color defects in tiles.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 15:58:54 +02:00
5fc6ae5c14 Draws outlines around the figures.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 15:48:04 +02:00
1e3716196e Removes the setting of the transparent color to fix the linter error.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-09 13:54:43 +02:00
4b5f11969b Changes the Norman's silhouette. Change the background color of the sprites to pink.
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-04-09 13:47:24 +02:00
a4a6ad2ab2 Introduces the transparent color. The default is the pink. 2026-04-09 13:46:05 +02:00
8921f02821 mouse handling refact
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 22:12:58 +02:00
211af18c26 debug mode fix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 21:51:43 +02:00
b337ae8516 main menu tweaks
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 19:12:39 +02:00
10316d3075 Controls menu 2026-04-02 18:51:17 +02:00
589b225ab0 remove configuration menu 2026-04-02 18:45:12 +02:00
7697b35336 input remapping + mouse control
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 18:39:36 +02:00
6e1cf1db3e remove situation management 2026-04-02 15:32:20 +02:00
020bfd4134 set version to beta2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-23 07:02:39 +01:00
87d4f5843b Merge pull request 'beta1 release' (#46) from develop into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #46
2026-03-22 22:49:23 +00:00
8e610f14a0 new ddr2 game
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 23:47:12 +01:00
753509b4ea Merge branch 'develop' into rle-screens
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 23:17:45 +01:00
7dbbbb4668 day switch and ddr backgrounds
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 23:13:34 +01:00
3290d0fb89 Merge branch 'feature/ddr_upgrade' into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 22:30:20 +01:00
4cf7df511b Merge branch 'develop' into feature/ddr_upgrade
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 22:27:12 +01:00
8b0bcdbe95 - lint fixes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 22:24:36 +01:00
7c3a011ffc - lint fixes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-22 22:10:56 +01:00
6ee874655e - lint fix :D 2026-03-22 22:06:35 +01:00
3420694287 - linter fixes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-22 21:55:12 +01:00
e12a27e4e1 - linter fixes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-22 21:53:51 +01:00
81b47c3760 rle screens 2026-03-22 21:53:29 +01:00
53ccda7702 - updated decision texts for less grammatical errors
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- fixed for "only_left" game maxing out before special win
- game ends with TO BE CONTINUED at ASC 4
2026-03-22 21:42:13 +01:00
2912d7c482 RLE lib
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 21:21:27 +01:00
284c5aa4c8 time based scroll
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-22 19:03:05 +01:00
93df710d14 asciiart update 2026-03-22 18:56:16 +01:00
4e6590174a - lint fix
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 23:31:40 +01:00
6a33be82e9 - added music and sounds to things
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- hooked up ddr logic into game logic
- TODO: "left_only" ddr needs tweak
2026-03-21 23:20:48 +01:00
mr.one
9a3c9ee28c - ddr special logic seems to be working in solation
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 17:26:39 +01:00
197cb9403b add tic & zip to gitignore
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-21 07:10:09 +01:00
mr.one
c41bf23a45 - musicator: generation + ddr operational
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- TODO: special logic, code cleanup
2026-03-21 01:41:30 +01:00
9d6d2c2c6f Merge pull request 'feature/imp-99-add-discussions' (#44) from feature/imp-99-add-discussions into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #44
2026-03-20 23:19:58 +00:00
Zoltan Timar
fb8ae157b6 fix: lint
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-21 00:18:52 +01:00
Zoltan Timar
803ddc482b Merge branch 'develop' into feature/imp-99-add-discussions
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 00:17:08 +01:00
Zoltan Timar
ed14a86997 fix: lint
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 00:16:38 +01:00
Zoltan Timar
9263a90961 fix: lint
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 00:15:18 +01:00
Zoltan Timar
05091c7af2 feat: added sumphore & coworker discussions until asc 3, fixed game flow bugs, added mysterious man screen texts
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-21 00:12:15 +01:00
Zoltan Timar
f79233521d fix: removed mm window, made it into screen
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-20 21:25:37 +01:00
mr.one
46d3ff2ebc - tools/musicator:\n - markov model generator and pattern generator operational\n- DDR sound generation in-progress
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-20 20:18:10 +01:00
Zoltan Timar
3e6fa41021 feat: added mm discussions
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-03-20 18:23:17 +01:00
d899a74411 set version to 1.0-beta1
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline failed
2026-03-20 15:33:00 +01:00
4c7d43fea3 set version to 1.0-alpha
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-03-20 15:30:39 +01:00
94563d32c0 Merge pull request 'test mode' (#42) from feature/test-mode into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
Reviewed-on: #42
2026-03-20 06:25:24 +00:00
68 changed files with 3173 additions and 894 deletions

4
.gitignore vendored
View File

@@ -1,6 +1,10 @@
.claude
.local
impostor.lua
impostor.original.lua
prompts
docs
minify.lua
*.tic
*.zip
NOTES_*

View File

@@ -10,7 +10,6 @@ globals = {
"Discussion",
"Util",
"Decision",
"Situation",
"Screen",
"Sprite",
"UI",
@@ -31,12 +30,12 @@ globals = {
"MenuWindow",
"GameWindow",
"PopupWindow",
"ConfigurationWindow",
"ControlsWindow",
"AudioTestWindow",
"MinigameButtonMashWindow",
"MinigameRhythmWindow",
"MinigameDDRWindow",
"MysteriousManWindow",
"MysteriousManScreen",
"DiscussionWindow",
"EndWindow",
"mset",
@@ -64,6 +63,12 @@ globals = {
"index_menu",
"Map",
"map",
"time",
"RLE",
"mouse",
"Mouse",
"print",
"musicator_generate_pattern",
}

View File

@@ -30,5 +30,5 @@
"files.associations": {
"*.conf": "bitbake",
"*.inc": "bitbake"
}
},
}

View File

@@ -6,7 +6,9 @@ init/init.context.lua
system/system.util.lua
system/system.print.lua
system/system.input.lua
system/system.mouse.lua
system/system.asciiart.lua
system/system.rle.lua
logic/logic.meter.lua
logic/logic.focus.lua
logic/logic.day.lua
@@ -17,6 +19,7 @@ logic/logic.glitch.lua
logic/logic.discussion.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
@@ -36,10 +39,7 @@ sprite/sprite.matrix_architect.lua
sprite/sprite.matrix_neo.lua
sprite/sprite.matrix_oraculum.lua
sprite/sprite.matrix_trinity.lua
situation/situation.manager.lua
situation/situation.drink_coffee.lua
decision/decision.manager.lua
decision/decision.have_a_coffee.lua
decision/decision.go_to_home.lua
decision/decision.go_to_toilet.lua
decision/decision.go_to_walking_to_office.lua
@@ -48,8 +48,10 @@ decision/decision.go_to_end.lua
decision/decision.go_to_walking_to_home.lua
decision/decision.go_to_sleep.lua
decision/decision.do_work.lua
decision/decision.start_discussion.lua
decision/decision.have_a_coffee.lua
decision/decision.sumphore_discussion.lua
discussion/discussion.sumphore.lua
discussion/discussion.coworker.lua
map/map.manager.lua
map/map.bedroom.lua
map/map.street.lua
@@ -69,13 +71,12 @@ window/window.intro.title.lua
window/window.intro.ttg.lua
window/window.intro.brief.lua
window/window.menu.lua
window/window.configuration.lua
window/window.controls.lua
window/window.audiotest.lua
window/window.popup.lua
window/window.minigame.mash.lua
window/window.minigame.rhythm.lua
window/window.minigame.ddr.lua
window/window.mysterious_man.lua
window/window.discussion.lua
window/window.continued.lua
window/window.game.lua

View 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

View File

@@ -1,48 +1,93 @@
--- @section Audio
Audio = {
music_playing = nil
}
--- Stops current music.
--- @within Audio
function Audio.music_stop() music() end
function Audio.music_stop()
music()
Audio.music_playing = nil
end
--- Plays track, doesn't restart if already playing.
function Audio.music_play(track)
if Audio.music_playing ~= track then
music(track)
Audio.music_playing = track
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.
--- @within Audio
function Audio.music_play_room_work() music(0) end
function Audio.music_play_room_work() Audio.music_play(0) end
--- Plays activity work music.
--- @within Audio
function Audio.music_play_activity_work() music(1) end
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.
--- Plays alarm sound effect
--- @within Audio
function Audio.sfx_alarm() sfx(61) end
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

View File

@@ -105,6 +105,15 @@ Songs = {
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
}
}

View File

@@ -4,12 +4,31 @@ Decision.register({
handle = function()
Meter.hide()
Util.go_to_screen_by_id("work")
MinigameDDRWindow.start("game", nil, {
on_win = function()
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"
MinigameDDRWindow.start("game", "generated", {
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
elseif (game_context.special_mode_condition and Context.ascension.level == 4) 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()]
})
end,
})

View File

@@ -1,6 +1,9 @@
Decision.register({
id = "go_to_home",
label = "Go to Home",
label = "Go Home",
condition = function()
return Context.have_been_to_office and Context.have_done_work_today
end,
handle = function()
Util.go_to_screen_by_id("home")
end,

View File

@@ -1,6 +1,9 @@
Decision.register({
id = "go_to_sleep",
label = "Go to Sleep",
condition = function()
return Context.have_been_to_office and Context.have_done_work_today
end,
handle = function()
Meter.hide()
Day.increase()
@@ -10,7 +13,11 @@ Decision.register({
focus_initial_radius = 0,
on_win = function()
local ascended = Ascension.consume_increase()
MysteriousManWindow.start({ skip_text = not ascended })
local level = Ascension.get_level()
MysteriousManScreen.start({
skip_text = not ascended,
text = ascended and MysteriousManScreen.get_text_for_level(level) or nil,
})
end,
})
end,

View File

@@ -1,6 +1,6 @@
Decision.register({
id = "go_to_walking_to_home",
label = "Walking to home",
label = "Walk home",
handle = function()
Util.go_to_screen_by_id("walking_to_home")
end,

View File

@@ -1,6 +1,6 @@
Decision.register({
id = "go_to_walking_to_office",
label = "Walking to office",
label = "Walk to office",
handle = function()
Util.go_to_screen_by_id("walking_to_office")
end,

View File

@@ -2,7 +2,13 @@ Decision.register({
id = "have_a_coffee",
label = "Have a Coffee",
handle = function()
local new_situation_id = Situation.apply("drink_coffee", Context.game.current_screen)
Context.game.current_situation = new_situation_id
local level = Ascension.get_level()
local disc_id = "coworker_disc_0"
-- TODO: Add more discussions for levels above 3
if level >= 1 and level <= 3 then
local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level)
disc_id = "coworker_disc" .. suffix
end
Discussion.start(disc_id, "game")
end,
})

View File

@@ -123,9 +123,13 @@ function Decision.draw(decisions, selected_decision_index)
local selected_decision = decisions[selected_decision_index]
local decision_label = Decision.get_label(selected_decision)
local text_y = bar_y + 4
Print.text("<", 2, text_y, Config.colors.light_blue)
Print.text_center(decision_label, Config.screen.width / 2, text_y, Config.colors.item)
Print.text(">", Config.screen.width - 6, text_y, Config.colors.light_blue)
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
@@ -134,6 +138,7 @@ end
--- @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()
@@ -142,5 +147,22 @@ function Decision.update(decisions, selected_decision_index)
Audio.sfx_beep()
selected_decision_index = Util.safeindex(decisions, selected_decision_index + 1)
end
return selected_decision_index
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

View File

@@ -7,6 +7,9 @@ Decision.register({
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,
})

View File

@@ -1,5 +1,8 @@
Decision.register({
id = "play_ddr",
label = "Play DDR (Random)",
handle = function() Meter.hide() MinigameDDRWindow.start("game", nil) end,
handle = function()
Meter.hide()
MinigameDDRWindow.start("game", nil)
end,
})

View File

@@ -1,5 +1,5 @@
Decision.register({
id = "start_discussion",
id = "sumphore_discussion",
label = function()
if Context.have_met_sumphore then
return "Talk to Sumphore"
@@ -9,10 +9,15 @@ Decision.register({
handle = function()
if not Context.have_met_sumphore then
Discussion.start("homeless_guy", "game")
elseif Ascension.get_level() == 0 then
Discussion.start("homeless_guy", "game", 4)
return
end
local level = Ascension.get_level()
-- TODO: Add more discussions for levels above 3
if level >= 1 and level <= 3 then
Discussion.start("sumphore_disc_asc_" .. level, "game")
else
Discussion.start("sumphore_day_3", "game")
Discussion.start("homeless_guy", "game", 4)
end
end,
})

View File

@@ -0,0 +1,137 @@
Discussion.register({
id = "coworker_disc_0",
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",
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",
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",
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",
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",
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",
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 },
},
},
},
})

View File

@@ -1,5 +1,5 @@
Discussion.register({
id = "sumphore_day_3",
id = "sumphore_disc_asc_1",
steps = {
{
question = "Are you still seeking the ox?",
@@ -17,6 +17,74 @@ Discussion.register({
},
})
Discussion.register({
id = "sumphore_disc_asc_2",
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",
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 = "homeless_guy",

View File

@@ -93,7 +93,7 @@ function Ascension.draw(x, y, options)
else
color = lit_color
end
print(ch, x + (i - 1) * spacing, y, color, false, 1, true)
Print.text_contour(ch, x + (i - 1) * spacing, y, color, false, 1)
end
end

View File

@@ -17,7 +17,9 @@ function Config.initial_data()
blue = 3,
white = 4,
item = 7,
meter_bg = 1
meter_bg = 1,
transparent = 12,
orange = 7
},
timing = {
minigame_win_duration = 180

View File

@@ -21,11 +21,14 @@ Context = {}
--- * 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/>
--- * game (table) Current game progress state. Contains: `current_screen` (string) active screen ID, `current_situation` (string|nil) active situation ID.<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/>
--- * 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 = {}
@@ -40,12 +43,18 @@ function Context.initial_data()
timer = Timer.get_initial(),
triggers = {},
home_norman_visible = false,
have_been_to_office = false,
have_done_work_today = false,
should_ascend = false,
have_met_sumphore = false,
office_sprites = {},
walking_to_office_sprites = {},
game = {
current_screen = "home",
current_situation = nil,
},
day_count = 1,
delta_time = 0,
last_frame_time = 0,
glitch = {
enabled = false,
state = "active",
@@ -90,13 +99,19 @@ function Context.new_game()
Context.game_in_progress = true
MenuWindow.refresh_menu_items()
Screen.get_by_id(Context.game.current_screen).init()
MysteriousManWindow.start({
MysteriousManScreen.start({
text = [[
Norman was never a bad
simulation engineer, but
we need to be careful in
letting him improve. We
need to distract him.
simulation engineer,
but
we need to be careful
letting him improve.
We need to distract him.
]],
on_text_complete = function()
Audio.sfx_alarm()
@@ -110,8 +125,7 @@ function Context.new_game()
instruction_text = "Wake up Norman!",
show_progress_text = false,
on_win = function()
Audio.music_play_wakingup()
Context.home_norman_visible = true
Audio.music_play_room_work()
Meter.show()
Window.set_current("game")
end,

View File

@@ -3,12 +3,12 @@ Util = {}
Meter = {}
Minigame = {}
Decision = {}
Situation = {}
Screen = {}
Map = {}
UI = {}
Print = {}
Input = {}
Mouse = {}
Sprite = {}
Audio = {}
Focus = {}
@@ -16,5 +16,7 @@ Day = {}
Timer = {}
Trigger = {}
Discussion = {}
RLE = {}
AsciiArt = {}
Ascension = {}
MysteriousManScreen = {}

View File

@@ -5,6 +5,9 @@ local _day_increase_handlers = {}
--- @within Day
function Day.increase()
Context.day_count = Context.day_count + 1
if Context.day_count == 3 then
Context.should_ascend = true
end
for _, handler in ipairs(_day_increase_handlers) do
handler()
end
@@ -25,7 +28,8 @@ Day.register_handler(function()
end)
Day.register_handler(function()
if Context.day_count == 3 then
if Context.should_ascend then
Ascension.increase()
end
Context.should_ascend = false
end)

View File

@@ -8,10 +8,11 @@ local COMBO_MAX_BONUS = 0.16
local COMBO_TIMEOUT_FRAMES = 600
-- Internal meters for tracking game progress and player stats.
Meter.COLOR_ISM = Config.colors.red
Meter.COLOR_ISM = Config.colors.orange
Meter.COLOR_WPM = Config.colors.blue
Meter.COLOR_BM = Config.colors.black
Meter.COLOR_BM = Config.colors.red
Meter.COLOR_BG = Config.colors.meter_bg
Meter.COLOR_CONTOUR = Config.colors.white
--- Gets initial meter values.
--- @within Meter
@@ -126,16 +127,16 @@ function Meter.draw()
local m = Context.meters
local max = Meter.get_max()
local bar_w = 44
local screen_w = Config.screen.width
local screen_h = Config.screen.height
local bar_w = screen_w * 0.25
local bar_h = 2
local bar_x = 182
local label_x = 228
local line_h = 5
local start_y = 1
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 bar_offset = math.floor((line_h - bar_h) / 2)
local meter_list = {
{ key = "wpm", label = "WPM", color = Meter.COLOR_WPM, row = 0 },
{ key = "ism", label = "ISM", color = Meter.COLOR_ISM, row = 1 },
@@ -144,15 +145,16 @@ function Meter.draw()
for _, meter in ipairs(meter_list) do
local label_y = start_y + meter.row * line_h
local bar_y = label_y + bar_offset
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)
if 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)
---print(meter.label, label_x, label_y, meter.color, false, 1, true)
end
local ascension_y = start_y + 3 * line_h + 1
Ascension.draw(bar_x, ascension_y, { spacing = 5 })
Ascension.draw(bar_x, ascension_y, { spacing = 8 })
end

View File

@@ -3,8 +3,8 @@
--- Draws a unified win message overlay.
--- @within Minigame
function Minigame.draw_win_overlay()
local text = "SUCCESS"
function Minigame.draw_win_overlay(win_text)
local text = win_text or "SUCCESS"
local tw = #text * 6
local th = 6
local padding = 4
@@ -12,8 +12,9 @@ function Minigame.draw_win_overlay()
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(text, Config.screen.width / 2, box_y + padding, Config.colors.white)
Print.text_center_contour(text, text_x, box_y + padding, Config.colors.black, false, 1, Config.colors.white)
end

View File

@@ -2,7 +2,7 @@
-- 000:0404005f574fc2c3c71d2b53fff1e8ab52367e2553ffa30000875129adff83769c00e436fa77a8ff004dc3c3c7ffccaa
-- </PALETTE>
-- <TILES>
-- 000:00000000c666666006606060c636366006606060c606366006666660c0000000
-- 000:0000000006666660066060600636366006606060060636600666666000000000
-- 001:0000000006666666060600600666666606000600066666660633633300000000
-- 002:0000000066666666006000066666666660060006666666666333363300000000
-- 004:1111111111111111111111111111111111111111111111111111111111111111
@@ -17,7 +17,7 @@
-- 013:1000000004444444044444440444444404444444044444440444444404444444
-- 014:0000000144444240444424204444424044442420444442404444242044444240
-- 015:0000000004244444024444440424400002444444042424240242424200000000
-- 016:000000004444442044444240c004442044444240e424242042424240c0000000
-- 016:0000000044444420444442400004442044444240e42424204242424000000000
-- 017:0000000002424240042424200244444004244440024404400424044002440440
-- 018:0000000002424240042424200444424004444420044042400440442004404240
-- 019:0222222200010012055016020150660205501602015066020550160200000000
@@ -33,7 +33,7 @@
-- 029:0440442004404240044044200444424004444420024242400424242000000000
-- 030:0000000006666660036060600360666006606060036660600360666003606060
-- 031:0222222200010010033033030130130103303303013013010330330300000000
-- 032:22222220c10012203033022030130220303302203013022030330220c0000000
-- 032:2222222001001220303302203013022030330220301302203033022000000000
-- 033:0333011103330111033301110333011103330111033301110333011100000000
-- 034:1110011111100111111001111110011111100111111001111110011100000000
-- 035:1110333011103330111033301110333011103330111033301110333000000000
@@ -49,7 +49,7 @@
-- 045:2222222022222220222222202222222022222220000000001111100011111000
-- 046:1111111111111111111111111000000002222222022222220222222202222222
-- 047:1111111111111111111111110000000022222222222222222222222222222222
-- 048:111111111111111111111111c000100022220444e222044422220444e2220444
-- 048:1111111111111111111111110000100022220444e222044422220444e2220444
-- 049:1111111111111111111111111100001100222201022222200222222002222220
-- 050:1111111011111110111111101111111011111110111111101111111011110000
-- 051:1111111111111111111111111111111111111111111111111111111100011111
@@ -81,7 +81,7 @@
-- 077:0000000011111110000000006611166655555551555555515555555166666111
-- 078:1111111111111111111100001110919111101010110191011019191000919190
-- 079:1111111111111111000000009191919000000010111110901111101000000090
-- 080:09191990c991990209199020c999020209902020c902020200202020c0000000
-- 080:0919199009919902091990200999020209902020090202020020202000000000
-- 081:0000000002020200202020200200022020002220000222202022222002222220
-- 082:5555555555555055555501056666010155550105555501055555010566610106
-- 083:5555555555555555000000000222222202222222022222220222222202222222
@@ -97,7 +97,7 @@
-- 093:0222022002220220022202200222022002220220022202200222022002220220
-- 094:1033330110333301103333011033330110333301103333011033330110000001
-- 095:3333101033331090333310103333109011113010111130901111301011113000
-- 096:00000000c111111100000000c106666601051555c105155501056555c0066611
-- 096:0000000001111111000000000106666601051555010515550105655500066611
-- 097:0000000011111110000000006666101055555010555550105555501066616000
-- 098:5555555555555550555555056666116655550055555500555555655566666611
-- 099:0555555500555555050555550661111105500555115005550055555500616666
@@ -113,7 +113,7 @@
-- 109:1111111011111110111111101111111011111110111111100000000566000066
-- 110:1111111111111111111111111111010011101044111010441110104400000010
-- 111:1111111111111111111111110011111144011111440111114401111110000000
-- 112:11111111111111111111111111111111111111111111111111110111c0004000
-- 112:1111111111111111111111111111111111111111111111111111011100004000
-- 113:3333104433331044333310003333111111113333111133331111333311113333
-- 114:4403111144031111000311113333111111110000111044441104444411014444
-- 115:3304440133044401330444013304440100104033440103334440333344103333
@@ -177,7 +177,7 @@
-- 173:0333333010333330103333301033333010333330100000001111111111111111
-- 174:111111111111111111111111111111111111111112222222a1a1a1a21a1a1a12
-- 175:1111111111111111111111111111111111111111444444444444444444444444
-- 176:00000000c111111001101010c131311001101010c101311001111110c0000000
-- 176:0000000001111110011010100131311001101010010131100111111000000000
-- 177:0000000001111111010100100111111101000100011111110133133300000000
-- 178:0000000011111111001000011111111110010001111111111333313300000000
-- 179:11111111111111111111111111111111111100001110b161110b100010810101
@@ -225,7 +225,7 @@
-- 221:0200000002000000020000000200000002000000020000000211111100000000
-- 222:0000000000000000000000000000000000000000000000001111111100000000
-- 223:000000000000000000000000000000000000000000000000111dd11100000000
-- 224:00000010c000001000000010c000001000000010c000001011111110c0000000
-- 224:0000001000000010000000100000001000000010000000101111111000000000
-- 225:3333333333333333333333333333333313131313313131311313131331313131
-- 226:0203333302033333020333330203333302031313020131310203131300013131
-- 227:3333302033333020333330203333302013131020313130201313102031313000
@@ -241,7 +241,7 @@
-- 237:1444402044140220444402204144022000402220220222200222222010222220
-- 238:3333330033333022333302223333022213130222313102221313101231313100
-- 239:0003010322200103222201032222010322220103222201012210101300013131
-- 240:02220111c222001102220000c222033302220333c222200002222222c2222222
-- 240:0222011102220011022200000222033302220333022220000222222202222222
-- 241:1002222001202220002022203002222031222220022222202222222022222220
-- 242:3333333333333330333333033333113313130013313100311313131331313131
-- 243:0333333300333333030333330331133303100313113001310013131300313131
@@ -251,130 +251,262 @@
-- 247:0000000011111110000000003333301013131010313130101313101031313000
-- </TILES>
-- <SPRITES>
-- 002:00000000000000000000444400044444000444440044ffff004fffff004f3333
-- 003:0000000000000000441600004242600044241000ff426000fff4100033f26000
-- 004:00000333000035550003655500365555003555ff00356fff00365f3f00355fff
-- 005:33000000553000005563000055563000ff553000fff53000f3f63000fff53000
-- 008:0000005a00005a55000055a50000a55a00005a5700005533000575f30000757f
-- 009:55a50000a55a50005a55a00057555500fff7a5003f335a00fff3f500fffffa00
-- 016:0000000000000000000000000000003000000353000035350003535100353535
-- 017:0000000000000000000000003000000053300000151300005151300015151300
-- 018:004f99ff000fffff0000ff3300000fff00003666000355550035652503163555
-- 019:99f41000fff26000ff600000f600000063300000555330005555530055535530
-- 020:00356f6f003655f60365511f3653122f3531222f363221220532232203322322
-- 021:f6f530006f563000f1156300f2215300f2226300221233002232130022121300
-- 022:000000000000005100000155000055150000157f0000551300057f3f00017fff
-- 023:00000000555000005155000015515000ff7f5100ff31f200fff3ff0033fff200
-- 024:0000057f00000005000000050000559900055911005599110055119900551199
-- 025:f3ff5000fff500005f5000004415100077995100449915007711955044119550
-- 026:00000000000001330001331300031333001333330031f7f7001133390031999f
-- 027:0000000013310000131330003333130033333100f7f113003337310099975300
-- 028:000000000000000000000a77000057770005777700577777005775a50077a7ff
-- 029:0000000000000000777a0000777750007777750077777750a5a57750fff7a770
-- 030:000000000000051500001151000555550001517f000557ff0001ff3f0007ff1f
-- 031:00000000150000005110000055500000f7110000ff750000f3f10000f1f50000
-- 032:0033535100353533003351ff00351f3f0003ff3f0003ffff00003ff3000323ff
-- 033:5555530033555300ff155300f3f15300f3ff3000ffff30003ff30000ff323000
-- 034:036135250316355503613525031633110333331103f333330333333300033333
-- 035:55565530555355305556553013335530133333303333ff303333333033330000
-- 036:00322322003223330033331a003ff31a003ff3a1003333a100003a1a00003a1a
-- 037:223213003332130011133300111f300011133000111300001113000011130000
-- 038:000007ff0001117f0016171f0155175501561755155117555f7157555ff15755
-- 039:fffff700333f2000ffff7100555571105555716155557111555575ff555575ff
-- 040:005599110055991100ff533300fff511007fff110007f5110000311300003113
-- 041:479915504499155047333f7014113ff011113f70111130003311300003113000
-- 042:0035777f0001776600005777000000550088885508bbb84508bbb8448bb8b844
-- 043:77775300677500007750000055880000558b8000548bb800228bbb80428b8b80
-- 044:005a7f3f0057ff3f07f7ffff07757fdf077a57fd0575357f005a66a70006aa6f
-- 045:ff3f7a50ff3ff750fffff777ffdf7577ddf75a77ff75157577a66a50ff6aa600
-- 046:0005ffff00005ff3000005ff0017775500242777002121710024277100212171
-- 047:fff500003f500000f50000005771000077720000171200001772000017120000
-- 048:00323123003231430032314300313339000f3333000033300000333000033330
-- 049:324303003443030034430300933313003333f000033300000333000003333000
-- 050:0003333000033330000333300003333000053530003311300031113000333330
-- 051:3333000033330000333300003333000035350000311330003111300033333000
-- 052:000031a1000031a100003a1a00003a1a00003333000003f3000003f300000330
-- 053:111300001113000011130000111300003333000003f3000003f3000003300000
-- 054:5ff7646607f59999000991330003993000099130000133300001221000011100
-- 055:666646ff999993f7339913000039930000991300001333000012210000111000
-- 056:0000311300003113000031130000311300003113000033330000165100001111
-- 057:0311300003113000031130000311300003113000033330000165100001111000
-- 058:8888b8445f78b8445f58884405031330000331300003133000053550000577f0
-- 059:218b8880428b87f5218885f5031330500331300003133000053550000577f000
-- 060:0056aa66001f6aaa001fa666007f6aaa00006aaa0000a666000007d7000001d1
-- 061:666aa650aaaa6f106666af10aaaa6f70aaaa60006666a000007d7000001d1000
-- 062:0024277700ff611100fff333005f533300003333000033300000333000001110
-- 063:77720000116f0000335ff000333f000033300000333000003330000001110000
-- 064:00000000000000000000011100003311000111170003317f000117ff00553333
-- 065:0000000000000000110000001110000077110000ff710000ff77000013330000
-- 066:00000000000000330000033300003333000033f7000035ff000535ff0007f333
-- 067:0000000030000000333300003333000037f30000ffff0000ffff0000f333f000
-- 068:000000000000000000000000000000000000000000000122000012440001447f
-- 069:00000000000000000000000000000000000000002210000044210000f7441000
-- 070:000000000000000000000333000038880003888800388881038388310338835f
-- 071:00000000000000003330000088880000883380003355380016665300f7ff7300
-- 072:0000000000000000000000550000055500005577000057ff00007f55000fff33
-- 073:0000000000000000555550005555550077777550ffffff507f755f703f333fff
-- 074:0000000000000011000001130000113100011313000131310013137c003131ff
-- 075:000000001110000013110000313110001313100031313100ffff7100fffff100
-- 076:0000000000000055000005550000055500005577000057ff0007ff55000fff99
-- 077:0000000055550000555550005555500075775500fffff5005f555f703f993ff0
-- 078:0000000000000000000000000000000000001111000014440000144400001555
-- 079:0000000000000000000000000000000011100000441000004410000055100000
-- 080:0077533f000ff77f0005fff500005ff70000015f0005f315005ff3ff00ff3333
-- 081:f33f0000f77f00005ff000007f500000f500000053f00000f3f50000333f0000
-- 082:000fff330005ffff000057f50000007f000333350031313f0031313303113133
-- 083:ff33f000ffff100055f10000ff70000055330000ff3130003331300033311300
-- 084:000447ff000fffff0007f33f000ff11f000f7fff0000ff750000042400001142
-- 085:ff744000ffff1000f33f7000f11f5000fff7f00057ff00004240000044110000
-- 086:038383390338833b038383130338337f0383835f003838350003838300003333
-- 087:99539900bb33bb0099513900fffff530ff66f1007fff53001555310033333000
-- 088:000f7ff300005fff000007ff0000007f0000021f000024410002444400124244
-- 089:fff3ff7ff1ffff50fffff700111f7000ffff1000111142004244442042442410
-- 090:005f5733005f1f2300015fff0000077f00011117001244210144414201421444
-- 091:ff33f500ff23f500fffff00011ff500077710000112410004442410042441410
-- 092:00005fff000005ff0000001f0000000100000114000018140000181400018114
-- 093:fffff500111f5000fff500001110000042411000424180004241810042411100
-- 094:00001fff00001f3f0000cf3f0000cfff00000cf3000023cc000131ff001f1f11
-- 095:ff1000003f1000003ff00000fff00000fc000000c3200000f13100001f1f1000
-- 096:00ff33330033133305f333330ff31333033333330f5313330ff1311107f13333
-- 097:333f0000333f5000333f5000333ff000333330003335f500111ff5003337f000
-- 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:333113003331130033333300213113004233330021317f0033317f5001317500
-- 100:0002441400244441024444240242442414424424144144241441442414414424
-- 101:4144210014444200424444204244422042444241424441414244414142444141
-- 102:000333330033a3530032a353003a332303a23333032a333303a23333032a3333
-- 103:3333300055353300553533002232323033333130333332303333313333333233
-- 104:001441440014424400144144001441240017f311001ff133001fff330007f333
-- 105:4244142042442410424414204244122012113f7031333ff033333ff030333f00
-- 106:01421444014414440142144431421444011111110fff1a5a0ff755a505755a51
-- 107:44441410424414104444141042441410111111105a5a55f5a5a5a5f5111a5170
-- 108:0001181400018114000118140001811300011333005fff33000f773300055333
-- 109:424181104241181042418110222318101113310033335ff03333f77033335500
-- 112:0553333300033330000333000033330000333300003333000011110000121200
-- 113:3330000033300000333000003330000033300000333000001111000012120000
-- 114:05f5313103003131033311310311113103111131003111300003333000012120
-- 115:0131130001311300013113000131130001311300003113000033330000121200
-- 116:0fff242407f72211005544200002442000024420000244200002222000024440
-- 117:424441f1112227f1024425500244200002442000024420000222200002444000
-- 118:0333333307ff333307f733330070333000003330000033300000111000001110
-- 119:33333575333331f1333335750033300000333000003330000011100000111100
-- 120:0000033300000333000003330000033300000333000003330000033300000111
-- 121:0033300000333000003330000033300000333000003330000033300010111100
-- 122:005015a100001a51000015a100001a51000015a1000022510000924900009999
-- 123:0315a150001a51000015a100001a51000015a100002251000092490000999900
-- 124:0000033300000333000003330000033300000333000003330000033300000111
-- 125:0333000003330000033300000333000003330000033300000333000000111000
-- 129:0000000000000000000000000000000000000000011515101151515151515151
-- 144:0000000500000015000000150000057f000005ff0000017f0000331700073331
-- 145:1ffffff7ff0fff0fff1fff1fffffffffff7555fffffffffffff111ff57fffff7
-- 146:1000000071000000f5000000ff000000ff500000ff5000007513100051333700
-- 160:0077333303331333333313333331333333313333355133335ff513335ff71111
-- 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:133337003733133077731330333331333333313333333155333337ff1111175f
-- 176:0775333300031333000333130000333300001333000033330000033300000111
-- 177:3335773333333533333333331313131333333333300000033000000310000001
-- 178:3333357533331000331300001333000033310000333300003331000011110000
-- 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:ffffffffff0010201020102010201020102010201020102000ffffffffff40404040404087f3f3f3f397a7b7c7d7a7e7f70818a7b7c7d7a7b7c7d7a70b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -387,7 +519,7 @@
-- 007:ffffffffffb264748494a4b4c4d46494649464940040e4f4b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 008:ffffffffffe1c30515d325d33545c3d355d3c3d365b17585e1ffffffffff4040404040404098989898404040404040404040404040404040404040407c1e7e8e1e1e1e9eae1ebe1e1e1e1e1e1e1e1e72821ebe1e72821ebe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 009:ffffffffffb264e395a5b594e39564c5d5946494e5b1b1f5b2ffffffffff4040404040404040404040404040404040404040404040404040404040405d1e05151ebe1ecedeeefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 010:ffffffffffe1c306162636d34656c395d5d3c3d376b1b1b1e1ffffffffff404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 010:ffffffffffe1c306162636d34656c3e3d5d3c3d376b1b1b1e1ffffffffff404040404040409898989840b9c9c9d9e9f90a0a0a0a4040400a0a0a0a407c1ee395eefe1e0f1f2f3f1e1e1e1e1e1e1e1ee3952f3f1ee3952f3f1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 011:ffffffffffb264946494649464948696a694649410201020b2ffffffffff4040404040404040404040401a2a3a4a5a6a7a40404040404040404040405d1ee3952f3f1e4f5f1ebe1e1e1e1e1e1e1e1ee3951ebe1ee3951ebe1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 012:ffffffffffe1c37282d3c3d3c3d3b6c6d6d3c3d300e6f607e1ffffffffff4040404040404098989898408a9aaabaca9ada40404040404040404040407c1e4f5f1ebe1e0515eefe1e1e1e1e1e1e1e1ee395eefe1ee395eefe1e7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 013:ffffffffffb264e395946494649464946494649465172737b2ffffffffffeaeaeaeaeaeaeafafafafaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea5d1e0515eefe1e6f7f2f3f1e1e1e1e1e1e1e1e6f7f2f3f1e6f7f2f3f1e5d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -396,26 +528,27 @@
-- 016:ffffffffff0010201020766777001020102010201020102000fffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f30b1b2b1b2b7667776777761b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- </MAP>
-- <SFX>
-- 000:060006400600064006000640060006400600060006000600060006000600060006000600060006000600060006000600060006000600060006000600300000000900
-- 016:00000000000000400040004000700070007000400040004000700070007000c000c000c000c000c000c000c000c000c000c000c000c000c000c000c0470000000000
-- 017:000000000000000000000000006000600060006000600060106020c030c050c060c080c0a0c0b0c0c0c0c0c0d0c0d0c0e0c0f0c0f0c0f0c0f0c0f0c0400000000000
-- 018:00c000c000c000c000c000c0006000600060006000600060200030005000600080009000a000b000c000d000d000e000e000e000f000f000f000f000500000000000
-- 019:0000000000000000000000d010d010d020d030d050d070d090d0b0d0c0d0e0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0500000000000
-- 017:030003000300030003000300036003600360036003600360136023c033c053c063c083c0a3c0b3c0c3c0c3c0d3c0d3c0e3c0f3c0f3c0f3c0f3c0f3c0400000000000
-- 018:03c003c003c003c003c003c0036003600360036003600360230033005300630083009300a300b300c300d300d300e300e300e300f300f300f300f300400000000000
-- 019:0300030003000300030003d013d013d023d033d053d073d093d0b3d0c3d0e3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0f3d0400000000000
-- 020:090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900500000000000
-- 021:01000100010001000100f10001100110011001100110f11001200120012001200120f1201130113011302130213021302130313041308130a130d130580000000000
-- 022:03b003100300030003000300130063009300b300c300d300d300e300e300e300f300f300f300f300f300f300f300f300f300f300f300f300f300f300400000000000
-- 032:010001100100011001000110010001100100010001000100010001000100010001000100010001000100010001000100010001000100010001000100400000000800
-- 033:000000010002000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d40000000004
-- 044:0600f6000620f6000600f6000610f600f600f6000600f600f600f600f6000600060006000600060006000600060006000600060006000600060006004600000f0f00
-- 045:0000f0000020f0000000f0000010f000f000f0000000f000f000f000f0000000000000000000000000000000000000000000000000000000000000004600000f0f00
-- 033:000000010002000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c40000000004
-- 034:02000240020002000200020002000200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f200f2004700000f0200
-- 044:0600f6000620f6000600f6000610f600f600f6000600f600f600f600f6000600060006000600060006000600060006000600060006000600060006001600000f0f00
-- 045:0000f0000020f0000000f0000010f000f000f0000000f000f000f000f0000000000000000000000000000000000000000000000000000000000000005600000f0f00
-- 048:090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900090009000900400000000000
-- 056:4100510061406140717081709100b100c100d100e100e100e100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f10058a000000600
-- 057:000000010002000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d40000000004
-- 058:41004110410041104100411041004110c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100500000080800
-- 059:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000
-- 060:220022002200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200100000000000
-- 061:9f009f00bf00df00df00ef00ef00ef00ef00ef00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00400000000000
-- 062:00000100010001000100510081008100910091009100a100a100a100a100a100b100b100b100b100c100c100c100d100d100d100e100e100e100f100484000000000
-- 063:00b000100000000000000000100060009000b000c000d000d000e000e000e000f000f000f000f000f000f000f000f000f000f000f000f000f000f000200000000000
-- 056:4100510061406140717081709100b100c100d100e100e100e100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100f100480000000600
-- 057:000000010002000300020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000004
-- 058:41004110410041104100411041004110c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100c100003000080800
-- 059:03000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030000b000000000
-- 060:220022002200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200820082008200500000000000
-- 061:9f009f00bf00df00df00ef00ef00ef00ef00ef00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00100000000000
-- 062:00000100010001000100510081008100910091009100a100a100a100a100a100b100b100b100b100c100c100c100d100d100d100e100e100e100f100580000000000
-- 063:00b000100000000000000000100060009000b000c000d000d000e000e000e000f000f000f000f000f000f000f000f000f000f000f000f000f000f000500000000000
-- </SFX>
-- <WAVES>
-- 000:bcceefceedddddc84333121268abaa99
@@ -433,10 +566,13 @@
-- 000:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000008c10000000008c10000000000000000000000000000000000000000000000000000000000000000004008b50000000000000000001008c10000004008b50000001008c10000000008c1000000e008b30000004008b50000001008c10000000008c10000000008c10000000008c10000000008c10000000008c1000000000000000000000000000000
-- 001:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007008b50000007008b50000001008c10000007008b50000001008c10000000008c10000007008b50000009008b50000001008c10000009008b50000001008c10000009008b50000009008b50000001008c10000009008b50000001008c1000000
-- 003:4008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d9000000000000000000000000000000000000000000
-- 004:49998d000000e0088b000000b0088b000881e0088b00000040088d000000e0088b000881b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e00889000000
-- 005:400881000000000881000000000881000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000
-- 004:43398d000000e0088b000000b0088b000881e0088b00000040088d000000e0088b000881b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e00889000000
-- 005:455981000000000881000000000881000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000
-- 008:4aa9b30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005008b30000000000000000000000000000000000000000000008b10000000000000000000008910000000000000000004008b30000000000000000000000000000000000000000000000000000000000000000000008b1000000000000000000f008b1000000000000000000000000000000000000000000000891000000000000000000000000000000000000000000
-- 009:4779d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d3000000000000000000
-- </PATTERNS>
-- <TRACKS>
-- 000:1000012000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff
-- 001:581000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 002:900082000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- </TRACKS>

View File

@@ -4,5 +4,5 @@
-- desc: Life of a programmer
-- site: https://git.teletype.hu/games/impostor
-- license: MIT License
-- version: 0.8
-- version: 1.0-beta2
-- script: lua

View File

@@ -7,6 +7,9 @@ Screen.register({
"go_to_sleep",
"go_to_end",
},
init = function()
Audio.music_play_room_work()
end,
background = "bedroom",
draw = function()
if Context.home_norman_visible and Window.get_current_id() == "game" then

View File

@@ -8,7 +8,6 @@ local _screens = {}
--- @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.situations table Array of situation ID strings. Defaults to {}.
--- @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.
@@ -16,9 +15,6 @@ 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.situations then
screen_data.situations = {}
end
if not screen_data.init then
screen_data.init = function() end
end
@@ -43,7 +39,6 @@ end
--- * name (string) Display name.<br/>
--- * decisions (table) Array of decision ID strings.<br/>
--- * background (string) Map ID used as background.<br/>
--- * situations (table) Array of situation ID strings.<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)
@@ -58,7 +53,6 @@ end
--- * 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/>
--- * situations (table) Array of situation ID strings.<br/>
--- * init (function) Called when the screen is entered.<br/>
--- * update (function) Called each frame while screen is active.<br/>
function Screen.get_all()

View File

@@ -1,20 +1,287 @@
--- Draws the mysterious screen with the mysterious man and the glowing TV.
function Screen.draw_the_mysterious_screen()
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}
--- @section MysteriousManScreen
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}
local STATE_TEXT = "text"
local STATE_DAY = "day"
local STATE_CHOICE = "choice"
local val_i=0
local run=0
for y=0,136-1 do
for x=0,240-1 do
if run==0 then
val_i=val_i+1
run=img_runs[val_i]
end
run=run-1
pix(x,y,img_values[val_i])
end
end
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 ascension_texts = {
[1] = ASC_01_TEXT,
[2] = ASC_12_TEXT,
[3] = ASC_23_TEXT,
[4] = ASC_34_TEXT,
[5] = ASC_45_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
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,
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.
-- @within MysteriousManScreen
function MysteriousManScreen.stay_in_bed()
Day.increase()
state = STATE_DAY
day_timer = day_display_seconds
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
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
if text_y < -lines * 8 or Input.select() then
text_done = true
text_done_timer = TEXT_DONE_HOLD_SECONDS
-- If skipped by user, go to day state immediately
if 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 Input.select() then
MysteriousManScreen.go_to_day_state()
-- to be continued
if 4 <= Ascension.get_level() then
Window.set_current("continued")
end
end
end
elseif state == STATE_DAY then
day_timer = day_timer - Context.delta_time
if day_timer <= 0 or Input.select() then
if trigger_flash_on_wake or Ascension.get_level() < 1 then
MysteriousManScreen.wake_up()
else
state = STATE_CHOICE
selected_choice = 1
end
end
elseif state == STATE_CHOICE then
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
local confirmed
selected_choice, confirmed = UI.update_menu(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
if 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,
draw = function()
if show_mysterious_screen 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
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,
})

View File

@@ -6,22 +6,45 @@ Screen.register({
"go_to_walking_to_home",
"have_a_coffee",
},
situations = {
"drink_coffee",
},
init = function()
Audio.music_play_room_work()
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}
}
Context.office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
end,
background = "office",
draw = function()
if Window.get_current_id() == "game" then
Sprite.draw_at("norman", 13 * 8, 9 * 8)
Sprite.draw_at("dev_buddy", 15 * 8, 9 * 8)
Sprite.draw_at("dev_project_manager", 6 * 8, 4 * 8)
Sprite.draw_at("dev_hr_girl", 12 * 8, 4 * 8)
Sprite.draw_at("dev_introvert", -4 + 5 * 8, 9 * 8)
Sprite.draw_at("dev_extrovert", 20 * 8, 4 * 8)
Sprite.draw_at("dev_girl", 23 * 8, 5 * 8)
Sprite.draw_at("dev_boy", 10 * 8, 11 * 8 + 4)
Sprite.draw_at("dev_guru", 22 * 8, 10 * 8 + 4)
Sprite.draw_at("dev_operator", 27 * 8, 10 * 8 + 4)
Sprite.draw_list(Context.office_sprites)
end
end
})

View File

@@ -6,6 +6,7 @@ Screen.register({
},
background = "bedroom",
init = function()
Audio.music_play_mystery()
Context.stat_screen_active = true
Meter.hide()
local cx = Config.screen.width * 0.75
@@ -15,10 +16,11 @@ Screen.register({
end,
update = function()
if not Context.stat_screen_active then return end
if Input.select() or Input.player_interact() then
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()
@@ -34,13 +36,13 @@ Screen.register({
Sprite.draw_at("norman", norman_x, norman_y)
Print.text_center("day " .. Context.day_count, cx, 10, Config.colors.white)
Print.text_center_contour("day " .. Context.day_count, cx, 10, Config.colors.black)
local narrative = "reflecting on my past and present\n...\nboth eventually flushed."
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(line, cx, text_y, Config.colors.light_grey)
Print.text_center_contour(line, cx, text_y, Config.colors.black)
text_y = text_y + 8
end
@@ -54,26 +56,26 @@ Screen.register({
local meter_start_y = text_y + 10
local meter_list = {
{ key = "wpm", label = "Work Productivity Meter" },
{ key = "ism", label = "Impostor Syndrome Meter" },
{ key = "bm", label = "Burnout Meter" },
{ 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(meter.label, cx, y, Config.colors.white)
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, Config.colors.blue)
rect(bar_x, bar_y, fill_w, bar_h, meter.color)
end
local decay_w = print(decay_text, 0, -6, 0, false, 1)
Print.text(decay_text, bar_x - decay_w - 4, bar_y, Config.colors.light_blue)
Print.text(mult_text, bar_x + bar_w + 4, bar_y, Config.colors.light_blue)
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

View File

@@ -5,6 +5,9 @@ Screen.register({
"go_to_home",
"go_to_office",
},
init = function()
Audio.music_play_room_work()
end,
background = "street",
draw = function()
if Window.get_current_id() == "game" then

View File

@@ -4,8 +4,33 @@ Screen.register({
decisions = {
"go_to_home",
"go_to_office",
"start_discussion",
"sumphore_discussion",
},
init = function()
Audio.music_play_room_work()
local possible_sprites = {
"matrix_trinity",
"matrix_neo",
{id="matrix_oraculum", y_correct=1 * 8},
"matrix_architect"
}
local possible_positions = {
{x = 5 * 8, y = 11 * 8},
{x = 7 * 8, y = 11 * 8},
{x = 9 * 8, y = 11 * 8},
{x = 11 * 8, y = 11 * 8},
{x = 13 * 8, y = 11 * 8},
{x = 15 * 8, y = 11 * 8},
{x = 18 * 8, y = 11 * 8},
{x = 21 * 8, y = 11 * 8},
{x = 24 * 8, y = 11 * 8},
{x = 27 * 8, y = 11 * 8},
}
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
end,
background = "street",
draw = function()
if Window.get_current_id() == "game" then
@@ -13,10 +38,8 @@ Screen.register({
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
Sprite.draw_at("dev_guard", 22 * 8, 2 * 8)
Sprite.draw_at("matrix_trinity", 5 * 8, 11 * 8)
Sprite.draw_at("matrix_neo", 7 * 8, 11 * 8)
Sprite.draw_at("matrix_oraculum", 9 * 8, 12 * 8)
Sprite.draw_at("matrix_architect", 11 * 8, 11 * 8)
Sprite.draw_list(Context.walking_to_office_sprites)
end
end
})

View File

@@ -1,7 +0,0 @@
Situation.register({
id = "drink_coffee",
handle = function()
Audio.sfx_select()
Sprite.show("norman", 100, 100)
end,
})

View File

@@ -1,84 +0,0 @@
--- @section Situation
local _situations = {}
--- Registers a situation definition.
--- @within Situation
--- @param situation table The situation data table.
--- @param situation.id string Unique situation identifier.<br/>
--- @param[opt] situation.screen_id string ID of the screen this situation belongs to.<br/>
--- @param[opt] situation.handle function Called when the situation is applied. Defaults to noop.<br/>
--- @param[opt] situation.update function Called each frame while situation is active. Defaults to noop.<br/>
function Situation.register(situation)
if not situation or not situation.id then
PopupWindow.show({"Error: Invalid situation object registered (missing id)!"})
return
end
if not situation.handle then
situation.handle = function() end
end
if not situation.update then
situation.update = function() end
end
if _situations[situation.id] then
trace("Warning: Overwriting situation with id: " .. situation.id)
end
_situations[situation.id] = situation
end
--- Gets a situation by ID.
--- @within Situation
--- @param id string The situation ID.
--- @return result table The situation table or nil. </br>
--- Fields: </br>
--- * id (string) Unique situation identifier.<br/>
--- * screen_id (string) ID of the screen this situation belongs to.<br/>
--- * handle (function) Called when the situation is applied.<br/>
--- * update (function) Called each frame while situation is active.<br/>
function Situation.get_by_id(id)
return _situations[id]
end
--- Gets all registered situations, optionally filtered by screen ID.
--- @within Situation
--- @param screen_id string Optional. If provided, returns situations associated with this screen ID.
--- @return result table A table containing all registered situation data, indexed by their IDs, or an array filtered by screen_id. </br>
--- Fields: </br>
--- * id (string) Unique situation identifier.<br/>
--- * screen_id (string) ID of the screen this situation belongs to.<br/>
--- * handle (function) Called when the situation is applied.<br/>
--- * update (function) Called each frame while situation is active.<br/>
function Situation.get_all(screen_id)
if screen_id then
local filtered_situations = {}
for _, situation in pairs(_situations) do
if situation.screen_id == screen_id then
table.insert(filtered_situations, situation)
end
end
return filtered_situations
end
return _situations
end
--- Applies a situation, checking screen compatibility and returning the new situation ID if successful.
--- @within Situation
--- @param id string The situation ID to apply.
--- @param current_screen_id string The ID of the currently active screen.
--- @return string|nil The ID of the applied situation if successful, otherwise nil.
function Situation.apply(id, current_screen_id)
local situation = Situation.get_by_id(id)
local screen = Screen.get_by_id(current_screen_id)
if not situation then
trace("Error: No situation found with id: " .. id)
return nil
end
if Util.contains(screen.situations, id) then
situation.handle()
return id
else
trace("Info: Situation " .. id .. " cannot be applied to current screen (id: " .. current_screen_id .. ").")
return nil
end
end

View File

@@ -3,7 +3,7 @@ local _sprites = {}
local _active_sprites = {}
local function draw_sprite_instance(sprite_data, params)
local colorkey = params.colorkey or sprite_data.colorkey or 0
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
@@ -73,6 +73,70 @@ function Sprite.generate_table(width, height, starting_s, x_base, y_base, x_step
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: " .. 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/>

View File

@@ -44,7 +44,7 @@ function AsciiArt.draw(text, options)
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 - 1, char_h - 1, color)
rect(x_offset + (j - 1) * char_w, current_y, char_w, char_h, color)
end
end
current_y = current_y + char_h + line_gap

View File

@@ -5,10 +5,9 @@ local INPUT_KEY_LEFT = 2
local INPUT_KEY_RIGHT = 3
local INPUT_KEY_A = 4
local INPUT_KEY_B = 5
local INPUT_KEY_Y = 7
local INPUT_KEY_SPACE = 48
local INPUT_KEY_BACKSPACE = 51
local INPUT_KEY_ENTER = 50
local INPUT_KEY_BACKSPACE = 51
--- Checks if Up is pressed.
--- @within Input
@@ -22,22 +21,12 @@ function Input.left() return btnp(INPUT_KEY_LEFT) end
--- Checks if Right is pressed.
--- @within Input
function Input.right() return btnp(INPUT_KEY_RIGHT) end
--- Checks if Space is pressed.
--- @within Input
function Input.space() return keyp(INPUT_KEY_SPACE) end
--- Checks if Select is pressed.
--- @within Input
function Input.select() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_SPACE) end
--- Checks if Menu Confirm is pressed.
function Input.select() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_SPACE) or Mouse.clicked() end
--- Checks if Back is pressed.
--- @within Input
function Input.menu_confirm() return btnp(INPUT_KEY_A) or keyp(INPUT_KEY_ENTER) end
--- Checks if Player Interact is pressed.
function Input.back() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_BACKSPACE) end
--- Checks if Enter is pressed.
--- @within Input
function Input.player_interact() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_ENTER) end
--- Checks if Menu Back is pressed.
--- @within Input
function Input.menu_back() return btnp(INPUT_KEY_Y) or keyp(INPUT_KEY_BACKSPACE) end
--- Checks if Toggle Popup is pressed.
--- @within Input
function Input.toggle_popup() return keyp(INPUT_KEY_ENTER) end
function Input.enter() return keyp(INPUT_KEY_ENTER) end

View File

@@ -17,6 +17,16 @@ end
--- @within Main
function TIC()
init_game()
Mouse.update()
local now = time()
if Context.last_frame_time == 0 then
Context.delta_time = 0
else
Context.delta_time = (now - Context.last_frame_time) / 1000
end
Context.last_frame_time = now
cls(Config.colors.black)
local handler = Window.get_current_handler() -- Get handler from Window manager
if handler then

View File

@@ -0,0 +1,81 @@
--- @section Mouse
local _mx, _my = 0, 0
local _mleft, _mleft_prev = false, false
local _consumed = false
--- Updates mouse state. Call once per frame.
--- @within Mouse
function Mouse.update()
_mleft_prev = _mleft
_consumed = false
local mt = {mouse()}
_mx, _my, _mleft = mt[1], mt[2], mt[3]
-- trace mouse position and tile for testing purposes
if Context.test_mode and Context.mouse_trace then
trace("Mouse: (" .. _mx .. "," .. _my .. "), tile: (" .. math.floor(_mx / 8) .. "," .. math.floor(_my / 8) .. ")")
end
end
--- Returns current mouse X position.
--- @within Mouse
function Mouse.x() return _mx end
--- Returns current mouse Y position.
--- @within Mouse
function Mouse.y() return _my end
--- Returns true if the mouse button was just pressed this frame (and not yet consumed).
--- @within Mouse
function Mouse.clicked() return _mleft and not _mleft_prev and not _consumed end
--- Returns true if the mouse button is held down.
--- @within Mouse
function Mouse.held() return _mleft end
--- Marks the current click as consumed so Mouse.clicked() won't fire again this frame.
--- @within Mouse
function Mouse.consume() _consumed = true end
--- Returns true if the mouse is within the given rectangle.
--- @within Mouse
--- @param x number Left edge.
--- @param y number Top edge.
--- @param w number Width.
--- @param h number Height.
function Mouse.in_rect(x, y, w, h)
return _mx >= x and _mx < x + w and _my >= y and _my < y + h
end
--- Returns true if the mouse is within the given circle.
--- @within Mouse
--- @param cx number Center x.
--- @param cy number Center y.
--- @param r number Radius.
function Mouse.in_circle(cx, cy, r)
local dx = _mx - cx
local dy = _my - cy
return (dx * dx + dy * dy) <= (r * r)
end
--- Returns true if the mouse was clicked inside the given rectangle, and consumes the click.
--- @within Mouse
--- @param rect table A table with fields: x, y, w, h.
function Mouse.zone(rect)
if Mouse.clicked() and Mouse.in_rect(rect.x, rect.y, rect.w, rect.h) then
Mouse.consume()
return true
end
return false
end
--- Returns true if the mouse was clicked inside the given circle, and consumes the click.
--- @within Mouse
--- @param circle table A table with fields: x, y, r.
function Mouse.zone_circle(circle)
if Mouse.clicked() and Mouse.in_circle(circle.x, circle.y, circle.r) then
Mouse.consume()
return true
end
return false
end

View File

@@ -10,7 +10,29 @@ function Print.text(text, x, y, color, fixed, scale)
local shadow_color = Config.colors.black
if color == shadow_color then shadow_color = Config.colors.light_grey end
scale = scale or 1
print(text, x + 1, y + 1, shadow_color, fixed, scale)
print(text, x + scale, y + scale, shadow_color, fixed, scale)
print(text, x, y, color, fixed, scale)
end
--- Prints text with a contour (outline) instead of shadow.
--- @within Print
--- @param text string The text to print.<br/>
--- @param x number The x-coordinate.<br/>
--- @param y number The y-coordinate.<br/>
--- @param color number The color of the text.<br/>
--- @param[opt] fixed boolean If true, uses fixed-width font.<br/>
--- @param[opt] scale number The scaling factor (also used for outline thickness).<br/>
--- @param[opt] contour_color number Outline color; defaults to black; if equal to text color, uses white.<br/>
function Print.text_contour(text, x, y, color, fixed, scale, contour_color)
scale = scale or 1
local cc = contour_color
if cc == nil then cc = Config.colors.black end
if color == cc then cc = Config.colors.white end
local ox = { -scale, scale, 0, 0, -scale, scale, -scale, scale }
local oy = { 0, 0, -scale, scale, -scale, -scale, scale, scale }
for i = 1, 8 do
print(text, x + ox[i], y + oy[i], cc, fixed, scale)
end
print(text, x, y, color, fixed, scale)
end
@@ -24,7 +46,23 @@ end
--- @param[opt] scale number The scaling factor.<br/>
function Print.text_center(text, x, y, color, fixed, scale)
scale = scale or 1
local text_width = print(text, 0, -6, 0, fixed, scale)
local text_width = print(text, 0, -6 * scale, 0, fixed, scale)
local centered_x = x - (text_width / 2)
Print.text(text, centered_x, y, color, fixed, scale)
end
--- Prints centered text with contour instead of shadow.
--- @within Print
--- @param text string The text to print.<br/>
--- @param x number The x-coordinate for centering.<br/>
--- @param y number The y-coordinate.<br/>
--- @param color number The color of the text.<br/>
--- @param[opt] fixed boolean If true, uses fixed-width font.<br/>
--- @param[opt] scale number The scaling factor.<br/>
--- @param[opt] contour_color number Outline color; defaults to black; if equal to text color, uses white.<br/>
function Print.text_center_contour(text, x, y, color, fixed, scale, contour_color)
scale = scale or 1
local text_width = print(text, 0, -6 * scale, 0, fixed, scale)
local centered_x = x - (text_width / 2)
Print.text_contour(text, centered_x, y, color, fixed, scale, contour_color)
end

22
inc/system/system.rle.lua Normal file
View File

@@ -0,0 +1,22 @@
--- @section RLE
RLE = {}
--- Draws an RLE-encoded image.
--- @param runs table Array of run lengths.
--- @param values table Array of pixel values (colors).
function RLE.draw(img_values, img_runs)
local SCREEN_WIDTH=240
local SCREEN_HEIGHT=136
local val_i=0
local run=0
for y=0,SCREEN_HEIGHT-1 do
for x=0,SCREEN_WIDTH-1 do
if run==0 then
val_i=val_i+1
run=img_runs[val_i]
end
run=run-1
pix(x,y,img_values[val_i])
end
end
end

View File

@@ -38,8 +38,12 @@ end
--- @within UI
--- @param items table A table of menu items.<br/>
--- @param selected_item number The current index of the selected item.<br/>
--- @param[opt] x number Menu x position (required for mouse support).<br/>
--- @param[opt] y number Menu y position (required for mouse support).<br/>
--- @param[opt] centered boolean Whether the menu is centered horizontally.<br/>
--- @return number selected_item The updated index of the selected item.
function UI.update_menu(items, selected_item)
--- @return boolean mouse_confirmed True if the user clicked on a menu item.
function UI.update_menu(items, selected_item, x, y, centered)
if Input.up() then
Audio.sfx_beep()
selected_item = selected_item - 1
@@ -53,7 +57,25 @@ function UI.update_menu(items, selected_item)
selected_item = 1
end
end
return selected_item
if x ~= nil and y ~= nil then
local menu_x = x
if centered then
local max_w = 0
for _, item in ipairs(items) do
local w = print(item.label, 0, -10, 0, false, 1, false)
if w > max_w then max_w = w end
end
menu_x = (Config.screen.width - max_w) / 2
end
for i, _ in ipairs(items) do
if Mouse.zone({ x = menu_x - 8, y = y + (i-1) * 10, w = Config.screen.width, h = 10 }) then
return i, true
end
end
end
return selected_item, false
end
--- Draws a bordered textbox with scrolling text.
@@ -68,12 +90,54 @@ end
--- @param[opt] bg_color number The background fill color (default: Config.colors.dark_grey).<br/>
--- @param[opt] border_color number The border color (default: Config.colors.white).<br/>
--- @param[opt] center_text boolean Whether to center each line inside the box. Defaults to false.<br/>
local function draw_rounded_rect_fill(x, y, w, h, corner_radius, color)
local inner_w = w - corner_radius * 2
local inner_h = h - corner_radius * 2
if inner_w > 0 and inner_h > 0 then
rect(x + corner_radius, y + corner_radius, inner_w, inner_h, color)
end
if inner_w > 0 and corner_radius > 0 then
rect(x + corner_radius, y, inner_w, corner_radius, color)
rect(x + corner_radius, y + h - corner_radius, inner_w, corner_radius, color)
end
if corner_radius > 0 and inner_h > 0 then
rect(x, y + corner_radius, corner_radius, inner_h, color)
rect(x + w - corner_radius, y + corner_radius, corner_radius, inner_h, color)
end
for row = 0, corner_radius - 1 do
for col = 0, corner_radius - 1 do
if col + row >= corner_radius - 1 then
pix(x + corner_radius - 1 - col, y + corner_radius - 1 - row, color)
pix(x + w - corner_radius + col, y + corner_radius - 1 - row, color)
pix(x + corner_radius - 1 - col, y + h - corner_radius + row, color)
pix(x + w - corner_radius + col, y + h - corner_radius + row, color)
end
end
end
end
local function draw_rounded_rect_border(x, y, w, h, corner_radius, color)
line(x + corner_radius, y, x + w - corner_radius - 1, y, color)
line(x + corner_radius, y + h - 1, x + w - corner_radius - 1, y + h - 1, color)
line(x, y + corner_radius, x, y + h - corner_radius - 1, color)
line(x + w - 1, y + corner_radius, x + w - 1, y + h - corner_radius - 1, color)
pix(x + corner_radius - 1, y + 1, color)
pix(x + 1, y + corner_radius - 1, color)
pix(x + w - corner_radius, y + 1, color)
pix(x + w - 2, y + corner_radius - 1, color)
pix(x + corner_radius - 1, y + h - 2, color)
pix(x + 1, y + h - corner_radius, color)
pix(x + w - corner_radius, y + h - 2, color)
pix(x + w - 2, y + h - corner_radius, color)
end
function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_color, border_color, center_text)
color = color or Config.colors.white
bg_color = bg_color or Config.colors.dark_grey
bg_color = bg_color or Config.colors.black
border_color = border_color or Config.colors.white
center_text = center_text or false
local r = 3
local padding = 4
local line_height = 8
local inner_x = box_x + padding
@@ -88,7 +152,7 @@ function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_c
base_y = inner_y + math.floor((visible_height - text_height) / 2)
end
rect(box_x, box_y, box_w, box_h, bg_color)
draw_rounded_rect_fill(box_x, box_y, box_w, box_h, r, bg_color)
for i, line in ipairs(lines) do
local ly = base_y + (i - 1) * line_height - scroll_y
@@ -101,7 +165,7 @@ function UI.draw_textbox(text, box_x, box_y, box_w, box_h, scroll_y, color, bg_c
end
end
rectb(box_x, box_y, box_w, box_h, border_color)
draw_rounded_rect_border(box_x, box_y, box_w, box_h, r, border_color)
end
--- Wraps text.

View File

@@ -40,3 +40,30 @@ function Util.contains(t, value)
end
return false
end
--- Deep copies tables
--- @within Util
--- @param orig any The value to deep copy.
--- @param seen any Used for recursive calls to handle loops
--- @return any any The copied object
function Util.deepcopy(orig, seen)
if type(orig) ~= "table" then
return orig
end
if seen and seen[orig] then
return seen[orig] -- handle cycles / shared refs
end
local copy = {}
seen = seen or {}
seen[orig] = copy
for k, v in pairs(orig) do
local new_k = Util.deepcopy(k, seen)
local new_v = Util.deepcopy(v, seen)
copy[new_k] = new_v
end
return setmetatable(copy, getmetatable(orig))
end

View File

@@ -107,9 +107,9 @@ function AudioTestWindow.update()
AudioTestWindow.menuitems = AudioTestWindow.generate_menuitems(
AudioTestWindow.list_func, AudioTestWindow.index_func
)
elseif Input.menu_confirm() then
elseif Input.select() then
AudioTestWindow.menuitems[AudioTestWindow.index_menu].decision()
elseif Input.menu_back() then
elseif Input.back() then
AudioTestWindow.back()
end
end

View File

@@ -1,102 +0,0 @@
--- @section ConfigurationWindow
ConfigurationWindow.controls = {}
ConfigurationWindow.selected_control = 1
--- Initializes configuration window.
--- @within ConfigurationWindow
function ConfigurationWindow.init()
ConfigurationWindow.controls = {
{
label = "Save",
action = function() Config.save() end,
type = "action_item"
},
{
label = "Restore Defaults",
action = function() Config.reset() end,
type = "action_item"
},
}
end
--- Draws configuration window.
--- @within ConfigurationWindow
function ConfigurationWindow.draw()
UI.draw_top_bar("Configuration")
local x_start = 10
local y_start = 40
local x_value_right_align = Config.screen.width - 10
local char_width = 4
for i, control in ipairs(ConfigurationWindow.controls) do
local current_y = y_start + (i - 1) * 12
local color = Config.colors.light_blue
if control.type == "numeric_stepper" then
local value = control.get()
local label_text = control.label
local value_text = string.format(control.format, value)
local value_x = x_value_right_align - (#value_text * char_width)
if i == ConfigurationWindow.selected_control then
color = Config.colors.item
Print.text("<", x_start - 8, current_y, color)
Print.text(label_text, x_start, current_y, color)
Print.text(value_text, value_x, current_y, color)
Print.text(">", x_value_right_align + 4, current_y, color)
else
Print.text(label_text, x_start, current_y, color)
Print.text(value_text, value_x, current_y, color)
end
elseif control.type == "action_item" then
local label_text = control.label
if i == ConfigurationWindow.selected_control then
color = Config.colors.item
Print.text("<", x_start - 8, current_y, color)
Print.text(label_text, x_start, current_y, color)
Print.text(">", x_start + 8 + (#label_text * char_width) + 4, current_y, color)
else
Print.text(label_text, x_start, current_y, color)
end
end
end
Print.text("Press B to go back", x_start, 120, Config.colors.light_grey)
end
--- Updates configuration window logic.
--- @within ConfigurationWindow
function ConfigurationWindow.update()
if Input.menu_back() then
GameWindow.set_state("menu")
return
end
if Input.up() then
ConfigurationWindow.selected_control = ConfigurationWindow.selected_control - 1
if ConfigurationWindow.selected_control < 1 then
ConfigurationWindow.selected_control = #ConfigurationWindow.controls
end
elseif Input.down() then
ConfigurationWindow.selected_control = ConfigurationWindow.selected_control + 1
if ConfigurationWindow.selected_control > #ConfigurationWindow.controls then
ConfigurationWindow.selected_control = 1
end
end
local control = ConfigurationWindow.controls[ConfigurationWindow.selected_control]
if control then
if control.type == "numeric_stepper" then
local current_value = control.get()
if Input.left() then
local new_value = math.max(control.min, current_value - control.step)
control.set(new_value)
elseif Input.right() then
local new_value = math.min(control.max, current_value + control.step)
control.set(new_value)
end
elseif control.type == "action_item" then
if Input.menu_confirm() then
control.action()
end
end
end
end

View File

@@ -26,7 +26,7 @@ end
--- @within ContinuedWindow
function ContinuedWindow.update()
ContinuedWindow.timer = ContinuedWindow.timer - 1
if ContinuedWindow.timer <= 0 or Input.select() or Input.menu_confirm() then
if ContinuedWindow.timer <= 0 or Input.select() or Input.select() then
Window.set_current("menu")
MenuWindow.refresh_menu_items()
end

View File

@@ -0,0 +1,44 @@
--- @section ControlsWindow
local _controls = {
{ action = "Navigate", keyboard = "Arrow keys", gamepad = "D-pad" },
{ action = "Select / OK", keyboard = "Space", gamepad = "Z button" },
{ action = "Back", keyboard = "Backspace", gamepad = "B button" },
{ action = "Click", keyboard = "Mouse", gamepad = "" },
}
--- Draws the controls window.
--- @within ControlsWindow
function ControlsWindow.draw()
UI.draw_top_bar("Controls")
local col_action = 4
local col_keyboard = 80
local col_gamepad = 170
local row_h = 10
local y_header = 18
local y_start = 30
Print.text("Action", col_action, y_header, Config.colors.light_grey)
Print.text("Keyboard", col_keyboard, y_header, Config.colors.light_grey)
Print.text("Gamepad", col_gamepad, y_header, Config.colors.light_grey)
line(col_action, y_header + 8, Config.screen.width - 4, y_header + 8, Config.colors.dark_grey)
for i, entry in ipairs(_controls) do
local y = y_start + (i - 1) * row_h
Print.text(entry.action, col_action, y, Config.colors.white)
Print.text(entry.keyboard, col_keyboard, y, Config.colors.light_blue)
if entry.gamepad ~= "" then
Print.text(entry.gamepad, col_gamepad, y, Config.colors.light_blue)
end
end
Print.text("Space / Z button or click to go back", col_action, Config.screen.height - 10, Config.colors.light_grey)
end
--- Updates the controls window logic.
--- @within ControlsWindow
function ControlsWindow.update()
if Input.back() or Input.select() then
Window.set_current("menu")
end
end

View File

@@ -24,7 +24,7 @@ function DiscussionWindow.draw()
TEXTBOX_W, TEXTBOX_H,
Context.discussion.scroll_y,
Config.colors.white,
Config.colors.dark_grey,
Config.colors.black,
Config.colors.light_blue,
true
)
@@ -37,9 +37,13 @@ function DiscussionWindow.draw()
local selected = answers[Context.discussion.selected_answer]
local label = selected.label
local answer_text_y = bar_y + 4
Print.text("<", 2, answer_text_y, Config.colors.light_blue)
Print.text_center(label, Config.screen.width / 2, answer_text_y, Config.colors.item)
Print.text(">", Config.screen.width - 6, answer_text_y, Config.colors.light_blue)
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, answer_text_y, left_arrow_color, false, 1, left_arrow_contour_color)
Print.text_center(label, Config.screen.width / 2, answer_text_y, Config.colors.orange)
Print.text_center_contour(">", Config.screen.width - 6, answer_text_y, right_arrow_color, false, 1, right_arrow_contour_color)
end
end

View File

@@ -52,7 +52,7 @@ function EndWindow.update()
end
end
if Input.menu_confirm() then
if Input.select() then
Audio.sfx_select()
if Context._end.selection == 1 then
Context._end.state = "ending"
@@ -69,7 +69,7 @@ function EndWindow.update()
end
end
elseif Context._end.state == "ending" then
if Input.menu_confirm() then
if Input.select() then
Window.set_current("menu")
MenuWindow.refresh_menu_items()
end

View File

@@ -38,7 +38,7 @@ end
--- @within GameWindow
function GameWindow.update()
Focus.update()
if Input.menu_back() then
if Input.back() then
Window.set_current("menu")
MenuWindow.refresh_menu_items()
return
@@ -48,14 +48,6 @@ function GameWindow.update()
if not screen or not screen.update then return end
screen.update()
-- Handle current situation updates
if Context.game.current_situation then
local current_situation_obj = Situation.get_by_id(Context.game.current_situation)
if current_situation_obj and type(current_situation_obj.update) == "function" then
current_situation_obj.update()
end
end
if Context.stat_screen_active then return end
-- Fetch and filter decisions locally
@@ -68,7 +60,7 @@ function GameWindow.update()
_selected_decision_index = 1
end
local new_selected_decision_index = Decision.update(
local new_selected_decision_index, mouse_confirmed = Decision.update(
_available_decisions,
_selected_decision_index
)
@@ -77,7 +69,7 @@ function GameWindow.update()
_selected_decision_index = new_selected_decision_index
end
if Input.select() then
if Input.select() or mouse_confirmed then
local selected_decision = _available_decisions[_selected_decision_index]
if selected_decision and selected_decision.handle then
Audio.sfx_select()

View File

@@ -1,6 +1,6 @@
--- @section BriefIntroWindow
BriefIntroWindow.y = Config.screen.height
BriefIntroWindow.speed = 0.5
BriefIntroWindow.speed = 30 -- pixels per second
BriefIntroWindow.text = [[
Norman Reds everyday life
seems ordinary: work,
@@ -24,14 +24,14 @@ end
--- Updates the brief intro window logic.
--- @within BriefIntroWindow
function BriefIntroWindow.update()
BriefIntroWindow.y = BriefIntroWindow.y - BriefIntroWindow.speed
BriefIntroWindow.y = BriefIntroWindow.y - (BriefIntroWindow.speed * Context.delta_time)
local lines = 1
for _ in string.gmatch(BriefIntroWindow.text, "\n") do
lines = lines + 1
end
if BriefIntroWindow.y < -lines * 8 or Input.select() or Input.menu_confirm() then
if BriefIntroWindow.y < -lines * 8 or Input.select() or Input.select() then
Window.set_current("menu")
end
end

View File

@@ -1,10 +1,10 @@
--- @section TitleIntroWindow
TitleIntroWindow.timer = 180 -- 3 seconds at 60fps
TitleIntroWindow.text = [[
## ### ### ### ### ### ### ### ## # #
# # # # # # # # # # # # # #
# # ### ### # # # # # ### # # #
## ### ### ### ### ### ### ### # # #
# # # # # # # # # # # # #
# # ### ### # # # # # ### # #
# # # # # # # # # # # #
## ### # ### # # ### # ### ## #
# # ### ### ## # #
@@ -30,7 +30,7 @@ end
--- @within TitleIntroWindow
function TitleIntroWindow.update()
TitleIntroWindow.timer = TitleIntroWindow.timer - 1
if TitleIntroWindow.timer <= 0 or Input.select() or Input.menu_confirm() then
if TitleIntroWindow.timer <= 0 or Input.select() or Input.select() then
Window.set_current("intro_ttg")
end
end

View File

@@ -27,13 +27,13 @@ function TTGIntroWindow.update()
TTGIntroWindow.glitch_started = true
end
-- Count menu_back presses during the intro
if Input.menu_back() then
-- Count enter presses during the intro
if Input.enter() then
TTGIntroWindow.space_count = TTGIntroWindow.space_count + 1
end
TTGIntroWindow.timer = TTGIntroWindow.timer - 1
if TTGIntroWindow.timer <= 0 or Input.menu_confirm() then
if TTGIntroWindow.timer <= 0 or Input.select() then
-- Evaluate exactly 3 presses at the end of the intro
if TTGIntroWindow.space_count == 3 then
Context.test_mode = true

View File

@@ -1,18 +1,64 @@
--- @section MenuWindow
local _menu_items = {}
local _click_timer = 0
local _anim = 0
local _menu_max_w = 0
local ANIM_SPEED = 2.5
local HEADER_H = 28
--- Calculates the animated x position of the menu block.
--- @within MenuWindow
--- @return number x The left edge x coordinate for the menu.
function MenuWindow.calc_menu_x()
local center_start = Config.screen.width / 2
local center_end = Config.screen.width * 0.72
local center = center_start + _anim * (center_end - center_start)
return math.floor(center - _menu_max_w / 2)
end
--- Draws the header with title and separator.
--- @within MenuWindow
function MenuWindow.draw_header()
rect(0, 0, Config.screen.width, HEADER_H, Config.colors.black)
rect(0, HEADER_H - 2, Config.screen.width, 2, Config.colors.light_blue)
local cx = Config.screen.width / 2
local subtitle = "Definitely not an"
if Context.test_mode then subtitle = subtitle .. " [TEST]" end
local sub_w = print(subtitle, 0, -6, 0, false, 1, true)
print(subtitle, math.floor(cx - sub_w / 2) + 1, 5, Config.colors.dark_grey, false, 1, true)
print(subtitle, math.floor(cx - sub_w / 2), 4, Config.colors.light_grey, false, 1, true)
Print.text_center("IMPOSTOR", cx, 12, Config.colors.item, false, 2)
end
--- Draws the 4x scaled Norman sprite on the left side of the screen.
--- @within MenuWindow
function MenuWindow.draw_norman()
local nx = math.floor(Config.screen.width * 0.45 / 2) - 32
local ny = HEADER_H + math.floor((Config.screen.height - HEADER_H - 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
--- Draws the menu window.
--- @within MenuWindow
function MenuWindow.draw()
local title = "Definitely not an Impostor"
if Context.test_mode then
title = title .. " (TEST MODE)"
cls(Config.colors.blue
)
MenuWindow.draw_header()
if _anim > 0 then
MenuWindow.draw_norman()
end
UI.draw_top_bar(title)
local menu_h = #_menu_items * 10
local y = 10 + (Config.screen.height - 10 - 10 - menu_h) / 2
UI.draw_menu(_menu_items, Context.current_menu_item, 0, y, true)
local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 10 - menu_h) / 2)
UI.draw_menu(_menu_items, Context.current_menu_item, MenuWindow.calc_menu_x(), y, false)
local ttg_text = "TTG"
local ttg_w = print(ttg_text, 0, -10, 0, false, 1, false)
@@ -22,9 +68,32 @@ end
--- Updates the menu window logic.
--- @within MenuWindow
function MenuWindow.update()
Context.current_menu_item = UI.update_menu(_menu_items, Context.current_menu_item)
if _anim < 1 then
_anim = math.min(1, _anim + ANIM_SPEED * Context.delta_time)
end
if Input.menu_confirm() then
local menu_h = #_menu_items * 10
local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 10 - menu_h) / 2)
if _click_timer > 0 then
_click_timer = _click_timer - Context.delta_time
if _click_timer <= 0 then
_click_timer = 0
local selected_item = _menu_items[Context.current_menu_item]
if selected_item and selected_item.decision then
selected_item.decision()
end
end
return
end
local new_item, mouse_confirmed = UI.update_menu(_menu_items, Context.current_menu_item, MenuWindow.calc_menu_x(), y, false)
Context.current_menu_item = new_item
if mouse_confirmed then
Audio.sfx_select()
_click_timer = 0.5
elseif Input.select() then
local selected_item = _menu_items[Context.current_menu_item]
if selected_item and selected_item.decision then
Audio.sfx_select()
@@ -64,11 +133,10 @@ function MenuWindow.exit()
exit()
end
--- Opens the configuration menu.
--- Opens the controls screen.
--- @within MenuWindow
function MenuWindow.configuration()
ConfigurationWindow.init()
GameWindow.set_state("configuration")
function MenuWindow.controls()
Window.set_current("controls")
end
--- Opens the audio test menu.
@@ -85,15 +153,15 @@ function MenuWindow.continued()
GameWindow.set_state("continued")
end
--- Opens the minigame ddr test menu.
--- Opens the DDR minigame test.
--- @within MenuWindow
function MenuWindow.ddr_test()
AudioTestWindow.init()
GameWindow.set_state("minigame_ddr")
MinigameDDRWindow.start("menu", nil)
MinigameDDRWindow.start("menu", "generated", { special_mode = "only_nothing" })
end
--- Refreshes menu items.
--- Refreshes the list of menu items based on current game state.
--- @within MenuWindow
function MenuWindow.refresh_menu_items()
_menu_items = {}
@@ -104,7 +172,7 @@ function MenuWindow.refresh_menu_items()
table.insert(_menu_items, {label = "New Game", decision = MenuWindow.new_game})
table.insert(_menu_items, {label = "Load Game", decision = MenuWindow.load_game})
table.insert(_menu_items, {label = "Configuration", decision = MenuWindow.configuration})
table.insert(_menu_items, {label = "Controls", decision = MenuWindow.controls})
if Context.test_mode then
table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test})
@@ -114,5 +182,13 @@ function MenuWindow.refresh_menu_items()
table.insert(_menu_items, {label = "Exit", decision = MenuWindow.exit})
_menu_max_w = 0
for _, item in ipairs(_menu_items) do
local w = print(item.label, 0, -10, 0, false, 1, false)
if w > _menu_max_w then _menu_max_w = w end
end
Context.current_menu_item = 1
_click_timer = 0
_anim = 0
end

View File

@@ -1,14 +1,62 @@
--- @section MinigameDDRWindow
---@class MinigameDDRState
---@field special_mode string
---@field bar_fill number
---@field max_fill number
---@field fill_per_hit number
---@field miss_penalty number
---@field bar_x number
---@field bar_y number
---@field bar_width number
---@field bar_height number
---@field arrow_size number
---@field arrow_spawn_timer number
---@field arrow_spawn_interval number
---@field arrow_fall_speed number
---@field arrows table
---@field target_y number
---@field target_arrows table
---@field hit_threshold number
---@field button_pressed_timers table
---@field button_press_duration number
---@field input_cooldowns table
---@field input_cooldown_duration number
---@field frame_counter number
---@field current_song table?
---@field pattern_index number
---@field use_pattern boolean
---@field generated_length number
---@field return_window string?
---@field win_timer number
---@field on_win fun(ctx: MinigameDDRState)|nil
---@field total_misses number
---@field total_hits number
---@field special_mode_condition boolean
---@field special_mode_counter number
---@field debug_song_key string|nil
---@field debug_status string|nil
--- Background drawing for DDR minigame.
--- @within MinigameDDRWindow
function MinigameDDRWindow.draw_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}
local img_runs = {809,40,5,26,178,42,4,7,30,10,127,103,11,1,116,124,116,124,115,18,60,47,115,10,105,10,115,9,108,9,114,9,108,9,114,9,108,9,114,9,33,31,44,9,114,9,34,28,46,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,108,9,114,9,109,8,114,9,109,8,114,9,109,9,112,10,105,1,3,9,111,11,104,2,3,9,111,11,101,5,3,9,111,11,101,5,3,9,111,9,103,5,3,9,111,9,99,1,2,6,3,9,111,9,99,1,2,8,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,1,3,1,2,11,1,9,111,9,3,1,88,1,5,12,1,9,111,9,3,1,88,1,3,14,1,9,111,9,3,1,88,3,1,1,2,11,1,9,111,9,3,1,88,3,1,14,1,9,111,9,3,1,88,3,2,13,2,8,111,9,3,1,88,3,3,12,3,8,110,9,3,1,88,3,3,12,3,9,109,9,3,1,88,3,1,14,3,9,109,9,3,1,90,1,1,7,3,4,3,9,109,9,3,1,90,1,1,5,6,3,3,9,108,10,3,1,44,1,4,1,40,1,1,5,7,2,3,9,108,10,3,1,44,1,4,1,38,3,1,5,7,3,2,9,108,10,3,1,48,1,39,3,1,5,7,2,3,9,108,10,3,1,88,3,1,5,6,3,3,9,108,10,3,1,32,5,4,4,3,2,38,3,1,15,2,9,108,10,3,1,41,5,2,2,2,4,32,3,1,15,2,9,108,10,2,2,41,9,2,5,3,1,3,1,23,3,1,9,1,5,2,9,108,10,2,2,41,12,35,3,1,7,4,4,2,9,108,10,2,2,32,26,30,3,1,5,7,3,2,9,108,10,2,2,36,21,31,9,7,3,2,9,108,10,2,2,35,23,30,10,6,3,2,9,108,10,2,2,35,23,30,11,4,4,2,9,108,10,2,2,34,24,30,19,2,9,108,10,2,2,33,25,30,19,2,9,108,10,2,2,32,28,28,19,2,9,108,10,2,2,32,30,26,19,2,9,108,10,2,2,33,31,24,19,2,9,108,10,2,4,37,17,32,19,2,10,107,10,2,5,85,19,2,10,107,10,2,109,2,10,107,12,1,107,3,10,107,133,107,133,107,133,107,133,107,133,107,133,107,133,118,111,129,6,63,3,28,10,121,13,98,6,142,7,76,6,129,10,13,5,77,15,109,4,31,4,78,4,24,7,75,173,66,176,64,177,62,178,62,178,62,56,31,2,7,2,2,3,2,1,2,1,61,8,62,56,114,8,62,56,114,8,62,56,114,8,62,56,114,8,62,9,8,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,114,9,61,8,9,39,115,8,61,8,8,40,115,8,61,56,115,8,61,13,2,1,1,1,1,16,1,1,2,17,115,8,61,13,2,1,1,1,2,1,1,10,2,1,1,1,2,1,2,7,1,6,115,8,60,14,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,5,9,2,1,1,1,2,1,2,6,2,6,115,8,59,15,2,1,1,1,6,8,2,1,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,14,115,8,59,6,1,11,1,1,7,10,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,10,9,8,1,1,2,1,2,1,1,12,115,9,58,6,1,1,2,8,1,1,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,8,1,1,2,1,2,1,1,11,116,9,58,6,1,1,2,1,2,7,9,10,2,1,2,1,1,11,116,9,58,57,116,9,58,58,115,9,102,13,116,9,58,48,2,10,1,3,22,84,5,7,58,158,7,5,6,6,75,3,7,2,7,1,7,2,6,1,6,2,6,2,4,4,6,2,6,1,6,2,6,3,6,1,6,5,7,8,7,9,11,1,9,2,50,1,22,1,14,5,3,5,3,4,3,6,3,5,2,16,2,5,3,14,2,6,2,6,5,5,11,4,30,1,50,1,37,6,3,5,3,5,2,6,3,5,3,6,1,7,2,6,2,15,2,6,2,5,6,5,12,1,15,1,106,93,2,8,10,5,2,6,2,5,4,4,100,7,3,13,2,65,3,8,3,5,2,5,4,4,3,5,4,4,100,94,2,5,3,5,4,4,6,5,2,5,105,86,2,13,3,5,4,4,6,5,2,1,1,3,102,4,2,24,2,62,1,1,2,7,3,5,4,5,4,5,2,8,4,2,92,5,2,24,2,61,5,5,7,2,5,6,3,4,3,7,102,88,6,7,12,8,6,1,3,7,103,87,6,7,11,9,6,1,3,7,110,1,9,9,5,1,2,1,3,47,162,1,10,6,27,34,162,1,10,6,27,34,97,3,57,14,2,7,26,66,9,33,5,2,17,223,17,223,18,222,47,193,53,1,3,163,19,1,85,109,14,29,36,151,784}
-- pal = {220,220,220,90,90,90}
-- pal = dcdcdc5a5a5a
RLE.draw(img_values, img_runs)
end
--- Gets initial DDR minigame configuration.
--- @within MinigameDDRWindow
--- @return result table The default DDR minigame configuration.
---@return MinigameDDRState
function MinigameDDRWindow.init_context()
local arrow_size = 12
local arrow_spacing = 30
local total_width = (4 * arrow_size) + (3 * arrow_spacing)
local start_x = (Config.screen.width - total_width) / 2
return {
special_mode = "normal", -- "normal", "only_special", "only_left", "only_nothing"
bar_fill = 0,
max_fill = 100,
fill_per_hit = 10,
@@ -22,7 +70,7 @@ function MinigameDDRWindow.init_context()
arrow_spawn_interval = 45,
arrow_fall_speed = 1.5,
arrows = {},
target_y = 115,
target_y = 120,
target_arrows = {
{ dir = "left", x = start_x },
{ dir = "down", x = start_x + arrow_size + arrow_spacing },
@@ -38,12 +86,95 @@ function MinigameDDRWindow.init_context()
current_song = nil,
pattern_index = 1,
use_pattern = false,
generated_length = 30,
return_window = nil,
win_timer = 0,
on_win = nil
on_win = nil,
total_misses = 0,
total_hits = 0,
special_mode_condition = true,
special_mode_counter = 0
}
end
--- Builds song data (and optional generated pattern) for the minigame.
--- @within MinigameDDRWindow
function MinigameDDRWindow.prepareSong(song, generated_length, special_mode)
local current_song = Util.deepcopy(song)
if current_song.generated then
local pattern = musicator_generate_pattern(generated_length, current_song.bpm, current_song.spd * 4)
current_song.pattern = pattern
current_song.end_frame = pattern[#pattern].frame
if special_mode == "only_special" then
for i, _ in ipairs(current_song.pattern) do
current_song.pattern[i].special = (i % 5 == 0)
end
end
end
return current_song
end
--- Handles hit feedback and special-mode scoring for one arrow.
--- @within MinigameDDRWindow
---@param game_context MinigameDDRState
function MinigameDDRWindow.on_arrow_hit_special(arrow, game_context)
local special_mode = game_context.special_mode
if special_mode == "normal" then
Audio.sfx_arrowhit(arrow.note)
elseif special_mode == "only_special" then
if arrow.special then
Audio.sfx_arrowhit(arrow.note)
game_context.special_mode_counter = game_context.special_mode_counter + 1
else
if game_context.special_mode_condition then Audio.sfx_bloop() end
game_context.special_mode_condition = false
end
elseif special_mode == "only_left" then
if arrow.dir == "left" then
Audio.sfx_arrowhit(arrow.note)
game_context.special_mode_counter = game_context.special_mode_counter + 1
if game_context.max_fill <= game_context.bar_fill + game_context.fill_per_hit then
game_context.bar_fill = game_context.bar_fill - game_context.fill_per_hit
end
else
if game_context.special_mode_condition then Audio.sfx_bloop() end
game_context.special_mode_condition = false
end
elseif special_mode == "only_nothing" then
if game_context.special_mode_condition then Audio.sfx_bloop() end
game_context.special_mode_condition = false
end
end
--- Ends the minigame: win timer, sfx, and special-mode pass/fail bookkeeping.
--- @within MinigameDDRWindow
function MinigameDDRWindow.on_end(game_context)
Audio.sfx_select()
game_context.win_timer = Config.timing.minigame_win_duration
local num_special = 0
for _, v in ipairs(game_context.current_song.pattern) do
if game_context.special_mode == "only_left" then
num_special = num_special + ((v.dir == "left" and 1) or 0)
else
num_special = num_special + ((v.special and 1) or 0)
end
end
local sm = game_context.special_mode
local was_ok = true
if sm == "normal" or sm == "only_special" or sm == "only_left" then
was_ok = game_context.special_mode_counter == num_special
end
game_context.special_mode_condition = game_context.special_mode_condition and was_ok
end
--- Initializes DDR minigame state.
--- @within MinigameDDRWindow
--- @param params table Optional parameters for configuration.<br/>
@@ -64,13 +195,22 @@ end
--- @param[opt] params table Optional parameters for minigame configuration.</br>
function MinigameDDRWindow.start(return_window, song_key, params)
MinigameDDRWindow.init(params)
Audio.music_play_activity_work()
Context.minigame_ddr.return_window = return_window or "game"
Context.minigame_ddr.debug_song_key = song_key
if song_key and Songs and Songs[song_key] then
Context.minigame_ddr.current_song = Songs[song_key]
Context.minigame_ddr.use_pattern = true
Context.minigame_ddr.pattern_index = 1
Context.minigame_ddr.debug_status = "Pattern loaded: " .. song_key
Context.minigame_ddr.current_song = MinigameDDRWindow.prepareSong(
Songs[song_key],
Context.minigame_ddr.generated_length,
Context.minigame_ddr.special_mode
)
else
Context.minigame_ddr.use_pattern = false
if song_key then
@@ -79,32 +219,45 @@ function MinigameDDRWindow.start(return_window, song_key, params)
Context.minigame_ddr.debug_status = "Random mode"
end
end
if not Context.test_mode then
Context.minigame_ddr.debug_status = ""
end
Window.set_current("minigame_ddr")
end
--- Spawns a random arrow.
--- @within MinigameDDRWindow
local function spawn_arrow()
trace("random arrow")
---@type MinigameDDRState
local mg = Context.minigame_ddr
local y0 = mg.bar_y + mg.bar_height + 10
local target = mg.target_arrows[math.random(1, 4)]
table.insert(mg.arrows, {
dir = target.dir,
x = target.x,
y = mg.bar_y + mg.bar_height + 10
y = y0
})
end
--- Spawns an arrow in a specific direction.
--- @within MinigameDDRWindow
--- @param direction string The direction of the arrow ("left", "down", "up", "right").
local function spawn_arrow_dir(direction)
local function spawn_arrow_dir(direction, note, special)
---@type MinigameDDRState
local mg = Context.minigame_ddr
local y0 = mg.bar_y + mg.bar_height + 10
for _, target in ipairs(mg.target_arrows) do
if target.dir == direction then
table.insert(mg.arrows, {
dir = direction,
x = target.x,
y = mg.bar_y + mg.bar_height + 10
y = y0,
note = note,
special = special
})
break
end
@@ -116,6 +269,7 @@ end
--- @param arrow table The arrow data.
--- @return boolean True if the arrow is hit, false otherwise.
local function check_hit(arrow)
---@type MinigameDDRState
local mg = Context.minigame_ddr
local distance = math.abs(arrow.y - mg.target_y)
return distance <= mg.hit_threshold
@@ -126,34 +280,53 @@ end
--- @param arrow table The arrow data.
--- @return boolean True if the arrow is missed, false otherwise.
local function check_miss(arrow)
---@type MinigameDDRState
local mg = Context.minigame_ddr
return arrow.y > mg.target_y + mg.hit_threshold
end
--- Draws an arrow.
--- Rotates a point (px, py) around (cx, cy) by a number of 90-degree CW steps.
--- @within MinigameDDRWindow
local function rotate(px, py, cx, cy, steps)
local dx, dy = px - cx, py - cy
for _ = 1, steps % 4 do
dx, dy = dy, -dx
end
return cx + dx, cy + dy
end
local arrow_rotations = { down = 0, right = 1, up = 2, left = 3 }
--- Draws an arrow by rotating the "down" arrow shape.
--- @within MinigameDDRWindow
--- @param x number The x-coordinate.
--- @param y number The y-coordinate.
--- @param direction string The direction of the arrow.
--- @param color number The color of the arrow.
local function draw_arrow(x, y, direction, color)
local size = 12
local size = 14
local half = size / 2
if direction == "left" then
tri(x + half, y, x, y + half, x + half, y + size, color)
rectb(x + half, y + half - 2, half, 4, color)
elseif direction == "right" then
tri(x + half, y, x + size, y + half, x + half, y + size, color)
rectb(x, y + half - 2, half, 4, color)
elseif direction == "up" then
tri(x, y + half, x + half, y, x + size, y + half, color)
rectb(x + half - 2, y + half, 4, half, color)
elseif direction == "down" then
tri(x, y + half, x + half, y + size, x + size, y + half, color)
rectb(x + half - 2, y, 4, half, color)
end
local pivot_x, pivot_y = x + half, y + half
local steps = arrow_rotations[direction] or 0
local head_left_x, head_left_y = rotate(x, y + half, pivot_x, pivot_y, steps)
local head_tip_x, head_tip_y = rotate(x + half, y + size, pivot_x, pivot_y, steps)
local head_right_x, head_right_y = rotate(x + size, y + half, pivot_x, pivot_y, steps)
tri(head_left_x, head_left_y,
head_tip_x, head_tip_y,
head_right_x, head_right_y, color)
local stem_top_x, stem_top_y = rotate(x + half - 3, y, pivot_x, pivot_y, steps)
local stem_bot_x, stem_bot_y = rotate(x + half + 3, y + half, pivot_x, pivot_y, steps)
local stem_x = math.min(stem_top_x, stem_bot_x)
local stem_y = math.min(stem_top_y, stem_bot_y)
local stem_w = math.abs(stem_bot_x - stem_top_x)
local stem_h = math.abs(stem_bot_y - stem_top_y)
rectb(stem_x, stem_y, stem_w, stem_h, color)
end
--- Updates DDR minigame logic.
--- @within MinigameDDRWindow
function MinigameDDRWindow.update()
@@ -162,9 +335,10 @@ function MinigameDDRWindow.update()
if mg.win_timer > 0 then
mg.win_timer = mg.win_timer - 1
if mg.win_timer == 0 then
Audio.music_stop()
Meter.on_minigame_complete()
if mg.on_win then
mg.on_win()
mg.on_win(mg)
else
Meter.show()
Window.set_current(mg.return_window)
@@ -174,22 +348,26 @@ function MinigameDDRWindow.update()
end
if mg.bar_fill >= mg.max_fill then
mg.win_timer = Config.timing.minigame_win_duration
MinigameDDRWindow.on_end(mg)
return
end
mg.frame_counter = mg.frame_counter + 1
if mg.use_pattern and mg.current_song and mg.current_song.end_frame then
if mg.frame_counter > mg.current_song.end_frame and #mg.arrows == 0 then
mg.win_timer = Config.timing.minigame_win_duration
MinigameDDRWindow.on_end(mg)
return
end
end
if mg.use_pattern and mg.current_song and mg.current_song.pattern then
local pattern = mg.current_song.pattern
while mg.pattern_index <= #pattern do
local spawn_entry = pattern[mg.pattern_index]
if mg.frame_counter >= spawn_entry.frame then
spawn_arrow_dir(spawn_entry.dir)
spawn_arrow_dir(spawn_entry.dir, spawn_entry.note, spawn_entry.special)
mg.pattern_index = mg.pattern_index + 1
else
break
@@ -202,37 +380,48 @@ function MinigameDDRWindow.update()
mg.arrow_spawn_timer = 0
end
end
-- move arrow downwards
local arrows_to_remove = {}
for i, arrow in ipairs(mg.arrows) do
arrow.y = arrow.y + mg.arrow_fall_speed
if check_miss(arrow) then
table.insert(arrows_to_remove, i)
mg.bar_fill = mg.bar_fill - mg.miss_penalty
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
mg.bar_fill = math.max(0, mg.bar_fill - mg.miss_penalty)
mg.total_misses = mg.total_misses + 1
end
end
-- iterate backwards to avoid index shift issues
for i = #arrows_to_remove, 1, -1 do
table.remove(mg.arrows, arrows_to_remove[i])
end
for dir, _ in pairs(mg.input_cooldowns) do
if mg.input_cooldowns[dir] > 0 then
mg.input_cooldowns[dir] = mg.input_cooldowns[dir] - 1
end
end
for dir, _ in pairs(mg.button_pressed_timers) do
if mg.button_pressed_timers[dir] > 0 then
mg.button_pressed_timers[dir] = mg.button_pressed_timers[dir] - 1
end
end
local input_map = {
left = Input.left(),
down = Input.down(),
up = Input.up(),
right = Input.right()
}
for _, target in ipairs(mg.target_arrows) do
if Mouse.zone({ x = target.x, y = mg.target_y, w = mg.arrow_size, h = mg.arrow_size }) then
input_map[target.dir] = true
end
end
for dir, pressed in pairs(input_map) do
if pressed and mg.input_cooldowns[dir] == 0 then
mg.input_cooldowns[dir] = mg.input_cooldown_duration
@@ -240,20 +429,17 @@ function MinigameDDRWindow.update()
local hit = false
for i, arrow in ipairs(mg.arrows) do
if arrow.dir == dir and check_hit(arrow) then
mg.bar_fill = mg.bar_fill + mg.fill_per_hit
if mg.bar_fill > mg.max_fill then
mg.bar_fill = mg.max_fill
end
MinigameDDRWindow.on_arrow_hit_special(arrow, mg)
mg.bar_fill = math.min(mg.max_fill, mg.bar_fill + mg.fill_per_hit)
table.remove(mg.arrows, i)
hit = true
break
end
end
if not hit then
mg.bar_fill = mg.bar_fill - 2
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
mg.bar_fill = math.max(0, mg.bar_fill - 2)
mg.total_misses = mg.total_misses + 1
end
end
end
@@ -262,6 +448,7 @@ end
--- Draws DDR minigame.
--- @within MinigameDDRWindow
function MinigameDDRWindow.draw()
---@type MinigameDDRState|nil
local mg = Context.minigame_ddr
if not mg then
cls(0)
@@ -272,13 +459,14 @@ function MinigameDDRWindow.draw()
end
return
end
if mg.return_window == "game" then
GameWindow.draw()
end
-- if mg.return_window == "game" then
-- GameWindow.draw()
-- end
rect(0, 0, Config.screen.width, Config.screen.height, Config.colors.black)
rect(mg.bar_x - 2, mg.bar_y - 2, mg.bar_width + 4, mg.bar_height + 4, Config.colors.light_grey)
rectb(mg.bar_x - 2, mg.bar_y - 2, mg.bar_width + 4, mg.bar_height + 4, Config.colors.dark_grey)
local fill_width = (mg.bar_fill / mg.max_fill) * mg.bar_width
MinigameDDRWindow.draw_background()
if fill_width > 0 then
local bar_color = Config.colors.light_blue
if mg.bar_fill > 66 then
@@ -288,8 +476,6 @@ function MinigameDDRWindow.draw()
end
rect(mg.bar_x, mg.bar_y, fill_width, mg.bar_height, bar_color)
end
local percentage = math.floor((mg.bar_fill / mg.max_fill) * 100)
Print.text_center(percentage .. "%", mg.bar_x + mg.bar_width / 2, mg.bar_y + 2, Config.colors.black)
if mg.target_arrows then
for _, target in ipairs(mg.target_arrows) do
local is_pressed = mg.button_pressed_timers[target.dir] and mg.button_pressed_timers[target.dir] > 0
@@ -299,16 +485,17 @@ function MinigameDDRWindow.draw()
end
if mg.arrows then
for _, arrow in ipairs(mg.arrows) do
draw_arrow(arrow.x, arrow.y, arrow.dir, Config.colors.blue)
local arrow_color = arrow.special and Config.colors.white or Config.colors.blue
draw_arrow(arrow.x, arrow.y, arrow.dir, arrow_color)
end
end
Print.text_center("Hit the arrows!", Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_grey)
Print.text_center_contour("Hit the arrows!", Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_blue)
local debug_y = 60
if mg.debug_status then
Print.text_center(mg.debug_status, Config.screen.width / 2, debug_y, Config.colors.item)
debug_y = debug_y + 10
end
if mg.use_pattern then
if mg.use_pattern and Context.test_mode then
Print.text_center(
"PATTERN MODE - Frame:" .. mg.frame_counter,
Config.screen.width / 2,
@@ -323,10 +510,17 @@ function MinigameDDRWindow.draw()
Config.colors.light_blue
)
end
else
elseif Context.test_mode then
Print.text_center("RANDOM MODE", Config.screen.width / 2, debug_y, Config.colors.blue)
end
if mg.win_timer > 0 then
if mg.win_timer > 0 then
if mg.special_mode_condition then
Minigame.draw_win_overlay("SUCCESS...?")
elseif mg.total_hits < 10 then
Minigame.draw_win_overlay("MEH...")
else
Minigame.draw_win_overlay()
end
end
end
end

View File

@@ -49,6 +49,7 @@ end
--- @param return_window string The window ID to return to after the minigame.<br/>
--- @param[opt] params table Optional parameters for minigame configuration.<br/>
function MinigameButtonMashWindow.start(return_window, params)
Audio.music_stop()
MinigameButtonMashWindow.init(params)
local mg = Context.minigame_button_mash
mg.return_window = return_window or "game"
@@ -70,6 +71,9 @@ function MinigameButtonMashWindow.update()
if mg.win_timer == 0 then
Meter.on_minigame_complete()
if mg.focus_center_x then Focus.stop() end
Context.home_norman_visible = true
Context.have_done_work_today = false
Context.have_been_to_office = false
if mg.on_win then
mg.on_win()
else
@@ -80,7 +84,11 @@ function MinigameButtonMashWindow.update()
return
end
if Input.select() then
local mouse_on_button = Mouse.zone_circle({ x = mg.button_x, y = mg.button_y, r = mg.button_size })
if Input.select() or mouse_on_button then
Audio.sfx_drum_high()
mg.bar_fill = mg.bar_fill + mg.fill_per_press
mg.button_pressed_timer = mg.button_press_duration
if mg.bar_fill > mg.target_points then
@@ -88,6 +96,7 @@ function MinigameButtonMashWindow.update()
end
end
if mg.bar_fill >= mg.target_points then
Audio.sfx_select()
mg.win_timer = Config.timing.minigame_win_duration
return
end
@@ -137,7 +146,7 @@ function MinigameButtonMashWindow.draw()
circ(mg.button_x, mg.button_y, mg.button_size - 2, button_color)
end
Print.text_center("Z", mg.button_x, mg.button_y - 3, button_color)
Print.text_center(mg.instruction_text, Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_grey)
Print.text_center_contour(mg.instruction_text, Config.screen.width / 2, mg.bar_y + mg.bar_height + 10, Config.colors.light_blue)
if mg.show_progress_text then
local points_text = math.floor(mg.bar_fill) .. "/" .. mg.target_points
Print.text_center(points_text, mg.bar_x + mg.bar_width / 2, mg.bar_y + 2, Config.colors.black)

View File

@@ -53,6 +53,7 @@ end
--- @param return_window string The window ID to return to after the minigame.<br/>
--- @param[opt] params table Optional parameters for minigame configuration.<br/>
function MinigameRhythmWindow.start(return_window, params)
Audio.music_stop()
MinigameRhythmWindow.init(params)
local mg = Context.minigame_rhythm
mg.return_window = return_window or "game"
@@ -95,7 +96,9 @@ function MinigameRhythmWindow.update()
if mg.press_cooldown > 0 then
mg.press_cooldown = mg.press_cooldown - 1
end
if Input.select() and mg.press_cooldown == 0 then
local mouse_on_button = Mouse.zone_circle({ x = mg.button_x, y = mg.button_y, r = mg.button_size })
if (Input.select() or mouse_on_button) and mg.press_cooldown == 0 then
mg.button_pressed_timer = mg.button_press_duration
mg.press_cooldown = mg.press_cooldown_duration
local target_left = mg.target_center - (mg.target_width / 2)
@@ -114,6 +117,7 @@ function MinigameRhythmWindow.update()
end
end
if mg.score >= mg.max_score then
Audio.sfx_select()
mg.win_timer = Config.timing.minigame_win_duration
return
end
@@ -143,14 +147,14 @@ function MinigameRhythmWindow.draw()
local target_left = mg.target_center - (mg.target_width / 2)
local target_x = mg.bar_x + (target_left * mg.bar_width)
local target_width_pixels = mg.target_width * mg.bar_width
rect(target_x, mg.bar_y, target_width_pixels, mg.bar_height, Config.colors.light_blue)
rect(target_x, mg.bar_y, target_width_pixels, mg.bar_height, Config.colors.orange)
local line_x = mg.bar_x + (mg.line_position * mg.bar_width)
rect(line_x - 1, mg.bar_y, 2, mg.bar_height, Config.colors.item)
Print.text_center(
rect(line_x - 1, mg.bar_y, 2, mg.bar_height, Config.colors.light_blue)
Print.text_center_contour(
"Sleep Norman ... Sleep!",
Config.screen.width / 2,
mg.bar_y + mg.bar_height + 14,
Config.colors.light_grey
Config.colors.light_blue
)
local button_color = Config.colors.light_grey
if mg.button_pressed_timer > 0 then

View File

@@ -1,198 +0,0 @@
--- @section MysteriousManWindow
local STATE_TEXT = "text"
local STATE_DAY = "day"
local STATE_CHOICE = "choice"
local DEFAULT_TEXT = [[
Misterious man appears
during your sleep.
He says nothing.
He doesn't need to.
He says nothing.
]]
local state = STATE_TEXT
local text_y = Config.screen.height
local text_speed = 0.2
local day_timer = 0
local day_display_frames = 120
local text_done = false
local text_done_timer = 0
local TEXT_DONE_HOLD_FRAMES = 120
local selected_choice = 1
local text = DEFAULT_TEXT
local day_text_override = nil
local on_text_complete = nil
local show_mysterious_screen = true
local trigger_flash_on_wake = false
local choices = {
{
label = "Wake Up",
},
{
label = "Stay in Bed",
},
}
--- Sets the scrolling text content.
--- @within MysteriousManWindow
--- @param new_text string The text to display.
function MysteriousManWindow.set_text(new_text)
text = new_text
end
--- Starts the mysterious man window.
--- @within MysteriousManWindow
--- @param[opt] options table Optional window 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 MysteriousManWindow.start(options)
options = options or {}
day_timer = 0
text_done = false
text_done_timer = 0
selected_choice = 1
text = options.text or DEFAULT_TEXT
local line_count = 1
for _ in string.gmatch(text, "\n") do line_count = line_count + 1 end
local text_block_h = line_count * 8
text_y = math.floor((Config.screen.height - text_block_h) / 2)
day_text_override = options.day_text
on_text_complete = options.on_text_complete
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_frames
else
show_mysterious_screen = true
state = STATE_TEXT
end
Window.set_current("mysterious_man")
end
local function go_to_day_state()
if on_text_complete then
on_text_complete()
on_text_complete = nil
end
if Window.get_current_id() ~= "mysterious_man" then
return
end
state = STATE_DAY
day_timer = day_display_frames
end
local function 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,
on_win = function()
Audio.music_play_wakingup()
Context.home_norman_visible = true
Meter.show()
if trigger_flash_on_wake then
trigger_flash_on_wake = false
Ascension.start_flash()
end
Window.set_current("game")
end,
})
end
local function stay_in_bed()
Day.increase()
state = STATE_DAY
day_timer = day_display_frames
end
--- Updates the mysterious man window logic.
--- @within MysteriousManWindow
function MysteriousManWindow.update()
if state == STATE_TEXT then
if not text_done then
text_y = text_y - text_speed
local lines = 1
for _ in string.gmatch(text, "\n") do
lines = lines + 1
end
if text_y < -lines * 8 then
text_done = true
text_done_timer = TEXT_DONE_HOLD_FRAMES
end
else
text_done_timer = text_done_timer - 1
if text_done_timer <= 0 then
go_to_day_state()
end
end
elseif state == STATE_DAY then
day_timer = day_timer - 1
if day_timer <= 0 or Input.select() then
if trigger_flash_on_wake or Ascension.get_level() < 1 then
wake_up()
else
state = STATE_CHOICE
selected_choice = 1
end
end
elseif state == STATE_CHOICE then
selected_choice = UI.update_menu(choices, selected_choice)
if Input.select() then
Audio.sfx_select()
if selected_choice == 1 then
wake_up()
else
stay_in_bed()
end
end
end
end
--- Draws the mysterious man window.
--- @within MysteriousManWindow
function MysteriousManWindow.draw()
rect(0, 0, Config.screen.width, Config.screen.height, Config.colors.black)
if show_mysterious_screen then
Screen.draw_the_mysterious_screen()
end
if state == STATE_TEXT then
local cx = Config.screen.width / 2
local line_y = text_y
for line in (text .. "\n"):gmatch("(.-)\n") do
Print.text_center(line, cx, line_y, Config.colors.light_grey)
line_y = line_y + 8
end
elseif state == STATE_DAY then
local day_text = day_text_override or ("Day " .. Context.day_count)
Print.text_center(
day_text,
Config.screen.width / 2,
Config.screen.height / 2 - 3,
Config.colors.white
)
elseif state == STATE_CHOICE then
local menu_x = (Config.screen.width - 60) / 2
local menu_y = (Config.screen.height - 20) / 2
UI.draw_menu(choices, selected_choice, menu_x, menu_y)
end
end

View File

@@ -28,7 +28,7 @@ end
--- @within PopupWindow
function PopupWindow.update()
if Context.popup.show then
if Input.menu_confirm() or Input.menu_back() then
if Input.select() or Input.back() then
PopupWindow.hide()
end
end

View File

@@ -16,8 +16,8 @@ Window.register("game", GameWindow)
PopupWindow = {}
Window.register("popup", PopupWindow)
ConfigurationWindow = {}
Window.register("configuration", ConfigurationWindow)
ControlsWindow = {}
Window.register("controls", ControlsWindow)
AudioTestWindow = {}
Window.register("audiotest", AudioTestWindow)
@@ -31,9 +31,6 @@ Window.register("minigame_rhythm", MinigameRhythmWindow)
MinigameDDRWindow = {}
Window.register("minigame_ddr", MinigameDDRWindow)
MysteriousManWindow = {}
Window.register("mysterious_man", MysteriousManWindow)
EndWindow = {}
Window.register("end", EndWindow)

View File

@@ -0,0 +1,46 @@
from mido import MidiFile
MIDI_FILE = "/tmp/teletype_impostor_musicator/maestro-v3.0.0/2018/MIDI-Unprocessed_Schubert7-9_MID--AUDIO_16_R2_2018_wav.midi"
# resolution: rows per beat (e.g. 4 = 16th notes)
ROWS_PER_BEAT = 4
names = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
def note_name(n):
octave = n // 12 - 1
return f"{names[n % 12]}-{octave}"
mid = MidiFile(MIDI_FILE)
tpb = mid.ticks_per_beat
row_ticks = tpb // ROWS_PER_BEAT
time = 0
rows = {}
for msg in mid:
time += msg.time
if msg.type == "note_on" and msg.velocity > 0:
row = int(time // row_ticks)
rows.setdefault(row, []).append(msg.note)
# build monophonic sequence (highest note wins)
max_row = max(rows.keys())
sequence = []
for r in range(max_row + 1):
if r in rows:
n = max(rows[r])
sequence.append(note_name(n))
else:
sequence.append("...")
# trim (optional)
sequence = sequence[:512]
# output as Lua
print("sequence = {")
for n in sequence:
print(f' "{n}",')
print("}")

View File

@@ -1,130 +1,127 @@
unpack = unpack or table.unpack
-- key separator: |
-- empty note: "..."
function build_markov_model(sequence, order)
local function make_key(tbl)
math.randomseed(os.time())
local unpack = unpack or table.unpack
local function make_key(tbl)
return table.concat(tbl, "|")
end
end
local function unmake_key(k)
local function unmake_key(k)
local result = {}
for t in string.gmatch(k, "[^|]+") do
result[#result + 1] = t
end
return result
end
local function add_key(str, value)
return str .. "|" .. value
end
local function split_last(full)
local i = full:match(".*()|")
return full:sub(1, i-1), full:sub(i+1)
end
local counts = {}
local totals = {}
-- count
for i = 1, #sequence - order do
local notes = make_key({unpack(sequence, i, i + order - 1)})
totals[notes] = (totals[notes] or 0) + 1
local notes_full = add_key(notes, sequence[i + order])
counts[notes_full] = (counts[notes_full] or 0) + 1
end
-- build model
local model = {}
for notes_full,count in pairs(counts) do
local notes, _ = split_last(notes_full)
model[notes_full] = count[notes_full] / total[notes]
end
return {
order = order,
model = model,
counts = counts -- keep raw counts (useful!)
}
end
function generate_sequence(model_data, length)
local model = model_data.model
local order = model_data.order
local function add_key(str, value)
return str .. "|" .. value
end
-- helper: split key into parts
local function split(k)
local function split_last(full)
local i = full:match(".*()|")
return full:sub(1, i-1), full:sub(i+1)
end
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
-- helper: split key into parts
local function split(k)
local t = {}
for part in string.gmatch(k, "[^|]+") do
t[#t+1] = part
end
return t
end
function build_markov_model(sequence, order)
-- TODO: add {"..." x order} to beginning?
local model = { }
-- count
for i = 1, #sequence - order do
local key = make_key({unpack(sequence, i, i + order - 1)})
local next_note = sequence[i + order]
local data = model[key] or { next={}, total=0 }
data.next[next_note] = (data.next[next_note] or 0) + 1
data.total = data.total + 1
model[key] = data
end
-- pick random starting state
local start_key
-- normalize
for temp_key,temp_data in pairs(model) do
for temp_note, temp_count in pairs(temp_data.next) do
model[temp_key].next[temp_note] = temp_count / temp_data.total
end
end
--[[
for k,v in pairs(model) do
print("-----" .. k)
for k2,v2 in pairs(v.next) do
print(k2, v2)
end
end
--]]
return {
order = order,
model = model
}
end
function generate_sequence(model_data, length)
local order = model_data.order
local model_data = model_data.model
-- random start key
local model_keys = {}
for k,_ in pairs(model) do
start_key = k
break
model_keys[#model_keys + 1] = k
end
local start_key = model_keys[math.ceil(math.random() * #model_keys)]
-- (optional: better random start)
for k,_ in pairs(model) do
if math.random() < 0.1 then
start_key = k
end
end
local parts = split(start_key)
-- initial sequence = first `order` items
local seq = {}
for i = 1, order do
seq[i] = parts[i]
end
-- sequence starts with the start key
local seq = unmake_key(start_key)
-- generation loop
while #seq < length do
-- build current state key
local state = table.concat({unpack(seq, #seq - order + 1, #seq)}, "|")
local current_key = table.concat({unpack(seq, #seq - order + 1, #seq)}, "|")
-- collect matching transitions
local matches = {}
for full,prob in pairs(model) do
if full:sub(1, #state) == state and full:sub(#state+1, #state+1) == "|" then
matches[#matches+1] = {key=full, prob=prob}
end
end
local chosen = "..."
if #matches == 0 then break end
-- weighted pick
local key_data = model[current_key]
if key_data then
local r = math.random()
local sum = 0
local chosen
for _,m in ipairs(matches) do
sum = sum + m.prob
if r <= sum then
chosen = m.key
break
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
if not chosen then
chosen = matches[#matches].key
end
-- print(current_key .. " --> " .. chosen)
-- extract next symbol (after last '|')
local next_symbol = chosen:match("|([^|]+)$")
seq[#seq+1] = next_symbol
seq[#seq+1] = chosen
end
return seq
end
-- todo: feed samples

View File

@@ -1 +0,0 @@
-- todo

View File

@@ -0,0 +1,42 @@
import sys
import re
ROWS_PER_BEAT = 4 # keep consistent with your MIDI extraction
SOUND = "piano"
def parse_sequence(text):
return re.findall(r'"([^"]+)"', text)
def to_strudel_notes(seq):
out = []
for n in seq:
if n == "..." or n == "---":
out.append("~")
else:
# C-5 → c5, C#5 → c#5
note = n.replace("-", "")
out.append(note.lower())
return out
def chunk(seq, size):
for i in range(0, len(seq), size):
yield seq[i:i+size]
# read from stdin
text = sys.stdin.read()
sequence = parse_sequence(text)
notes = to_strudel_notes(sequence)
# group into musical lines (4 beats)
lines = []
for group in chunk(notes, ROWS_PER_BEAT * 4):
lines.append(" ".join(group))
pattern = "\n".join(lines)
print("note(`")
print(pattern)
print(f"`).sound(\"{SOUND}\")")
# npm install -g strudel-cli

414
tools/musicator/teach.lua Normal file
View File

@@ -0,0 +1,414 @@
-- teach the musicator
-- uses samples from: https://magenta.tensorflow.org/datasets/maestro#v300
--require("luarocks.loader")
--require("luamidi")
require("./musicator")
local inspect = require("inspect")
math.randomseed(os.time())
function flatten(v)
local res = {}
local function flatten(v)
if type(v) ~= "table" then
table.insert(res, v)
return
end
for _, v in ipairs(v) do
flatten(v)
end
end
flatten(v)
return res
end
local training_data = {
-- simple ascending phrase
{
"C-4","...","D-4","...","E-4","...","G-4","...",
"E-4","...","D-4","...","C-4","...","...","..."
},
-- descending answer
{
"G-4","...","F-4","...","E-4","...","D-4","...",
"C-4","...","D-4","...","E-4","...","...","..."
},
-- arpeggio major
{
"C-4","...","E-4","...","G-4","...","C-5","...",
"G-4","...","E-4","...","C-4","...","...","..."
},
-- arpeggio minor
{
"A-4","...","C-5","...","E-5","...","A-5","...",
"E-5","...","C-5","...","A-4","...","...","..."
},
-- stepwise melody (folk-like)
{
"D-4","...","E-4","...","F-4","...","G-4","...",
"F-4","...","E-4","...","D-4","...","...","..."
},
-- repeated note rhythm
{
"C-5","C-5","...","C-5","...","C-5","C-5","...",
"D-5","...","E-5","...","...","...","...","..."
},
-- bounce pattern
{
"C-5","...","G-4","...","C-5","...","G-4","...",
"D-5","...","A-4","...","D-5","...","...","..."
},
-- scale run up
{
"C-4","D-4","E-4","F-4","G-4","A-4","B-4","C-5",
"...","...","...","...","...","...","...","..."
},
-- scale run down
{
"C-5","B-4","A-4","G-4","F-4","E-4","D-4","C-4",
"...","...","...","...","...","...","...","..."
},
-- syncopated feel
{
"C-5","...","...","D-5","...","...","E-5","...",
"C-5","...","...","G-4","...","...","...","..."
},
-- triplet-ish feel (simulated)
{
"E-5","D-5","C-5","...","E-5","D-5","C-5","...",
"G-4","...","...","...","...","...","...","..."
},
-- small jumps
{
"C-5","...","E-5","...","D-5","...","F-5","...",
"E-5","...","C-5","...","...","...","...","..."
},
-- call
{
"G-4","...","A-4","...","C-5","...","A-4","...",
"...","...","...","...","...","...","...","..."
},
-- response
{
"E-4","...","F-4","...","G-4","...","F-4","...",
"D-4","...","...","...","...","...","...","..."
},
-- denser pattern (DDR-like)
{
"C-5","D-5","E-5","...","D-5","E-5","F-5","...",
"E-5","D-5","C-5","...","...","...","...","..."
},
-- alternating pattern (good for gameplay)
{
"C-5","...","E-5","...","C-5","...","E-5","...",
"D-5","...","F-5","...","D-5","...","...","..."
},
-- higher register variant
{
"G-5","...","A-5","...","B-5","...","D-6","...",
"B-5","...","A-5","...","G-5","...","...","..."
},
-- low register grounding
{
"C-4","...","G-3","...","C-4","...","G-3","...",
"F-3","...","C-4","...","...","...","...","..."
},
-- variant of ascending with offset
{
"...","C-4","...","D-4","...","E-4","...","G-4",
"...","E-4","...","D-4","...","C-4","...","..."
},
-- staggered rhythm
{
"C-4","...","...","D-4","...","E-4","...","...",
"G-4","...","E-4","...","D-4","...","...","..."
},
-- broken arpeggio (different spacing)
{
"C-4","E-4","...","G-4","...","C-5","...",
"G-4","E-4","...","C-4","...","...","...","..."
},
-- minor variation (shifted)
{
"...","A-4","C-5","...","E-5","...","A-5","...",
"E-5","...","C-5","...","A-4","...","...","..."
},
-- repeated + variation
{
"D-4","D-4","...","E-4","...","F-4","F-4","...",
"G-4","...","F-4","...","E-4","...","...","..."
},
-- zig-zag motion
{
"C-5","...","E-5","...","D-5","...","F-5","...",
"E-5","...","G-5","...","F-5","...","...","..."
},
-- alternating step/jump
{
"C-5","...","D-5","...","G-5","...","F-5","...",
"E-5","...","C-5","...","D-5","...","...","..."
},
-- denser burst pattern
{
"C-5","D-5","E-5","F-5","...","E-5","D-5","C-5",
"...","...","...","...","...","...","...","..."
},
-- rolling pattern
{
"E-5","...","D-5","...","C-5","...","D-5","...",
"E-5","...","G-5","...","E-5","...","...","..."
},
-- syncopation variant
{
"...","C-5","...","...","E-5","...","...","G-5",
"...","E-5","...","C-5","...","...","...","..."
},
-- low-high interplay
{
"C-4","...","G-4","...","C-5","...","G-4","...",
"E-4","...","C-4","...","...","...","...","..."
},
-- descending but staggered
{
"C-5","...","...","B-4","...","A-4","...","...",
"G-4","...","F-4","...","E-4","...","...","..."
},
-- small trill-like feel
{
"E-5","F-5","E-5","...","E-5","F-5","E-5","...",
"D-5","...","C-5","...","...","...","...","..."
},
-- call variant (shifted timing)
{
"...","G-4","...","A-4","...","C-5","...","A-4",
"...","...","...","...","...","...","...","..."
},
-- response variant
{
"...","E-4","...","F-4","...","G-4","...","F-4",
"D-4","...","...","...","...","...","...","..."
},
-- dense DDR-ish alternating
{
"C-5","...","D-5","...","C-5","...","D-5","...",
"E-5","...","F-5","...","E-5","...","...","..."
},
-- higher variation arpeggio
{
"G-5","...","B-5","...","D-6","...","G-6","...",
"D-6","...","B-5","...","G-5","...","...","..."
},
-- low groove pattern
{
"C-3","...","C-4","...","G-3","...","C-4","...",
"F-3","...","C-4","...","...","...","...","..."
},
-- slightly chaotic (good for branching)
{
"C-5","...","E-5","D-5","...","G-5","...","F-5",
"...","D-5","...","C-5","...","...","...","..."
},
-- mixed density
{
"C-5","D-5","...","E-5","...","F-5","G-5","...",
"E-5","...","D-5","C-5","...","...","...","..."
},
-- offset staircase up
{
"...","C-4","D-4","E-4","F-4","G-4","A-4","B-4",
"C-5","...","...","...","...","...","...","..."
},
-- offset staircase down
{
"...","C-5","B-4","A-4","G-4","F-4","E-4","D-4",
"C-4","...","...","...","...","...","...","..."
},
-- dense zigzag
{
"C-5","E-5","D-5","F-5","E-5","G-5","F-5","A-5",
"G-5","...","...","...","...","...","...","..."
},
-- jack pattern (DDR classic)
{
"C-5","C-5","C-5","...","C-5","C-5","...","...",
"D-5","D-5","...","...","...","...","...","..."
},
-- alternating two-note burst
{
"C-5","D-5","C-5","D-5","C-5","D-5","...","...",
"E-5","F-5","E-5","F-5","...","...","...","..."
},
-- wide jumps
{
"C-4","...","G-5","...","D-4","...","A-5","...",
"E-4","...","B-5","...","...","...","...","..."
},
-- rolling triplet-ish
{
"C-5","E-5","G-5","...","E-5","C-5","E-5","...",
"G-5","...","...","...","...","...","...","..."
},
-- syncopated dense
{
"...","C-5","D-5","...","E-5","...","F-5","G-5",
"...","E-5","...","C-5","...","...","...","..."
},
-- mirrored pattern
{
"C-5","D-5","E-5","F-5","E-5","D-5","C-5","...",
"...","...","...","...","...","...","...","..."
},
-- chord-outline arpeggio feel
{
"C-4","...","G-4","...","E-5","...","G-4","...",
"C-4","...","...","...","...","...","...","..."
},
-- broken rhythm variant
{
"C-5","...","D-5","E-5","...","F-5","...","G-5",
"E-5","...","D-5","...","C-5","...","...","..."
},
-- fast burst then rest
{
"C-5","D-5","E-5","F-5","G-5","A-5","...","...",
"...","...","...","...","...","...","...","..."
},
-- low-high bounce fast
{
"C-3","C-5","C-3","C-5","C-3","C-5","...","...",
"G-3","G-5","G-3","G-5","...","...","...","..."
},
-- repeated with shift
{
"...","E-5","...","E-5","...","E-5","...","...",
"D-5","...","C-5","...","...","...","...","..."
},
-- clustered mid
{
"D-5","E-5","F-5","...","E-5","D-5","C-5","...",
"D-5","...","...","...","...","...","...","..."
},
-- broken descending
{
"C-6","...","A-5","...","F-5","...","D-5","...",
"C-5","...","...","...","...","...","...","..."
},
-- chaotic jumpy (important for branching)
{
"C-5","...","F-5","D-5","...","A-5","...","E-5",
"...","G-5","...","C-5","...","...","...","..."
},
-- double-step pattern
{
"C-5","D-5","D-5","E-5","E-5","F-5","...","...",
"G-5","...","...","...","...","...","...","..."
},
-- uneven spacing
{
"C-5","...","...","D-5","E-5","...","...","F-5",
"G-5","...","...","...","...","...","...","..."
},
-- fast alternating high
{
"G-5","A-5","G-5","A-5","G-5","A-5","...","...",
"F-5","E-5","...","...","...","...","...","..."
},
-- low groove with variation
{
"C-3","...","C-4","...","G-3","...","D-4","...",
"F-3","...","C-4","...","...","...","...","..."
},
-- semi-random filler (very useful)
{
"C-5","...","E-5","...","D-5","...","G-5","...",
"F-5","...","A-5","...","E-5","...","...","..."
},
-- near-repetition (state collision booster)
{
"C-5","...","D-5","...","E-5","...","C-5","...",
"D-5","...","E-5","...","C-5","...","...","..."
},
-- same but shifted (VERY important)
{
"...","C-5","...","D-5","...","E-5","...","C-5",
"...","D-5","...","E-5","...","C-5","...","..."
},
}
local model = build_markov_model(flatten(training_data), 2)
print(inspect(model))
--[[
local generated = generate_sequence(model, 100)
for i,v in ipairs(generated) do
print(v)
end
--]]