Resolve pending gameplay tickets

This commit is contained in:
Loïc Wisniewski 2026-09-02 13:43:56 +02:00
commit c2ebbfaa2d
28 changed files with 618 additions and 188 deletions

View file

@ -5,7 +5,7 @@
script = ExtResource("1")
definition_id = "distorder"
display_name = "Energy Propulser"
description = "Unarmed propulsion hull that burns energy to drive the whole fleet faster. While one flies with you, a fader lets you push cruise speed past its default."
description = "Unarmed propulsion hull that burns energy to drive the whole fleet faster. It also grants an aimed fleet dash; each Propulser carries its own recharge."
build_cost = 14
build_resource_costs = {"ore": 14, "energy": 8}
build_time = 16.0
@ -18,7 +18,7 @@ component_scenes = Array[PackedScene]([ExtResource("2")])
base_stats = {"max_speed": 645.0, "acceleration": 1845.0, "delivery_speed": 180.0}
unlock_id = "unlock_distorder"
unlock_name = "Unlock Energy Propulser"
unlock_description = "Research a propulsion hull that burns energy to drive the whole fleet faster - while one flies with you, a fader lets you push cruise speed past its default."
unlock_description = "Research a propulsion hull that burns energy to drive the whole fleet faster and grants an aimed fleet dash."
unlock_cost = 0
unlock_resource_costs = {"energy": 50}
research_point_cost = 1

View file

@ -33,6 +33,11 @@ var fleet_target: Vector2 = Vector2.ZERO
# travelling along. Zero heading means the fleet is not being steered.
var anchor: Vector2 = Vector2.ZERO
var steer_direction: Vector2 = Vector2.ZERO
# An aimed Propulser burst translates the complete formation as one body.
var dash_direction: Vector2 = Vector2.ZERO
var dash_remaining: float = 0.0
const DASH_SPEED: float = 1400.0
func _ready() -> void:
@ -227,12 +232,29 @@ func move_in_direction(direction: Vector2) -> void:
steer_direction = direction.normalized()
func dash(direction: Vector2, distance: float) -> bool:
if direction.length() < 0.001 or distance <= 0.0 or ships.is_empty():
return false
if dash_remaining <= 0.0:
anchor = get_formation_origin()
dash_direction = direction.normalized()
dash_remaining = distance
return true
func is_dashing() -> bool:
return dash_remaining > 0.0
func _process(delta: float) -> void:
# #158: a building may pay a local cost out of the hold when the fleet is
# actually standing at it, and the autoload has no other way to know
# where that is. Written before the early return below - a parked fleet
# is exactly the case that matters.
GameState.fleet_position = get_centroid()
if is_dashing():
_advance_dash(delta)
return
if steer_direction == Vector2.ZERO:
return
# The anchor - the fleet's formation centre - travels at cruise_speed.
@ -244,6 +266,29 @@ func _process(delta: float) -> void:
_assign_slot_targets()
func _advance_dash(delta: float) -> void:
var step: float = minf(DASH_SPEED * delta, dash_remaining)
var displacement: Vector2 = dash_direction * step
dash_remaining -= step
anchor += displacement
fleet_target += displacement
for ship in ships:
if not is_instance_valid(ship):
continue
# Move the hull and its destination together. That preserves every
# formation offset and prevents normal steering from trying to "repair"
# the burst by pulling ships back toward their pre-dash targets.
ship.position += displacement
ship.move_target += displacement
ship.velocity = Vector2.ZERO
ship.fleet_velocity = Vector2.ZERO
ship.rotation = dash_direction.angle() + PI / 2.0
if dash_remaining <= 0.001:
dash_remaining = 0.0
if steer_direction != Vector2.ZERO:
_assign_slot_targets()
func _effective_cruise_speed() -> float:
return cruise_speed * GameState.fleet_speed_multiplier

View file

@ -1,9 +1,25 @@
class_name VesselDistorderComponent
extends Node
extends "res://vessels/VesselCooldownFillComponent.gd"
# Pure presence marker (#59) - no per-shot behaviour, no tick(). Its only
# job is to make Ship.has_capability("distorder") true, which is what
# SpeedFader.gd/Fleet.gd key off to reveal and apply the speed boost.
# Energy drain while boosted is a later ticket (#62).
# The presence marker used by the speed fader, plus the Energy Propulser's
# independently recharging dash (#179). Keeping both on one component means
# a vessel is the source of both propulsion capabilities and carries its own
# cooldown just like gas bombers and laser vessels do.
var capability_id := "distorder"
const DASH_COOLDOWN: float = 5.0
const READY_COLOR := Color(0.85, 0.95, 0.15)
const COOLING_COLOR := Color(0.28, 0.31, 0.08)
func _init() -> void:
cooldown_duration = DASH_COOLDOWN
func consume_dash() -> bool:
return _begin_cooldown(DASH_COOLDOWN)
func _ready_color() -> Color: return READY_COLOR
func _cooling_color() -> Color: return COOLING_COLOR

View file

@ -2,5 +2,5 @@
[ext_resource type="Script" path="res://vessels/VesselDistorderComponent.gd" id="1"]
[node name="DistorderComponent" type="Node"]
[node name="DistorderComponent" type="Node2D"]
script = ExtResource("1")