refact
This commit is contained in:
181
bomberman.lua
181
bomberman.lua
@@ -37,6 +37,14 @@ local AI_BOMB_COOLDOWN = 90
|
|||||||
-- Movement
|
-- Movement
|
||||||
local MOVE_SPEED = 2
|
local MOVE_SPEED = 2
|
||||||
|
|
||||||
|
-- Directions (up, down, left, right)
|
||||||
|
local DIRECTIONS = {
|
||||||
|
{0, -1},
|
||||||
|
{0, 1},
|
||||||
|
{-1, 0},
|
||||||
|
{1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
-- Sprite indices (SPRITES section loads at 256+)
|
-- Sprite indices (SPRITES section loads at 256+)
|
||||||
local PLAYER_BLUE = 256
|
local PLAYER_BLUE = 256
|
||||||
local PLAYER_RED = 257
|
local PLAYER_RED = 257
|
||||||
@@ -44,17 +52,18 @@ local BOMB_SPRITE = 258
|
|||||||
local BREAKABLE_WALL_SPRITE = 259
|
local BREAKABLE_WALL_SPRITE = 259
|
||||||
local SOLID_WALL_SPRITE = 260
|
local SOLID_WALL_SPRITE = 260
|
||||||
|
|
||||||
-- Colors
|
-- Colors (Sweetie 16 palette)
|
||||||
local COLOR_BLACK = 0
|
local COLOR_BLACK = 0
|
||||||
local COLOR_SHADOW = 1
|
local COLOR_SHADOW = 1
|
||||||
local COLOR_RED = 2
|
local COLOR_RED = 2
|
||||||
local COLOR_ORANGE = 3
|
local COLOR_ORANGE = 3
|
||||||
local COLOR_YELLOW = 4
|
local COLOR_YELLOW = 4
|
||||||
local COLOR_GREEN = 6
|
local COLOR_GREEN = 6
|
||||||
local COLOR_GREEN_LIGHT = 11
|
local COLOR_BLUE = 9
|
||||||
local COLOR_BLUE = 12
|
local COLOR_BLUE_LIGHT = 10
|
||||||
|
local COLOR_CYAN = 11
|
||||||
|
local COLOR_LIGHT = 12 -- f4f4f4 - lightest color
|
||||||
local COLOR_GRAY_LIGHT = 13
|
local COLOR_GRAY_LIGHT = 13
|
||||||
local COLOR_WHITE = 15
|
|
||||||
|
|
||||||
-- Game states
|
-- Game states
|
||||||
local GAME_STATE_SPLASH = 0
|
local GAME_STATE_SPLASH = 0
|
||||||
@@ -286,8 +295,8 @@ function Map.can_move_to(gridX, gridY, player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Map.is_spawn_area(row, col)
|
function Map.is_spawn_area(row, col)
|
||||||
local lastCol = MAP_WIDTH - 1 -- 28
|
local lastCol = MAP_WIDTH - 1 -- 26 (for MAP_WIDTH=27)
|
||||||
local lastRow = MAP_HEIGHT - 1 -- 16
|
local lastRow = MAP_HEIGHT - 1 -- 14 (for MAP_HEIGHT=15)
|
||||||
-- Top-left spawn (2,2) and adjacent
|
-- Top-left spawn (2,2) and adjacent
|
||||||
if (row == 2 and col == 2) or (row == 2 and col == 3) or (row == 3 and col == 2) then
|
if (row == 2 and col == 2) or (row == 2 and col == 3) or (row == 3 and col == 2) then
|
||||||
return true
|
return true
|
||||||
@@ -375,8 +384,8 @@ function TopBar.draw()
|
|||||||
|
|
||||||
-- Player 1 (left side) - blue
|
-- Player 1 (left side) - blue
|
||||||
if p1 then
|
if p1 then
|
||||||
print("P1", 2, 2, COLOR_BLUE)
|
print("P1", 2, 2, COLOR_BLUE_LIGHT)
|
||||||
print("W:"..State.score[1], 16, 2, COLOR_BLUE)
|
print("W:"..State.score[1], 16, 2, COLOR_BLUE_LIGHT)
|
||||||
print("B:"..p1.maxBombs, 40, 2, COLOR_YELLOW)
|
print("B:"..p1.maxBombs, 40, 2, COLOR_YELLOW)
|
||||||
print("P:"..p1.bombPower, 64, 2, COLOR_ORANGE)
|
print("P:"..p1.bombPower, 64, 2, COLOR_ORANGE)
|
||||||
end
|
end
|
||||||
@@ -427,14 +436,14 @@ function Menu.update()
|
|||||||
UI.print_shadow("Clone", 100, 40, COLOR_BLUE, false, 2)
|
UI.print_shadow("Clone", 100, 40, COLOR_BLUE, false, 2)
|
||||||
|
|
||||||
local unselected = COLOR_GRAY_LIGHT
|
local unselected = COLOR_GRAY_LIGHT
|
||||||
local p1_color = (State.menu_selection == 1) and COLOR_GREEN_LIGHT or unselected
|
local p1_color = (State.menu_selection == 1) and COLOR_CYAN or unselected
|
||||||
local p2_color = (State.menu_selection == 2) and COLOR_GREEN_LIGHT or unselected
|
local p2_color = (State.menu_selection == 2) and COLOR_CYAN or unselected
|
||||||
local help_color = (State.menu_selection == 3) and COLOR_GREEN_LIGHT or unselected
|
local help_color = (State.menu_selection == 3) and COLOR_CYAN or unselected
|
||||||
local credits_color = (State.menu_selection == 4) and COLOR_GREEN_LIGHT or unselected
|
local credits_color = (State.menu_selection == 4) and COLOR_CYAN or unselected
|
||||||
local exit_color = (State.menu_selection == 5) and COLOR_GREEN_LIGHT or unselected
|
local exit_color = (State.menu_selection == 5) and COLOR_CYAN or unselected
|
||||||
|
|
||||||
local cursor_y = 60 + (State.menu_selection - 1) * 14
|
local cursor_y = 60 + (State.menu_selection - 1) * 14
|
||||||
UI.print_shadow(">", 60, cursor_y, COLOR_GREEN_LIGHT)
|
UI.print_shadow(">", 60, cursor_y, COLOR_CYAN)
|
||||||
|
|
||||||
UI.print_shadow("1 Player Game", 70, 60, p1_color)
|
UI.print_shadow("1 Player Game", 70, 60, p1_color)
|
||||||
UI.print_shadow("2 Player Game", 70, 74, p2_color)
|
UI.print_shadow("2 Player Game", 70, 74, p2_color)
|
||||||
@@ -477,24 +486,24 @@ function Help.update()
|
|||||||
UI.print_shadow("Help", 100, 8, COLOR_BLUE, false, 2)
|
UI.print_shadow("Help", 100, 8, COLOR_BLUE, false, 2)
|
||||||
|
|
||||||
-- Controls section
|
-- Controls section
|
||||||
UI.print_shadow("Controls", 20, 30, COLOR_WHITE)
|
UI.print_shadow("Controls", 20, 30, COLOR_LIGHT)
|
||||||
|
|
||||||
-- P1 controls
|
-- P1 controls
|
||||||
rect(20, 42, 90, 30, COLOR_SHADOW)
|
rect(20, 42, 90, 30, COLOR_SHADOW)
|
||||||
rect(19, 41, 90, 30, COLOR_BLUE)
|
rect(19, 41, 90, 30, COLOR_BLUE_LIGHT)
|
||||||
print("Player 1 (Blue)", 22, 44, COLOR_WHITE)
|
print("Player 1 (Blue)", 22, 44, COLOR_LIGHT)
|
||||||
print("Move: Arrow Keys", 22, 54, COLOR_WHITE)
|
print("Move: Arrow Keys", 22, 54, COLOR_LIGHT)
|
||||||
print("Bomb: SPACE", 22, 64, COLOR_WHITE)
|
print("Bomb: SPACE", 22, 64, COLOR_LIGHT)
|
||||||
|
|
||||||
-- P2 controls
|
-- P2 controls
|
||||||
rect(130, 42, 90, 30, COLOR_SHADOW)
|
rect(130, 42, 90, 30, COLOR_SHADOW)
|
||||||
rect(129, 41, 90, 30, COLOR_RED)
|
rect(129, 41, 90, 30, COLOR_RED)
|
||||||
print("Player 2 (Red)", 132, 44, COLOR_WHITE)
|
print("Player 2 (Red)", 132, 44, COLOR_LIGHT)
|
||||||
print("Move: W A S D", 132, 54, COLOR_WHITE)
|
print("Move: W A S D", 132, 54, COLOR_LIGHT)
|
||||||
print("Bomb: G", 132, 64, COLOR_WHITE)
|
print("Bomb: G", 132, 64, COLOR_LIGHT)
|
||||||
|
|
||||||
-- Powerups section
|
-- Powerups section
|
||||||
UI.print_shadow("Powerups", 20, 80, COLOR_WHITE)
|
UI.print_shadow("Powerups", 20, 80, COLOR_LIGHT)
|
||||||
|
|
||||||
-- Bomb powerup
|
-- Bomb powerup
|
||||||
rect(22, 93, 5, 5, COLOR_SHADOW)
|
rect(22, 93, 5, 5, COLOR_SHADOW)
|
||||||
@@ -509,7 +518,7 @@ function Help.update()
|
|||||||
print("+1 Blast range", 32, 104, COLOR_ORANGE)
|
print("+1 Blast range", 32, 104, COLOR_ORANGE)
|
||||||
|
|
||||||
-- Back instruction
|
-- Back instruction
|
||||||
UI.print_shadow("Press SPACE to return", 60, 122, COLOR_GREEN_LIGHT)
|
UI.print_shadow("Press SPACE to return", 60, 122, COLOR_CYAN)
|
||||||
|
|
||||||
if Input.action_pressed() then
|
if Input.action_pressed() then
|
||||||
State.game_state = GAME_STATE_MENU
|
State.game_state = GAME_STATE_MENU
|
||||||
@@ -525,12 +534,12 @@ function Credits.update()
|
|||||||
|
|
||||||
UI.print_shadow("Credits", 90, 20, COLOR_BLUE, false, 2)
|
UI.print_shadow("Credits", 90, 20, COLOR_BLUE, false, 2)
|
||||||
|
|
||||||
UI.print_shadow("Author: Zsolt Tasnadi", 60, 50, 12)
|
UI.print_shadow("Author: Zsolt Tasnadi", 60, 50, COLOR_LIGHT)
|
||||||
UI.print_shadow("Powered by Claude", 68, 66, 12)
|
UI.print_shadow("Powered by Claude", 68, 66, COLOR_LIGHT)
|
||||||
UI.print_shadow("Sponsored by Zen Heads", 52, 82, 12)
|
UI.print_shadow("Sponsored by Zen Heads", 52, 82, COLOR_LIGHT)
|
||||||
UI.print_shadow("Happy X-MAS!", 80, 98, COLOR_RED)
|
UI.print_shadow("Happy X-MAS!", 80, 98, COLOR_RED)
|
||||||
|
|
||||||
UI.print_shadow("Press SPACE to return", 60, 122, COLOR_GREEN_LIGHT)
|
UI.print_shadow("Press SPACE to return", 60, 122, COLOR_CYAN)
|
||||||
|
|
||||||
if Input.action_pressed() then
|
if Input.action_pressed() then
|
||||||
State.game_state = GAME_STATE_MENU
|
State.game_state = GAME_STATE_MENU
|
||||||
@@ -792,38 +801,8 @@ function AI.is_dangerous(gridX, gridY)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function AI.in_blast_zone(gridX, gridY, bombGridX, bombGridY)
|
|
||||||
if gridX == bombGridX and gridY == bombGridY then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if gridY == bombGridY and math.abs(gridX - bombGridX) <= 1 then
|
|
||||||
if gridX < bombGridX then
|
|
||||||
return State.map[gridY][gridX + 1] ~= SOLID_WALL
|
|
||||||
elseif gridX > bombGridX then
|
|
||||||
return State.map[gridY][gridX - 1] ~= SOLID_WALL
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if gridX == bombGridX and math.abs(gridY - bombGridY) <= 1 then
|
|
||||||
if gridY < bombGridY then
|
|
||||||
return State.map[gridY + 1][gridX] ~= SOLID_WALL
|
|
||||||
elseif gridY > bombGridY then
|
|
||||||
return State.map[gridY - 1][gridX] ~= SOLID_WALL
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function AI.has_adjacent_breakable_wall(gridX, gridY)
|
function AI.has_adjacent_breakable_wall(gridX, gridY)
|
||||||
local dirs = {
|
for _, dir in ipairs(DIRECTIONS) do
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0}
|
|
||||||
}
|
|
||||||
for _, dir in ipairs(dirs) do
|
|
||||||
local checkX = gridX + dir[1]
|
local checkX = gridX + dir[1]
|
||||||
local checkY = gridY + dir[2]
|
local checkY = gridY + dir[2]
|
||||||
if checkX >= 1 and checkX <= MAP_WIDTH and checkY >= 1 and checkY <= MAP_HEIGHT then
|
if checkX >= 1 and checkX <= MAP_WIDTH and checkY >= 1 and checkY <= MAP_HEIGHT then
|
||||||
@@ -864,15 +843,9 @@ end
|
|||||||
function AI.find_safe_cell(gridX, gridY, player)
|
function AI.find_safe_cell(gridX, gridY, player)
|
||||||
-- Find a cell to escape to that's OUTSIDE the bomb's blast line
|
-- Find a cell to escape to that's OUTSIDE the bomb's blast line
|
||||||
local power = player.bombPower
|
local power = player.bombPower
|
||||||
local dirs = {
|
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- First try: find a path that gets us completely out of blast line
|
-- First try: find a path that gets us completely out of blast line
|
||||||
for _, dir in ipairs(dirs) do
|
for _, dir in ipairs(DIRECTIONS) do
|
||||||
local newX = gridX + dir[1]
|
local newX = gridX + dir[1]
|
||||||
local newY = gridY + dir[2]
|
local newY = gridY + dir[2]
|
||||||
if Map.can_move_to(newX, newY, player) and not AI.is_dangerous(newX, newY) then
|
if Map.can_move_to(newX, newY, player) and not AI.is_dangerous(newX, newY) then
|
||||||
@@ -881,7 +854,7 @@ function AI.find_safe_cell(gridX, gridY, player)
|
|||||||
return {newX, newY}
|
return {newX, newY}
|
||||||
end
|
end
|
||||||
-- If not, check if we can turn corner to get out
|
-- If not, check if we can turn corner to get out
|
||||||
for _, dir2 in ipairs(dirs) do
|
for _, dir2 in ipairs(DIRECTIONS) do
|
||||||
local safeX = newX + dir2[1]
|
local safeX = newX + dir2[1]
|
||||||
local safeY = newY + dir2[2]
|
local safeY = newY + dir2[2]
|
||||||
if Map.can_move_to(safeX, safeY, player) and not AI.is_dangerous(safeX, safeY) then
|
if Map.can_move_to(safeX, safeY, player) and not AI.is_dangerous(safeX, safeY) then
|
||||||
@@ -944,7 +917,7 @@ function AI.move_and_bomb(player, target)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build direction list
|
-- Build direction list (preferred directions first, then all others)
|
||||||
local dirs = {}
|
local dirs = {}
|
||||||
if dx > 0 then table.insert(dirs, {1, 0})
|
if dx > 0 then table.insert(dirs, {1, 0})
|
||||||
elseif dx < 0 then table.insert(dirs, {-1, 0})
|
elseif dx < 0 then table.insert(dirs, {-1, 0})
|
||||||
@@ -953,13 +926,7 @@ function AI.move_and_bomb(player, target)
|
|||||||
elseif dy < 0 then table.insert(dirs, {0, -1})
|
elseif dy < 0 then table.insert(dirs, {0, -1})
|
||||||
end
|
end
|
||||||
|
|
||||||
local all_dirs = {
|
for _, d in ipairs(DIRECTIONS) do
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0}
|
|
||||||
}
|
|
||||||
for _, d in ipairs(all_dirs) do
|
|
||||||
table.insert(dirs, d)
|
table.insert(dirs, d)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1010,16 +977,10 @@ function AI.update(player, target)
|
|||||||
local in_danger = AI.is_dangerous(player.gridX, player.gridY)
|
local in_danger = AI.is_dangerous(player.gridX, player.gridY)
|
||||||
|
|
||||||
if in_danger then
|
if in_danger then
|
||||||
local dirs = {
|
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0}
|
|
||||||
}
|
|
||||||
local best_dir = nil
|
local best_dir = nil
|
||||||
local best_safe = false
|
local best_safe = false
|
||||||
|
|
||||||
for _, dir in ipairs(dirs) do
|
for _, dir in ipairs(DIRECTIONS) do
|
||||||
local newX = player.gridX + dir[1]
|
local newX = player.gridX + dir[1]
|
||||||
local newY = player.gridY + dir[2]
|
local newY = player.gridY + dir[2]
|
||||||
if Map.can_move_to(newX, newY, player) then
|
if Map.can_move_to(newX, newY, player) then
|
||||||
@@ -1103,19 +1064,19 @@ function Player.update_movement(player)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player.handle_input(player)
|
function Player.handle_input(player, input)
|
||||||
if player.moving then return end
|
if player.moving then return end
|
||||||
|
|
||||||
local newGridX = player.gridX
|
local newGridX = player.gridX
|
||||||
local newGridY = player.gridY
|
local newGridY = player.gridY
|
||||||
|
|
||||||
if Input.up() then
|
if input.up() then
|
||||||
newGridY = player.gridY - 1
|
newGridY = player.gridY - 1
|
||||||
elseif Input.down() then
|
elseif input.down() then
|
||||||
newGridY = player.gridY + 1
|
newGridY = player.gridY + 1
|
||||||
elseif Input.left() then
|
elseif input.left() then
|
||||||
newGridX = player.gridX - 1
|
newGridX = player.gridX - 1
|
||||||
elseif Input.right() then
|
elseif input.right() then
|
||||||
newGridX = player.gridX + 1
|
newGridX = player.gridX + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1124,36 +1085,27 @@ function Player.handle_input(player)
|
|||||||
player.gridY = newGridY
|
player.gridY = newGridY
|
||||||
end
|
end
|
||||||
|
|
||||||
if Input.action_pressed() then
|
if input.action() then
|
||||||
Bomb.place(player)
|
Bomb.place(player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player.handle_input_p2(player)
|
-- Input configurations for each player
|
||||||
if player.moving then return end
|
local P1_INPUT = {
|
||||||
|
up = Input.up,
|
||||||
|
down = Input.down,
|
||||||
|
left = Input.left,
|
||||||
|
right = Input.right,
|
||||||
|
action = Input.action_pressed
|
||||||
|
}
|
||||||
|
|
||||||
local newGridX = player.gridX
|
local P2_INPUT = {
|
||||||
local newGridY = player.gridY
|
up = Input.p2_up,
|
||||||
|
down = Input.p2_down,
|
||||||
if Input.p2_up() then
|
left = Input.p2_left,
|
||||||
newGridY = player.gridY - 1
|
right = Input.p2_right,
|
||||||
elseif Input.p2_down() then
|
action = Input.p2_action
|
||||||
newGridY = player.gridY + 1
|
}
|
||||||
elseif Input.p2_left() then
|
|
||||||
newGridX = player.gridX - 1
|
|
||||||
elseif Input.p2_right() then
|
|
||||||
newGridX = player.gridX + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if Map.can_move_to(newGridX, newGridY, player) then
|
|
||||||
player.gridX = newGridX
|
|
||||||
player.gridY = newGridY
|
|
||||||
end
|
|
||||||
|
|
||||||
if Input.p2_action() then
|
|
||||||
Bomb.place(player)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Player.reset(player)
|
function Player.reset(player)
|
||||||
player.gridX = player.spawnX
|
player.gridX = player.spawnX
|
||||||
@@ -1230,10 +1182,9 @@ function Game.update()
|
|||||||
Player.update_movement(player)
|
Player.update_movement(player)
|
||||||
if player.is_ai then
|
if player.is_ai then
|
||||||
AI.update(player, human_player)
|
AI.update(player, human_player)
|
||||||
elseif idx == 1 then
|
|
||||||
Player.handle_input(player)
|
|
||||||
else
|
else
|
||||||
Player.handle_input_p2(player)
|
local input = (idx == 1) and P1_INPUT or P2_INPUT
|
||||||
|
Player.handle_input(player, input)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user