v0.3 refact round

This commit is contained in:
2025-12-07 22:39:22 +01:00
parent fd4441d694
commit 0e0815d761

View File

@@ -3,286 +3,334 @@
-- desc: Life of a programmer in the Matrix -- desc: Life of a programmer in the Matrix
-- site: http://teletype.hu -- site: http://teletype.hu
-- license: MIT License -- license: MIT License
-- version: 0.1 -- version: 0.3
-- script: lua -- script: lua
-- Game constants --------------------------------------------------------------------------------
SCREEN_WIDTH = 240 -- Game Configuration
SCREEN_HEIGHT = 136 --------------------------------------------------------------------------------
local Config = {
-- Colors screen = {
COLOR_BLACK = 0 width = 240,
COLOR_LIGHT_GREY = 13 height = 136
COLOR_DARK_GREY = 14 },
COLOR_GREEN = 6 colors = {
COLOR_NPC = 8 black = 0,
light_grey = 13,
-- Game state dark_grey = 14,
STATE_SPLASH = 0 green = 6,
STATE_MENU = 1 npc = 8
STATE_GAME = 2 },
STATE_DIALOG = 3 player = {
w = 8,
-- Player constants h = 8,
PLAYER_WIDTH = 8 start_x = 120,
PLAYER_HEIGHT = 8 start_y = 128,
PLAYER_START_X = 120 },
PLAYER_START_Y = 128 physics = {
gravity = 0.5,
-- Ground constants jump_power = -5,
GROUND_X = 0 move_speed = 1.5,
GROUND_Y = 136 max_jumps = 2,
GROUND_W = 240 },
GROUND_H = 8 timing = {
splash_duration = 120 -- 2 seconds at 60fps
-- Physics constants }
GRAVITY = 0.5
JUMP_POWER = -5
MOVE_SPEED = 1.5
MAX_JUMPS = 2
-- Global variables (initialized)
local gameState = STATE_SPLASH
local currentScreen = 1
local dialog_text = ""
local splash_timer = 120 -- 2 seconds at 60fps
-- Player properties
local player = {
x = PLAYER_START_X,
y = PLAYER_START_Y,
w = PLAYER_WIDTH,
h = PLAYER_HEIGHT,
vx = 0,
vy = 0,
jumps = 0
} }
-- Ground properties --------------------------------------------------------------------------------
local ground = { -- Game States
x = GROUND_X, --------------------------------------------------------------------------------
y = GROUND_Y, local GAME_STATE_SPLASH = 0
w = GROUND_W, local GAME_STATE_MENU = 1
h = GROUND_H local GAME_STATE_GAME = 2
local GAME_STATE_DIALOG = 3
--------------------------------------------------------------------------------
-- Modules
--------------------------------------------------------------------------------
local Splash = {}
local Menu = {}
local Game = {}
local UI = {}
local Input = {}
--------------------------------------------------------------------------------
-- Game State
--------------------------------------------------------------------------------
local State = {
game_state = GAME_STATE_SPLASH,
current_screen = 1,
dialog_text = "",
splash_timer = Config.timing.splash_duration,
player = {
x = Config.player.start_x,
y = Config.player.start_y,
w = Config.player.w,
h = Config.player.h,
vx = 0,
vy = 0,
jumps = 0
},
ground = {
x = 0,
y = Config.screen.height,
w = Config.screen.width,
h = 8
},
menu_items = {"Play", "Exit"},
selected_menu_item = 1,
-- Screen data
screens = {
{ -- Screen 1
name = "Screen 1",
platforms = {
{x = 80, y = 110, w = 40, h = 8},
{x = 160, y = 90, w = 40, h = 8}
},
npcs = {
{x = 180, y = 82, name = "Trinity"},
{x = 90, y = 102, name = "Oracle"}
}
},
{ -- Screen 2
name = "Screen 2",
platforms = {
{x = 30, y = 100, w = 50, h = 8},
{x = 100, y = 80, w = 50, h = 8},
{x = 170, y = 60, w = 50, h = 8}
},
npcs = {
{x = 120, y = 72, name = "Morpheus"},
{x = 40, y = 92, name = "Tank"}
}
},
{ -- Screen 3
name = "Screen 3",
platforms = {
{x = 50, y = 110, w = 30, h = 8},
{x = 100, y = 90, w = 30, h = 8},
{x = 150, y = 70, w = 30, h = 8},
{x = 200, y = 50, w = 30, h = 8}
},
npcs = {
{x = 210, y = 42, name = "Agent Smith"},
{x = 160, y = 62, name = "Cypher"}
}
}
}
} }
-- Menu properties --------------------------------------------------------------------------------
local menuItems = {"Play", "Exit"} -- Input Module
local selectedMenuItem = 1 --------------------------------------------------------------------------------
function Input.up() return btnp(0) end
function Input.down() return btnp(1) end
function Input.left() return btn(2) end
function Input.right() return btn(3) end
function Input.action() return btnp(4) end
function Input.back() return btnp(5) end
local function draw_splash() --------------------------------------------------------------------------------
cls(COLOR_BLACK) -- UI Module
print("Mr. Anderson's", 78, 60, COLOR_LIGHT_GREY) --------------------------------------------------------------------------------
print("Addventure", 90, 70, COLOR_LIGHT_GREY) function UI.draw_top_bar(title)
rect(0, 0, Config.screen.width, 10, Config.colors.black)
print(title, 3, 2, Config.colors.light_grey)
end end
local function update_splash() function UI.draw_dialog()
splash_timer = splash_timer - 1 rect(40, 50, 160, 40, Config.colors.black)
if splash_timer <= 0 then rectb(40, 50, 160, 40, Config.colors.dark_grey)
gameState = STATE_MENU print(State.dialog_text, 120 - #State.dialog_text * 2, 68, Config.colors.light_grey)
end
--------------------------------------------------------------------------------
-- Splash Module
--------------------------------------------------------------------------------
function Splash.draw()
cls(Config.colors.black)
print("Mr. Anderson's", 78, 60, Config.colors.light_grey)
print("Addventure", 90, 70, Config.colors.light_grey)
end
function Splash.update()
State.splash_timer = State.splash_timer - 1
if State.splash_timer <= 0 then
State.game_state = GAME_STATE_MENU
end end
end end
local function draw_top_bar(title) --------------------------------------------------------------------------------
rect(0, 0, SCREEN_WIDTH, 10, COLOR_BLACK) -- Menu Module
print(title, 3, 2, COLOR_LIGHT_GREY) --------------------------------------------------------------------------------
end function Menu.draw()
cls(Config.colors.light_grey)
local function draw_menu() UI.draw_top_bar("Main Menu")
cls(COLOR_LIGHT_GREY) for i, item in ipairs(State.menu_items) do
draw_top_bar("Main Menu") local color = Config.colors.dark_grey
for i, item in ipairs(menuItems) do if i == State.selected_menu_item then
local color = COLOR_DARK_GREY color = Config.colors.green
if i == selectedMenuItem then
color = COLOR_GREEN
end end
print(item, 108, 70 + (i-1)*10, color) print(item, 108, 70 + (i-1)*10, color)
end end
end end
local function update_menu() function Menu.update()
if btnp(0) then -- Up if Input.up() then
selectedMenuItem = selectedMenuItem - 1 State.selected_menu_item = State.selected_menu_item - 1
if selectedMenuItem < 1 then if State.selected_menu_item < 1 then
selectedMenuItem = #menuItems State.selected_menu_item = #State.menu_items
end end
elseif btnp(1) then -- Down elseif Input.down() then
selectedMenuItem = selectedMenuItem + 1 State.selected_menu_item = State.selected_menu_item + 1
if selectedMenuItem > #menuItems then if State.selected_menu_item > #State.menu_items then
selectedMenuItem = 1 State.selected_menu_item = 1
end end
end end
if btnp(4) or btnp(5) then -- A or B button if Input.action() or Input.back() then
if selectedMenuItem == 1 then -- Play if State.selected_menu_item == 1 then -- Play
-- Reset player state and screen for a new game -- Reset player state and screen for a new game
player.x = PLAYER_START_X State.player.x = Config.player.start_x
player.y = PLAYER_START_Y State.player.y = Config.player.start_y
player.vx = 0 State.player.vx = 0
player.vy = 0 State.player.vy = 0
player.jumps = 0 State.player.jumps = 0
currentScreen = 1 State.current_screen = 1
gameState = STATE_GAME State.game_state = GAME_STATE_GAME
elseif selectedMenuItem == 2 then -- Exit elseif State.selected_menu_item == 2 then -- Exit
exit() exit()
end end
end end
end end
-- Screen data --------------------------------------------------------------------------------
local screens = { -- Game Module
{ -- Screen 1 --------------------------------------------------------------------------------
name = "Screen 1", function Game.draw()
platforms = { local currentScreenData = State.screens[State.current_screen]
{x = 80, y = 110, w = 40, h = 8},
{x = 160, y = 90, w = 40, h = 8} cls(Config.colors.light_grey)
}, UI.draw_top_bar(currentScreenData.name)
npcs = {
{x = 180, y = 82, name = "Trinity"},
{x = 90, y = 102, name = "Oracle"}
}
},
{ -- Screen 2
name = "Screen 2",
platforms = {
{x = 30, y = 100, w = 50, h = 8},
{x = 100, y = 80, w = 50, h = 8},
{x = 170, y = 60, w = 50, h = 8}
},
npcs = {
{x = 120, y = 72, name = "Morpheus"},
{x = 40, y = 92, name = "Tank"}
}
},
{ -- Screen 3
name = "Screen 3",
platforms = {
{x = 50, y = 110, w = 30, h = 8},
{x = 100, y = 90, w = 30, h = 8},
{x = 150, y = 70, w = 30, h = 8},
{x = 200, y = 50, w = 30, h = 8}
},
npcs = {
{x = 210, y = 42, name = "Agent Smith"},
{x = 160, y = 62, name = "Cypher"}
}
}
}
local function game_update() -- Draw platforms
-- Handle input for _, p in ipairs(currentScreenData.platforms) do
if btn(2) then rect(p.x, p.y, p.w, p.h, Config.colors.dark_grey)
player.vx = -MOVE_SPEED end
elseif btn(3) then
player.vx = MOVE_SPEED -- Draw NPCs
else for _, npc in ipairs(currentScreenData.npcs) do
player.vx = 0 rect(npc.x, npc.y, Config.player.w, Config.player.h, Config.colors.npc)
end end
if btnp(4) and player.jumps < MAX_JUMPS then -- Draw ground
player.vy = JUMP_POWER rect(State.ground.x, State.ground.y, State.ground.w, State.ground.h, Config.colors.dark_grey)
player.jumps = player.jumps + 1
-- Draw player
rect(State.player.x, State.player.y, State.player.w, State.player.h, Config.colors.green)
end
function Game.update()
-- Handle input
if Input.left() then
State.player.vx = -Config.physics.move_speed
elseif Input.right() then
State.player.vx = Config.physics.move_speed
else
State.player.vx = 0
end
if Input.action() and State.player.jumps < Config.physics.max_jumps then
State.player.vy = Config.physics.jump_power
State.player.jumps = State.player.jumps + 1
end end
-- Update player position -- Update player position
player.x = player.x + player.vx State.player.x = State.player.x + State.player.vx
player.y = player.y + player.vy State.player.y = State.player.y + State.player.vy
-- Screen transition -- Screen transition
if player.x > SCREEN_WIDTH - player.w then if State.player.x > Config.screen.width - State.player.w then
if currentScreen < #screens then if State.current_screen < #State.screens then
currentScreen = currentScreen + 1 State.current_screen = State.current_screen + 1
player.x = 0 State.player.x = 0
else else
player.x = SCREEN_WIDTH - player.w State.player.x = Config.screen.width - State.player.w
end end
elseif player.x < 0 then elseif State.player.x < 0 then
if currentScreen > 1 then if State.current_screen > 1 then
currentScreen = currentScreen - 1 State.current_screen = State.current_screen - 1
player.x = SCREEN_WIDTH - player.w State.player.x = Config.screen.width - State.player.w
else else
player.x = 0 State.player.x = 0
end end
end end
-- Apply gravity -- Apply gravity
player.vy = player.vy + GRAVITY State.player.vy = State.player.vy + Config.physics.gravity
local currentScreenData = screens[currentScreen] local currentScreenData = State.screens[State.current_screen]
local currentPlatforms = currentScreenData.platforms
-- Collision detection with platforms -- Collision detection with platforms
for i, p in ipairs(currentPlatforms) do for _, p in ipairs(currentScreenData.platforms) do
if player.vy > 0 and player.y + player.h >= p.y and player.y + player.h <= p.y + p.h and player.x + player.w > p.x and player.x < p.x + p.w then if State.player.vy > 0 and State.player.y + State.player.h >= p.y and State.player.y + State.player.h <= p.y + p.h and State.player.x + State.player.w > p.x and State.player.x < p.x + p.w then
player.y = p.y - player.h State.player.y = p.y - State.player.h
player.vy = 0 State.player.vy = 0
player.jumps = 0 State.player.jumps = 0
end end
end end
-- Collision detection with ground -- Collision detection with ground
if player.y + player.h > ground.y and player.x + player.w > ground.x and player.x < ground.x + ground.w then if State.player.y + State.player.h > State.ground.y then
player.y = ground.y - player.h State.player.y = State.ground.y - State.player.h
player.vy = 0 State.player.vy = 0
player.jumps = 0 State.player.jumps = 0
end end
-- NPC interaction -- NPC interaction
if btnp(4) then if Input.action() then
for i, npc in ipairs(currentScreenData.npcs) do for _, npc in ipairs(currentScreenData.npcs) do
if math.abs(player.x - npc.x) < 12 and math.abs(player.y - npc.y) < 12 then if math.abs(State.player.x - npc.x) < 12 and math.abs(State.player.y - npc.y) < 12 then
dialog_text = npc.name State.dialog_text = npc.name
gameState = STATE_DIALOG State.game_state = GAME_STATE_DIALOG
end end
end end
end end
-- Clear screen
cls(COLOR_LIGHT_GREY)
draw_top_bar(currentScreenData.name)
-- Draw platforms
for i, p in ipairs(currentPlatforms) do
rect(p.x, p.y, p.w, p.h, COLOR_DARK_GREY)
end
-- Draw NPCs
for i, npc in ipairs(currentScreenData.npcs) do
rect(npc.x, npc.y, PLAYER_WIDTH, PLAYER_HEIGHT, COLOR_NPC)
end
-- Draw ground
rect(ground.x, ground.y, ground.w, ground.h, COLOR_DARK_GREY)
-- Draw player
rect(player.x, player.y, player.w, player.h, COLOR_GREEN)
end end
local function draw_dialog() function Game.update_dialog()
rect(40, 50, 160, 40, COLOR_BLACK) if Input.action() or Input.back() then
rectb(40, 50, 160, 40, COLOR_DARK_GREY) State.game_state = GAME_STATE_GAME
print(dialog_text, 120 - #dialog_text * 2, 68, COLOR_DARK_GREY)
end
local function update_dialog()
if btnp(4) or btnp(5) then
gameState = STATE_GAME
end end
end end
--------------------------------------------------------------------------------
-- Main Game Loop
--------------------------------------------------------------------------------
local STATE_HANDLERS = {
[GAME_STATE_SPLASH] = function()
Splash.update()
Splash.draw()
end,
[GAME_STATE_MENU] = function()
Menu.update()
Menu.draw()
end,
[GAME_STATE_GAME] = function()
Game.update()
Game.draw()
end,
[GAME_STATE_DIALOG] = function()
Game.draw() -- Draw game behind dialog
UI.draw_dialog()
Game.update_dialog()
end,
}
function TIC() function TIC()
if gameState == STATE_SPLASH then local handler = STATE_HANDLERS[State.game_state]
update_splash() if handler then
draw_splash() handler()
elseif gameState == STATE_MENU then
update_menu()
draw_menu()
elseif gameState == STATE_GAME then
game_update()
elseif gameState == STATE_DIALOG then
game_update() -- keep drawing the game state in the background
draw_dialog()
update_dialog()
end end
end end
@@ -314,5 +362,4 @@ end
-- <PALETTE> -- <PALETTE>
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57 -- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
-- </PALETTE> -- </PALETTE>