extends Node2D @export var DEBUG_MULTIPLAYER: bool = true var player_scene = preload("res://scripts/entities/player/player.tscn") var pot_scene = preload("res://scripts/entities/world/pot.tscn") var has_started = false var round_started = false var start_round = false var round_finished = false func _ready() -> void: MultiplayerManager.addPlayerSignal.connect(_addPlayer) MultiplayerManager.delPlayerSignal.connect(_delPlayer) MultiplayerManager.finished_hosting.connect(_finishedHosting) MultiplayerManager.countdownFinished.connect(_finishedCountdown) #if id == 1: #var pot:CharacterBody2D = pot_scene.instantiate() #pot.position = Vector2(90,80) #$SpawnRoot.add_child(pot) if DEBUG_MULTIPLAYER: # Add a random delay to ensure instances start at different times var random_delay = randf_range(0.1, 0.5) await get_tree().create_timer(random_delay).timeout call_deferred("_setup_debug_multiplayer") pass func _finishedCountdown(): round_started = true # reset all players hp, kills and deaths $TimerRound.start($TimerRound.wait_time) # sync to other players! if multiplayer.is_server(): _syncTimerToPlayer.rpc($TimerRound.time_left) pass func time_to_minutes_secs(time: float): var mins = int(floor(int(time) / 60.0)) time -= mins * 60 var secs = int(time) #var mili = int((time - int(time)) * 100) var extraSecZero = "0" if secs < 10 else "" var extraMinZero = "0" if mins < 10 else "" return extraMinZero + str(mins) + ":" + extraSecZero + str(secs) func _process(_delta: float) -> void: if round_finished == false and round_started == false and start_round == false and multiplayer != null and multiplayer.is_server(): # make sure atleast 2 players are connected var _players = 0 for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl: _players += 1 #if _players == 2: #start_round_func.rpc() if round_started: $HUD/MarginContainerUpperRight/HBoxContainer/VBoxContainer/LabelTimeValue.text = time_to_minutes_secs($TimerRound.time_left) pass @rpc("call_local", "reliable") func start_round_func(): if start_round == true: return start_round = true MultiplayerManager.start_round() pass func _finishedHosting(): has_started = true pass func _addPlayer(id: int): print("add player:", id) #if id == 1: #var pot:CharacterBody2D = pot_scene.instantiate() #pot.position = Vector2(90,80) #$SpawnRoot.add_child(pot, true) var player: CharacterBody2D = player_scene.instantiate() player.name = str(id) #find empty 16x16 tile to spawn player ' if get_parent().get_parent() != null and get_parent().get_parent().has_node("TileMapLayerLower"): var tile_map = get_parent().get_parent().get_node("TileMapLayerLower") if tile_map != null: var player_cell = tile_map.local_to_map(self.global_position) var cell_tile_data = tile_map.get_cell_tile_data(player_cell) if cell_tile_data != null: var terrainData = cell_tile_data.get_custom_data("terrain") if terrainData != null and terrainData == 8: # 8 = stairs terrainMultiplier = 0.5 pass pass' # Only server calculates spawn positions - clients receive position via sync if multiplayer.is_server(): var best_spawn = Vector2(0, 0) var best_distance = -1 for spawnP: Node2D in $PlayerSpawnPoints.get_children(): var pos = spawnP.position var min_distance = INF # find spawn position which is furthest from all players... for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl: var dist = pl.position.distance_to(pos) min_distance = min(min_distance, dist) # Keep the smallest distance pass # Choose the spawn with the largest minimum distance if min_distance > best_distance: best_distance = min_distance best_spawn = pos player.position = best_spawn print("Server: Spawning player ", id, " at position: ", best_spawn) # else: clients will receive position via MultiplayerSynchronizer after add_child $SpawnRoot.add_child(player) if id == multiplayer.get_unique_id(): player.initStats(MultiplayerManager.character_data) # iniitate with own stats, cuz this is us... if multiplayer.is_server(): if !$TimerRound.is_stopped(): _syncTimerToPlayer.rpc_id(id, $TimerRound.time_left) pass @rpc("reliable") func _syncTimerToPlayer(iTimeLeft: float): round_started = true $TimerRound.start(iTimeLeft) pass func _delPlayer(id: int): if !$SpawnRoot.has_node(str(id)): return $SpawnRoot.get_node(str(id)).queue_free() pass func _on_timer_timeout() -> void: if has_started: var countPots = 0 for child in $SpawnRoot.get_children(): if "object_name" in child: countPots += 1 pass if countPots < 8: var pot = pot_scene.instantiate() pot.is_spawning = true pot.positionZ = 90 pot.position = Vector2(64 + 16 * randi_range(0, 5), 64 + 16 * randi_range(0, 5)) # Set server as authority for pot synchronization pot.set_multiplayer_authority(1) Console.print("Pot spawned with authority: ", pot.get_multiplayer_authority()) $SpawnRoot.add_child(pot, true) $TimerSpawnPots.wait_time = randf_range(0.2, 1.4) $TimerSpawnPots.start() # restart timer... pass # Replace with function body. func _on_timer_round_timeout() -> void: round_started = false $TimerUntilNextRound.start($TimerUntilNextRound.wait_time) if multiplayer.is_server(): MultiplayerManager.round_finished.rpc() pass # Replace with function body. func _on_timer_until_next_round_timeout() -> void: if multiplayer.is_server(): for pl2: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl2: var best_spawn = Vector2(0, 0) var best_distance = -1 for spawnP: Node2D in $PlayerSpawnPoints.get_children(): var pos = spawnP.position var min_distance = INF # find spawn position which is furthest from all players... for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl and pl != pl2: var dist = pl.position.distance_to(pos) min_distance = min(min_distance, dist) # Keep the smallest distance pass # Choose the spawn with the largest minimum distance if min_distance > best_distance: best_distance = min_distance best_spawn = pos pl2.position = best_spawn # reset player positions... start_round = false MultiplayerManager.new_round_started() if multiplayer.is_server(): start_round_func.rpc() pass # Replace with function body. func _setup_debug_multiplayer(): # Get the character select scene to access the selected character var character_select = get_tree().get_first_node_in_group("character_select") if not character_select: # Try to find it in the scene tree character_select = get_tree().current_scene.get_node_or_null("CanvasLayer/CharacterSelect") if character_select and character_select.has_method("get_current_character_stats"): # Set the character data from the character select MultiplayerManager.character_data = character_select.get_current_character_stats() else: # Fallback: create a default character MultiplayerManager.character_data = CharacterStats.new() MultiplayerManager.character_data.character_name = "DebugPlayer" $MainMenu._showHostButton() # Determine if this instance should host or join # Check for command line arguments first var should_host = false var args = OS.get_cmdline_args() # Check if --host or --join argument was passed if "--host" in args: should_host = true print("DEBUG: Host mode specified via command line argument") elif "--join" in args: should_host = false print("DEBUG: Join mode specified via command line argument") else: # Fallback: use process ID for deterministic behavior var process_id = OS.get_process_id() should_host = process_id % 2 == 1 print("DEBUG: No command line args, using process ID: ", process_id, " Should host: ", should_host) if should_host: print("DEBUG: Starting as HOST") $MainMenu._on_button_host_pressed() else: print("DEBUG: Starting as CLIENT") $MainMenu._on_button_join_pressed() func _exit_tree(): # Clean up the debug lock file when exiting if DEBUG_MULTIPLAYER: var lock_file_path = "user://debug_host.lock" if FileAccess.file_exists(lock_file_path): DirAccess.remove_absolute(lock_file_path) print("DEBUG: Cleaned up host lock file")