class_name Wintermaul extends Stage signal lives_changed class Teams extends Resource: var top := Team.new() var bottom := Team.new() func list() -> Array[Team]: return [top, bottom] func names() -> Array[String]: return ["top", "bottom"] class Team extends Resource: var lives := 0 var players: Array[Player] = [] var teams := Teams.new() var income_frequency := 15.0 class PriceMapTower: static var cost: int = 5 static func component_range(number_of_towers: int = 1): return 100 * number_of_towers static func component_range_level(component: TowerComponent, number_of_towers: int = 1): return pow(5, component.level) * number_of_towers static func component_attack(number_of_towers: int = 1): return 100 * number_of_towers static func component_attack_level(component: TowerComponent, number_of_towers: int = 1): return 10 * component.level * number_of_towers static func component_frost(number_of_towers: int = 1): return 100 * number_of_towers static func component_frost_level(component: TowerComponent, number_of_towers: int = 1): return 10 * component.level * number_of_towers static func component_burn(number_of_towers: int = 1): return 100 * number_of_towers static func component_burn_level(component: TowerComponent, number_of_towers: int = 1): return 10 * component.level * number_of_towers static func component_poison(number_of_towers: int = 1): return 100 * number_of_towers static func component_poison_level(component: TowerComponent, number_of_towers: int = 1): return 10 * component.level * number_of_towers var starting_lives := 10 var starting_money := 50 var starting_income := 5 var use_premade_teams := false var premade_teams := {} func _init(): super._init() set_player_script(preload("res://Stages/Wintermaul/player.gd")) func _ready(): super._ready() # initialize team lives teams.top.lives = starting_lives teams.bottom.lives = starting_lives # initialize player resources if multiplayer.is_server(): for player in Network.get_players(): Network.update_player.rpc(player.id, { "money": starting_money, "income": starting_income, }) # set camera limits $Camera.limit_left = $Map.get_used_rect().position.x * %Map.tile_set.tile_size.x - %Map.tile_set.tile_size.x - 8 * %Map.tile_set.tile_size.y $Camera.limit_right = $Map.get_used_rect().end.x * %Map.tile_set.tile_size.x - %Map.tile_set.tile_size.x + 8 * %Map.tile_set.tile_size.y $Camera.limit_top = %Map.get_used_rect().position.y * %Map.tile_set.tile_size.y - 8 * %Map.tile_set.tile_size.y $Camera.limit_bottom = %Map.get_used_rect().end.y * %Map.tile_set.tile_size.y + 8 * %Map.tile_set.tile_size.y # add players to teams Network.player_joined.connect(add_player) if use_premade_teams: for team_name in premade_teams: for id in premade_teams[team_name]: teams[team_name].players.append(Network.get_player(id)) else: for id in Network.get_ordered_player_ids(): add_player(Network.get_player(id)) # set camera for local player if get_team(Client.player) == teams.top: $Camera.set_center($BuilderCollisions/TeamTop/CollisionShape2D.global_position) elif get_team(Client.player) == teams.bottom: $Camera.set_center($BuilderCollisions/TeamBottom/CollisionShape2D.global_position) # set players for HUD teams %HUD.get_node("%TeamTop").players = teams.top.players %HUD.get_node("%TeamBottom").players = teams.bottom.players # initialize timer and start $IncomeTimer.wait_time = income_frequency $IncomeTimer.start() # initialize lives display update_lives("top", 0) update_lives("bottom", 0) func _process(_delta: float) -> void: %HUD.time.text = "Time: %.0fs" % clamp($IncomeTimer.time_left + 0.5, 0, $IncomeTimer.wait_time) @rpc("any_peer", "call_local") func place_tower(remote_data: Dictionary): var data: Tower.NetworkData = dict_to_inst(remote_data) var tower = Tower.from_network_data(data) var player = Network.get_player(tower.owner_id) player.towers[tower.global_position] = tower player.money -= PriceMapTower.cost Network.players_changed.emit() _place_tower(%Towers, tower) Client.placed_tower.emit(tower) @rpc("any_peer", "call_local") func spawn_unit(remote_data: Dictionary): var data: Unit.NetworkData = dict_to_inst(remote_data) var unit := Unit.from_network_data(data) var player = Network.get_player(unit.owner_id) player.units.append(unit) player.money -= unit.unit_resource.cost player.income += unit.unit_resource.income Network.players_changed.emit() if multiplayer.is_server(): unit.reached_goal.connect(func(): var team = get_team(player) if team == teams.top: update_lives.rpc("bottom", -1) elif team == teams.bottom: update_lives.rpc("top", -1) ) _spawn_unit(%Towers, unit) func can_place_tower(): if Client.player.money < PriceMapTower.cost: add_status_message("Not enough money to build tower") return false return true func can_spawn_unit(unit: Unit): if Client.player.money < unit.unit_resource.cost: add_status_message("Not enough money to spawn unit") return false return true func add_player(player: Player): if teams.bottom.players.size() < teams.top.players.size(): teams.bottom.players.append(player) else: teams.top.players.append(player) func get_team(player: Player) -> Team: for team in teams.list(): if player in team.players: return team return null func get_spawn(): return %Spawn func get_overwrite_target(): var team = get_team(Client.player) if team == teams.top: return [$Paths/PathNodeLeftDown, $Paths/PathNodeRightDown].pick_random() elif team == teams.bottom: return [$Paths/PathNodeLeftUp, $Paths/PathNodeRightUp].pick_random() func get_builder_collision_masks(): var team = get_team(Client.player) if team == teams.top: return [28] elif team == teams.bottom: return [27] func _on_income_timer_timeout() -> void: if multiplayer.is_server(): for player in Network.get_players(): Network.update_player.rpc(player.id, { "money": player.income, }) reset_timer.rpc() @rpc("authority", "call_local") func reset_timer(): $IncomeTimer.wait_time = income_frequency $IncomeTimer.start() @rpc("authority", "call_local") func update_lives(team: String, lives: int): teams[team].lives += lives lives_changed.emit() if lives < 0: add_status_message("Team " + team + " lost " + str(abs(lives)) + " life") if teams[team].lives <= 0: add_status_message("Team " + team + " lost the game")