4
0
This commit is contained in:
Zsolt Tasnadi
2025-12-04 17:46:24 +01:00
parent 70f18d4f3b
commit 94a412d168

View File

@@ -25,16 +25,6 @@ local EMPTY = 0
local SOLID_WALL = 1 local SOLID_WALL = 1
local BREAKABLE_WALL = 2 local BREAKABLE_WALL = 2
-- Timing constants
local BOMB_TIMER = 90
local EXPLOSION_TIMER = 30
local SPREAD_DELAY = 6 -- ticks per cell spread
local SPLASH_DURATION = 90 -- 1.5 seconds at 60fps
local WIN_SCREEN_DURATION = 60
-- Movement
local MOVE_SPEED = 2
-- Directions (up, down, left, right) -- Directions (up, down, left, right)
local DIRECTIONS = { local DIRECTIONS = {
{0, -1}, {0, -1},
@@ -70,7 +60,7 @@ local GAME_STATE_MENU = 1
local GAME_STATE_PLAYING = 2 local GAME_STATE_PLAYING = 2
local GAME_STATE_HELP = 3 local GAME_STATE_HELP = 3
local GAME_STATE_CREDITS = 4 local GAME_STATE_CREDITS = 4
local GAME_STATE_CONFIG = 5 local GAME_STATE_SETTINGS = 5
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Game Configuration (easy to tweak game parameters) -- Game Configuration (easy to tweak game parameters)
@@ -100,8 +90,6 @@ local Config = {
-- Map settings -- Map settings
map = { map = {
width = 27,
height = 15,
breakable_wall_chance = 0.7, breakable_wall_chance = 0.7,
powerup_spawn_chance = 0.3, powerup_spawn_chance = 0.3,
generator = "classic", generator = "classic",
@@ -148,7 +136,7 @@ local Splash = {}
local Menu = {} local Menu = {}
local Help = {} local Help = {}
local Credits = {} local Credits = {}
local ConfigMenu = {} local Settings = {}
local WinScreen = {} local WinScreen = {}
local GameBoard = {} local GameBoard = {}
local Bomb = {} local Bomb = {}
@@ -162,9 +150,10 @@ local Game = {}
local State = { local State = {
game_state = GAME_STATE_SPLASH, game_state = GAME_STATE_SPLASH,
splash_timer = SPLASH_DURATION, splash_timer = 0, -- Will be set from Config on first frame
initialized = false, -- Config loaded flag
menu_selection = 1, menu_selection = 1,
config_selection = 1, settings_selection = 1,
two_player_mode = false, two_player_mode = false,
players = {}, players = {},
powerups = {}, powerups = {},
@@ -452,11 +441,8 @@ function MapGenerators.corridors(row, col)
return EMPTY return EMPTY
end end
-- Current map generator (change this to switch map styles)
Map.current_generator = "classic"
function Map.generate(generator_name) function Map.generate(generator_name)
generator_name = generator_name or Map.current_generator generator_name = generator_name or Config.map.generator
local generator = MapGenerators[generator_name] or MapGenerators.classic local generator = MapGenerators[generator_name] or MapGenerators.classic
for row = 1, MAP_HEIGHT do for row = 1, MAP_HEIGHT do
@@ -557,13 +543,12 @@ end
-- Splash module -- Splash module
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
local config_loaded = false
function Splash.update() function Splash.update()
-- Load saved config on first frame -- Initialize on first frame
if not config_loaded then if not State.initialized then
ConfigMenu.load() Settings.load()
config_loaded = true State.splash_timer = Config.timing.splash_duration
State.initialized = true
end end
cls(COLOR_BLACK) cls(COLOR_BLACK)
@@ -584,7 +569,7 @@ end
local MENU_ITEMS = { local MENU_ITEMS = {
{label = "1 Player Game", action = function() State.two_player_mode = false; State.game_state = GAME_STATE_PLAYING; Game.init() end}, {label = "1 Player Game", action = function() State.two_player_mode = false; State.game_state = GAME_STATE_PLAYING; Game.init() end},
{label = "2 Player Game", action = function() State.two_player_mode = true; State.game_state = GAME_STATE_PLAYING; Game.init() end}, {label = "2 Player Game", action = function() State.two_player_mode = true; State.game_state = GAME_STATE_PLAYING; Game.init() end},
{label = "Settings", action = function() State.game_state = GAME_STATE_CONFIG end}, {label = "Settings", action = function() State.game_state = GAME_STATE_SETTINGS end},
{label = "Help", action = function() State.game_state = GAME_STATE_HELP end}, {label = "Help", action = function() State.game_state = GAME_STATE_HELP end},
{label = "Credits", action = function() State.game_state = GAME_STATE_CREDITS end}, {label = "Credits", action = function() State.game_state = GAME_STATE_CREDITS end},
{label = "Exit", action = exit}, {label = "Exit", action = exit},
@@ -691,14 +676,14 @@ function Credits.update()
end end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- ConfigMenu module (persistent settings) -- Settings module (persistent settings menu)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- luacheck: globals pmem -- luacheck: globals pmem
-- Settings definition: each setting maps to a pmem slot -- Settings definition: each setting maps to a pmem slot
-- pmem stores integers, so we use multipliers for decimals -- pmem stores integers, so we use multipliers for decimals
local CONFIG_ITEMS = { local SETTINGS_ITEMS = {
{label = "Start Bombs", path = {"player", "start_bombs"}, min = 1, max = 5, step = 1, pmem_slot = 0}, {label = "Start Bombs", path = {"player", "start_bombs"}, min = 1, max = 5, step = 1, pmem_slot = 0},
{label = "Start Power", path = {"player", "start_power"}, min = 1, max = 5, step = 1, pmem_slot = 1}, {label = "Start Power", path = {"player", "start_power"}, min = 1, max = 5, step = 1, pmem_slot = 1},
{label = "Move Speed", path = {"player", "move_speed"}, min = 1, max = 4, step = 1, pmem_slot = 2}, {label = "Move Speed", path = {"player", "move_speed"}, min = 1, max = 4, step = 1, pmem_slot = 2},
@@ -756,37 +741,33 @@ local function get_display_value(item, value)
return tostring(value) return tostring(value)
end end
function ConfigMenu.load() function Settings.load()
-- Check if pmem has been initialized -- Check if pmem has been initialized
if pmem(PMEM_INIT_SLOT) ~= PMEM_INIT_VALUE then if pmem(PMEM_INIT_SLOT) ~= PMEM_INIT_VALUE then
-- First run - save defaults -- First run - save defaults
ConfigMenu.save() Settings.save()
pmem(PMEM_INIT_SLOT, PMEM_INIT_VALUE) pmem(PMEM_INIT_SLOT, PMEM_INIT_VALUE)
return return
end end
-- Load values from pmem -- Load values from pmem
for _, item in ipairs(CONFIG_ITEMS) do for _, item in ipairs(SETTINGS_ITEMS) do
local stored = pmem(item.pmem_slot) local stored = pmem(item.pmem_slot)
if stored >= item.min and stored <= item.max then if stored >= item.min and stored <= item.max then
set_config_value(item, stored) set_config_value(item, stored)
end end
end end
-- Apply map generator setting
if Config.map.generator then
Map.current_generator = Config.map.generator
end
end end
function ConfigMenu.save() function Settings.save()
for _, item in ipairs(CONFIG_ITEMS) do for _, item in ipairs(SETTINGS_ITEMS) do
local value = get_config_value(item) local value = get_config_value(item)
pmem(item.pmem_slot, value) pmem(item.pmem_slot, value)
end end
end end
function ConfigMenu.update() function Settings.update()
cls(COLOR_BLACK) cls(COLOR_BLACK)
UI.print_shadow("Settings", 85, 4, COLOR_BLUE, false, 2) UI.print_shadow("Settings", 85, 4, COLOR_BLUE, false, 2)
@@ -794,9 +775,9 @@ function ConfigMenu.update()
local start_y = 22 local start_y = 22
local item_height = 11 local item_height = 11
for i, item in ipairs(CONFIG_ITEMS) do for i, item in ipairs(SETTINGS_ITEMS) do
local y = start_y + (i - 1) * item_height local y = start_y + (i - 1) * item_height
local is_selected = (State.config_selection == i) local is_selected = (State.settings_selection == i)
local color = is_selected and COLOR_CYAN or COLOR_GRAY_LIGHT local color = is_selected and COLOR_CYAN or COLOR_GRAY_LIGHT
-- Cursor -- Cursor
@@ -815,8 +796,8 @@ function ConfigMenu.update()
end end
-- Back option -- Back option
local back_y = start_y + #CONFIG_ITEMS * item_height + 4 local back_y = start_y + #SETTINGS_ITEMS * item_height + 4
local back_selected = (State.config_selection == #CONFIG_ITEMS + 1) local back_selected = (State.settings_selection == #SETTINGS_ITEMS + 1)
if back_selected then if back_selected then
print(">", 71, back_y, COLOR_CYAN) print(">", 71, back_y, COLOR_CYAN)
end end
@@ -826,33 +807,29 @@ function ConfigMenu.update()
print("UP/DOWN:select LEFT/RIGHT:change", 28, 128, COLOR_GRAY_LIGHT) print("UP/DOWN:select LEFT/RIGHT:change", 28, 128, COLOR_GRAY_LIGHT)
-- Input handling -- Input handling
local max_selection = #CONFIG_ITEMS + 1 local max_selection = #SETTINGS_ITEMS + 1
if Input.up_pressed() then if Input.up_pressed() then
State.config_selection = State.config_selection - 1 State.settings_selection = State.settings_selection - 1
if State.config_selection < 1 then State.config_selection = max_selection end if State.settings_selection < 1 then State.settings_selection = max_selection end
elseif Input.down_pressed() then elseif Input.down_pressed() then
State.config_selection = State.config_selection + 1 State.settings_selection = State.settings_selection + 1
if State.config_selection > max_selection then State.config_selection = 1 end if State.settings_selection > max_selection then State.settings_selection = 1 end
elseif Input.left_pressed() and State.config_selection <= #CONFIG_ITEMS then elseif Input.left_pressed() and State.settings_selection <= #SETTINGS_ITEMS then
local item = CONFIG_ITEMS[State.config_selection] local item = SETTINGS_ITEMS[State.settings_selection]
local value = get_config_value(item) local value = get_config_value(item)
value = value - item.step value = value - item.step
if value < item.min then value = item.max end if value < item.min then value = item.max end
set_config_value(item, value) set_config_value(item, value)
elseif Input.right_pressed() and State.config_selection <= #CONFIG_ITEMS then elseif Input.right_pressed() and State.settings_selection <= #SETTINGS_ITEMS then
local item = CONFIG_ITEMS[State.config_selection] local item = SETTINGS_ITEMS[State.settings_selection]
local value = get_config_value(item) local value = get_config_value(item)
value = value + item.step value = value + item.step
if value > item.max then value = item.min end if value > item.max then value = item.min end
set_config_value(item, value) set_config_value(item, value)
elseif Input.action_pressed() or Input.back_pressed() then elseif Input.action_pressed() or Input.back_pressed() then
-- Apply map generator Settings.save()
if Config.map.generator then State.settings_selection = 1
Map.current_generator = Config.map.generator
end
ConfigMenu.save()
State.config_selection = 1
State.game_state = GAME_STATE_MENU State.game_state = GAME_STATE_MENU
end end
end end
@@ -911,7 +888,7 @@ function Bomb.draw_explosions()
if expl.spread <= 0 then if expl.spread <= 0 then
rect(drawX, drawY, TILE_SIZE, TILE_SIZE, COLOR_RED) rect(drawX, drawY, TILE_SIZE, TILE_SIZE, COLOR_RED)
else else
local progress = 1 - (expl.spread / (expl.dist * SPREAD_DELAY)) local progress = 1 - (expl.spread / (expl.dist * Config.bomb.spread_delay))
if progress > 0 then if progress > 0 then
local size = math.floor(TILE_SIZE * progress) local size = math.floor(TILE_SIZE * progress)
local off = math.floor((TILE_SIZE - size) / 2) local off = math.floor((TILE_SIZE - size) / 2)
@@ -936,7 +913,7 @@ function Bomb.place(player)
table.insert(State.bombs, { table.insert(State.bombs, {
x = bombX, x = bombX,
y = bombY, y = bombY,
timer = BOMB_TIMER, timer = Config.bomb.timer,
owner = player, owner = player,
power = player.bombPower power = player.bombPower
}) })
@@ -972,9 +949,9 @@ local function spread_explosion(bombX, bombY, gridX, gridY, power, is_horizontal
table.insert(State.explosions, { table.insert(State.explosions, {
x = explX, x = explX,
y = explY, y = explY,
timer = EXPLOSION_TIMER, timer = Config.bomb.explosion_duration,
dist = dist, dist = dist,
spread = dist * SPREAD_DELAY spread = dist * Config.bomb.spread_delay
}) })
if is_breakable then break end if is_breakable then break end
@@ -988,7 +965,7 @@ function Bomb.explode(bombX, bombY, power)
table.insert(State.explosions, { table.insert(State.explosions, {
x = bombX, x = bombX,
y = bombY, y = bombY,
timer = EXPLOSION_TIMER, timer = Config.bomb.explosion_duration,
dist = 0, dist = 0,
spread = 0 spread = 0
}) })
@@ -1338,16 +1315,16 @@ function Player.update_movement(player)
local targetY = (player.gridY - 1) * TILE_SIZE local targetY = (player.gridY - 1) * TILE_SIZE
if player.pixelX < targetX then if player.pixelX < targetX then
player.pixelX = math.min(player.pixelX + MOVE_SPEED, targetX) player.pixelX = math.min(player.pixelX + Config.player.move_speed, targetX)
player.moving = true player.moving = true
elseif player.pixelX > targetX then elseif player.pixelX > targetX then
player.pixelX = math.max(player.pixelX - MOVE_SPEED, targetX) player.pixelX = math.max(player.pixelX - Config.player.move_speed, targetX)
player.moving = true player.moving = true
elseif player.pixelY < targetY then elseif player.pixelY < targetY then
player.pixelY = math.min(player.pixelY + MOVE_SPEED, targetY) player.pixelY = math.min(player.pixelY + Config.player.move_speed, targetY)
player.moving = true player.moving = true
elseif player.pixelY > targetY then elseif player.pixelY > targetY then
player.pixelY = math.max(player.pixelY - MOVE_SPEED, targetY) player.pixelY = math.max(player.pixelY - Config.player.move_speed, targetY)
player.moving = true player.moving = true
else else
player.moving = false player.moving = false
@@ -1446,7 +1423,7 @@ end
function Game.set_winner(player_num) function Game.set_winner(player_num)
State.winner = player_num State.winner = player_num
State.win_timer = WIN_SCREEN_DURATION State.win_timer = Config.timing.win_screen_duration
State.score[player_num] = State.score[player_num] + 1 State.score[player_num] = State.score[player_num] + 1
end end
@@ -1521,7 +1498,7 @@ local STATE_HANDLERS = {
[GAME_STATE_MENU] = Menu.update, [GAME_STATE_MENU] = Menu.update,
[GAME_STATE_HELP] = Help.update, [GAME_STATE_HELP] = Help.update,
[GAME_STATE_CREDITS] = Credits.update, [GAME_STATE_CREDITS] = Credits.update,
[GAME_STATE_CONFIG] = ConfigMenu.update, [GAME_STATE_SETTINGS] = Settings.update,
[GAME_STATE_PLAYING] = update_playing, [GAME_STATE_PLAYING] = update_playing,
} }