diff --git a/impostor.inc b/impostor.inc index ed729fd..d69c758 100644 --- a/impostor.inc +++ b/impostor.inc @@ -1,13 +1,26 @@ meta/meta.header.lua init/init.modules.lua init/init.config.lua +system/system.util.lua init/init.windows.lua +desition/desition.manager.lua +desition/desition.go_to_home.lua +desition/desition.go_to_toilet.lua +desition/desition.go_to_walking_to_office.lua +desition/desition.go_to_office.lua +desition/desition.go_to_walking_to_home.lua +desition/desition.play_button_mash.lua +desition/desition.play_rhythm.lua +desition/desition.play_ddr.lua +screen/screen.manager.lua +screen/screen.home.lua +screen/screen.toilet.lua +screen/screen.walking_to_office.lua +screen/screen.office.lua +screen/screen.walking_to_home.lua init/init.context.lua data/data.songs.lua system/system.print.lua -system/system.util.lua -entity/entity.npc.lua -entity/entity.item.lua system/system.input.lua system/system.audio.lua system/system.ui.lua diff --git a/inc/desition/desition.go_to_home.lua b/inc/desition/desition.go_to_home.lua new file mode 100644 index 0000000..89ed3a4 --- /dev/null +++ b/inc/desition/desition.go_to_home.lua @@ -0,0 +1,8 @@ +DesitionManager.register({ + id = "go_to_home", + label = "Go to Home", + handle = function() + Util.go_to_screen_by_id("home") + end, + condition = function() return true end +}) diff --git a/inc/desition/desition.go_to_office.lua b/inc/desition/desition.go_to_office.lua new file mode 100644 index 0000000..75b47ac --- /dev/null +++ b/inc/desition/desition.go_to_office.lua @@ -0,0 +1,8 @@ +DesitionManager.register({ + id = "go_to_office", + label = "Go to Office", + handle = function() + Util.go_to_screen_by_id("office") + end, + condition = function() return true end +}) diff --git a/inc/desition/desition.go_to_toilet.lua b/inc/desition/desition.go_to_toilet.lua new file mode 100644 index 0000000..7127b94 --- /dev/null +++ b/inc/desition/desition.go_to_toilet.lua @@ -0,0 +1,8 @@ +DesitionManager.register({ + id = "go_to_toilet", + label = "Go to Toilet", + handle = function() + Util.go_to_screen_by_id("toilet") + end, + condition = function() return true end +}) diff --git a/inc/desition/desition.go_to_walking_to_home.lua b/inc/desition/desition.go_to_walking_to_home.lua new file mode 100644 index 0000000..46cc488 --- /dev/null +++ b/inc/desition/desition.go_to_walking_to_home.lua @@ -0,0 +1,8 @@ +DesitionManager.register({ + id = "go_to_walking_to_home", + label = "Go to Walking to home", + handle = function() + Util.go_to_screen_by_id("walking_to_home") + end, + condition = function() return true end +}) diff --git a/inc/desition/desition.go_to_walking_to_office.lua b/inc/desition/desition.go_to_walking_to_office.lua new file mode 100644 index 0000000..458b3a8 --- /dev/null +++ b/inc/desition/desition.go_to_walking_to_office.lua @@ -0,0 +1,8 @@ +DesitionManager.register({ + id = "go_to_walking_to_office", + label = "Go to Walking to office", + handle = function() + Util.go_to_screen_by_id("walking_to_office") + end, + condition = function() return true end +}) diff --git a/inc/desition/desition.manager.lua b/inc/desition/desition.manager.lua new file mode 100644 index 0000000..1c43b7f --- /dev/null +++ b/inc/desition/desition.manager.lua @@ -0,0 +1,42 @@ +DesitionManager = {} + +local _desitions = {} -- Private table to store all desitions + +-- Registers a decision object with the manager +-- desition_object: A table containing id, label, handle(), and condition() +function DesitionManager.register(desition_object) + if not desition_object or not desition_object.id then + PopupWindow.show({"Error: Invalid desition object registered (missing id)!"}) + return + end + if not desition_object.label then + PopupWindow.show({"Error: Invalid desition object registered (missing label)!"}) + return + end + + -- Ensure handle() and condition() methods exist with defaults if missing + if not desition_object.condition then + desition_object.condition = function() return true end + end + if not desition_object.handle then + desition_object.handle = function() end + end + + if _desitions[desition_object.id] then + -- Optional: warning if overwriting an existing desition + -- trace("Warning: Overwriting desition with id: " .. desition_object.id) + end + _desitions[desition_object.id] = desition_object +end + +-- Retrieves a desition by its id +-- id: unique string identifier of the desition +-- Returns the desition object, or nil if not found +function DesitionManager.get(id) + return _desitions[id] +end + +-- Optional: a way to get all registered desitions, if needed (e.g., for debug) +function DesitionManager.get_all() + return _desitions +end diff --git a/inc/desition/desition.play_button_mash.lua b/inc/desition/desition.play_button_mash.lua new file mode 100644 index 0000000..754e292 --- /dev/null +++ b/inc/desition/desition.play_button_mash.lua @@ -0,0 +1,6 @@ +DesitionManager.register({ + id = "play_button_mash", + label = "Play Button Mash", + handle = function() MinigameButtonMashWindow.start(WINDOW_GAME) end, + condition = function() return true end +}) diff --git a/inc/desition/desition.play_ddr.lua b/inc/desition/desition.play_ddr.lua new file mode 100644 index 0000000..f03be37 --- /dev/null +++ b/inc/desition/desition.play_ddr.lua @@ -0,0 +1,6 @@ +DesitionManager.register({ + id = "play_ddr", + label = "Play DDR (Random)", + handle = function() MinigameDDRWindow.start(WINDOW_GAME, nil) end, + condition = function() return true end +}) diff --git a/inc/desition/desition.play_rhythm.lua b/inc/desition/desition.play_rhythm.lua new file mode 100644 index 0000000..82688ec --- /dev/null +++ b/inc/desition/desition.play_rhythm.lua @@ -0,0 +1,6 @@ +DesitionManager.register({ + id = "play_rhythm", + label = "Play Rhythm Game", + handle = function() MinigameRhythmWindow.start(WINDOW_GAME) end, + condition = function() return true end +}) diff --git a/inc/entity/entity.item.lua b/inc/entity/entity.item.lua deleted file mode 100644 index 44b9dc6..0000000 --- a/inc/entity/entity.item.lua +++ /dev/null @@ -1,7 +0,0 @@ -function Item.use() - Print.text("Used item: " .. Context.dialog.active_entity.name) -end -function Item.look_at() - PopupWindow.show_description_dialog(Context.dialog.active_entity, Context.dialog.active_entity.desc) -end - diff --git a/inc/entity/entity.npc.lua b/inc/entity/entity.npc.lua deleted file mode 100644 index c4a90e2..0000000 --- a/inc/entity/entity.npc.lua +++ /dev/null @@ -1,13 +0,0 @@ -function NPC.talk_to() - local npc = Context.dialog.active_entity - if npc.dialog and npc.dialog.start then - PopupWindow.set_dialog_node("start") - else - -- if no dialog, go back - GameWindow.set_state(WINDOW_GAME) - end -end -function NPC.fight() end -function NPC.go_back() - GameWindow.set_state(WINDOW_GAME) -end diff --git a/inc/init/init.config.lua b/inc/init/init.config.lua index 82f454c..a0d6ab3 100644 --- a/inc/init/init.config.lua +++ b/inc/init/init.config.lua @@ -8,7 +8,6 @@ local DEFAULT_CONFIG = { light_grey = 13, dark_grey = 14, green = 6, - npc = 8, item = 12 -- yellow }, player = { @@ -29,19 +28,26 @@ local Config = { local CONFIG_SAVE_BANK = 7 local CONFIG_MAGIC_VALUE_ADDRESS = 2 +local CONFIG_SPLASH_DURATION_ADDRESS = 3 -- New address for splash duration local CONFIG_MAGIC_VALUE = 0xDE -- A magic number to check if config is saved function Config.save() - -- Save physics settings mset(CONFIG_MAGIC_VALUE, CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) -- Mark as saved + --mset(Config.timing.splash_duration, CONFIG_SPLASH_DURATION_ADDRESS, CONFIG_SAVE_BANK) end function Config.load() - Config.restore_defaults() - -- Check if config has been saved before using a magic value + if mget(CONFIG_MAGIC_VALUE_ADDRESS, CONFIG_SAVE_BANK) == CONFIG_MAGIC_VALUE then + -- Config has been saved, load values + Config.timing.splash_duration = mget(CONFIG_SPLASH_DURATION_ADDRESS, CONFIG_SAVE_BANK) + else + -- No saved config, restore defaults + Config.restore_defaults() + end end function Config.restore_defaults() + Config.timing.splash_duration = DEFAULT_CONFIG.timing.splash_duration -- Any other configurable items should be reset here end diff --git a/inc/init/init.context.lua b/inc/init/init.context.lua index 9199b60..6f3a949 100644 --- a/inc/init/init.context.lua +++ b/inc/init/init.context.lua @@ -28,13 +28,9 @@ local function get_initial_data() }, current_screen = 1, splash_timer = Config.timing.splash_duration, - dialog = { - text = "", - menu_items = {}, - selected_menu_item = 1, - active_entity = nil, - showing_description = false, - current_node_key = nil + popup = { -- New popup table + show = false, + content = {} -- Array of strings }, player = { sprite_id = Config.player.sprite_id @@ -47,390 +43,9 @@ local function get_initial_data() }, menu_items = {}, selected_menu_item = 1, + selected_desition_index = 1, -- New desition index game_in_progress = false, -- New flag - screens = clone_table({ - { - name = "Screen 1", - npcs = { - { - name = "Button Mash Minigame", - sprite_id = 4, - dialog = { - start = { - text = "Ready to test your reflexes? Prove your speed!", - options = { - {label = "Let's do it!", next_node = "__MINIGAME_BUTTON_MASH__"}, - {label = "Not now.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "Come back when you're ready.", - options = {} - } - } - }, - { - name = "Trinity", - sprite_id = 2, - dialog = { - start = { - text = "Hello, Neo.", - options = { - {label = "Who are you?", next_node = "who_are_you"}, - {label = "My name is not Neo.", next_node = "not_neo"}, - {label = "...", next_node = "silent"} - } - }, - who_are_you = { - text = "I am Trinity. I've been looking for you.", - options = { - {label = "The famous hacker?", next_node = "famous_hacker"}, - {label = "Why me?", next_node = "why_me"} - } - }, - not_neo = { - text = "I know. But you will be.", - options = { - {label = "What are you talking about?", next_node = "who_are_you"} - } - }, - silent = { - text = "You're not much of a talker, are you?", - options = { - {label = "I guess not.", next_node = "dialog_end"} - } - }, - famous_hacker = { - text = "The one and only.", - options = { - {label = "Wow.", next_node = "dialog_end"} - } - }, - why_me = { - text = "Morpheus believes you are The One.", - options = { - {label = "The One?", next_node = "the_one"} - } - }, - the_one = { - text = "The one who will save us all.", - options = { - {label = "I'm just a programmer.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "We'll talk later.", - options = {} -- No options, ends conversation - } - } - }, - { - name = "Oracle", - sprite_id = 3, - dialog = { - start = { - text = "I know what you're thinking. 'Am I in the right place?'", - options = { - {label = "Who are you?", next_node = "who_are_you"}, - {label = "I guess I am.", next_node = "you_are"} - } - }, - who_are_are = { - text = "I'm the Oracle. And you're right on time. Want a cookie?", - options = { - {label = "Sure.", next_node = "cookie"}, - {label = "No, thank you.", next_node = "no_cookie"} - } - }, - you_are = { - text = "Of course you are. Sooner or later, everyone comes to see me. Want a cookie?", - options = { - {label = "Yes, please.", next_node = "cookie"}, - {label = "I'm good.", next_node = "no_cookie"} - } - }, - cookie = { - text = "Here you go. Now, what's really on your mind?", - options = { - {label = "Am I The One?", next_node = "the_one"}, - {label = "What is the Matrix?", next_node = "the_matrix"} - } - }, - no_cookie = { - text = "Suit yourself. Now, what's troubling you?", - options = { - {label = "Am I The One?", next_node = "the_one"}, - {label = "What is the Matrix?", next_node = "the_matrix"} - } - }, - the_one = { - text = "Being The One is just like being in love. No one can tell you you're in love, you just know it. Through and through. Balls to bones.", - options = { - {label = "So I'm not?", next_node = "dialog_end"} - } - }, - the_matrix = { - text = "The Matrix is a system, Neo. That system is our enemy. But when you're inside, you look around, what do you see? The very minds of the people we are trying to save.", - options = { - {label = "I see.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "You have to understand, most of these people are not ready to be unplugged.", - options = {} - } - } - }, - }, - items = { - { - name = "Key", - sprite_id = 4, - desc = "A rusty old key. It might open something." - } - } - }, - { - -- Screen 2 - name = "Screen 2", - npcs = { - { - name = "Rhythm Master", - sprite_id = 4, - dialog = { - start = { - text = "Test your timing! Hit the mark when the moment is right.", - options = { - {label = "Let's go!", next_node = "__MINIGAME_RHYTHM__"}, - {label = "Not now.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "Come back when you're ready to test your reflexes.", - options = {} - } - } - }, - { - name = "Morpheus", - sprite_id = 5, - dialog = { - start = { - text = "At last. Welcome, Neo. As you no doubt have guessed, I am Morpheus.", - options = { - {label = "It's an honor to meet you.", next_node = "honor"}, - {label = "You've been looking for me.", next_node = "looking_for_me"} - } - }, - honor = { - text = "No, the honor is mine.", - options = { - {label = "What is this place?", next_node = "what_is_this_place"} - } - }, - looking_for_me = { - text = "I have. For some time.", - options = { - {label = "What is this place?", next_node = "what_is_this_place"} - } - }, - what_is_this_place = { - text = "This is the construct. It's our loading program. We can load anything from clothing, to equipment, weapons, training simulations. Anything we need.", - options = { - {label = "Right.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "I've been waiting for you, Neo. We have much to discuss.", - options = {} -- Ends conversation - } - } - }, - { - name = "Tank", - sprite_id = 6, - dialog = { - start = { - text = "Hey, Neo! Welcome to the construct. I'm Tank.", - options = { - {label = "Good to meet you.", next_node = "good_to_meet_you"}, - {label = "This place is incredible.", next_node = "incredible"} - } - }, - good_to_meet_you = { - text = "You too! We've been waiting for you. Need anything? Training? Weapons?", - options = { - {label = "Training?", next_node = "training"}, - {label = "I'm good for now.", next_node = "dialog_end"} - } - }, - incredible = { - text = "Isn't it? The boss's design. We can load anything we need. What do you want to learn?", - options = { - {label = "Show me.", next_node = "training"} - } - }, - training = { - text = "Jujitsu? Kung Fu? How about... all of them?", - options = { - {label = "All of them.", next_node = "all_of_them"} - } - }, - all_of_them = { - text = "Operator, load the combat training program.", - options = { - {label = "...", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "Just holler if you need anything. Anything at all.", - options = {} - } - } - } - }, - items = { - { - name = "Potion", - sprite_id = 7, - desc = "A glowing red potion. It looks potent." - } - } - }, - { - -- Screen 3 - name = "Screen 3", - npcs = { - { - name = "DDR Rhythm Master", - sprite_id = 4, - dialog = { - start = { - text = "Test your reflexes! Hit the arrows in time with the music. Choose your difficulty:", - options = { - {label = "Test Song", next_node = "test"}, - {label = "Test Song 2", next_node = "test_2"}, - {label = "Random Mode", next_node = "random"}, - {label = "Not now.", next_node = "dialog_end"} - } - }, - test = { - text = "Test song selected. Show me what you got!", - options = { - {label = "Start!", next_node = "__MINIGAME_DDR:test_song__"} - } - }, - test_2 = { - text = "Test song 2 selected. Show me what you got!", - options = { - {label = "Start!", next_node = "__MINIGAME_DDR:test_song_2__"} - } - }, - random = { - text = "Random arrows! No pattern, just react!", - options = { - {label = "Start!", next_node = "__MINIGAME_DDR__"} - } - }, - dialog_end = { - text = "Come back when you're ready to dance!", - options = {} - } - } - }, - { - name = "Agent Smith", - sprite_id = 8, - dialog = { - start = { - text = "Mr. Anderson. We've been expecting you.", - options = { - {label = "My name is Neo.", next_node = "name_is_neo"}, - {label = "...", next_node = "silent"} - } - }, - name_is_neo = { - text = "Whatever you say. You're here for a reason.", - options = { - {label = "What reason?", next_node = "what_reason"} - } - }, - silent = { - text = "The silent type. It doesn't matter. You are an anomaly.", - options = { - {label = "What do you want?", next_node = "what_reason"} - } - }, - what_reason = { - text = "To be deleted. The system has no place for your kind.", - options = { - {label = "I won't let you.", next_node = "wont_let_you"} - } - }, - wont_let_you = { - text = "You hear that, Mr. Anderson? That is the sound of inevitability.", - options = { - {label = "...", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "It is purpose that created us. Purpose that connects us. Purpose that pulls us. That guides us. That drives us. It is purpose that defines. Purpose that binds us.", - options = {} - } - } - }, - { - name = "Cypher", - sprite_id = 9, - dialog = { - start = { - text = "Well, well. The new messiah. Welcome to the real world.", - options = { - {label = "You don't seem happy.", next_node = "not_happy"}, - {label = "...", next_node = "silent"} - } - }, - not_happy = { - text = "Happy? Ignorance is bliss, Neo. We've been fighting this war for years. For what?", - options = { - {label = "For freedom.", next_node = "freedom"} - } - }, - silent = { - text = "Not a talker, huh? Smart. Less to regret later. Want a drink?", - options = { - {label = "Sure.", next_node = "drink"}, - {label = "No thanks.", next_node = "no_drink"} - } - }, - drink = { - text = "Good stuff. The little things you miss, you know? Like a good steak.", - options = { - {label = "I guess.", next_node = "dialog_end"} - } - }, - no_drink = { - text = "Your loss. More for me.", - options = { - {label = "...", next_node = "dialog_end"} - } - }, - freedom = { - text = "Freedom... right. If Morpheus told you you could fly, would you believe him?", - options = { - {label = "He's our leader.", next_node = "dialog_end"} - } - }, - dialog_end = { - text = "Just be careful who you trust.", - options = {} - } - } - } - }, - items = {} - } - }) + screens = {} -- Initialize as empty, populated on reset } end @@ -450,6 +65,22 @@ local function reset_context_to_initial_state() for k, v in pairs(initial_data) do Context[k] = v end + + -- Populate Context.screens from ScreenManager, ensuring indexed array + Context.screens = {} + Context.screen_indices_by_id = {} -- Renamed for clarity, stores index + -- The screen order needs to be explicit to ensure consistent numerical indices + local screen_order = {"home", "toilet", "walking_to_office", "office", "walking_to_home"} + for i, screen_id in ipairs(screen_order) do + local screen_data = ScreenManager.get_by_id(screen_id) + if screen_data then + table.insert(Context.screens, screen_data) + Context.screen_indices_by_id[screen_id] = i -- Store index + else + -- Handle error if a screen is not registered + PopupWindow.show({"Error: Screen '" .. screen_id .. "' not registered!"}) + end + end end -- Initially populate Context with data @@ -482,4 +113,4 @@ function Context.load_game() Context.game_in_progress = true MenuWindow.refresh_menu_items() -end \ No newline at end of file +end diff --git a/inc/init/init.modules.lua b/inc/init/init.modules.lua index 033a857..e98cb49 100644 --- a/inc/init/init.modules.lua +++ b/inc/init/init.modules.lua @@ -10,10 +10,10 @@ local MinigameRhythmWindow = {} local MinigameDDRWindow = {} local Util = {} +local DesitionManager = {} +local ScreenManager = {} -- New declaration local UI = {} local Print = {} local Input = {} -local NPC = {} -local Item = {} local Player = {} local Audio = {} diff --git a/inc/screen/screen.home.lua b/inc/screen/screen.home.lua new file mode 100644 index 0000000..efb059c --- /dev/null +++ b/inc/screen/screen.home.lua @@ -0,0 +1,8 @@ +ScreenManager.register({ + id = "home", + name = "Home", + decisions = { + "go_to_toilet", + "go_to_walking_to_office", + } +}) diff --git a/inc/screen/screen.manager.lua b/inc/screen/screen.manager.lua new file mode 100644 index 0000000..8a2a08f --- /dev/null +++ b/inc/screen/screen.manager.lua @@ -0,0 +1,27 @@ +ScreenManager = {} + +local _screens = {} -- Internal list to hold screen data + +-- Public property to access the registered screens as an indexed array +function ScreenManager.get_screens_array() + local screens_array = {} + for _, screen_data in pairs(_screens) do + table.insert(screens_array, screen_data) + end + return screens_array +end + +-- Registers a screen with the manager +-- screen_data: A table containing id, name, and decisions for the screen +function ScreenManager.register(screen_data) + if _screens[screen_data.id] then + -- Optional: warning if overwriting an existing screen + -- trace("Warning: Overwriting screen with id: " .. screen_data.id) + end + _screens[screen_data.id] = screen_data +end + +-- Retrieves a screen by its id (if needed directly) +function ScreenManager.get_by_id(screen_id) + return _screens[screen_id] +end diff --git a/inc/screen/screen.office.lua b/inc/screen/screen.office.lua new file mode 100644 index 0000000..7a619b0 --- /dev/null +++ b/inc/screen/screen.office.lua @@ -0,0 +1,10 @@ +ScreenManager.register({ + id = "office", + name = "Office", + decisions = { + "play_button_mash", + "play_rhythm", + "play_ddr", + "go_to_walking_to_home", + } +}) diff --git a/inc/screen/screen.toilet.lua b/inc/screen/screen.toilet.lua new file mode 100644 index 0000000..ebe9543 --- /dev/null +++ b/inc/screen/screen.toilet.lua @@ -0,0 +1,7 @@ +ScreenManager.register({ + id = "toilet", + name = "Toilet", + decisions = { + "go_to_home", + } +}) diff --git a/inc/screen/screen.walking_to_home.lua b/inc/screen/screen.walking_to_home.lua new file mode 100644 index 0000000..a12739f --- /dev/null +++ b/inc/screen/screen.walking_to_home.lua @@ -0,0 +1,8 @@ +ScreenManager.register({ + id = "walking_to_home", + name = "Walking to home", + decisions = { + "go_to_home", + "go_to_office", + } +}) diff --git a/inc/screen/screen.walking_to_office.lua b/inc/screen/screen.walking_to_office.lua new file mode 100644 index 0000000..95a89df --- /dev/null +++ b/inc/screen/screen.walking_to_office.lua @@ -0,0 +1,8 @@ +ScreenManager.register({ + id = "walking_to_office", + name = "Walking to office", + decisions = { + "go_to_home", + "go_to_office", + } +}) diff --git a/inc/system/system.ui.lua b/inc/system/system.ui.lua index 375c602..d3e4694 100644 --- a/inc/system/system.ui.lua +++ b/inc/system/system.ui.lua @@ -87,3 +87,38 @@ function UI.create_action_item(label, action) type = "action_item" } end + +function UI.draw_desition_selector(desitions, selected_desition_index) + local bar_height = 16 + local bar_y = Config.screen.height - bar_height + + rect(0, bar_y, Config.screen.width, bar_height, Config.colors.dark_grey) + + if #desitions > 0 then + local selected_desition = desitions[selected_desition_index] + local desition_label = selected_desition.label + local text_width = #desition_label * 4 -- Assuming 4 pixels per char + local text_y = bar_y + 4 + + -- Center the decision label + local text_x = (Config.screen.width - text_width) / 2 + + -- Draw left arrow at the far left + Print.text("<", 2, text_y, Config.colors.green) + -- Draw selected desition label + Print.text(desition_label, text_x, text_y, Config.colors.item) -- Highlight color + -- Draw right arrow at the far right + Print.text(">", Config.screen.width - 6, text_y, Config.colors.green) -- 6 = 2 (right margin) + 4 (char width) + end +end + +function UI.update_desition_selector(desitions, selected_desition_index) + if Input.left() then + Audio.sfx_beep() + selected_desition_index = Util.safeindex(desitions, selected_desition_index - 1) + elseif Input.right() then + Audio.sfx_beep() + selected_desition_index = Util.safeindex(desitions, selected_desition_index + 1) + end + return selected_desition_index +end diff --git a/inc/system/system.util.lua b/inc/system/system.util.lua index 909defa..139a377 100644 --- a/inc/system/system.util.lua +++ b/inc/system/system.util.lua @@ -3,3 +3,13 @@ Util = {} function Util.safeindex(array, index) return ((index - 1 + #array) % #array) + 1 end + +function Util.go_to_screen_by_id(screen_id) + local screen_index = Context.screen_indices_by_id[screen_id] + if screen_index then + Context.current_screen = screen_index + Context.selected_desition_index = 1 -- Reset selected decision on new screen + else + PopupWindow.show({"Error: Screen '" .. screen_id .. "' not found or not indexed!"}) + end +end \ No newline at end of file diff --git a/inc/window/window.audiotest.lua b/inc/window/window.audiotest.lua index cbe7a7f..3ea5e5d 100644 --- a/inc/window/window.audiotest.lua +++ b/inc/window/window.audiotest.lua @@ -10,7 +10,7 @@ function AudioTestWindow.generate_menuitems(list_func, index_func) return { { label = "Play music/sound: " .. (list_func[index_func] or "?"), - action = function() + desition = function() local current_func = Audio[list_func[index_func]] if current_func then current_func() @@ -21,13 +21,13 @@ function AudioTestWindow.generate_menuitems(list_func, index_func) }, { label = "Stop playing music", - action = function() + desition = function() Audio.music_stop() end }, { label = "Back", - action = function() + desition = function() AudioTestWindow.back() end }, @@ -78,7 +78,7 @@ function AudioTestWindow.update() AudioTestWindow.index_func = Util.safeindex(AudioTestWindow.list_func, AudioTestWindow.index_func + 1) AudioTestWindow.menuitems = AudioTestWindow.generate_menuitems(AudioTestWindow.list_func, AudioTestWindow.index_func) elseif Input.menu_confirm() then - AudioTestWindow.menuitems[AudioTestWindow.index_menu].action() + AudioTestWindow.menuitems[AudioTestWindow.index_menu].desition() elseif Input.menu_back() then AudioTestWindow.back() end diff --git a/inc/window/window.configuration.lua b/inc/window/window.configuration.lua index 6f1c619..77acd11 100644 --- a/inc/window/window.configuration.lua +++ b/inc/window/window.configuration.lua @@ -5,11 +5,11 @@ ConfigurationWindow = { function ConfigurationWindow.init() ConfigurationWindow.controls = { - UI.create_action_item( + UI.create_desition_item( "Save", function() Config.save() end ), - UI.create_action_item( + UI.create_desition_item( "Restore Defaults", function() Config.restore_defaults() end ), @@ -46,7 +46,7 @@ function ConfigurationWindow.draw() Print.text(label_text, x_start, current_y, color) Print.text(value_text, value_x, current_y, color) end - elseif control.type == "action_item" then + elseif control.type == "desition_item" then local label_text = control.label if i == ConfigurationWindow.selected_control then color = Config.colors.item @@ -91,9 +91,9 @@ function ConfigurationWindow.update() local new_value = math.min(control.max, current_value + control.step) control.set(new_value) end - elseif control.type == "action_item" then + elseif control.type == "desition_item" then if Input.menu_confirm() then - control.action() + control.desition() end end end diff --git a/inc/window/window.game.lua b/inc/window/window.game.lua index 8b116b3..4bede42 100644 --- a/inc/window/window.game.lua +++ b/inc/window/window.game.lua @@ -2,6 +2,20 @@ function GameWindow.draw() local currentScreenData = Context.screens[Context.current_screen] UI.draw_top_bar(currentScreenData.name) + + if currentScreenData and currentScreenData.decisions and #currentScreenData.decisions > 0 then + local available_desitions = {} + for _, desition_id in ipairs(currentScreenData.decisions) do + local desition_obj = DesitionManager.get(desition_id) + if desition_obj and desition_obj.condition() then -- Check condition directly + table.insert(available_desitions, desition_obj) + end + end + -- If no available desitions, display nothing or a message + if #available_desitions > 0 then + UI.draw_desition_selector(available_desitions, Context.selected_desition_index) + end + end end function GameWindow.update() @@ -10,26 +24,54 @@ function GameWindow.update() MenuWindow.refresh_menu_items() return end - if Input.select() then - if Context.current_screen == #Context.screens then - Context.current_screen = 1 - else - Context.current_screen = Context.current_screen + 1 + + -- Handle screen changing using up/down + if Input.up() then + Context.current_screen = Context.current_screen - 1 + if Context.current_screen < 1 then + Context.current_screen = #Context.screens end + Context.selected_desition_index = 1 -- Reset selected decision on screen change + elseif Input.down() then + Context.current_screen = Context.current_screen + 1 + if Context.current_screen > #Context.screens then + Context.current_screen = 1 + end + Context.selected_desition_index = 1 -- Reset selected decision on screen change end - if Input.player_interact() then - -- Get the current screen's NPCs - local currentScreenData = Context.screens[Context.current_screen] - if currentScreenData and currentScreenData.npcs and #currentScreenData.npcs > 0 then - -- For now, interact with the first NPC on the screen - -- TODO: Add proximity detection to find nearest NPC - local npc = currentScreenData.npcs[1] - PopupWindow.show_menu_dialog(npc, { - {label = "Talk to", action = NPC.talk_to}, - {label = "Fight", action = NPC.fight}, - {label = "Go back", action = NPC.go_back} - }, WINDOW_POPUP) + local currentScreenData = Context.screens[Context.current_screen] + + if currentScreenData and currentScreenData.decisions and #currentScreenData.decisions > 0 then + local available_desitions = {} + for _, desition_id in ipairs(currentScreenData.decisions) do + local desition_obj = DesitionManager.get(desition_id) + if desition_obj and desition_obj.condition() then -- Check condition directly + table.insert(available_desitions, desition_obj) + end + end + + -- If no available desitions, we can't update or execute + if #available_desitions == 0 then return end + + -- Update selected decision using left/right inputs + local new_selected_desition_index = UI.update_desition_selector( + available_desitions, + Context.selected_desition_index + ) + + -- Only update Context if the selection actually changed to avoid unnecessary re-assignment + if new_selected_desition_index ~= Context.selected_desition_index then + Context.selected_desition_index = new_selected_desition_index + end + + -- Execute selected decision on Input.select() + if Input.select() then + local selected_desition = available_desitions[Context.selected_desition_index] + if selected_desition and selected_desition.handle then -- Call handle directly + Audio.sfx_select() -- Play sound for selection + selected_desition.handle() + end end end end @@ -37,4 +79,4 @@ end function GameWindow.set_state(new_state) Context.active_window = new_state -- Add any state-specific initialization/cleanup here later if needed -end \ No newline at end of file +end diff --git a/inc/window/window.menu.lua b/inc/window/window.menu.lua index a5318ce..3fe597a 100644 --- a/inc/window/window.menu.lua +++ b/inc/window/window.menu.lua @@ -8,9 +8,9 @@ function MenuWindow.update() if Input.menu_confirm() then local selected_item = Context.menu_items[Context.selected_menu_item] - if selected_item and selected_item.action then + if selected_item and selected_item.desition then Audio.sfx_select() - selected_item.action() + selected_item.desition() end end end @@ -51,15 +51,15 @@ function MenuWindow.refresh_menu_items() Context.menu_items = {} -- Start with an empty table if Context.game_in_progress then - table.insert(Context.menu_items, {label = "Resume Game", action = MenuWindow.resume_game}) - table.insert(Context.menu_items, {label = "Save Game", action = MenuWindow.save_game}) + table.insert(Context.menu_items, {label = "Resume Game", desition = MenuWindow.resume_game}) + table.insert(Context.menu_items, {label = "Save Game", desition = MenuWindow.save_game}) end - table.insert(Context.menu_items, {label = "New Game", action = MenuWindow.new_game}) - table.insert(Context.menu_items, {label = "Load Game", action = MenuWindow.load_game}) - table.insert(Context.menu_items, {label = "Configuration", action = MenuWindow.configuration}) - table.insert(Context.menu_items, {label = "Audio Test", action = MenuWindow.audio_test}) - table.insert(Context.menu_items, {label = "Exit", action = MenuWindow.exit}) + table.insert(Context.menu_items, {label = "New Game", desition = MenuWindow.new_game}) + table.insert(Context.menu_items, {label = "Load Game", desition = MenuWindow.load_game}) + table.insert(Context.menu_items, {label = "Configuration", desition = MenuWindow.configuration}) + table.insert(Context.menu_items, {label = "Audio Test", desition = MenuWindow.audio_test}) + table.insert(Context.menu_items, {label = "Exit", desition = MenuWindow.exit}) Context.selected_menu_item = 1 -- Reset selection after refreshing end diff --git a/inc/window/window.minigame.ddr.lua b/inc/window/window.minigame.ddr.lua index 5c24460..1b52cb7 100644 --- a/inc/window/window.minigame.ddr.lua +++ b/inc/window/window.minigame.ddr.lua @@ -304,7 +304,7 @@ function MinigameDDRWindow.draw() if mg.bar_fill > 66 then bar_color = Config.colors.item -- yellow elseif mg.bar_fill > 33 then - bar_color = Config.colors.npc + bar_color = Config.colors.bar end rect(mg.bar_x, mg.bar_y, fill_width, mg.bar_height, bar_color) @@ -326,7 +326,7 @@ function MinigameDDRWindow.draw() -- Draw falling arrows (blue) if mg.arrows then for _, arrow in ipairs(mg.arrows) do - draw_arrow(arrow.x, arrow.y, arrow.dir, Config.colors.npc) -- blue color + draw_arrow(arrow.x, arrow.y, arrow.dir, Config.colors.bar) -- blue color end end @@ -345,6 +345,6 @@ function MinigameDDRWindow.draw() Print.text_center("Pattern Len:" .. #mg.current_song.pattern .. " Index:" .. mg.pattern_index, Config.screen.width / 2, debug_y + 10, Config.colors.green) end else - Print.text_center("RANDOM MODE", Config.screen.width / 2, debug_y, Config.colors.npc) + Print.text_center("RANDOM MODE", Config.screen.width / 2, debug_y, Config.colors.bar) end end diff --git a/inc/window/window.minigame.mash.lua b/inc/window/window.minigame.mash.lua index ecd2765..abae6e2 100644 --- a/inc/window/window.minigame.mash.lua +++ b/inc/window/window.minigame.mash.lua @@ -83,7 +83,7 @@ function MinigameButtonMashWindow.draw() if mg.bar_fill > 66 then bar_color = Config.colors.item -- yellow elseif mg.bar_fill > 33 then - bar_color = Config.colors.npc -- medium color + bar_color = Config.colors.bar -- medium color end rect(mg.bar_x, mg.bar_y, fill_width, mg.bar_height, bar_color) diff --git a/inc/window/window.popup.lua b/inc/window/window.popup.lua index 501a45c..f8342f8 100644 --- a/inc/window/window.popup.lua +++ b/inc/window/window.popup.lua @@ -1,128 +1,44 @@ -function PopupWindow.set_dialog_node(node_key) - -- Special handling for minigame trigger - if node_key == "__MINIGAME_BUTTON_MASH__" then - MinigameButtonMashWindow.start(WINDOW_GAME) - return - end +-- Simplified PopupWindow module +local POPUP_X = 40 +local POPUP_Y = 40 +local POPUP_WIDTH = 160 +local POPUP_HEIGHT = 80 +local TEXT_MARGIN_X = POPUP_X + 10 +local TEXT_MARGIN_Y = POPUP_Y + 10 +local LINE_HEIGHT = 8 -- Assuming 8 pixels per line for default font - -- Special handling for rhythm minigame trigger - if node_key == "__MINIGAME_RHYTHM__" then - MinigameRhythmWindow.start(WINDOW_GAME) - return - end +function PopupWindow.show(content_strings) + Context.popup.show = true + Context.popup.content = content_strings or {} -- Ensure it's a table + GameWindow.set_state(WINDOW_POPUP) -- Set active window to popup +end - -- Special handling for DDR minigame trigger - -- Format: __MINIGAME_DDR__ or __MINIGAME_DDR:song_key__ - local song_key = node_key:match("^__MINIGAME_DDR:(.+)__$") - if song_key then - -- Extract song key from the node (format: __MINIGAME_DDR/test_song__) - trace('Playing song: ' .. song_key) - MinigameDDRWindow.start(WINDOW_GAME, song_key) - return - end - if node_key == "__MINIGAME_DDR__" then - MinigameDDRWindow.start(WINDOW_GAME, nil) - return - end - - local npc = Context.dialog.active_entity - local node = npc.dialog[node_key] - - if not node then - GameWindow.set_state(WINDOW_GAME) - return - end - - Context.dialog.current_node_key = node_key - Context.dialog.text = node.text - - local menu_items = {} - if node.options then - for _, option in ipairs(node.options) do - table.insert(menu_items, { - label = option.label, - action = function() - PopupWindow.set_dialog_node(option.next_node) - end - }) - end - end - - -- if no options, it's the end of this branch. - if #menu_items == 0 then - table.insert(menu_items, { - label = "Go back", - action = function() GameWindow.set_state(WINDOW_GAME) end - }) - end - - Context.dialog.menu_items = menu_items - Context.dialog.selected_menu_item = 1 - Context.dialog.showing_description = false - GameWindow.set_state(WINDOW_POPUP) +function PopupWindow.hide() + Context.popup.show = false + Context.popup.content = {} -- Clear content + GameWindow.set_state(WINDOW_GAME) -- Return to game window end function PopupWindow.update() - if Context.dialog.showing_description then - if Input.menu_confirm() or Input.menu_back() then - Context.dialog.showing_description = false - Context.dialog.text = "" -- Clear the description text - -- No need to change active_window, as it remains in WINDOW_POPUP - end - else - Context.dialog.selected_menu_item = UI.update_menu(Context.dialog.menu_items, Context.dialog.selected_menu_item) - - if Input.menu_confirm() then - local selected_item = Context.dialog.menu_items[Context.dialog.selected_menu_item] - if selected_item and selected_item.action then - selected_item.action() - end - end - - if Input.menu_back() then - GameWindow.set_state(WINDOW_GAME) + if Context.popup.show then + if Input.menu_confirm() or Input.menu_back() then -- Allow either A or B to close + PopupWindow.hide() end end end -function PopupWindow.show_menu_dialog(entity, menu_items, dialog_active_window) - Context.dialog.active_entity = entity - Context.dialog.text = "" -- Initial dialog text is empty, name is title - GameWindow.set_state(dialog_active_window or WINDOW_POPUP) - Context.dialog.showing_description = false - Context.dialog.menu_items = menu_items - Context.dialog.selected_menu_item = 1 -end - -function PopupWindow.show_description_dialog(entity, description_text) - Context.dialog.active_entity = entity - Context.dialog.text = description_text - GameWindow.set_state(WINDOW_POPUP) - Context.dialog.showing_description = true - -- No menu items needed for description dialog -end - function PopupWindow.draw() - rect(40, 40, 160, 80, Config.colors.black) - rectb(40, 40, 160, 80, Config.colors.green) + if Context.popup.show then + rect(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.black) + rectb(POPUP_X, POPUP_Y, POPUP_WIDTH, POPUP_HEIGHT, Config.colors.green) - -- Display the entity's name as the dialog title - if Context.dialog.active_entity and Context.dialog.active_entity.name then - Print.text(Context.dialog.active_entity.name, 120 - #Context.dialog.active_entity.name * 2, 45, Config.colors.green) - end + local current_y = TEXT_MARGIN_Y + for _, line in ipairs(Context.popup.content) do + Print.text(line, TEXT_MARGIN_X, current_y, Config.colors.light_grey) + current_y = current_y + LINE_HEIGHT + end - -- Display the dialog content (description for "look at", or initial name/dialog for others) - local wrapped_lines = UI.word_wrap(Context.dialog.text, 25) -- Max 25 chars per line - local current_y = 55 -- Starting Y position for the first line of content - for _, line in ipairs(wrapped_lines) do - Print.text(line, 50, current_y, Config.colors.light_grey) - current_y = current_y + 8 -- Move to the next line (8 pixels for default font height + padding) - end - - -- Adjust menu position based on the number of wrapped lines - if not Context.dialog.showing_description then - UI.draw_menu(Context.dialog.menu_items, Context.dialog.selected_menu_item, 50, current_y + 2) - else - Print.text("[A] Go Back", 50, current_y + 10, Config.colors.green) + -- Instruction to close + Print.text("[A] Close", TEXT_MARGIN_X, POPUP_Y + POPUP_HEIGHT - LINE_HEIGHT - 2, Config.colors.green) end end