125 lines
5.1 KiB
GDScript
125 lines
5.1 KiB
GDScript
extends Node2D
|
|
|
|
# Space-side: bootstraps the fleet on Main.tscn startup (a fresh 5-ship
|
|
# fleet at the origin on first launch, or GameState's surviving ship
|
|
# count/position if we just returned from a planet's surface), shows an
|
|
# "enter atmosphere" prompt (name + button) when the fleet is standing on
|
|
# a landable planet, and - on press - fades out and swaps to a completely
|
|
# separate PlanetSurface.tscn scene (not an overlay toggled in place), so
|
|
# asteroids/enemies genuinely cannot exist there rather than merely being
|
|
# hidden. Fleet size and the world position/zoom to return to are handed
|
|
# off via the GameState autoload, since change_scene_to_file() tears down
|
|
# this whole scene tree.
|
|
|
|
@export var fade_duration: float = 0.4
|
|
@export var prompt_debounce_time: float = 0.15
|
|
|
|
@onready var planet_field: Node2D = get_node("../PlanetField")
|
|
@onready var asteroid_field: Node2D = get_node("../AsteroidField")
|
|
@onready var fleet: Node2D = get_node("../Fleet")
|
|
@onready var camera: Camera2D = get_node("../Camera2D")
|
|
@onready var planet_name_label: Label = get_node("../UI/PlanetNameLabel")
|
|
@onready var enter_button: IconButton = get_node("../UI/EnterAtmosphereButton")
|
|
@onready var world_map: Control = get_node("../UI/WorldMap")
|
|
@onready var build_controller: Node2D = get_node("../BuildController")
|
|
@onready var fade_rect: ColorRect = get_node("../FadeOverlay/FadeRect")
|
|
|
|
var current_planet: Node2D = null
|
|
var transitioning: bool = false
|
|
var pending_planet: Node2D = null
|
|
var pending_time: float = 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
enter_button.pressed.connect(_on_enter_pressed)
|
|
|
|
if GameState.has_return_position:
|
|
fleet.spawn_ships(GameState.surviving_ship_count, GameState.return_world_position)
|
|
camera.position = GameState.return_world_position
|
|
camera.zoom = Vector2(GameState.return_camera_zoom, GameState.return_camera_zoom)
|
|
GameState.has_return_position = false
|
|
else:
|
|
fleet.spawn_ships(5, Vector2.ZERO)
|
|
|
|
# AsteroidField/PlanetField normally only populate nearby chunks once
|
|
# their own check_interval timer elapses (~1s) - fine during normal
|
|
# continuous play (things appear far off-screen, gradually), but
|
|
# right after a fresh scene load it means an empty-looking world for
|
|
# a second before asteroids/the planet visibly pop in. Force an
|
|
# immediate synchronous populate now that the fleet is correctly
|
|
# positioned, so nothing is missing on the very first visible frame.
|
|
asteroid_field.update_chunks()
|
|
planet_field.update_chunks()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if transitioning:
|
|
return
|
|
|
|
if world_map.visible or build_controller.is_showing_building_prompt():
|
|
# EnterAtmosphereButton shares one "action" slot with the map's
|
|
# RecenterButton and with BuildController's building prompt - it doesn't make sense to enter
|
|
# a planet's atmosphere while looking at the star map anyway, so
|
|
# suppress the prompt entirely rather than let it sit hidden
|
|
# behind the map's backdrop only to pop back in the instant the
|
|
# map closes.
|
|
current_planet = null
|
|
pending_planet = null
|
|
planet_name_label.visible = false
|
|
enter_button.visible = false
|
|
return
|
|
|
|
# Debounced: only commit a visible/hidden change once the raw
|
|
# landing-detection result has stayed the same for prompt_debounce_time
|
|
# in a row, instead of reacting to every single frame's reading. Belt
|
|
# and suspenders on top of get_planet_fleet_is_on() already using the
|
|
# fleet's centroid (a single stable number - see Planet.gd) rather
|
|
# than any individual ship's position, so this only has to absorb
|
|
# genuine sub-debounce-window boundary crossings, not constant noise.
|
|
var detected_planet: Node2D = planet_field.get_planet_fleet_is_on()
|
|
if detected_planet != pending_planet:
|
|
pending_planet = detected_planet
|
|
pending_time = 0.0
|
|
else:
|
|
pending_time += delta
|
|
|
|
if pending_time < prompt_debounce_time or pending_planet == current_planet:
|
|
return
|
|
|
|
current_planet = pending_planet
|
|
if current_planet != null:
|
|
planet_name_label.text = current_planet.planet_name
|
|
planet_name_label.visible = true
|
|
enter_button.visible = true
|
|
else:
|
|
planet_name_label.visible = false
|
|
enter_button.visible = false
|
|
|
|
|
|
func _on_enter_pressed() -> void:
|
|
if transitioning or current_planet == null:
|
|
return
|
|
transitioning = true
|
|
planet_name_label.visible = false
|
|
enter_button.visible = false
|
|
|
|
GameState.has_return_position = true
|
|
GameState.surviving_ship_count = fleet.ships.size()
|
|
# The planet's own position, not fleet.get_centroid() - the fleet only
|
|
# needs to be somewhere within the planet's landing radius (not
|
|
# centered exactly on it) for the prompt to show, so the centroid can
|
|
# be tens of units off from the planet. Returning should always place
|
|
# the fleet back at the planet itself.
|
|
GameState.return_world_position = current_planet.position
|
|
GameState.return_camera_zoom = camera.zoom.x
|
|
GameState.planet_name = current_planet.planet_name
|
|
GameState.planet_id = current_planet.planet_id
|
|
GameState.planet_definition_id = current_planet.definition_id
|
|
|
|
var tween: Tween = create_tween()
|
|
tween.tween_property(fade_rect, "color:a", 1.0, fade_duration)
|
|
tween.tween_callback(_enter_surface)
|
|
|
|
|
|
func _enter_surface() -> void:
|
|
get_tree().change_scene_to_file("res://PlanetSurface.tscn")
|