Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
extends Area2D
|
|
|
|
|
|
|
|
|
|
signal destroyed
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
const VESSEL_DEFINITIONS = preload("res://VesselDefinitions.gd")
|
|
|
|
|
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
@export var max_speed: float = 460.0
|
|
|
|
|
@export var acceleration: float = 450.0
|
|
|
|
|
@export var rotation_speed: float = 8.0
|
|
|
|
|
@export var arrive_radius: float = 6.0
|
2026-08-11 22:50:09 +02:00
|
|
|
@export var vessel_type: String = "interceptor"
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
var definition: Resource
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
var glow_color: Color = Color(0.25, 0.85, 1.0)
|
2026-08-14 22:12:57 +02:00
|
|
|
var core_color: Color = Color.WHITE
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
var velocity: Vector2 = Vector2.ZERO
|
|
|
|
|
var fleet_velocity: Vector2 = Vector2.ZERO
|
|
|
|
|
var move_target: Vector2
|
2026-08-14 22:12:57 +02:00
|
|
|
|
|
|
|
|
# Compatibility accessors keep old saves/tests readable while shield state
|
|
|
|
|
# itself belongs exclusively to the optional ShieldComponent.
|
|
|
|
|
var shield_charges: int:
|
|
|
|
|
get:
|
|
|
|
|
var component: Node = get_component("shield")
|
|
|
|
|
return component.charges if component != null else 0
|
|
|
|
|
set(value):
|
|
|
|
|
var component: Node = get_component("shield")
|
|
|
|
|
if component != null:
|
|
|
|
|
component.set_charges(value)
|
|
|
|
|
|
|
|
|
|
var shield_recharge_left: float:
|
|
|
|
|
get:
|
|
|
|
|
var component: Node = get_component("shield")
|
|
|
|
|
return component.recharge_left if component != null else 0.0
|
|
|
|
|
set(value):
|
|
|
|
|
var component: Node = get_component("shield")
|
|
|
|
|
if component != null:
|
|
|
|
|
component.recharge_left = value
|
|
|
|
|
|
|
|
|
|
var projectile_damage: int:
|
|
|
|
|
get:
|
|
|
|
|
var component: Node = get_component("weapon")
|
|
|
|
|
return component.projectile_damage if component != null else 0
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
|
move_target = position
|
|
|
|
|
add_to_group("player_ships")
|
|
|
|
|
area_entered.connect(_on_area_entered)
|
2026-08-14 22:12:57 +02:00
|
|
|
definition = VESSEL_DEFINITIONS.get_definition(vessel_type)
|
|
|
|
|
if definition == null:
|
|
|
|
|
return
|
|
|
|
|
glow_color = definition.glow_color
|
|
|
|
|
for component_scene in definition.component_scenes:
|
|
|
|
|
var component: Node = component_scene.instantiate()
|
|
|
|
|
add_child(component)
|
|
|
|
|
if component.has_method("setup"):
|
|
|
|
|
component.setup()
|
2026-08-11 22:50:09 +02:00
|
|
|
apply_vessel_stats()
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
queue_redraw()
|
|
|
|
|
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
func get_component(capability: String) -> Node:
|
|
|
|
|
for child in get_children():
|
|
|
|
|
if child.get("capability_id") == capability:
|
|
|
|
|
return child
|
|
|
|
|
return null
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
func has_capability(capability: String) -> bool:
|
|
|
|
|
return get_component(capability) != null
|
2026-08-11 22:50:09 +02:00
|
|
|
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
func set_move_target(world_pos: Vector2) -> void:
|
|
|
|
|
move_target = world_pos
|
2026-08-11 22:50:09 +02:00
|
|
|
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
func apply_vessel_stats() -> void:
|
|
|
|
|
if definition == null:
|
|
|
|
|
definition = VESSEL_DEFINITIONS.get_definition(vessel_type)
|
|
|
|
|
if definition == null:
|
2026-08-11 22:50:09 +02:00
|
|
|
return
|
2026-08-14 22:12:57 +02:00
|
|
|
max_speed = GameState.vessel_stat(vessel_type, "max_speed")
|
|
|
|
|
acceleration = GameState.vessel_stat(vessel_type, "acceleration")
|
|
|
|
|
for child in get_children():
|
|
|
|
|
if child.has_method("apply_stats"):
|
|
|
|
|
child.apply_stats()
|
2026-08-11 22:50:09 +02:00
|
|
|
|
|
|
|
|
|
2026-08-14 22:12:57 +02:00
|
|
|
func has_shield() -> bool:
|
|
|
|
|
var component: Node = get_component("shield")
|
|
|
|
|
return component != null and component.is_active()
|
2026-08-11 22:50:09 +02:00
|
|
|
|
|
|
|
|
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
func _physics_process(delta: float) -> void:
|
2026-08-14 22:12:57 +02:00
|
|
|
for child in get_children():
|
|
|
|
|
if child.has_method("tick"):
|
|
|
|
|
child.tick(delta)
|
|
|
|
|
|
2026-08-16 17:59:04 +02:00
|
|
|
# #59: multiplied here, at the point of use, rather than baked into the
|
|
|
|
|
# cached max_speed/acceleration fields apply_vessel_stats() sets - those
|
|
|
|
|
# stay the true base values, so a stat upgrade researched mid-boost can't
|
|
|
|
|
# corrupt them, and the fader takes effect the instant it moves with no
|
|
|
|
|
# re-apply needed. arrive_radius is scaled too: at N physics ticks/sec a
|
|
|
|
|
# ship's per-tick step is `speed * delta`, and once that step outgrows
|
|
|
|
|
# the (otherwise fixed) arrive_radius it can overshoot the snap-to-target
|
|
|
|
|
# zone every tick while chasing a target that is ALSO now receding N
|
|
|
|
|
# times faster - a small, boost-proportional radius keeps the same
|
|
|
|
|
# "snaps onto its slot" formation tightness at any boost level that
|
|
|
|
|
# 1.0x already has.
|
|
|
|
|
var boost: float = GameState.fleet_speed_multiplier
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
var to_target: Vector2 = move_target - position
|
|
|
|
|
var distance: float = to_target.length()
|
2026-08-16 17:59:04 +02:00
|
|
|
if distance <= arrive_radius * boost:
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
position = move_target
|
|
|
|
|
velocity = Vector2.ZERO
|
|
|
|
|
else:
|
2026-08-16 17:59:04 +02:00
|
|
|
var effective_acceleration: float = acceleration * boost
|
|
|
|
|
var braking_speed: float = sqrt(2.0 * effective_acceleration * distance)
|
|
|
|
|
var desired_speed: float = min(max_speed * boost, braking_speed)
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
var desired_velocity: Vector2 = to_target.normalized() * desired_speed
|
2026-08-16 17:59:04 +02:00
|
|
|
velocity = velocity.move_toward(desired_velocity, effective_acceleration * delta)
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
position += velocity * delta
|
|
|
|
|
|
|
|
|
|
var motion: Vector2 = velocity + fleet_velocity
|
|
|
|
|
if motion.length() > 8.0:
|
|
|
|
|
var target_angle: float = motion.angle() + PI / 2.0
|
|
|
|
|
rotation = lerp_angle(rotation, target_angle, rotation_speed * delta)
|
|
|
|
|
|
|
|
|
|
|
2026-08-15 14:43:29 +02:00
|
|
|
func take_damage(_amount: int) -> bool:
|
|
|
|
|
# Ships have no hit points - any unabsorbed hit is lethal. A shield
|
|
|
|
|
# charge absorbs a hit entirely regardless of its amount, matching
|
|
|
|
|
# VesselShieldComponent's existing all-or-nothing semantics; `_amount`
|
|
|
|
|
# is accepted (not `int`-clamped away) so callers like EnemyProjectile
|
|
|
|
|
# have a normal damage parameter to pass even though it isn't graded here.
|
2026-08-14 22:12:57 +02:00
|
|
|
for child in get_children():
|
|
|
|
|
if child.has_method("absorb_hit") and child.absorb_hit():
|
2026-08-15 14:43:29 +02:00
|
|
|
return true
|
2026-08-11 22:50:09 +02:00
|
|
|
ShipExplosionVFX.spawn(get_tree().root, position)
|
|
|
|
|
destroyed.emit()
|
|
|
|
|
queue_free()
|
2026-08-15 14:43:29 +02:00
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_area_entered(area: Area2D) -> void:
|
|
|
|
|
if not (area.is_in_group("enemies") or area.is_in_group("asteroids")):
|
|
|
|
|
return
|
|
|
|
|
var absorbed: bool = take_damage(1)
|
|
|
|
|
if absorbed and area.is_in_group("asteroids") and is_instance_valid(area):
|
|
|
|
|
area.queue_free()
|
2026-08-11 22:50:09 +02:00
|
|
|
|
|
|
|
|
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
func _draw() -> void:
|
2026-08-14 22:12:57 +02:00
|
|
|
var points: PackedVector2Array = definition.hull_points if definition != null else PackedVector2Array([
|
|
|
|
|
Vector2(0, -22), Vector2(-14, 16), Vector2(14, 16),
|
|
|
|
|
])
|
Initial commit: SpaceshipTest, a top-down space RTS in Godot 4.7.1
A fleet of neon-outlined ships explores a procedurally generated, seeded
infinite world: tap/joystick steering, pinch zoom, formation pivoting,
planet landings, an ore economy with extractors and haulers, and player
built structures (mothership, ship crafter, command centre).
All art is hand-drawn with draw_* calls rather than textures, and the
backgrounds are camera-relative shaders so they stay precise at large
world coordinates.
Includes a 35-test headless suite (tests/run_tests.sh) plus screenshot
tooling that renders real frames through Xvfb and Mesa softpipe, since
--headless has no GPU.
Excluded from version control: .godot/ (import cache, and it holds
export credentials), builds/, android/build/ and packaged APKs - all
regenerable, and ~325 MB combined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 18:41:58 +02:00
|
|
|
NeonDraw.draw_glowing_polygon(self, points, glow_color, 1.0, core_color)
|