refact
This commit is contained in:
178
bomberman.lua
178
bomberman.lua
@@ -31,9 +31,6 @@ local EXPLOSION_TIMER = 30
|
|||||||
local SPREAD_DELAY = 6 -- ticks per cell spread
|
local SPREAD_DELAY = 6 -- ticks per cell spread
|
||||||
local SPLASH_DURATION = 90 -- 1.5 seconds at 60fps
|
local SPLASH_DURATION = 90 -- 1.5 seconds at 60fps
|
||||||
local WIN_SCREEN_DURATION = 60
|
local WIN_SCREEN_DURATION = 60
|
||||||
local AI_MOVE_DELAY = 20
|
|
||||||
local AI_BOMB_COOLDOWN = 90
|
|
||||||
local BOMB_DANGER_THRESHOLD = 30 -- AI considers bomb dangerous below this timer
|
|
||||||
|
|
||||||
-- Movement
|
-- Movement
|
||||||
local MOVE_SPEED = 2
|
local MOVE_SPEED = 2
|
||||||
@@ -74,8 +71,67 @@ 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
|
||||||
|
|
||||||
-- Powerup spawn chance
|
--------------------------------------------------------------------------------
|
||||||
local POWERUP_SPAWN_CHANCE = 0.3
|
-- Game Configuration (easy to tweak game parameters)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Config = {
|
||||||
|
-- Player settings
|
||||||
|
player = {
|
||||||
|
move_speed = 2,
|
||||||
|
start_bombs = 1,
|
||||||
|
start_power = 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Bomb settings
|
||||||
|
bomb = {
|
||||||
|
timer = 90,
|
||||||
|
explosion_duration = 30,
|
||||||
|
spread_delay = 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- AI settings
|
||||||
|
ai = {
|
||||||
|
move_delay = 20,
|
||||||
|
bomb_cooldown = 90,
|
||||||
|
danger_threshold = 30,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Map settings
|
||||||
|
map = {
|
||||||
|
width = 27,
|
||||||
|
height = 15,
|
||||||
|
breakable_wall_chance = 0.7,
|
||||||
|
powerup_spawn_chance = 0.3,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Timing
|
||||||
|
timing = {
|
||||||
|
splash_duration = 90,
|
||||||
|
win_screen_duration = 60,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Sound System (centralized audio management)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Sound = {
|
||||||
|
effects = {
|
||||||
|
explosion = {id = 0, note = nil, duration = 30},
|
||||||
|
pickup = {id = 1, note = nil, duration = 8},
|
||||||
|
-- Add new sounds here:
|
||||||
|
-- menu_select = {id = 2, note = nil, duration = 10},
|
||||||
|
-- player_death = {id = 3, note = nil, duration = 20},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sound.play(effect_name)
|
||||||
|
local effect = Sound.effects[effect_name]
|
||||||
|
if effect then
|
||||||
|
sfx(effect.id, effect.note, effect.duration)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Modules
|
-- Modules
|
||||||
@@ -178,7 +234,7 @@ function Powerup.init()
|
|||||||
State.powerups = {}
|
State.powerups = {}
|
||||||
for row = 1, MAP_HEIGHT do
|
for row = 1, MAP_HEIGHT do
|
||||||
for col = 1, MAP_WIDTH do
|
for col = 1, MAP_WIDTH do
|
||||||
if State.map[row][col] == BREAKABLE_WALL and math.random() < POWERUP_SPAWN_CHANCE then
|
if State.map[row][col] == BREAKABLE_WALL and math.random() < Config.map.powerup_spawn_chance then
|
||||||
table.insert(State.powerups, {
|
table.insert(State.powerups, {
|
||||||
gridX = col,
|
gridX = col,
|
||||||
gridY = row,
|
gridY = row,
|
||||||
@@ -211,7 +267,7 @@ function Powerup.check_pickup()
|
|||||||
local config = Powerup.get_config(pw.type)
|
local config = Powerup.get_config(pw.type)
|
||||||
config.apply(player)
|
config.apply(player)
|
||||||
table.remove(State.powerups, i)
|
table.remove(State.powerups, i)
|
||||||
sfx(1, nil, 8)
|
Sound.play("pickup")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -322,28 +378,98 @@ function Map.is_spawn_area(row, col)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function Map.generate()
|
--------------------------------------------------------------------------------
|
||||||
|
-- Map Generators (extensible map generation system)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local MapGenerators = {}
|
||||||
|
|
||||||
|
-- Classic Bomberman grid pattern
|
||||||
|
function MapGenerators.classic(row, col)
|
||||||
|
if row % 2 == 1 and col % 2 == 1 and row > 1 and col > 1 then
|
||||||
|
return SOLID_WALL
|
||||||
|
elseif math.random() < Config.map.breakable_wall_chance then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Open arena with fewer pillars
|
||||||
|
function MapGenerators.arena(row, col)
|
||||||
|
-- Only pillars at every 4th position
|
||||||
|
if row % 4 == 1 and col % 4 == 1 and row > 1 and col > 1 then
|
||||||
|
return SOLID_WALL
|
||||||
|
elseif math.random() < Config.map.breakable_wall_chance * 0.5 then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dense maze with more walls
|
||||||
|
function MapGenerators.maze(row, col)
|
||||||
|
if row % 2 == 1 and col % 2 == 1 and row > 1 and col > 1 then
|
||||||
|
return SOLID_WALL
|
||||||
|
elseif math.random() < 0.85 then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Corridors pattern
|
||||||
|
function MapGenerators.corridors(row, col)
|
||||||
|
-- Horizontal corridors at rows 4, 8, 12
|
||||||
|
if (row == 4 or row == 8 or row == 12) and col > 1 and col < MAP_WIDTH then
|
||||||
|
if math.random() < 0.3 then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
-- Vertical corridors at cols 7, 14, 21
|
||||||
|
if (col == 7 or col == 14 or col == 21) and row > 1 and row < MAP_HEIGHT then
|
||||||
|
if math.random() < 0.3 then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
-- Rest is classic pattern
|
||||||
|
if row % 2 == 1 and col % 2 == 1 and row > 1 and col > 1 then
|
||||||
|
return SOLID_WALL
|
||||||
|
elseif math.random() < Config.map.breakable_wall_chance then
|
||||||
|
return BREAKABLE_WALL
|
||||||
|
end
|
||||||
|
return EMPTY
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Current map generator (change this to switch map styles)
|
||||||
|
Map.current_generator = "classic"
|
||||||
|
|
||||||
|
function Map.generate(generator_name)
|
||||||
|
generator_name = generator_name or Map.current_generator
|
||||||
|
local generator = MapGenerators[generator_name] or MapGenerators.classic
|
||||||
|
|
||||||
for row = 1, MAP_HEIGHT do
|
for row = 1, MAP_HEIGHT do
|
||||||
for col = 1, MAP_WIDTH do
|
for col = 1, MAP_WIDTH do
|
||||||
-- Border walls
|
-- Border walls (always)
|
||||||
if row == 1 or row == MAP_HEIGHT or col == 1 or col == MAP_WIDTH then
|
if row == 1 or row == MAP_HEIGHT or col == 1 or col == MAP_WIDTH then
|
||||||
State.map[row][col] = SOLID_WALL
|
State.map[row][col] = SOLID_WALL
|
||||||
-- Spawn areas MUST be empty
|
-- Spawn areas MUST be empty (always)
|
||||||
elseif Map.is_spawn_area(row, col) then
|
elseif Map.is_spawn_area(row, col) then
|
||||||
State.map[row][col] = EMPTY
|
State.map[row][col] = EMPTY
|
||||||
-- Grid pattern solid walls (odd row AND odd col, but not border)
|
-- Use selected generator for the rest
|
||||||
elseif row % 2 == 1 and col % 2 == 1 and row > 1 and col > 1 then
|
|
||||||
State.map[row][col] = SOLID_WALL
|
|
||||||
-- Random: breakable wall or empty
|
|
||||||
else
|
else
|
||||||
if math.random() < 0.7 then
|
State.map[row][col] = generator(row, col)
|
||||||
State.map[row][col] = BREAKABLE_WALL
|
|
||||||
else
|
|
||||||
State.map[row][col] = EMPTY
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper to get available generators
|
||||||
|
function Map.get_generators()
|
||||||
|
local names = {}
|
||||||
|
for name, _ in pairs(MapGenerators) do
|
||||||
|
table.insert(names, name)
|
||||||
end
|
end
|
||||||
|
return names
|
||||||
end
|
end
|
||||||
|
|
||||||
function Map.draw_shadows()
|
function Map.draw_shadows()
|
||||||
@@ -670,7 +796,7 @@ end
|
|||||||
|
|
||||||
function Bomb.explode(bombX, bombY, power)
|
function Bomb.explode(bombX, bombY, power)
|
||||||
power = power or 1
|
power = power or 1
|
||||||
sfx(0, nil, 30)
|
Sound.play("explosion")
|
||||||
table.insert(State.explosions, {
|
table.insert(State.explosions, {
|
||||||
x = bombX,
|
x = bombX,
|
||||||
y = bombY,
|
y = bombY,
|
||||||
@@ -752,7 +878,7 @@ function AI.is_dangerous(gridX, gridY)
|
|||||||
local power = bomb.power or 1
|
local power = bomb.power or 1
|
||||||
|
|
||||||
-- Only urgent if bomb is about to explode
|
-- Only urgent if bomb is about to explode
|
||||||
if bomb.timer < BOMB_DANGER_THRESHOLD then
|
if bomb.timer < Config.ai.danger_threshold then
|
||||||
if gridX == bombGridX and gridY == bombGridY then
|
if gridX == bombGridX and gridY == bombGridY then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -891,7 +1017,7 @@ function AI.move_and_bomb(player, target)
|
|||||||
player.lastGridX = player.gridX
|
player.lastGridX = player.gridX
|
||||||
player.lastGridY = player.gridY
|
player.lastGridY = player.gridY
|
||||||
Bomb.place(player)
|
Bomb.place(player)
|
||||||
player.bombCooldown = AI_BOMB_COOLDOWN
|
player.bombCooldown = Config.ai.bomb_cooldown
|
||||||
AI.escape_from_bomb(player)
|
AI.escape_from_bomb(player)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -983,7 +1109,7 @@ function AI.update(player, target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
player.moveTimer = player.moveTimer + 1
|
player.moveTimer = player.moveTimer + 1
|
||||||
if player.moveTimer < AI_MOVE_DELAY then return end
|
if player.moveTimer < Config.ai.move_delay then return end
|
||||||
|
|
||||||
player.moveTimer = 0
|
player.moveTimer = 0
|
||||||
AI.move_and_bomb(player, target)
|
AI.move_and_bomb(player, target)
|
||||||
@@ -1007,9 +1133,9 @@ function Player.create(gridX, gridY, color, is_ai)
|
|||||||
pixelX = (gridX - 1) * TILE_SIZE,
|
pixelX = (gridX - 1) * TILE_SIZE,
|
||||||
pixelY = (gridY - 1) * TILE_SIZE,
|
pixelY = (gridY - 1) * TILE_SIZE,
|
||||||
moving = false,
|
moving = false,
|
||||||
maxBombs = 1,
|
maxBombs = Config.player.start_bombs,
|
||||||
activeBombs = 0,
|
activeBombs = 0,
|
||||||
bombPower = 1,
|
bombPower = Config.player.start_power,
|
||||||
color = color,
|
color = color,
|
||||||
is_ai = is_ai,
|
is_ai = is_ai,
|
||||||
moveTimer = 0,
|
moveTimer = 0,
|
||||||
@@ -1093,9 +1219,9 @@ function Player.reset(player)
|
|||||||
player.pixelX = (player.spawnX - 1) * TILE_SIZE
|
player.pixelX = (player.spawnX - 1) * TILE_SIZE
|
||||||
player.pixelY = (player.spawnY - 1) * TILE_SIZE
|
player.pixelY = (player.spawnY - 1) * TILE_SIZE
|
||||||
player.moving = false
|
player.moving = false
|
||||||
player.maxBombs = 1
|
player.maxBombs = Config.player.start_bombs
|
||||||
player.activeBombs = 0
|
player.activeBombs = 0
|
||||||
player.bombPower = 1
|
player.bombPower = Config.player.start_power
|
||||||
player.bombCooldown = 0
|
player.bombCooldown = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user