Compare commits

...

7 Commits

Author SHA1 Message Date
6a99ad76b8 set version to 1.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-30 17:54:47 +02:00
5094ba2b9c Merge pull request 'feature/IMP-112-ascension-8-9' (#59) from feature/IMP-112-ascension-8-9 into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: http://git.teletype.hu/games/impostor/pulls/59
2026-04-29 21:27:23 +00:00
Zoltan Timar
44a7d10037 lint fix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-29 23:25:54 +02:00
Zoltan Timar
1b991f1f62 Merge branch 'feature/IMP-112-ascension-8-9' of https://git.teletype.hu/games/impostor into feature/IMP-112-ascension-8-9
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
# Conflicts:
#	inc/screen/screen.mysterious_man.lua
2026-04-29 23:21:42 +02:00
Zoltan Timar
0d569ccf56 correcting bugs and texts 2026-04-29 23:20:04 +02:00
77d6f95721 ascension flash label, scrollable menu
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-29 23:15:22 +02:00
e2cd3d6dc7 linter fixes 2026-04-29 22:53:40 +02:00
11 changed files with 156 additions and 73 deletions

View File

@@ -4,12 +4,15 @@
globals = { globals = {
"AsciiArt", "AsciiArt",
"Ascension", "Ascension",
"AscendDebugWindow",
"Audio", "Audio",
"AudioTestWindow", "AudioTestWindow",
"BriefIntroWindow", "BriefIntroWindow",
"CodeGenerator", "CodeGenerator",
"Config", "Config",
"CommuteGlitch",
"Context", "Context",
"ContextDebug",
"ContinuedWindow", "ContinuedWindow",
"ControlsWindow", "ControlsWindow",
"CreditsWindow", "CreditsWindow",
@@ -67,6 +70,7 @@ globals = {
"music", "music",
"musicator_generate_pattern", "musicator_generate_pattern",
"pix", "pix",
"poke4",
"print", "print",
"rect", "rect",
"rectb", "rectb",

View File

@@ -1,9 +1,7 @@
meta/meta.header.lua meta/meta.header.lua
init/init.module.lua init/init.module.lua
init/init.config.lua init/init.config.lua
init/init.ascension.lua
init/init.context.lua init/init.context.lua
init/init.context_debug.lua
system/system.util.lua system/system.util.lua
system/system.print.lua system/system.print.lua
system/system.input.lua system/system.input.lua
@@ -11,6 +9,7 @@ system/system.textinput.lua
system/system.mouse.lua system/system.mouse.lua
system/system.asciiart.lua system/system.asciiart.lua
system/system.rle.lua system/system.rle.lua
logic/logic.ascension.lua
logic/logic.meter.lua logic/logic.meter.lua
logic/logic.focus.lua logic/logic.focus.lua
logic/logic.day.lua logic/logic.day.lua
@@ -21,6 +20,7 @@ logic/logic.glitch.lua
logic/logic.commute_glitch.lua logic/logic.commute_glitch.lua
logic/logic.codegenerator.lua logic/logic.codegenerator.lua
logic/logic.discussion.lua logic/logic.discussion.lua
system/system.debug.lua
system/system.ui.lua system/system.ui.lua
audio/audio.manager.lua audio/audio.manager.lua
audio/audio.generator.lua audio/audio.generator.lua

View File

@@ -15,12 +15,12 @@ end
--- Plays track at optional speed. Doesn't restart if track and speed are unchanged. --- Plays track at optional speed. Doesn't restart if track and speed are unchanged.
--- @param track number Track index. --- @param track number Track index.
--- @param[opt] speed number TIC-80 music speed override (-1 = default). --- @param[opt] tempo number TIC-80 music speed override (-1 = default).
function Audio.music_play(track, speed) function Audio.music_play(track, tempo)
if Audio.music_playing ~= track or Audio.music_playing_tempo ~= speed then if Audio.music_playing ~= track or Audio.music_playing_tempo ~= tempo then
music(track, -1, -1, true, false, -1, speed or -1) music(track, -1, -1, true, false, -1, tempo or -1)
Audio.music_playing = track Audio.music_playing = track
Audio.music_playing_tempo = speed Audio.music_playing_tempo = tempo
end end
end end
@@ -54,8 +54,8 @@ function Audio.music_play_room_() end
--- Plays room work music. Speed scales with commute glitch level when active. --- Plays room work music. Speed scales with commute glitch level when active.
--- @within Audio --- @within Audio
function Audio.music_play_room_work(speed) function Audio.music_play_room_work(tempo)
Audio.music_play(0, speed or -1) Audio.music_play(0, tempo or -1)
end end
--- Plays activity work music. --- Plays activity work music.

View File

@@ -124,24 +124,25 @@ Discussion.register({
on_end = Meter.apply_sumphore_discussion_reward, on_end = Meter.apply_sumphore_discussion_reward,
steps = { steps = {
{ {
question = "You saw something you weren't supposed to, didn't you.", question = "You saw the seams, didn't you. Good. That means the work is finally wearing thin.",
answers = { answers = {
{ label = "I don't know what you mean.", next_step = 2 }, { label = "Wearing thin how?", next_step = 2 },
{ label = "Maybe.", next_step = 2 }, { label = "Maybe.", next_step = 2 },
}, },
}, },
{ {
question = "The world around you has seams. Your coworkers slip sometimes. Say things that don't quite fit.", question = "Not your body. The part of you that still keeps score, still tries to be productive. Let that run empty and the world will slip again.",
answers = { answers = {
{ label = "They seem fine to me.", next_step = nil }, { label = "You want me to stop trying?", next_step = 3 },
{ label = "I've noticed something odd.", next_step = 3 }, { label = "I've noticed something odd.", next_step = 3 },
}, },
}, },
{ {
question = "Count those moments. Six of them should be enough to see the whole picture.", question = "Drain the work out of yourself. When that measure hits nothing, you'll see what was waiting behind it.",
answers = { answers = {
{ label = "Six of what, exactly?", next_step = nil, on_select = function() { label = "The work measure?", next_step = nil, on_select = function()
Meter.add("ism", 5) Meter.add("ism", 5)
Meter.add("wpm", -100)
end }, end },
{ label = "How would you know any of this?", next_step = nil }, { label = "How would you know any of this?", next_step = nil },
}, },

View File

@@ -145,6 +145,11 @@ function Ascension.draw_flash()
local flash_color = (pulse > 0.5) and Config.colors.white or Config.colors.light_grey local flash_color = (pulse > 0.5) and Config.colors.white or Config.colors.light_grey
rect(0, 0, sw, sh, flash_color) rect(0, 0, sw, sh, flash_color)
local cx = math.floor(sw / 2)
local cy = math.floor(sh / 2)
Print.text_center("Level Up!", cx, cy - 12, Config.colors.black, false, 2)
Print.text_center("One step closer to ascension", cx, cy + 6, Config.colors.black, false, 1)
if _flash_timer >= _flash_total then if _flash_timer >= _flash_total then
_flash_active = false _flash_active = false
Ascension.start_fade() Ascension.start_fade()

View File

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

View File

@@ -95,6 +95,8 @@ local ASC_78_TEXT = [[
local ASC_89_TEXT = [[ local ASC_89_TEXT = [[
Norman Norman
you created this simulation you created this simulation
in the first place. in the first place.
@@ -109,20 +111,24 @@ local ASC_89_TEXT = [[
have forgoten that. have forgoten that.
But But
it doesn't matter anymore. it doesn't matter anymore.
You actually are more You are definitely
than you think you are. not an impostor.
So now,
so now
@@ -132,17 +138,16 @@ local ASC_89_TEXT = [[
before it takes over before it takes over
the world the world.
One more thing:
also, You really need to stop
you really need to stop
talking to yourself talking to yourself
@@ -332,7 +337,7 @@ Screen.register({
lines = lines + 1 lines = lines + 1
end end
local skippable = Ascension.get_level() ~= 8 local skippable = Ascension.get_level() < 8
if text_y < -lines * 8 or (skippable and Input.select()) then if text_y < -lines * 8 or (skippable and Input.select()) then
text_done = true text_done = true
text_done_timer = TEXT_DONE_HOLD_SECONDS text_done_timer = TEXT_DONE_HOLD_SECONDS

View File

@@ -9,53 +9,79 @@ function UI.draw_top_bar(title)
end end
--- Draws a menu. --- Draws a menu.
--- Items with header=true are drawn as non-selectable section headers in small font.
--- @within UI --- @within UI
--- @param items table A table of menu items.<br/> --- @param items table A table of menu items.<br/>
--- @param selected_item number The index of the currently selected item.<br/> --- @param selected_item number The index of the currently selected item.<br/>
--- @param x number The x-coordinate for the menu (ignored if centered is true).<br/> --- @param x number The x-coordinate for the menu (ignored if centered is true).<br/>
--- @param y number The y-coordinate for the menu.<br/> --- @param y number The y-coordinate for the menu.<br/>
--- @param[opt] centered boolean Whether to center the menu block horizontally. Defaults to false.<br/> --- @param[opt] centered boolean Whether to center the menu block horizontally. Defaults to false.<br/>
function UI.draw_menu(items, selected_item, x, y, centered) --- @param[opt] scroll_offset number 0-based index of the first visible item. Defaults to 0.<br/>
--- @param[opt] visible_count number Maximum number of items to draw. Defaults to all.<br/>
function UI.draw_menu(items, selected_item, x, y, centered, scroll_offset, visible_count)
scroll_offset = scroll_offset or 0
visible_count = visible_count or #items
if centered then if centered then
local max_w = 0 local max_w = 0
for _, item in ipairs(items) do for _, item in ipairs(items) do
if not item.header then
local w = print(item.label, 0, -10, 0, false, 1, false) local w = print(item.label, 0, -10, 0, false, 1, false)
if w > max_w then max_w = w end if w > max_w then max_w = w end
end end
end
x = (Config.screen.width - max_w) / 2 x = (Config.screen.width - max_w) / 2
end end
for i, item in ipairs(items) do local current_y = y
local current_y = y + (i-1)*10 for i = scroll_offset + 1, math.min(#items, scroll_offset + visible_count) do
local item = items[i]
if item.header then
Print.text(item.label, x, current_y, Config.colors.dark_grey, true, 1)
current_y = current_y + 8
else
if i == selected_item then if i == selected_item then
Print.text(">", x - 8, current_y, Config.colors.light_blue) Print.text(">", x - 8, current_y, Config.colors.light_blue)
end end
Print.text(item.label, x, current_y, Config.colors.light_blue) Print.text(item.label, x, current_y, Config.colors.light_blue)
current_y = current_y + 10
end
end end
end end
--- Updates menu selection. --- Updates menu selection. Skips items with header=true during navigation.
--- @within UI --- @within UI
--- @param items table A table of menu items.<br/> --- @param items table A table of menu items.<br/>
--- @param selected_item number The current index of the selected item.<br/> --- @param selected_item number The current index of the selected item.<br/>
--- @param[opt] x number Menu x position (required for mouse support).<br/> --- @param[opt] x number Menu x position (required for mouse support).<br/>
--- @param[opt] y number Menu y position (required for mouse support).<br/> --- @param[opt] y number Menu y position (required for mouse support).<br/>
--- @param[opt] centered boolean Whether the menu is centered horizontally.<br/> --- @param[opt] centered boolean Whether the menu is centered horizontally.<br/>
--- @param[opt] scroll_offset number 0-based index of the first visible item. Defaults to 0.<br/>
--- @param[opt] visible_count number Number of visible items (for mouse hit zones). Defaults to all.<br/>
--- @return number selected_item The updated index of the selected item. --- @return number selected_item The updated index of the selected item.
--- @return boolean mouse_confirmed True if the user clicked on a menu item. --- @return boolean mouse_confirmed True if the user clicked on a menu item.
function UI.update_menu(items, selected_item, x, y, centered) function UI.update_menu(items, selected_item, x, y, centered, scroll_offset, visible_count)
scroll_offset = scroll_offset or 0
visible_count = visible_count or #items
local n = #items
local function find_selectable(start, dir)
local idx = start
for _ = 1, n do
if not items[idx].header then return idx end
idx = (idx - 1 + dir + n) % n + 1
end
return start
end
if Input.up() then if Input.up() then
Audio.sfx_beep() Audio.sfx_beep()
selected_item = selected_item - 1 local prev = (selected_item - 2 + n) % n + 1
if selected_item < 1 then selected_item = find_selectable(prev, -1)
selected_item = #items
end
elseif Input.down() then elseif Input.down() then
Audio.sfx_beep() Audio.sfx_beep()
selected_item = selected_item + 1 local next_i = selected_item % n + 1
if selected_item > #items then selected_item = find_selectable(next_i, 1)
selected_item = 1
end
end end
if x ~= nil and y ~= nil then if x ~= nil and y ~= nil then
@@ -63,16 +89,24 @@ function UI.update_menu(items, selected_item, x, y, centered)
if centered then if centered then
local max_w = 0 local max_w = 0
for _, item in ipairs(items) do for _, item in ipairs(items) do
if not item.header then
local w = print(item.label, 0, -10, 0, false, 1, false) local w = print(item.label, 0, -10, 0, false, 1, false)
if w > max_w then max_w = w end if w > max_w then max_w = w end
end end
end
menu_x = (Config.screen.width - max_w) / 2 menu_x = (Config.screen.width - max_w) / 2
end end
for i, _ in ipairs(items) do local current_y = y
if Mouse.zone({ x = menu_x - 8, y = y + (i-1) * 10, w = Config.screen.width, h = 10 }) then for i = scroll_offset + 1, math.min(n, scroll_offset + visible_count) do
local item = items[i]
local step = item.header and 8 or 10
if not item.header then
if Mouse.zone({ x = menu_x - 8, y = current_y, w = Config.screen.width, h = 10 }) then
return i, true return i, true
end end
end end
current_y = current_y + step
end
end end
return selected_item, false return selected_item, false

View File

@@ -5,6 +5,7 @@ local _anim = 0
local _menu_max_w = 0 local _menu_max_w = 0
local ANIM_SPEED = 2.5 local ANIM_SPEED = 2.5
local HEADER_H = 28 local HEADER_H = 28
MenuWindow._scroll_offset = 0
--- Calculates the animated x position of the menu block. --- Calculates the animated x position of the menu block.
--- @within MenuWindow --- @within MenuWindow
@@ -45,6 +46,17 @@ function MenuWindow.draw_norman()
spr(305, nx + 32, ny + 64, Config.colors.transparent, 4) spr(305, nx + 32, ny + 64, Config.colors.transparent, 4)
end end
--- Adjusts _scroll_offset so the selected item is within the visible window.
--- @within MenuWindow
function MenuWindow.ensure_visible()
local sel = Context.current_menu_item
if sel <= MenuWindow._scroll_offset then
MenuWindow._scroll_offset = sel - 1
elseif sel > MenuWindow._scroll_offset + 5 then
MenuWindow._scroll_offset = sel - 5
end
end
--- Draws the menu window. --- Draws the menu window.
--- @within MenuWindow --- @within MenuWindow
function MenuWindow.draw() function MenuWindow.draw()
@@ -56,9 +68,19 @@ function MenuWindow.draw()
MenuWindow.draw_norman() MenuWindow.draw_norman()
end end
local menu_h = #_menu_items * 10 local menu_x = MenuWindow.calc_menu_x()
local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 10 - menu_h) / 2) local arrow_cx = math.floor(menu_x + _menu_max_w / 2)
UI.draw_menu(_menu_items, Context.current_menu_item, MenuWindow.calc_menu_x(), y, false) local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 50) / 2)
if MenuWindow._scroll_offset > 0 then
Print.text_center("^", arrow_cx, y - 8, Config.colors.light_blue)
end
UI.draw_menu(_menu_items, Context.current_menu_item, menu_x, y, false, MenuWindow._scroll_offset, 5)
if MenuWindow._scroll_offset + 5 < #_menu_items then
Print.text_center("v", arrow_cx, y + 52, Config.colors.light_blue)
end
local ttg_text = "TTG" local ttg_text = "TTG"
local ttg_w = print(ttg_text, 0, -10, 0, false, 1, false) local ttg_w = print(ttg_text, 0, -10, 0, false, 1, false)
@@ -72,8 +94,8 @@ function MenuWindow.update()
_anim = math.min(1, _anim + ANIM_SPEED * Context.delta_time) _anim = math.min(1, _anim + ANIM_SPEED * Context.delta_time)
end end
local menu_h = #_menu_items * 10 local menu_x = MenuWindow.calc_menu_x()
local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 10 - menu_h) / 2) local y = HEADER_H + math.floor((Config.screen.height - HEADER_H - 50) / 2)
if _click_timer > 0 then if _click_timer > 0 then
_click_timer = _click_timer - Context.delta_time _click_timer = _click_timer - Context.delta_time
@@ -87,8 +109,9 @@ function MenuWindow.update()
return return
end end
local new_item, mouse_confirmed = UI.update_menu(_menu_items, Context.current_menu_item, MenuWindow.calc_menu_x(), y, false) local new_item, mouse_confirmed = UI.update_menu(_menu_items, Context.current_menu_item, menu_x, y, false, MenuWindow._scroll_offset, 5)
Context.current_menu_item = new_item Context.current_menu_item = new_item
MenuWindow.ensure_visible()
if mouse_confirmed then if mouse_confirmed then
Audio.sfx_select() Audio.sfx_select()
@@ -186,6 +209,12 @@ function MenuWindow.ascend_debug()
GameWindow.set_state("ascend_debug") GameWindow.set_state("ascend_debug")
end end
--- Triggers the Level Up flash animation for testing.
--- @within MenuWindow
function MenuWindow.level_up_flash()
Ascension.start_flash()
end
--- Refreshes the list of menu items based on current game state. --- Refreshes the list of menu items based on current game state.
--- @within MenuWindow --- @within MenuWindow
function MenuWindow.refresh_menu_items() function MenuWindow.refresh_menu_items()
@@ -200,6 +229,8 @@ function MenuWindow.refresh_menu_items()
table.insert(_menu_items, {label = "Credits", decision = MenuWindow.credits}) table.insert(_menu_items, {label = "Credits", decision = MenuWindow.credits})
if Context.test_mode then if Context.test_mode then
table.insert(_menu_items, {label = "Debug Menu", header = true})
table.insert(_menu_items, {label = "Level Up Flash", decision = MenuWindow.level_up_flash})
table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test}) table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test})
table.insert(_menu_items, {label = "To Be Continued...", decision = MenuWindow.continued}) table.insert(_menu_items, {label = "To Be Continued...", decision = MenuWindow.continued})
table.insert(_menu_items, {label = "DDR Test", decision = MenuWindow.ddr_test}) table.insert(_menu_items, {label = "DDR Test", decision = MenuWindow.ddr_test})
@@ -212,11 +243,14 @@ function MenuWindow.refresh_menu_items()
_menu_max_w = 0 _menu_max_w = 0
for _, item in ipairs(_menu_items) do for _, item in ipairs(_menu_items) do
if not item.header then
local w = print(item.label, 0, -10, 0, false, 1, false) local w = print(item.label, 0, -10, 0, false, 1, false)
if w > _menu_max_w then _menu_max_w = w end if w > _menu_max_w then _menu_max_w = w end
end end
end
Context.current_menu_item = 1 Context.current_menu_item = 1
MenuWindow._scroll_offset = 0
_click_timer = 0 _click_timer = 0
_anim = 0 _anim = 0
end end