extends Node2D # #105 (#97g): keeps the vessels stationed at outposts in step with what # GameState says is stationed there. # # Garrison vessels are NOT in Fleet.ships. They are spawned here, under a # node of their own, so the formation, the fleet's speed fader and # max_fleet_size() all continue to be about the fleet - a garrison that # counted against the fleet's slots would mean stationing a vessel cost the # player a place in their own wedge, which is the opposite of what an outpost # is for. # # The record is the truth, as everywhere else: this node is rebuilt from # GameState.garrison_crew() when the space scene loads, so a garrison # survives a trip to a planet surface. var ship_scene: PackedScene = preload("res://vessels/Ship.tscn") const GARRISON_COMPONENT = preload("res://vessels/VesselGarrisonComponent.gd") # {outpost_id: [ship, ...]} var stationed: Dictionary = {} func _ready() -> void: add_to_group("garrison_field") sync() func _process(_delta: float) -> void: sync() func ships_at(outpost_id: int) -> Array: var out: Array = [] for ship in stationed.get(outpost_id, []): if is_instance_valid(ship) and not ship.is_queued_for_deletion(): out.append(ship) return out func all_ships() -> Array: var out: Array = [] for outpost_id in stationed: out.append_array(ships_at(outpost_id)) return out func sync() -> void: # Retire whole outposts that no longer exist, then reconcile the rest. for outpost_id in stationed.keys(): if GameState.find_building(outpost_id).is_empty(): for ship in ships_at(outpost_id): ship.queue_free() stationed.erase(outpost_id) for outpost_id in GameState.outpost_ids(): var record: Dictionary = GameState.find_building(outpost_id) var crew: Array = GameState.garrison_crew(outpost_id) var here: Array = ships_at(outpost_id) stationed[outpost_id] = here if here.size() == crew.size(): continue if here.size() > crew.size(): # The record shrank under us - a vessel was released or lost # while this node was not looking. for i in range(crew.size(), here.size()): here[i].queue_free() stationed[outpost_id] = here.slice(0, crew.size()) continue for i in range(here.size(), crew.size()): here.append(_spawn(outpost_id, String(crew[i]), record.position, i)) func _spawn(outpost_id: int, vessel_type: String, at: Vector2, index: int) -> Node2D: var ship: Area2D = ship_scene.instantiate() # Before add_child, so Ship._ready() builds the right vessel - the same # order Fleet.add_ship() relies on. ship.vessel_type = vessel_type var spread: float = TAU * float(index) / 4.0 ship.position = at + Vector2(cos(spread), sin(spread)) * 170.0 add_child(ship) # #165: marked as belonging to an outpost, so the ram rule leaves it # alone. A stationed vessel orbits and re-docks with its own structure # constantly; without this the collision damage meant for a charging # fleet vessel destroyed the garrison the moment it launched. ship.add_to_group("garrison_ships") var component: Node = GARRISON_COMPONENT.new() component.outpost_id = outpost_id component.station = at ship.add_child(component) component.setup() # A garrison vessel that dies frees its berth, so #104 sends another. ship.destroyed.connect(_on_garrison_lost.bind(outpost_id, vessel_type)) return ship func _on_garrison_lost(outpost_id: int, vessel_type: String) -> void: var crew: Array = GameState.garrison_crew(outpost_id) var index: int = crew.find(vessel_type) if index >= 0: crew.remove_at(index)