Files
impostor/inc/init/init.context.lua
Zsolt Tasnadi 3fbce5aced
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
purge
2026-01-17 00:10:55 +01:00

422 lines
15 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local SAVE_GAME_BANK = 6
local SAVE_GAME_MAGIC_VALUE_ADDRESS = 0
local SAVE_GAME_MAGIC_VALUE = 0xCA
local SAVE_GAME_PLAYER_X_ADDRESS = 1
local SAVE_GAME_PLAYER_Y_ADDRESS = 2
local SAVE_GAME_PLAYER_VX_ADDRESS = 3
local SAVE_GAME_PLAYER_VY_ADDRESS = 4
local SAVE_GAME_selectS_ADDRESS = 5
local SAVE_GAME_CURRENT_SCREEN_ADDRESS = 6
local VX_VY_OFFSET = 128 -- Offset for negative velocities
-- Helper for deep copying tables
local function clone_table(t)
local copy = {}
for k, v in pairs(t) do
if type(v) == "table" then
copy[k] = clone_table(v)
else
copy[k] = v
end
end
return copy
end
-- This function returns a table containing only the initial *data* for Context
local function get_initial_data()
return {
active_window = WINDOW_SPLASH,
intro = {
y = Config.screen.height,
speed = 0.5,
text = "Norman Reds everyday life\nseems ordinary: work,\nmeetings, coffee, and\nendless notifications.\nBut beneath the surface\n— within him, or around\nhim — something is\nconstantly building, and\nit soon becomes clear\nthat there is more going\non than meets the eye."
},
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
},
player = {
sprite_id = Config.player.sprite_id
},
ground = {
x = 0,
y = Config.screen.height,
w = Config.screen.width,
h = 8
},
menu_items = {},
selected_menu_item = 1,
game_in_progress = false, -- New flag
screens = clone_table({
{
-- Screen 1
name = "Screen 1",
npcs = {
{
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 = "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 = "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 = {}
}
})
}
end
Context = {}
local function reset_context_to_initial_state()
local initial_data = get_initial_data()
-- Clear existing data properties from Context (but not methods)
for k in pairs(Context) do
if type(Context[k]) ~= "function" then -- Only clear data, leave functions
Context[k] = nil
end
end
-- Copy all initial data properties into Context
for k, v in pairs(initial_data) do
Context[k] = v
end
end
-- Initially populate Context with data
reset_context_to_initial_state()
-- Now define the methods for Context
function Context.new_game()
reset_context_to_initial_state()
Context.game_in_progress = true
MenuWindow.refresh_menu_items()
end
function Context.save_game()
if not Context.game_in_progress then return end
mset(SAVE_GAME_MAGIC_VALUE, SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK)
mset(Context.current_screen, SAVE_GAME_CURRENT_SCREEN_ADDRESS, SAVE_GAME_BANK)
end
function Context.load_game()
if mget(SAVE_GAME_MAGIC_VALUE_ADDRESS, SAVE_GAME_BANK) ~= SAVE_GAME_MAGIC_VALUE then
-- No saved game found, start a new one
Context.new_game()
return
end
reset_context_to_initial_state() -- Reset data, preserve methods
Context.current_screen = mget(SAVE_GAME_CURRENT_SCREEN_ADDRESS, SAVE_GAME_BANK)
Context.game_in_progress = true
MenuWindow.refresh_menu_items()
end