diff --git a/scripts/game_manager.gd b/scripts/game_manager.gd index c8b024e..5c9bbf3 100644 --- a/scripts/game_manager.gd +++ b/scripts/game_manager.gd @@ -11,6 +11,10 @@ enum GameState { var current_state: GameState = GameState.HUB var _previous_state: GameState = GameState.HUB +const RunEndScreenScene = preload("res://scripts/ui/run_end_screen.gd") + +var _run_end_screen: CanvasLayer = null + func _ready() -> void: EventBus.run_started.connect(_on_run_started) EventBus.run_ended.connect(_on_run_ended) @@ -48,7 +52,10 @@ func start_run() -> void: func _on_run_started() -> void: change_state(GameState.RUN) -func _on_run_ended(_survived: bool) -> void: - # Body parts are always kept — save before returning to hub +func _on_run_ended(reached_hub: bool) -> void: + # Save immediately — body parts and bank are always kept SaveManager.save() - call_deferred("go_to_hub") + # Show end screen; it calls go_to_hub() when ready + _run_end_screen = RunEndScreenScene.new() + get_tree().root.add_child(_run_end_screen) + _run_end_screen.show_result(reached_hub) diff --git a/scripts/ui/run_end_screen.gd b/scripts/ui/run_end_screen.gd new file mode 100644 index 0000000..1a25cc9 --- /dev/null +++ b/scripts/ui/run_end_screen.gd @@ -0,0 +1,99 @@ +extends CanvasLayer + +# --------------------------------------------------------------------------- +# Run End Screen +# Instantiated by GameManager on run_ended. Call show_result(bool) after +# adding to tree. Calls GameManager.go_to_hub() when ready. +# --------------------------------------------------------------------------- + +const FADE_IN_TIME := 0.3 +const FADE_OUT_TIME := 0.3 +const DEATH_HOLD := 2.5 + +var _root: Control = null + +func _ready() -> void: + layer = 10 + process_mode = Node.PROCESS_MODE_ALWAYS + + +func show_result(reached_hub: bool) -> void: + if reached_hub: + _show_victory() + else: + _show_death() + + +# --------------------------------------------------------------------------- +# Death screen — auto-advances after DEATH_HOLD seconds +# --------------------------------------------------------------------------- +func _show_death() -> void: + _build_root(Color(0, 0, 0, 0.85)) + + _add_label("SYSTEMS OFFLINE", 36, Vector2(0, -80)) + _add_label("FLOOR %d" % RunManager.current_floor, 20, Vector2(0, 0)) + _add_label("ALL RUN DATA LOST", 16, Vector2(0, 40)) + + _fade_in() + await get_tree().create_timer(DEATH_HOLD).timeout + _fade_out_then_hub() + + +# --------------------------------------------------------------------------- +# Victory screen — player must click to continue +# --------------------------------------------------------------------------- +func _show_victory() -> void: + _build_root(Color(0, 0, 0, 0.75)) + + _add_label("RUN COMPLETE", 36, Vector2(0, -100)) + _add_label("FLOOR %d CLEARED" % RunManager.current_floor, 20, Vector2(0, -40)) + _add_label("SCRAP RETURNED: %d" % UpgradeManager.wallet_credits, 18, Vector2(0, 10)) + + var btn := Button.new() + btn.text = "RETURN TO HUB" + btn.position = Vector2(-80, 70) + btn.pressed.connect(_fade_out_then_hub) + _root.add_child(btn) + + _fade_in() + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +func _build_root(bg_color: Color) -> void: + if _root: + _root.queue_free() + + var bg := ColorRect.new() + bg.color = bg_color + bg.anchor_right = 1.0 + bg.anchor_bottom = 1.0 + bg.mouse_filter = Control.MOUSE_FILTER_STOP + add_child(bg) + + _root = Control.new() + _root.set_anchors_preset(Control.PRESET_CENTER) + bg.add_child(_root) + + +func _add_label(text: String, font_size: int, offset: Vector2) -> void: + var lbl := Label.new() + lbl.text = text + lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + lbl.add_theme_font_size_override("font_size", font_size) + lbl.position = offset + Vector2(-200, 0) + lbl.custom_minimum_size = Vector2(400, 0) + _root.add_child(lbl) + + +func _fade_in() -> void: + modulate.a = 0.0 + var tw := create_tween() + tw.tween_property(self, "modulate:a", 1.0, FADE_IN_TIME) + + +func _fade_out_then_hub() -> void: + var tw := create_tween() + tw.tween_property(self, "modulate:a", 0.0, FADE_OUT_TIME) + tw.tween_callback(func(): GameManager.go_to_hub())