class_name Board extends Node2D signal board_state_changed 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] = {} var buildings: Dictionary[Vector2i, Building] = {} var active_building: Building var active_tile: Tile var is_destroying_building: bool = false var current_map_coord: Vector2i var prev_map_coord: Vector2i var is_controlling_camera: bool = false @onready var tile_map: TileMapLayer = %Tiles @onready var board_state: Node2D = %BoardState 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 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) if active_building != null: active_building.position = tile_map.map_to_local(current_map_coord) 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() prev_map_coord = current_map_coord if event.is_action_pressed("select"): if active_tile != null: place_active_tile() elif active_building != null: place_active_building() if is_destroying_building: destroy_current_building() _handle_building_rotation(event) _handle_spawn_rotation(event) 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 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 active_building.modulate = Color(1, 1, 1, 0.5) active_building.is_placing = true if active_building is PostOffice: active_building.place() func place_active_tile() -> void: if tiles.has(current_map_coord) and tiles[current_map_coord] is Ground: 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 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] 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 tiles[current_map_coord].free() active_tile.modulate = Color(1, 1, 1, 1) active_tile.coords = current_map_coord 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 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 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: 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: remove_building(building) 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() 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: 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() 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 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 match direction: Direction.UP: result = Direction.RIGHT Direction.RIGHT: result = Direction.DOWN Direction.DOWN: result = Direction.LEFT Direction.LEFT: result = Direction.UP if count == 1: return result return get_next_direction(result, count - 1) 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 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