refact
This commit is contained in:
115
bomberman.lua
115
bomberman.lua
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user