class_name BoardGame extends Node signal all_players_ready_for_day_start const HOME_SCENE = preload("uid://bto4vblqk2inb") const HQ_SCENE = preload("uid://wdgig5aclnpx") #const DOWN_SPAWN_SCENE = preload("uid://d4ltd1geg7s2p") const BANK_SCENE = preload("uid://c06fsqdixer1c") const BAR_SCENE = preload("uid://cfk268flnsbhb") const CAFE_SCENE = preload("uid://bgtw051fiveeo") const FORK_SCENE = preload("uid://cphy0vtj14ob0") const GUN_SHOP_SCENE = preload("uid://c1kyedmrep0tu") const HOSPITAL_SCENE = preload("uid://bytldu3y1jak3") const OFFICE_SCENE = preload("uid://mixrqf035krk") const SHOP_SCENE = preload("uid://dbn63mv0peqf") const CHURCH_SCENE = preload("uid://brn0nbkela0m4") const CITY_HALL_SCENE = preload("uid://dtnejoimqiu0o") const DEMOLITIONS_SCENE = preload("uid://dvmglvbersupv") #const POST_OFFICE_SCENE = preload("uid://bpi8owv5lxyjy") const BASE_DECK = [ BANK_SCENE, BAR_SCENE, CAFE_SCENE, FORK_SCENE, GUN_SHOP_SCENE, HOSPITAL_SCENE, OFFICE_SCENE, SHOP_SCENE, CHURCH_SCENE, CITY_HALL_SCENE, DEMOLITIONS_SCENE ] const WINNING_MONEY_AMOUNT: int = 200 var citizen_count: int = 0: set(value): #print(value) citizen_count = value if citizen_count == 0: handle_citizens_finished() var buildings_added_to_draft: int = 0 var spawn_placement_actions: int = 0 var draft_index_to_be_deleted: int = -1 var num_players_ready_for_day_start: int = 0 var is_playing_day: bool = false var current_board_state: BoardState: set(value): current_board_state = value #controls.check_controls_enabled() #controls.set_info() var pending_board_state: BoardState: set(value): pending_board_state = value #controls.check_controls_enabled() var original_board_state: BoardState var deck: Array[Building] = [] @onready var board: Board = %Board @onready var controls: Controls = %Controls @onready var game_over: Control = %GameOver func _init() -> void: Globals.board_game = self @rpc("any_peer", "call_local", "reliable") func setup_building_deck() -> void: for i in range(current_board_state.players.size()): for scene in BASE_DECK: deck.append(scene.instantiate()) @rpc("any_peer", "call_local", "reliable") func advance_board_state(board_state: Dictionary) -> void: pending_board_state = null current_board_state = BoardState.deserialize(board_state) board.reset() board.set_board_state(current_board_state) controls.set_board_state(current_board_state) if current_board_state.state == BoardState.State.INITIAL_SETUP: if current_board_state.turn >= 0: current_board_state.players.reverse() current_board_state.current_player = current_board_state.players[0] controls.set_info() start_day() elif Globals.game.this_player.id == current_board_state.current_player.id: if current_board_state.turn >= -current_board_state.players.size(): board.set_active_building(HQ_SCENE.instantiate()) else: board.set_active_building(HOME_SCENE.instantiate()) elif current_board_state.state == BoardState.State.PLACING_SPAWNS: start_day() if current_board_state.players_passed == Globals.game.players.size(): current_board_state.players_passed = 0 end_day() controls.check_controls_enabled() controls.set_info() original_board_state = BoardState.deserialize(current_board_state.serialize()) @rpc("any_peer", "call_local", "reliable") func update_board_state(board_state: Dictionary) -> void: current_board_state = BoardState.deserialize(board_state) board.reset() board.set_board_state(current_board_state) controls.set_board_state(current_board_state) controls.check_controls_enabled() controls.set_info() @rpc("any_peer", "call_local", "reliable") func add_building_to_draft(building_data: Dictionary) -> void: current_board_state.real_estate_market.append(Building.deserialize(building_data)) buildings_added_to_draft += 1 if buildings_added_to_draft >= Globals.game.players.size(): buildings_added_to_draft = 0 current_board_state.state = BoardState.State.PLAY controls.check_controls_enabled() controls.set_info() original_board_state = BoardState.deserialize(current_board_state.serialize()) update_board_state.rpc(current_board_state.serialize()) @rpc("any_peer", "call_local", "reliable") func ready_for_day_start() -> void: num_players_ready_for_day_start += 1 if num_players_ready_for_day_start >= Globals.game.players.size(): all_players_ready_for_day_start.emit() func select_tile(tile: Tile) -> void: board.set_active_tile(Tile.deserialize(tile.serialize())) func select_building(building: Building) -> void: if building is Home: board.remove_home(current_board_state.current_player) if building is HQ: board.remove_hq(current_board_state.current_player) board.set_active_building(Building.deserialize(building.serialize())) draft_index_to_be_deleted = current_board_state.real_estate_market.find_custom( func(b: Building) -> bool: return ( b.get_script() == building.get_script() and b.cost == building.cost and b.player == building.player ) ) func cancel_placement() -> void: draft_index_to_be_deleted = -1 if is_instance_valid(board.active_tile): board.active_tile.queue_free() board.active_tile = null if is_instance_valid(board.active_building): board.active_building.queue_free() board.active_building = null func start_day() -> void: if ( current_board_state.day > 0 and current_board_state.state != BoardState.State.PLACING_SPAWNS and current_board_state.state != BoardState.State.DESTROYING_BUILDINGS ): current_board_state.players.sort_custom( func(p1: Player, p2: Player) -> bool: return p1.money > p2.money ) current_board_state.current_player = current_board_state.players[0] await all_players_ready_for_day_start if current_board_state.state == BoardState.State.DESTROYING_BUILDINGS: if current_board_state.buildings_to_destroy > 0: if current_board_state.current_player.id == Globals.game.this_player.id: board.is_destroying_building = true return current_board_state.state = BoardState.State.DRAFT if current_board_state.state == BoardState.State.PLACING_SPAWNS: if current_board_state.spawn_placements > 0: if current_board_state.current_player.id == Globals.game.this_player.id: board.call_deferred( "set_active_building", load("uid://d4ltd1geg7s2p").instantiate() ) return current_board_state.state = BoardState.State.DRAFT current_board_state.day += 1 current_board_state.turn = 1 current_board_state.state = BoardState.State.DRAFT for p in current_board_state.players: p.build_actions_taken = 0 _tally_votes() reduce_draft_costs() seed(Globals.game.sum_player_ids()) deck.shuffle() #deck.push_front(POST_OFFICE_SCENE.instantiate()) var turn_index := current_board_state.get_this_player_index() controls.give_hand(deck.slice(0 + (3 * turn_index), 3 + (3 * turn_index))) controls.set_info() func end_day() -> void: controls.end_day() get_tree().call_group("Pausable", "unpause") is_playing_day = true func update_player_votes(player: Player, votes: int) -> void: var idx = current_board_state.get_player_index(player) current_board_state.players[idx].votes = votes func update_player_money(player: Player, money: int) -> void: var idx = current_board_state.get_player_index(player) current_board_state.players[idx].money = money func reduce_draft_costs() -> void: for building in current_board_state.real_estate_market: building.cost = ceili(building.cost / 2.0) controls.set_board_state(current_board_state) func handle_board_state_changed() -> void: pending_board_state = BoardState.new() pending_board_state.day = current_board_state.day pending_board_state.spawn_placements = current_board_state.spawn_placements pending_board_state.buildings_to_destroy = current_board_state.buildings_to_destroy if ( current_board_state.state != BoardState.State.DRAFT and current_board_state.state != BoardState.State.PLACING_SPAWNS ): pending_board_state.turn = current_board_state.turn + 1 pending_board_state.state = current_board_state.state pending_board_state.players = current_board_state.players pending_board_state.players.push_back(pending_board_state.players.pop_front()) if ( pending_board_state.state == BoardState.State.INITIAL_SETUP and pending_board_state.turn == -pending_board_state.players.size() ): pending_board_state.players.reverse() pending_board_state.current_player = current_board_state.players[0] pending_board_state.tiles = board.tiles.values() for building in board.buildings.values(): if !pending_board_state.buildings.has(building): pending_board_state.buildings.append(building) pending_board_state.real_estate_market = current_board_state.real_estate_market controls.check_controls_enabled() controls.set_info() func handle_board_state_confirmed() -> void: if draft_index_to_be_deleted != -1: pending_board_state.real_estate_market.remove_at(draft_index_to_be_deleted) draft_index_to_be_deleted = -1 advance_board_state.rpc(pending_board_state.serialize()) func handle_pass() -> void: current_board_state.players_passed += 1 current_board_state.turn += 1 current_board_state.players.push_back(current_board_state.players.pop_front()) current_board_state.current_player = current_board_state.players[0] advance_board_state.rpc(current_board_state.serialize()) func handle_citizens_finished() -> void: for child in board.board_state.get_children(): if child.get_groups().has("PostTurnActions"): child.handle_post_turn_actions() is_playing_day = false controls.reset_turn() var winning_player_idx = current_board_state.players.find_custom( func(p: Player) -> bool: return p.money >= WINNING_MONEY_AMOUNT ) if winning_player_idx != -1: game_over.set_winning_player(current_board_state.players[winning_player_idx]) else: start_day() ready_for_day_start.rpc() func _tally_votes() -> void: var most_votes: int = ( current_board_state.players.map(func(p: Player) -> int: return p.votes).max() ) var players_with_most_votes: Array[Player] = current_board_state.players.filter( func(p: Player) -> bool: return p.votes == most_votes ) if players_with_most_votes.size() == 1: players_with_most_votes[0].building_permits += 1 for player in current_board_state.players: player.votes = 0 func queue_spawn_placement(num_placements: int) -> void: current_board_state.spawn_placements += num_placements * current_board_state.players.size() current_board_state.state = BoardState.State.PLACING_SPAWNS func queue_building_destruction(num: int) -> void: current_board_state.buildings_to_destroy += num * current_board_state.players.size() current_board_state.state = BoardState.State.DESTROYING_BUILDINGS func sell_building_permit() -> void: if current_board_state.players[0].building_permits > 0: current_board_state.players[0].building_permits -= 1 current_board_state.players[0].money += 20 current_board_state.turn += 1 current_board_state.players.push_back(current_board_state.players.pop_front()) current_board_state.current_player = current_board_state.players[0] advance_board_state.rpc(current_board_state.serialize())