extends Node3D const PLAYER_SCENE: PackedScene = preload("res://scenes/entities/player/player.tscn") @onready var players_root: Node3D = $EntityRoot/Players @onready var ground_shape: CollisionShape3D = $Ground/GroundShape @onready var npc_system: Node = $Systems/NpcSystem @onready var wave_system: Node = $Systems/WaveSystem func _ready() -> void: add_to_group("world") _setup_ground_collision() Net.world_ready.connect(_on_world_ready) Net.peer_world_loaded.connect(_on_peer_world_loaded) if Net.is_host(): multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) Net.reset_world_ready() Net.mark_world_loaded() func _exit_tree() -> void: if Net.is_host(): if multiplayer.peer_connected.is_connected(_on_peer_connected): multiplayer.peer_connected.disconnect(_on_peer_connected) if multiplayer.peer_disconnected.is_connected(_on_peer_disconnected): multiplayer.peer_disconnected.disconnect(_on_peer_disconnected) if Net.world_ready.is_connected(_on_world_ready): Net.world_ready.disconnect(_on_world_ready) if Net.peer_world_loaded.is_connected(_on_peer_world_loaded): Net.peer_world_loaded.disconnect(_on_peer_world_loaded) func _setup_ground_collision() -> void: var shape := WorldBoundaryShape3D.new() shape.plane = Plane(Vector3.UP, 0.0) ground_shape.shape = shape func _on_world_ready() -> void: if not Net.is_host(): return _spawn_player(1) for id in multiplayer.get_peers(): _spawn_player(id) npc_system.spawn_default_npcs() if wave_system and wave_system.has_method("start_wave"): wave_system.start_wave(GameState.current_wave) func _on_peer_connected(id: int) -> void: Net.tell_peer_to_load_scene(id, GameState.SCENE_WORLD) func _on_peer_world_loaded(peer_id: int) -> void: if not Net.is_host(): return _spawn_player(peer_id) func _on_peer_disconnected(id: int) -> void: var node := players_root.get_node_or_null(str(id)) if node: node.queue_free() func _spawn_player(peer_id: int) -> void: if players_root.get_node_or_null(str(peer_id)) != null: return var p: CharacterBody3D = PLAYER_SCENE.instantiate() p.name = str(peer_id) var spawn := _spawn_position_for(peer_id) players_root.add_child(p, true) p.global_position = spawn func _spawn_position_for(peer_id: int) -> Vector3: if GameState.portal_return_position != Vector3.ZERO: var off: float = float(peer_id) * 0.7 return GameState.portal_return_position + Vector3(cos(off) * 1.2, 1.5, sin(off) * 1.2) var angle: float = wrapf(float(peer_id) * 1.7, 0.0, TAU) return Vector3(cos(angle) * 5.0, 1.5, sin(angle) * 5.0)