352 lines
12 KiB
GDScript3
Raw Normal View History

2026-04-13 11:34:00 -05:00
class_name Board extends Node2D
signal board_state_changed
2026-04-13 11:34:00 -05:00
enum Direction { NONE, UP, DOWN, LEFT, RIGHT }
const GROUND = preload("uid://c5y5ksmwhevd")
const UP_SPAWN = preload("uid://6ywvgci44ttv")
const RIGHT_SPAWN = preload("uid://c1y3s7daosghf")
const DOWN_SPAWN = preload("uid://d4ltd1geg7s2p")
const LEFT_SPAWN = preload("uid://noi2ko4hceus")
const SOUP_KITCHEN_SCENE = preload("uid://dmfnipmjntenc")
var tiles: Dictionary[Vector2i, Tile] = {}
2026-04-13 11:34:00 -05:00
var buildings: Dictionary[Vector2i, Building] = {}
var active_building: Building
var active_tile: Tile
2026-04-29 13:59:35 -05:00
var is_destroying_building: bool = false
2026-04-13 11:34:00 -05:00
var current_map_coord: Vector2i
var prev_map_coord: Vector2i
var is_controlling_camera: bool = false
2026-04-13 11:34:00 -05:00
@onready var tile_map: TileMapLayer = %Tiles
@onready var board_state: Node2D = %BoardState
2026-04-13 11:34:00 -05:00
2026-04-13 11:34:00 -05:00
func _input(event: InputEvent) -> void:
if event.is_action_pressed("camera_engage"):
is_controlling_camera = true
return
if event.is_action_released("camera_engage"):
is_controlling_camera = false
if is_controlling_camera:
return
#if Globals.board_game.current_board_state.current_player != Globals.game.this_player:
#return
2026-04-13 11:34:00 -05:00
if event is InputEventMouseMotion:
current_map_coord = tile_map.local_to_map(tile_map.get_local_mouse_position())
if active_tile != null:
active_tile.position = tile_map.map_to_local(current_map_coord)
2026-04-13 11:34:00 -05:00
if active_building != null:
active_building.position = tile_map.map_to_local(current_map_coord)
2026-04-29 13:59:35 -05:00
if is_destroying_building:
if buildings.has(current_map_coord):
buildings[current_map_coord].show_bomb()
if prev_map_coord != current_map_coord and buildings.has(prev_map_coord):
buildings[prev_map_coord].hide_bomb()
2026-04-13 11:34:00 -05:00
prev_map_coord = current_map_coord
if event.is_action_pressed("select"):
if active_tile != null:
place_active_tile()
2026-04-13 11:34:00 -05:00
elif active_building != null:
place_active_building()
2026-04-29 13:59:35 -05:00
if is_destroying_building:
destroy_current_building()
_handle_building_rotation(event)
_handle_spawn_rotation(event)
2026-04-13 11:34:00 -05:00
func set_active_tile(tile: Tile) -> void:
active_tile = tile
board_state.add_child(active_tile)
active_tile.modulate = Color(1, 1, 1, 0.5)
active_tile.player = Globals.board_game.current_board_state.current_player
active_tile.is_placing = true
2026-04-13 11:34:00 -05:00
func set_active_building(building: Building) -> void:
active_building = building
board_state.add_child(active_building)
active_building.player = Globals.board_game.current_board_state.current_player
2026-04-13 11:34:00 -05:00
active_building.modulate = Color(1, 1, 1, 0.5)
active_building.is_placing = true
2026-04-29 13:59:35 -05:00
if active_building is PostOffice:
active_building.place()
2026-04-13 11:34:00 -05:00
func place_active_tile() -> void:
if tiles.has(current_map_coord) and tiles[current_map_coord] is Ground:
2026-04-28 21:06:45 -05:00
tiles[current_map_coord].free()
tiles.erase(current_map_coord)
if !tiles.has(current_map_coord) and !buildings.has(current_map_coord):
Globals.board_game.current_board_state.players[0].money -= active_tile.cost
if Globals.board_game.current_board_state.state != BoardState.State.INITIAL_SETUP:
Globals.board_game.current_board_state.players[0].build_actions_taken += 1
active_tile.modulate = Color(1, 1, 1, 1)
active_tile.coords = current_map_coord
2026-04-24 12:35:46 -05:00
active_tile.day_placed = Globals.board_game.current_board_state.day
tiles[current_map_coord] = active_tile
active_tile = null
board_state_changed.emit()
elif (
tiles.has(current_map_coord)
and tiles[current_map_coord].day_placed < Globals.board_game.current_board_state.day
):
var tile = tiles[current_map_coord]
2026-04-28 21:06:45 -05:00
if (
Globals.board_game.current_board_state.players[0].money
>= ((tile.cost * 2) + active_tile.cost)
):
var tile_player_index = Globals.board_game.current_board_state.get_player_index(
tile.player
)
Globals.board_game.current_board_state.players[tile_player_index].money += tile.cost * 2
Globals.board_game.current_board_state.players[0].money -= tile.cost * 2
Globals.board_game.current_board_state.players[0].money -= active_tile.cost
2026-04-28 21:06:45 -05:00
tiles[current_map_coord].free()
active_tile.modulate = Color(1, 1, 1, 1)
active_tile.coords = current_map_coord
2026-04-24 12:35:46 -05:00
active_tile.day_placed = Globals.board_game.current_board_state.day
tiles[current_map_coord] = active_tile
active_tile = null
board_state_changed.emit()
func place_active_building() -> void:
active_building.starting_coord = current_map_coord
active_building.player = Globals.board_game.current_board_state.current_player
for coord in active_building.get_tile_coords():
if tiles.has(coord) and !(tiles[coord] is Ground):
return
if buildings.has(coord):
return
for surr_coord in tile_map.get_surrounding_cells(coord):
if buildings.has(surr_coord):
return
Globals.board_game.current_board_state.players[0].money -= active_building.cost
if Globals.board_game.current_board_state.state != BoardState.State.INITIAL_SETUP:
Globals.board_game.current_board_state.players[0].build_actions_taken += 1
active_building.modulate = Color(1, 1, 1, 1)
for coord in active_building.get_tile_coords():
buildings[coord] = active_building
2026-04-28 21:06:45 -05:00
tiles[coord].free()
tiles.erase(coord)
if active_building is Spawn:
Globals.board_game.current_board_state.spawn_placements -= 1
board_state_changed.emit()
active_building = null
2026-04-29 13:59:35 -05:00
func destroy_current_building() -> void:
if buildings.has(current_map_coord):
var building = buildings[current_map_coord]
if building is not Home and building is not HQ:
remove_building(building)
Globals.board_game.current_board_state.buildings_to_destroy -= 1
func remove_home(player: Player) -> void:
for coord in buildings.keys():
var building = buildings[coord]
if building is Home and building.player.id == player.id:
2026-04-29 13:59:35 -05:00
remove_building(building)
func remove_hq(player: Player) -> void:
for coord in buildings.keys():
var building = buildings[coord]
if building is HQ and building.player.id == player.id:
2026-04-29 13:59:35 -05:00
remove_building(building)
2026-04-29 13:59:35 -05:00
func remove_building(building: Building) -> void:
for coord in building.get_tile_coords():
buildings.erase(coord)
var ground: Ground = GROUND.instantiate()
board_state.add_child(ground)
ground.position = tile_map.map_to_local(coord)
ground.coords = coord
tiles[coord] = ground
building.free()
2026-04-13 11:34:00 -05:00
func _handle_building_rotation(event: InputEvent) -> void:
if active_building == null or active_building is Spawn:
return
if event.is_action_pressed("rotate_tile_up"):
active_building.rotation_degrees += 90
active_building.tile_rotation = get_next_direction(active_building.tile_rotation)
elif event.is_action_pressed("rotate_tile_down"):
active_building.rotation_degrees -= 90
active_building.tile_rotation = get_previous_direction(active_building.tile_rotation)
func _handle_spawn_rotation(event: InputEvent) -> void:
if active_building == null or active_building is not Spawn:
return
if event.is_action_pressed("rotate_tile_up"):
var curr_position = active_building.position
active_building.queue_free()
match active_building.direction:
Direction.UP:
set_active_building(RIGHT_SPAWN.instantiate())
Direction.RIGHT:
set_active_building(DOWN_SPAWN.instantiate())
Direction.DOWN:
set_active_building(LEFT_SPAWN.instantiate())
Direction.LEFT:
set_active_building(UP_SPAWN.instantiate())
active_building.position = curr_position
elif event.is_action_pressed("rotate_tile_down"):
var curr_position = active_building.position
active_building.queue_free()
match active_building.direction:
Direction.UP:
set_active_building(LEFT_SPAWN.instantiate())
Direction.RIGHT:
set_active_building(UP_SPAWN.instantiate())
Direction.DOWN:
set_active_building(RIGHT_SPAWN.instantiate())
Direction.LEFT:
set_active_building(DOWN_SPAWN.instantiate())
active_building.position = curr_position
func reset() -> void:
for child in board_state.get_children():
child.free()
func set_board_state(bs: BoardState) -> void:
2026-04-28 21:06:45 -05:00
tiles.clear()
buildings.clear()
for tile in bs.tiles:
board_state.add_child(tile)
tiles[tile.coords] = tile
tile.position = tile_map.map_to_local(tile.coords)
for building in bs.buildings:
board_state.add_child(building)
for coord in building.get_tile_coords():
buildings[coord] = building
building.position = tile_map.map_to_local(building.starting_coord)
building.rotation_degrees += 90 * building.get_rotation_count()
func initialize() -> void:
for i in range(Globals.game.players.size()):
var random_spawn = Vector2i(randi_range(0, 9), randi_range(0, 9))
for x in range(10):
for y in range(10):
var coord = Vector2i(x, y)
if i == 1:
coord.x += 10
if i == 2:
coord.y += 10
if i == 3:
coord += Vector2i(10, 10)
if x == random_spawn.x and y == random_spawn.y:
var possible_spawns = [UP_SPAWN, RIGHT_SPAWN, DOWN_SPAWN, LEFT_SPAWN]
if random_spawn.x == 9:
possible_spawns.erase(RIGHT_SPAWN)
elif random_spawn.x == 0:
possible_spawns.erase(LEFT_SPAWN)
if random_spawn.y == 9:
possible_spawns.erase(DOWN_SPAWN)
elif random_spawn.y == 0:
possible_spawns.erase(UP_SPAWN)
var spawn: Building = possible_spawns.pick_random().instantiate()
board_state.add_child(spawn)
spawn.starting_coord = coord
spawn.position = tile_map.map_to_local(coord)
buildings[coord] = spawn
else:
var ground: Tile = GROUND.instantiate()
board_state.add_child(ground)
ground.coords = coord
ground.position = tile_map.map_to_local(coord)
tiles[coord] = ground
var extra_spawn_coord := get_random_available_tile_coord()
2026-04-20 13:33:56 -05:00
var extra_spawn = [UP_SPAWN, RIGHT_SPAWN, DOWN_SPAWN, LEFT_SPAWN].pick_random().instantiate()
board_state.add_child(extra_spawn)
extra_spawn.starting_coord = extra_spawn_coord
2026-04-20 13:33:56 -05:00
extra_spawn.position = tile_map.map_to_local(extra_spawn_coord)
tiles[extra_spawn_coord].queue_free()
tiles.erase(extra_spawn_coord)
buildings[extra_spawn_coord] = extra_spawn
var soup_kitchen_coords := get_random_available_tile_coord()
var soup_kitchen: SoupKitchen = SOUP_KITCHEN_SCENE.instantiate()
board_state.add_child(soup_kitchen)
soup_kitchen.starting_coord = soup_kitchen_coords
for skc in soup_kitchen.get_tile_coords():
buildings[skc] = soup_kitchen
tiles[skc].free()
tiles.erase(skc)
soup_kitchen.position = tile_map.map_to_local(soup_kitchen_coords)
func get_random_available_tile_coord() -> Vector2i:
return tiles.keys().filter(is_cross_shape_available).pick_random()
func is_cross_shape_available(coord: Vector2i) -> bool:
return (
tiles[coord] is Ground
and !buildings.has(coord)
and tiles.get(Vector2i(coord.x + 1, coord.y)) is Ground
and !buildings.has(Vector2i(coord.x + 1, coord.y))
and tiles.get(Vector2i(coord.x - 1, coord.y)) is Ground
and !buildings.has(Vector2i(coord.x - 1, coord.y))
and tiles.get(Vector2i(coord.x, coord.y + 1)) is Ground
and !buildings.has(Vector2i(coord.x, coord.y + 1))
and tiles.get(Vector2i(coord.x, coord.y - 1)) is Ground
and !buildings.has(Vector2i(coord.x, coord.y - 1))
)
static func get_next_direction(direction: Direction, count: int = 1) -> Direction:
var result := direction
if count == 0:
return result
2026-04-13 11:34:00 -05:00
match direction:
Direction.UP:
result = Direction.RIGHT
2026-04-13 11:34:00 -05:00
Direction.RIGHT:
result = Direction.DOWN
2026-04-13 11:34:00 -05:00
Direction.DOWN:
result = Direction.LEFT
2026-04-13 11:34:00 -05:00
Direction.LEFT:
result = Direction.UP
if count == 1:
return result
return get_next_direction(result, count - 1)
2026-04-13 11:34:00 -05:00
static func get_previous_direction(direction: Direction) -> Direction:
match direction:
Direction.UP:
return Direction.LEFT
Direction.RIGHT:
return Direction.UP
Direction.DOWN:
return Direction.RIGHT
Direction.LEFT:
return Direction.DOWN
_:
return direction
2026-04-13 11:34:00 -05:00
static func get_direction_vector(direction: Direction) -> Vector2i:
match direction:
Direction.UP:
return Vector2i.UP
Direction.RIGHT:
return Vector2i.RIGHT
Direction.DOWN:
return Vector2i.DOWN
Direction.LEFT:
return Vector2i.LEFT
_:
return Vector2i.ZERO