diff --git a/level/scripts/level.gd b/level/scripts/level.gd index 1675240..b231f2c 100644 --- a/level/scripts/level.gd +++ b/level/scripts/level.gd @@ -45,6 +45,9 @@ func _ready(): # Reshape the menu into the gladiator outfitting screen _setup_outfitting_menu() + # Clients: if the host closes the arena, bank winnings and return to camp + Network.server_disconnected.connect(_on_server_closed) + # Create or find weapons container if has_node("WeaponsContainer"): weapons_container = get_node("WeaponsContainer") @@ -81,6 +84,19 @@ func _on_connected_to_server(): print("[Level] Connected to server! Cleaning up manual weapons") _cleanup_manual_weapons_on_client() +## The host closed the arena while we were connected (client side). +## Leaving alive counts as retiring - bank everything, then back to camp. +func _on_server_closed(): + print("[Level] Server disconnected - returning to camp") + if GameState.run_active: + GameState.retire_run() + + if has_node("/root/HUD"): + get_node("/root/HUD").reset() + + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + get_tree().reload_current_scene() + ## Called by Network when multiplayer peer is set up func initialize_multiplayer(): print("[Level] initialize_multiplayer called. is_server: ", multiplayer.is_server()) @@ -235,6 +251,9 @@ func _on_player_connected(peer_id, player_info): for orb_id in _active_orbs.keys(): rpc_id(peer_id, "_spawn_health_orb_local", orb_id, _active_orbs[orb_id]) + # Sync the current wave number so the late joiner's HUD isn't stuck on "Prepare..." + GameState.rpc_id(peer_id, "sync_wave", GameState.current_wave) + # Sync equipped weapons for all existing players to the newly joined player print("[Server] Syncing equipped weapons to newly connected peer: ", peer_id) for player_node in players_container.get_children(): diff --git a/level/ui/scripts/escape_menu.gd b/level/ui/scripts/escape_menu.gd index e924772..21cd30b 100644 --- a/level/ui/scripts/escape_menu.gd +++ b/level/ui/scripts/escape_menu.gd @@ -54,14 +54,19 @@ func toggle_menu(): if player and player.is_multiplayer_authority(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED -## Retiring is only possible while alive with an active run +## Alive with an active run: retire (bank winnings). +## Dead but spectating a live session: plain trip home (settlement already done). func _update_retire_button(): if not _retire_button: return - var can_retire = GameState.run_active and player and not player.is_dead - _retire_button.visible = can_retire - if can_retire: + if GameState.run_active and player and not player.is_dead: + _retire_button.visible = true _retire_button.text = "Retire to Camp (+%dg)" % GameState.run_gold + elif multiplayer.multiplayer_peer != null and player and player.is_dead: + _retire_button.visible = true + _retire_button.text = "Back to Camp" + else: + _retire_button.visible = false ## Called when Resume button is clicked func _on_resume_pressed(): diff --git a/level/ui/scripts/run_results.gd b/level/ui/scripts/run_results.gd index 2dd08a5..811566b 100644 --- a/level/ui/scripts/run_results.gd +++ b/level/ui/scripts/run_results.gd @@ -80,6 +80,15 @@ func _ready(): _add_spacer(vbox, 16) + # Spectate option - only when a teammate is still fighting + if _teammates_still_alive(): + var spectate_button = Button.new() + spectate_button.text = "Spectate" + spectate_button.custom_minimum_size = Vector2(0, 44) + spectate_button.pressed.connect(_on_spectate_pressed) + vbox.add_child(spectate_button) + _add_spacer(vbox, 6) + # Return button var return_button = Button.new() return_button.text = "Return to Camp" @@ -90,6 +99,25 @@ func _ready(): # Make sure the mouse is usable on the overlay Input.mouse_mode = Input.MOUSE_MODE_VISIBLE +## Any other living player in the arena? +func _teammates_still_alive() -> bool: + if multiplayer.multiplayer_peer == null: + return false + var level = get_tree().get_current_scene() + if not level or not level.has_node("PlayersContainer"): + return false + var my_id = multiplayer.get_unique_id() + for p in level.get_node("PlayersContainer").get_children(): + if p is Character and not p.is_dead and str(p.name).to_int() != my_id: + return true + return false + +## Close the overlay and watch the run play out (escape menu can leave later). +## For the host this also keeps the server alive for everyone else. +func _on_spectate_pressed(): + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + queue_free() + func _add_header(parent: Control, text: String): var label = Label.new() label.text = text