spaceship/Planet.gd

140 lines
5.5 KiB
GDScript3
Raw Normal View History

extends Node2D
2026-08-14 16:05:32 +02:00
# A landable world-object - unlike regular Asteroid.gd, planets are
# stationary ("Big Stable Asteroid" - stable means it doesn't drift or
# destroy ships on touch). Whether the fleet counts as "landed" is decided
# by PlanetField comparing the fleet's CENTROID distance to landing_radius,
# not per-ship Area2D collision - a single stable number, immune to the
# constant minor jostling of individual ships holding formation, which
# used to make the "enter atmosphere" prompt flicker on/off as ships
# drifted in and out of the boundary independently of each other even
# while the fleet as a whole was clearly sitting on the planet.
@export var planet_name: String = ""
2026-08-13 21:07:35 +02:00
@export var planet_type: String = "rocky"
@export var resource_type: String = "ore"
2026-08-14 16:05:32 +02:00
@export var surface_profile_id: String = "rocky"
@export var landing_radius: float = 140.0
# Set by whoever spawns this planet (PlanetField, seeded from the chunk's
# own coordinates) so the outline shape is stable across respawns of the
# same chunk, instead of reading as a "different" planet every time.
@export var shape_seed: int = 0
var shape_points: PackedVector2Array = PackedVector2Array()
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
# An extractor is built down on this planet's SURFACE, in a different scene
# the player is no longer looking at, so from space nothing distinguished a
# planet already being mined from an untouched one - you had to land to find
# out. This is a derrick planted on the rim: the same amber the world map
# uses for its extractor badge, so the two read as the same fact.
const EXTRACTOR_MARKER_COLOR: Color = Color(1.0, 0.85, 0.3)
const DERRICK_HEIGHT: float = 44.0
const DERRICK_HALF_WIDTH: float = 16.0
# Kept in step with GameState.extractor_added rather than polled per frame.
# An extractor is built from SPACE (BuildController._planet_under_fleet(),
# with the fleet parked on the planet), so this node is already in the tree
# with its _ready() long since run when the answer changes - resolving it
# only at _ready() left the planet looking untouched until it happened to
# despawn and respawn.
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
var shows_extractor: bool = false
2026-08-13 21:07:35 +02:00
var gas_phase: float = 0.0
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
func _ready() -> void:
add_to_group("planets")
z_index = -1
var rng: RandomNumberGenerator = RandomNumberGenerator.new()
rng.seed = shape_seed
var point_count: int = 10
for i in range(point_count):
var angle: float = (float(i) / point_count) * TAU
var radius: float = rng.randf_range(70.0, 100.0)
shape_points.append(Vector2(cos(angle), sin(angle)) * radius)
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
shows_extractor = GameState.has_extractor_at(planet_name)
GameState.extractor_added.connect(_on_extractor_added)
queue_redraw()
2026-08-13 21:07:35 +02:00
func _process(delta: float) -> void:
if planet_type == "gas_giant":
gas_phase += delta
queue_redraw()
func _on_extractor_added(built_on: String) -> void:
# Only the planet it was actually built on, and only a redraw when the
# answer really changed - every other planet in range gets this too.
if built_on != planet_name or shows_extractor:
return
shows_extractor = true
queue_redraw()
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
func extractor_derrick_anchor() -> Vector2:
# The rim vertex the derrick stands on: the shape's own topmost point,
# rather than a fixed radius, so it sits ON the outline whatever lumpy
# shape the seed drew instead of floating off it or sinking inside it.
var base: Vector2 = Vector2(0.0, -100.0)
for p in shape_points:
if p.y < base.y:
base = p
return base
func extractor_derrick_tip() -> Vector2:
# Exposed separately from the polygon because "does the derrick clear the
# planet's outline" is a question about the TIP alone. The base corners
# sit a half-width off to either side, so they are sqrt(|base|^2 + w^2)
# from the centre and can clear the rim on their own even when the tower
# points straight into the ground.
var base: Vector2 = extractor_derrick_anchor()
var out: Vector2 = base.normalized()
if out == Vector2.ZERO:
out = Vector2.UP
return base + out * DERRICK_HEIGHT
func extractor_derrick_points() -> PackedVector2Array:
var base: Vector2 = extractor_derrick_anchor()
var out: Vector2 = base.normalized()
if out == Vector2.ZERO:
out = Vector2.UP
var side: Vector2 = Vector2(-out.y, out.x)
var tip: Vector2 = extractor_derrick_tip()
return PackedVector2Array([
base - side * DERRICK_HALF_WIDTH,
tip - side * (DERRICK_HALF_WIDTH * 0.35),
tip + side * (DERRICK_HALF_WIDTH * 0.35),
base + side * DERRICK_HALF_WIDTH,
])
func _draw() -> void:
2026-08-13 21:07:35 +02:00
if planet_type == "gas_giant":
_draw_gas_giant()
if shows_extractor:
NeonDraw.draw_glowing_polygon(self, extractor_derrick_points(), EXTRACTOR_MARKER_COLOR, 1.0)
return
NeonDraw.draw_glowing_polygon(self, shape_points, Color(0.6, 0.55, 0.5), 1.0)
Show buildings on the world map, and extractors on their planets Forgejo #11: buildings the player has placed now appear on the world map. They are placed freely in space rather than tied to a chunk, so they get their own pass in WorldMapContent._draw() rather than riding along with the chunk markers. Each type has its own shape and colour, mirroring Building.gd's constants so a marker reads as the same building you built. Forgejo #9: a planet with an extractor built on it now shows it, in both places you look at a planet - an amber derrick on its outline in space, and a matching badge on its world-map marker. The extractor lives on PlanetSurface.tscn, a scene you are not looking at from space, so an already-mined planet was otherwise indistinguishable from an untouched one until you landed on it. The positioning steps are factored out (building_markers(), chunk_has_extractor_badge(), extractor_derrick_tip()) so headless tests can assert on them - --headless has no GPU and draws no pixels, so there is nothing to check in _draw() itself. Both features were also rendered for real via tests/screenshot.sh and looked at. extractor_derrick_tip() exists because the first version of the "derrick is not buried inside the planet" check measured the furthest of all four derrick points, which passes a derrick pointing straight into the ground: the base corners sit a half-width off the rim vertex and clear the outline on their own. Mutation-testing caught it. screenshot.sh now clears a stale /tmp/.X99-lock. A run killed mid-screenshot leaves it behind and Xvfb then refuses :99, which resurfaces later as Godot's misleading "X11 Display is not available". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00
if shows_extractor:
NeonDraw.draw_glowing_polygon(self, extractor_derrick_points(), EXTRACTOR_MARKER_COLOR, 1.0)
2026-08-13 21:07:35 +02:00
func _draw_gas_giant() -> void:
# A round, animated giant with drifting latitudinal bands. It is visibly
# unlike the rocky outline and keeps moving even while the fleet is idle.
var rim := PackedVector2Array()
for i in range(32):
var a := TAU * float(i) / 32.0
rim.append(Vector2(cos(a) * 118.0, sin(a) * 118.0))
NeonDraw.draw_glowing_polygon(self, rim, Color(0.4, 0.78, 1.0), 0.9)
for row in [-62.0, -28.0, 8.0, 43.0, 73.0]:
var band := PackedVector2Array()
var half_width: float = sqrt(maxf(0.0, 118.0 * 118.0 - row * row))
for i in range(15):
var x := lerpf(-half_width, half_width, float(i) / 14.0)
band.append(Vector2(x, row + sin(x * 0.045 + gas_phase * 1.6 + row) * 7.0))
NeonDraw.draw_glowing_path(self, band, Color(0.85, 0.52, 1.0), 0.75, 0.65)