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 +--]]