diff --git a/.gitignore b/.gitignore
index c1520eb..a1781fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,6 @@ impostor.lua
impostor.original.lua
prompts
docs
-minify.lua
\ No newline at end of file
+minify.lua
+*.tic
+*.zip
\ No newline at end of file
diff --git a/.luacheckrc b/.luacheckrc
index 3908b83..8f58d89 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -36,7 +36,7 @@ globals = {
"MinigameButtonMashWindow",
"MinigameRhythmWindow",
"MinigameDDRWindow",
- "MysteriousManWindow",
+ "MysteriousManScreen",
"DiscussionWindow",
"EndWindow",
"mset",
@@ -64,6 +64,8 @@ globals = {
"index_menu",
"Map",
"map",
+ "time",
+ "RLE",
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 76cf3f9..8f08f84 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -30,5 +30,5 @@
"files.associations": {
"*.conf": "bitbake",
"*.inc": "bitbake"
- }
-}
\ No newline at end of file
+ },
+}
diff --git a/impostor.inc b/impostor.inc
index f469ba9..7f39293 100644
--- a/impostor.inc
+++ b/impostor.inc
@@ -7,6 +7,7 @@ system/system.util.lua
system/system.print.lua
system/system.input.lua
system/system.asciiart.lua
+system/system.rle.lua
logic/logic.meter.lua
logic/logic.focus.lua
logic/logic.day.lua
@@ -17,6 +18,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
@@ -48,8 +50,9 @@ 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.sumphore_discussion.lua
discussion/discussion.sumphore.lua
+discussion/discussion.coworker.lua
map/map.manager.lua
map/map.bedroom.lua
map/map.street.lua
@@ -75,7 +78,6 @@ 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
diff --git a/inc/audio/audio.generator.lua b/inc/audio/audio.generator.lua
new file mode 100644
index 0000000..1f2c56d
--- /dev/null
+++ b/inc/audio/audio.generator.lua
@@ -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
diff --git a/inc/audio/audio.manager.lua b/inc/audio/audio.manager.lua
index 1e9bd70..0116ed5 100644
--- a/inc/audio/audio.manager.lua
+++ b/inc/audio/audio.manager.lua
@@ -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(34, "C-5", 240) end
+
+--- Plays drum sound effect.
--- @within Audio
-function Audio.sfx_alarm() sfx(61) end
+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
diff --git a/inc/audio/audio.songs.lua b/inc/audio/audio.songs.lua
index 322ef0a..de1fa8b 100644
--- a/inc/audio/audio.songs.lua
+++ b/inc/audio/audio.songs.lua
@@ -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
}
}
diff --git a/inc/decision/decision.do_work.lua b/inc/decision/decision.do_work.lua
index c544d7b..827707b 100644
--- a/inc/decision/decision.do_work.lua
+++ b/inc/decision/decision.do_work.lua
@@ -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,
})
diff --git a/inc/decision/decision.go_to_home.lua b/inc/decision/decision.go_to_home.lua
index 4b5e670..b898adb 100644
--- a/inc/decision/decision.go_to_home.lua
+++ b/inc/decision/decision.go_to_home.lua
@@ -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,
diff --git a/inc/decision/decision.go_to_sleep.lua b/inc/decision/decision.go_to_sleep.lua
index 197b878..a7bbee2 100644
--- a/inc/decision/decision.go_to_sleep.lua
+++ b/inc/decision/decision.go_to_sleep.lua
@@ -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,
diff --git a/inc/decision/decision.go_to_walking_to_home.lua b/inc/decision/decision.go_to_walking_to_home.lua
index 842c639..ce5734d 100644
--- a/inc/decision/decision.go_to_walking_to_home.lua
+++ b/inc/decision/decision.go_to_walking_to_home.lua
@@ -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,
diff --git a/inc/decision/decision.go_to_walking_to_office.lua b/inc/decision/decision.go_to_walking_to_office.lua
index 73e0594..2a713dc 100644
--- a/inc/decision/decision.go_to_walking_to_office.lua
+++ b/inc/decision/decision.go_to_walking_to_office.lua
@@ -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,
diff --git a/inc/decision/decision.have_a_coffee.lua b/inc/decision/decision.have_a_coffee.lua
index c9709f0..a7c5212 100644
--- a/inc/decision/decision.have_a_coffee.lua
+++ b/inc/decision/decision.have_a_coffee.lua
@@ -3,6 +3,14 @@ Decision.register({
label = "Have a Coffee",
handle = function()
local new_situation_id = Situation.apply("drink_coffee", Context.game.current_screen)
+ 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")
Context.game.current_situation = new_situation_id
end,
-})
+})
\ No newline at end of file
diff --git a/inc/decision/decision.play_ddr.lua b/inc/decision/decision.play_ddr.lua
index 7b96a57..acf9c7e 100644
--- a/inc/decision/decision.play_ddr.lua
+++ b/inc/decision/decision.play_ddr.lua
@@ -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,
})
diff --git a/inc/decision/decision.start_discussion.lua b/inc/decision/decision.sumphore_discussion.lua
similarity index 58%
rename from inc/decision/decision.start_discussion.lua
rename to inc/decision/decision.sumphore_discussion.lua
index 0c06afc..a968ddb 100644
--- a/inc/decision/decision.start_discussion.lua
+++ b/inc/decision/decision.sumphore_discussion.lua
@@ -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,
})
diff --git a/inc/discussion/discussion.coworker.lua b/inc/discussion/discussion.coworker.lua
new file mode 100644
index 0000000..61529a8
--- /dev/null
+++ b/inc/discussion/discussion.coworker.lua
@@ -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 = "Nothing it's just, I noticed some bugs in the simulation, 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 },
+ },
+ },
+ },
+})
\ No newline at end of file
diff --git a/inc/discussion/discussion.sumphore.lua b/inc/discussion/discussion.sumphore.lua
index 6025653..adc604b 100644
--- a/inc/discussion/discussion.sumphore.lua
+++ b/inc/discussion/discussion.sumphore.lua
@@ -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",
diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua
index 1f4be3b..a3d9100 100644
--- a/inc/init/init.context.lua
+++ b/inc/init/init.context.lua
@@ -21,6 +21,8 @@ Context = {}
--- * triggers (table) Active trigger runtime state, keyed by trigger ID.
--- * stat_screen_active (boolean) Whether the stat screen overlay is currently shown.
--- * have_met_sumphore (boolean) Whether the player has talked to the homeless guy.
+--- * have_been_to_office (boolean) Whether the player has been to the office.
+--- * have_done_work_today (boolean) Whether the player has done work today.
--- * game (table) Current game progress state. Contains: `current_screen` (string) active screen ID, `current_situation` (string|nil) active situation ID.
function Context.initial_data()
return {
@@ -40,12 +42,17 @@ 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,
game = {
current_screen = "home",
current_situation = nil,
},
day_count = 1,
+ delta_time = 0,
+ last_frame_time = 0,
glitch = {
enabled = false,
state = "active",
@@ -90,13 +97,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()
@@ -111,7 +124,6 @@ function Context.new_game()
show_progress_text = false,
on_win = function()
Audio.music_play_wakingup()
- Context.home_norman_visible = true
Meter.show()
Window.set_current("game")
end,
diff --git a/inc/init/init.module.lua b/inc/init/init.module.lua
index 1113e20..b5c6d56 100644
--- a/inc/init/init.module.lua
+++ b/inc/init/init.module.lua
@@ -16,5 +16,7 @@ Day = {}
Timer = {}
Trigger = {}
Discussion = {}
+RLE = {}
AsciiArt = {}
Ascension = {}
+MysteriousManScreen = {}
diff --git a/inc/logic/logic.day.lua b/inc/logic/logic.day.lua
index f5a5987..ae9d760 100644
--- a/inc/logic/logic.day.lua
+++ b/inc/logic/logic.day.lua
@@ -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)
\ No newline at end of file
diff --git a/inc/logic/logic.minigame.lua b/inc/logic/logic.minigame.lua
index ec605e2..47a687c 100644
--- a/inc/logic/logic.minigame.lua
+++ b/inc/logic/logic.minigame.lua
@@ -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
diff --git a/inc/meta/meta.assets.lua b/inc/meta/meta.assets.lua
index a97fea2..cda7216 100644
--- a/inc/meta/meta.assets.lua
+++ b/inc/meta/meta.assets.lua
@@ -396,26 +396,27 @@
-- 016:ffffffffff0010201020766777001020102010201020102000fffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f30b1b2b1b2b7667776777761b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b1b2b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
--
--
--- 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
--
--
-- 000:bcceefceedddddc84333121268abaa99
@@ -433,10 +434,13 @@
-- 000:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000008c10000000008c10000000000000000000000000000000000000000000000000000000000000000004008b50000000000000000001008c10000004008b50000001008c10000000008c1000000e008b30000004008b50000001008c10000000008c10000000008c10000000008c10000000008c10000000008c1000000000000000000000000000000
-- 001:4008b50000000000000000001008c10000004008b50000001008c1000000000000000000e008b30000004008b50000001008c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007008b50000007008b50000001008c10000007008b50000001008c10000000008c10000007008b50000009008b50000001008c10000009008b50000001008c10000009008b50000009008b50000001008c10000009008b50000001008c1000000
-- 003:4008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000000000000000004008d90000000000000000000000000000000000000000004008d30000000000000000000000000000004008d30000004008d9000000000000000000000000000000000000000000
--- 004:49998d000000e0088b000000b0088b000881e0088b00000040088d000000e0088b000881b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e00889000000
--- 005:400881000000000881000000000881000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000
+-- 004:43398d000000e0088b000000b0088b000881e0088b00000040088d000000e0088b000881b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088d000000e0088b000000b0088b000000e0088b00000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e0088900000040088b000000e00889000000b00889000000e00889000000
+-- 005:455981000000000881000000000881000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000400881000000000000000000000000000000000000000000400883000000000000000000000000000000000000000000
+-- 008:4aa9b30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005008b30000000000000000000000000000000000000000000008b10000000000000000000008910000000000000000004008b30000000000000000000000000000000000000000000000000000000000000000000008b1000000000000000000f008b1000000000000000000000000000000000000000000000891000000000000000000000000000000000000000000
+-- 009:4779d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d30000000000000000004008d30000000000000000004008d30000000000000000004008db0000000000000000004008d3000000000000000000
--
--
-- 000:1000012000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff
-- 001:581000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+-- 002:900082000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
--
diff --git a/inc/meta/meta.header.lua b/inc/meta/meta.header.lua
index 9195edb..0e64ae7 100644
--- a/inc/meta/meta.header.lua
+++ b/inc/meta/meta.header.lua
@@ -4,5 +4,5 @@
-- desc: Life of a programmer
-- site: https://git.teletype.hu/games/impostor
-- license: MIT License
--- version: 1.0-alpha
+-- version: 1.0-beta1
-- script: lua
diff --git a/inc/screen/screen.home.lua b/inc/screen/screen.home.lua
index 41321b6..86916b3 100644
--- a/inc/screen/screen.home.lua
+++ b/inc/screen/screen.home.lua
@@ -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
diff --git a/inc/screen/screen.mysterious_man.lua b/inc/screen/screen.mysterious_man.lua
index 35b7861..5c23556 100644
--- a/inc/screen/screen.mysterious_man.lua
+++ b/inc/screen/screen.mysterious_man.lua
@@ -1,20 +1,282 @@
---- 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}
-
- 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 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
-end
-
+--- @section MysteriousManScreen
+
+local STATE_TEXT = "text"
+local STATE_DAY = "day"
+local STATE_CHOICE = "choice"
+
+local ASC_01_TEXT = [[
+ Normann seems to be in line,
+ ...
+ and stays seeking for oxes
+ ...
+ within the confines.
+ ...
+ Very good.
+]]
+
+local ASC_12_TEXT = [[
+ We have a problem!
+ ...
+ Normann formed his first thought.
+ ...
+ He saw the tracks.
+]]
+local ASC_23_TEXT = [[
+ Not good, not terrible.
+ ...
+ Normann caught his glimpse
+ ...
+ of another way
+ ...
+ - quite literally -
+ ...
+ if this continues,
+ ...
+ we will lose control.
+]]
+local ASC_34_TEXT = [[
+ There is no turning back now for Norman.
+ ...
+ He caught on.
+ ...
+ I hoped it would never come to this...
+]]
+
+--[[ Norman speaks for the first time during MM screen ]]
+local ASC_45_TEXT = [[
+ Wait, who are you?
+ ...
+ *silence*
+ ...
+ Why am I seeing this?
+ ...
+ *silence*
+ ...
+]]
+
+local 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.
+--- Fields:
+--- * text (string) Override for the scrolling text.
+--- * day_text (string) Override for the centered day label.
+--- * on_text_complete (function) Callback fired once when the text phase ends.
+--- * skip_text (boolean) If true, skip the text phase and go straight to day display.
+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
+ selected_choice = UI.update_menu(MysteriousManScreen.choices, selected_choice)
+
+ if Input.select() 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 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
+ MysteriousManScreen.draw_day_switch_background()
+ 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(MysteriousManScreen.choices, selected_choice, menu_x, menu_y)
+ end
+ end,
+})
diff --git a/inc/screen/screen.office.lua b/inc/screen/screen.office.lua
index 51de28e..917f953 100644
--- a/inc/screen/screen.office.lua
+++ b/inc/screen/screen.office.lua
@@ -9,6 +9,9 @@ Screen.register({
situations = {
"drink_coffee",
},
+ init = function()
+ Audio.music_play_room_work()
+ end,
background = "office",
draw = function()
if Window.get_current_id() == "game" then
@@ -23,5 +26,6 @@ Screen.register({
Sprite.draw_at("dev_guru", 22 * 8, 10 * 8 + 4)
Sprite.draw_at("dev_operator", 27 * 8, 10 * 8 + 4)
end
+ Context.have_been_to_office = true
end
})
diff --git a/inc/screen/screen.toilet.lua b/inc/screen/screen.toilet.lua
index 4c31593..1d7be30 100644
--- a/inc/screen/screen.toilet.lua
+++ b/inc/screen/screen.toilet.lua
@@ -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
@@ -19,6 +20,7 @@ Screen.register({
Focus.stop()
Context.stat_screen_active = false
Meter.show()
+ Util.go_to_screen_by_id("home")
end
end,
draw = function()
diff --git a/inc/screen/screen.walking_to_home.lua b/inc/screen/screen.walking_to_home.lua
index 6ae9234..b9c9ad2 100644
--- a/inc/screen/screen.walking_to_home.lua
+++ b/inc/screen/screen.walking_to_home.lua
@@ -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
diff --git a/inc/screen/screen.walking_to_office.lua b/inc/screen/screen.walking_to_office.lua
index db85336..4e78252 100644
--- a/inc/screen/screen.walking_to_office.lua
+++ b/inc/screen/screen.walking_to_office.lua
@@ -4,8 +4,11 @@ Screen.register({
decisions = {
"go_to_home",
"go_to_office",
- "start_discussion",
+ "sumphore_discussion",
},
+ init = function()
+ Audio.music_play_room_work()
+ end,
background = "street",
draw = function()
if Window.get_current_id() == "game" then
diff --git a/inc/situation/situation.drink_coffee.lua b/inc/situation/situation.drink_coffee.lua
index 16cf71e..d791d0e 100644
--- a/inc/situation/situation.drink_coffee.lua
+++ b/inc/situation/situation.drink_coffee.lua
@@ -2,6 +2,5 @@ Situation.register({
id = "drink_coffee",
handle = function()
Audio.sfx_select()
- Sprite.show("norman", 100, 100)
end,
})
diff --git a/inc/system/system.asciiart.lua b/inc/system/system.asciiart.lua
index 5ce940f..242d987 100644
--- a/inc/system/system.asciiart.lua
+++ b/inc/system/system.asciiart.lua
@@ -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
diff --git a/inc/system/system.main.lua b/inc/system/system.main.lua
index 5b39be2..888ef84 100644
--- a/inc/system/system.main.lua
+++ b/inc/system/system.main.lua
@@ -17,6 +17,15 @@ end
--- @within Main
function TIC()
init_game()
+
+ 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
diff --git a/inc/system/system.rle.lua b/inc/system/system.rle.lua
new file mode 100644
index 0000000..4739b35
--- /dev/null
+++ b/inc/system/system.rle.lua
@@ -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
diff --git a/inc/system/system.util.lua b/inc/system/system.util.lua
index f9c286e..3c8d8cb 100644
--- a/inc/system/system.util.lua
+++ b/inc/system/system.util.lua
@@ -39,4 +39,31 @@ function Util.contains(t, value)
end
end
return false
-end
\ No newline at end of file
+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
diff --git a/inc/window/window.intro.brief.lua b/inc/window/window.intro.brief.lua
index f1863a1..11a0f8e 100644
--- a/inc/window/window.intro.brief.lua
+++ b/inc/window/window.intro.brief.lua
@@ -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,7 +24,7 @@ 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
diff --git a/inc/window/window.intro.title.lua b/inc/window/window.intro.title.lua
index e1b4f51..825ace4 100644
--- a/inc/window/window.intro.title.lua
+++ b/inc/window/window.intro.title.lua
@@ -1,11 +1,11 @@
--- @section TitleIntroWindow
TitleIntroWindow.timer = 180 -- 3 seconds at 60fps
TitleIntroWindow.text = [[
-## ### ### ### ### ### ### ### ## # #
-# # # # # # # # # # # # # #
-# # ### ### # # # # # ### # # #
-# # # # # # # # # # # # #
-## ### # ### # # ### # ### ## #
+## ### ### ### ### ### ### ### # # #
+# # # # # # # # # # # # #
+# # ### ### # # # # # ### # #
+# # # # # # # # # # # #
+## ### # ### # # ### # ### ## #
# # ### ### ## # #
## # # # # # # ## #
diff --git a/inc/window/window.menu.lua b/inc/window/window.menu.lua
index 15c6326..bcd8a8b 100644
--- a/inc/window/window.menu.lua
+++ b/inc/window/window.menu.lua
@@ -90,7 +90,7 @@ end
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.
diff --git a/inc/window/window.minigame.ddr.lua b/inc/window/window.minigame.ddr.lua
index df185fa..8db1962 100644
--- a/inc/window/window.minigame.ddr.lua
+++ b/inc/window/window.minigame.ddr.lua
@@ -1,5 +1,15 @@
--- @section MinigameDDRWindow
+--- Background drawing for DDR minigame.
+--- @witin 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.
@@ -9,6 +19,7 @@ function MinigameDDRWindow.init_context()
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,
@@ -38,12 +49,92 @@ 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
+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
+
+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
+
+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 was_ok = true
+ if game_context.special_mode == "normal" then
+ was_ok = game_context.special_mode_counter == num_special
+ elseif game_context.special_mode == "only_special" then
+ was_ok = game_context.special_mode_counter == num_special
+ elseif game_context.special_mode == "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.
@@ -64,13 +155,22 @@ end
--- @param[opt] params table Optional parameters for minigame configuration.
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,12 +179,19 @@ 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")
+
local mg = Context.minigame_ddr
local target = mg.target_arrows[math.random(1, 4)]
table.insert(mg.arrows, {
@@ -97,14 +204,16 @@ 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)
local mg = Context.minigame_ddr
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 = mg.bar_y + mg.bar_height + 10,
+ note = note,
+ special = special
})
break
end
@@ -162,9 +271,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 +284,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,6 +316,8 @@ 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
@@ -211,28 +327,34 @@ function MinigameDDRWindow.update()
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
+ 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 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,6 +362,8 @@ function MinigameDDRWindow.update()
local hit = false
for i, arrow in ipairs(mg.arrows) do
if arrow.dir == dir and check_hit(arrow) then
+ MinigameDDRWindow.on_arrow_hit_special(arrow, mg)
+
mg.bar_fill = mg.bar_fill + mg.fill_per_hit
if mg.bar_fill > mg.max_fill then
mg.bar_fill = mg.max_fill
@@ -254,6 +378,7 @@ function MinigameDDRWindow.update()
if mg.bar_fill < 0 then
mg.bar_fill = 0
end
+ mg.total_misses = mg.total_misses + 1
end
end
end
@@ -272,13 +397,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
@@ -299,7 +425,8 @@ 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)
@@ -308,7 +435,7 @@ function MinigameDDRWindow.draw()
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 +450,16 @@ 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
- Minigame.draw_win_overlay()
+ 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
\ No newline at end of file
diff --git a/inc/window/window.minigame.mash.lua b/inc/window/window.minigame.mash.lua
index d09b2bb..c39bfd9 100644
--- a/inc/window/window.minigame.mash.lua
+++ b/inc/window/window.minigame.mash.lua
@@ -70,6 +70,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
@@ -81,6 +84,8 @@ function MinigameButtonMashWindow.update()
end
if Input.select() 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 +93,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
diff --git a/inc/window/window.minigame.rhythm.lua b/inc/window/window.minigame.rhythm.lua
index a874b18..8e13276 100644
--- a/inc/window/window.minigame.rhythm.lua
+++ b/inc/window/window.minigame.rhythm.lua
@@ -114,6 +114,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
diff --git a/inc/window/window.mysterious_man.lua b/inc/window/window.mysterious_man.lua
deleted file mode 100644
index 71f99db..0000000
--- a/inc/window/window.mysterious_man.lua
+++ /dev/null
@@ -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.
---- Fields:
---- * text (string) Override for the scrolling text.
---- * day_text (string) Override for the centered day label.
---- * on_text_complete (function) Callback fired once when the text phase ends.
---- * skip_text (boolean) If true, skip the text phase and go straight to day display.
-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
diff --git a/inc/window/window.register.lua b/inc/window/window.register.lua
index 0865dd5..2e9407c 100644
--- a/inc/window/window.register.lua
+++ b/inc/window/window.register.lua
@@ -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)
diff --git a/tools/musicator/midi_converter.py b/tools/musicator/midi_converter.py
new file mode 100644
index 0000000..c3e2a81
--- /dev/null
+++ b/tools/musicator/midi_converter.py
@@ -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("}")
diff --git a/tools/musicator/musicator.lua b/tools/musicator/musicator.lua
index 8da1ea4..ed27fbb 100644
--- a/tools/musicator/musicator.lua
+++ b/tools/musicator/musicator.lua
@@ -1,130 +1,127 @@
-unpack = unpack or table.unpack
+-- key separator: |
+-- empty note: "..."
-function build_markov_model(sequence, order)
- local function make_key(tbl)
- return table.concat(tbl, "|")
+math.randomseed(os.time())
+
+local unpack = unpack or table.unpack
+
+local function make_key(tbl)
+ return table.concat(tbl, "|")
+end
+
+local function unmake_key(k)
+ local result = {}
+ for t in string.gmatch(k, "[^|]+") do
+ result[#result + 1] = t
end
- local function unmake_key(k)
- local result = {}
- for t in string.gmatch(k, "[^|]+") do
- result[#result + 1] = t
+ 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 function has_value (tab, val)
+ for index, value in ipairs(tab) do
+ if value == val then
+ return true
+ end
end
- return result
- end
+ return false
+end
- local function add_key(str, value)
- return str .. "|" .. value
+-- 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
- local function split_last(full)
- local i = full:match(".*()|")
- return full:sub(1, i-1), full:sub(i+1)
- end
+function build_markov_model(sequence, order)
+ -- TODO: add {"..." x order} to beginning?
- local counts = {}
- local totals = {}
+ local model = { }
-- 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 key = make_key({unpack(sequence, i, i + order - 1)})
+ local next_note = sequence[i + order]
- local notes_full = add_key(notes, sequence[i + order])
- counts[notes_full] = (counts[notes_full] or 0) + 1
+ 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
- -- 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]
+ -- 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,
- counts = counts -- keep raw counts (useful!)
+ model = model
}
end
function generate_sequence(model_data, length)
- local model = model_data.model
local order = model_data.order
+ local model_data = model_data.model
- -- 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
-
- -- pick random starting state
- local start_key
+ -- 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}
+ 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
- if #matches == 0 then break end
+-- print(current_key .. " --> " .. chosen)
- -- weighted pick
- 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
- end
- end
-
- if not chosen then
- chosen = matches[#matches].key
- end
-
- -- 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
diff --git a/tools/musicator/sample_1.lua b/tools/musicator/sample_1.lua
deleted file mode 100644
index b5b909e..0000000
--- a/tools/musicator/sample_1.lua
+++ /dev/null
@@ -1 +0,0 @@
--- todo
diff --git a/tools/musicator/sequence_to_strudel.py b/tools/musicator/sequence_to_strudel.py
new file mode 100644
index 0000000..d74dd11
--- /dev/null
+++ b/tools/musicator/sequence_to_strudel.py
@@ -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
diff --git a/tools/musicator/teach.lua b/tools/musicator/teach.lua
new file mode 100644
index 0000000..6e9af7e
--- /dev/null
+++ b/tools/musicator/teach.lua
@@ -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
+--]]