Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7da4531f25 | ||
|
|
f5c60f8805 |
5 changed files with 93 additions and 41 deletions
63
GameState.gd
63
GameState.gd
|
|
@ -1155,6 +1155,40 @@ func _dispatch_transporters() -> void:
|
|||
var mothership: Variant = get_mothership_position()
|
||||
if mothership == null:
|
||||
return # nowhere to load from
|
||||
# Supply already-queued production and research before collecting passive
|
||||
# extractor output. Extractors can refill forever, so servicing them first
|
||||
# can otherwise consume every transporter each time one becomes idle and
|
||||
# permanently starve a partly supplied order.
|
||||
for b in buildings:
|
||||
var needed_resource := ""
|
||||
for resource_type in resource_types():
|
||||
if outstanding_resource_cost(int(b.id), resource_type) > 0 \
|
||||
and bank_inventory.amount(resource_type) > 0:
|
||||
needed_resource = resource_type
|
||||
break
|
||||
if needed_resource == "":
|
||||
continue
|
||||
# One at a time per building: without this every idle transporter
|
||||
# would be sent after the same shortfall and they would all arrive
|
||||
# carrying resources nobody needs any more.
|
||||
if _transporter_inbound_to(int(b.id)):
|
||||
continue
|
||||
var chosen: Dictionary = {}
|
||||
var best_d: float = INF
|
||||
for t in transporters:
|
||||
if t.state != TRANSPORTER_IDLE:
|
||||
continue
|
||||
var here: Vector2 = transporter_position(t)
|
||||
var d: float = here.distance_squared_to(b.position)
|
||||
if d < best_d:
|
||||
best_d = d
|
||||
chosen = t
|
||||
if chosen.is_empty():
|
||||
return # every transporter is busy
|
||||
chosen.target_id = int(b.id)
|
||||
chosen.resource_type = needed_resource
|
||||
_begin_leg(chosen, transporter_position(chosen), mothership, TRANSPORTER_TO_MOTHERSHIP)
|
||||
|
||||
# A distant extractor is worth visiting only once it has a full hold ready.
|
||||
# This keeps the transporter system meaningful: mining is local until a
|
||||
# vessel physically carries a researched-sized load home.
|
||||
|
|
@ -1180,35 +1214,6 @@ func _dispatch_transporters() -> void:
|
|||
chosen_extractor.target_id = 0
|
||||
_begin_leg(chosen_extractor, transporter_position(chosen_extractor), extractor.position,
|
||||
TRANSPORTER_TO_EXTRACTOR)
|
||||
for b in buildings:
|
||||
var needed_resource := ""
|
||||
for resource_type in resource_types():
|
||||
if outstanding_resource_cost(int(b.id), resource_type) > 0 \
|
||||
and bank_inventory.amount(resource_type) > 0:
|
||||
needed_resource = resource_type
|
||||
break
|
||||
if needed_resource == "":
|
||||
continue
|
||||
# One at a time per building: without this every idle transporter
|
||||
# would be sent after the same shortfall and they would all arrive
|
||||
# carrying ore nobody needs any more.
|
||||
if _transporter_inbound_to(int(b.id)):
|
||||
continue
|
||||
var chosen: Dictionary = {}
|
||||
var best_d: float = INF
|
||||
for t in transporters:
|
||||
if t.state != TRANSPORTER_IDLE:
|
||||
continue
|
||||
var here: Vector2 = transporter_position(t)
|
||||
var d: float = here.distance_squared_to(b.position)
|
||||
if d < best_d:
|
||||
best_d = d
|
||||
chosen = t
|
||||
if chosen.is_empty():
|
||||
return # every transporter is busy
|
||||
chosen.target_id = int(b.id)
|
||||
chosen.resource_type = needed_resource
|
||||
_begin_leg(chosen, transporter_position(chosen), mothership, TRANSPORTER_TO_MOTHERSHIP)
|
||||
|
||||
|
||||
func _advance_transporters(delta: float) -> void:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ const COOLING_COLOR := Color(0.38, 0.16, 0.32)
|
|||
|
||||
|
||||
func setup() -> void:
|
||||
# Keep the ship's normal neon outline above the charge fill.
|
||||
show_behind_parent = true
|
||||
queue_redraw()
|
||||
|
||||
|
||||
|
|
@ -37,18 +39,46 @@ func cooldown_progress() -> float:
|
|||
return 1.0 - cooldown_left / maxf(cooldown_duration, 0.001)
|
||||
|
||||
|
||||
func _filled_hull(points: PackedVector2Array, progress: float) -> PackedVector2Array:
|
||||
if points.size() < 3 or progress <= 0.001:
|
||||
return PackedVector2Array()
|
||||
if progress >= 0.999:
|
||||
return points.duplicate()
|
||||
var top := INF
|
||||
var bottom := -INF
|
||||
for point in points:
|
||||
top = minf(top, point.y)
|
||||
bottom = maxf(bottom, point.y)
|
||||
var fill_y: float = lerpf(bottom, top, clampf(progress, 0.0, 1.0))
|
||||
var clipped := PackedVector2Array()
|
||||
var previous: Vector2 = points[-1]
|
||||
var previous_inside := previous.y >= fill_y
|
||||
for current in points:
|
||||
var current_inside := current.y >= fill_y
|
||||
if current_inside != previous_inside:
|
||||
var amount: float = (fill_y - previous.y) / (current.y - previous.y)
|
||||
clipped.append(previous.lerp(current, amount))
|
||||
if current_inside:
|
||||
clipped.append(current)
|
||||
previous = current
|
||||
previous_inside = current_inside
|
||||
return clipped
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var parent_ship: Node = get_parent()
|
||||
var core: Color = parent_ship.core_color if parent_ship != null else Color.WHITE
|
||||
if is_ready():
|
||||
# Bright, complete halo: this bomber owns one available shot.
|
||||
NeonDraw.draw_glowing_circle(self, Vector2.ZERO, 31.0, READY_COLOR, 0.9, core)
|
||||
draw_circle(Vector2.ZERO, 4.0, Color(READY_COLOR, 0.5))
|
||||
if parent_ship == null or parent_ship.definition == null:
|
||||
return
|
||||
# A muted full ring says "not ready"; the bright arc fills clockwise so
|
||||
# the individual bomber also communicates how close it is to rearming.
|
||||
draw_arc(Vector2.ZERO, 31.0, 0.0, TAU, 48, Color(COOLING_COLOR, 0.65), 4.0, true)
|
||||
var progress := cooldown_progress()
|
||||
if progress > 0.001:
|
||||
draw_arc(Vector2.ZERO, 31.0, -PI / 2.0,
|
||||
-PI / 2.0 + TAU * progress, 48, Color(READY_COLOR, 0.92), 4.0, true)
|
||||
var core: Color = parent_ship.core_color if parent_ship != null else Color.WHITE
|
||||
var hull: PackedVector2Array = parent_ship.definition.hull_points
|
||||
if is_ready():
|
||||
# A fully lit hull means this individual bomber owns an available shot.
|
||||
draw_colored_polygon(hull, Color(READY_COLOR, 0.42))
|
||||
NeonDraw.draw_glowing_polygon(self, hull, READY_COLOR, 0.75, core, 0.75)
|
||||
return
|
||||
# Rearming is shown inside the bomber instead of as a shield-like ring:
|
||||
# the muted silhouette fills from its engines toward its nose.
|
||||
draw_colored_polygon(hull, Color(COOLING_COLOR, 0.58))
|
||||
var filled := _filled_hull(hull, cooldown_progress())
|
||||
if filled.size() >= 3:
|
||||
draw_colored_polygon(filled, Color(READY_COLOR, 0.68))
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ func _ready() -> void:
|
|||
var skill: GasBombController = main.get_node("GasBombController")
|
||||
skill._process(0.0)
|
||||
# Show both per-ship states in one frame: the first bomber is rearming,
|
||||
# while the second has the bright complete ready halo and supplies the aim.
|
||||
fleet.ships[2].get_component("gas_bomber").consume_shot(GasBombController.COOLDOWN)
|
||||
# while the second has a fully lit hull and supplies the aim.
|
||||
var rearming: Node = fleet.ships[2].get_component("gas_bomber")
|
||||
rearming.consume_shot(GasBombController.COOLDOWN)
|
||||
rearming.tick(GasBombController.COOLDOWN * 0.5)
|
||||
main.get_node("Camera2D").zoom = Vector2(1.8, 1.8)
|
||||
var button: Control = main.get_node("UI/GasBombButton")
|
||||
var press := InputEventScreenTouch.new()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ func _process(delta: float) -> bool:
|
|||
skill._process(0.0)
|
||||
if first.is_ready() or not second.is_ready() or not skill.button.enabled:
|
||||
return _fail("the first shot disabled more than its firing bomber")
|
||||
if not first._filled_hull(fleet.ships[0].definition.hull_points, 0.0).is_empty() \
|
||||
or first._filled_hull(fleet.ships[0].definition.hull_points, 0.5).size() < 3:
|
||||
return _fail("bomber cooldown did not produce an empty-to-filling hull")
|
||||
if skill.projectiles.size() != 1:
|
||||
return _fail("the first bomber did not launch exactly one projectile")
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ func _initialize() -> void:
|
|||
state.stored_vessels.clear()
|
||||
state.reset_progression()
|
||||
state.rocks = 0
|
||||
state.gas = 30
|
||||
state.gas = 10
|
||||
state.build_time_scale = 0.01
|
||||
|
||||
state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
||||
|
|
@ -18,8 +18,20 @@ func _initialize() -> void:
|
|||
if not state.queue_lab_research(lab_id, "unlock_gas_bomber"):
|
||||
_fail("could not queue gas research")
|
||||
return
|
||||
if state.outstanding_resource_cost(lab_id, GameState.RESOURCE_GAS) != 30:
|
||||
_fail("lab did not request 30 gas")
|
||||
# Reproduce a first 20-unit delivery followed by the remaining 10 being
|
||||
# available at the mothership. Full extractors must not take priority over
|
||||
# completing this already-partly-supplied research order.
|
||||
state.deposit_resource_to_building(lab_id, GameState.RESOURCE_GAS, 20)
|
||||
state.add_extractor("Ore A", Vector2(240, 0), GameState.RESOURCE_ORE)
|
||||
state.add_extractor("Ore B", Vector2(360, 0), GameState.RESOURCE_ORE)
|
||||
for extractor in state.extractors:
|
||||
extractor.inventory.deposit(GameState.RESOURCE_ORE, state.transporter_load_limit())
|
||||
if state.outstanding_resource_cost(lab_id, GameState.RESOURCE_GAS) != 10:
|
||||
_fail("partly supplied lab did not request its remaining 10 gas")
|
||||
return
|
||||
state._dispatch_transporters()
|
||||
if not state._transporter_inbound_to(lab_id):
|
||||
_fail("extractor collection took priority over waiting research")
|
||||
return
|
||||
|
||||
# Large steps make each physical leg complete in one call; multiple calls
|
||||
|
|
|
|||
Loading…
Reference in a new issue