Add timed craft queue and hangar
This commit is contained in:
parent
b6427b080d
commit
82552464d2
14 changed files with 660 additions and 191 deletions
149
GameState.gd
149
GameState.gd
|
|
@ -294,12 +294,15 @@ func reset_progression() -> void:
|
|||
# on the same spot.
|
||||
var next_building_id: int = 1
|
||||
|
||||
# Vessels that have been paid for and built but not yet handed to the fleet,
|
||||
# as a list of VESSEL_* types - a shield vessel finishing while the player is
|
||||
# on a planet must not come back as an interceptor. Production is simulated
|
||||
# here, in the autoload, the same way extractor haulers are, so an order can
|
||||
# complete where there is no Fleet node to hand a ship to.
|
||||
var vessels_owed: Array = []
|
||||
# Finished vessels waiting at the mothership for the player to collect
|
||||
# (#23), as a list of VESSEL_* types - a shield vessel must not come back as
|
||||
# an interceptor. They are NOT handed to the fleet automatically: the player
|
||||
# picks what to crew from the hangar, so a full fleet no longer stops a
|
||||
# crafter from building.
|
||||
#
|
||||
# Production is simulated here, in the autoload, the same way extractor
|
||||
# haulers are, so a build finishes whether or not the player is in space.
|
||||
var stored_vessels: Array = []
|
||||
|
||||
signal order_completed(building_id: int, type: String)
|
||||
|
||||
|
|
@ -337,6 +340,24 @@ const VESSEL_NAMES: Dictionary = {
|
|||
|
||||
const SHIELD_COST: int = 14
|
||||
|
||||
# How long one vessel takes to build, once its ore is actually in the
|
||||
# building. A queue you can watch and cancel needs time to exist in.
|
||||
const BUILD_TIMES: Dictionary = {
|
||||
VESSEL_INTERCEPTOR: 10.0,
|
||||
VESSEL_SHIELD: 16.0,
|
||||
VESSEL_TRANSPORTER: 8.0,
|
||||
}
|
||||
|
||||
|
||||
# Test seam. Headless tests need the timed path - queued, started, finishing
|
||||
# - without sitting through a real 10-second build each, and a suite that
|
||||
# slow stops being run. Left at 1.0 in the game.
|
||||
var build_time_scale: float = 1.0
|
||||
|
||||
|
||||
func build_time(product: String) -> float:
|
||||
return float(BUILD_TIMES.get(product, 10.0)) * build_time_scale
|
||||
|
||||
# Researched at the ship lab, per vessel type. Separate from SKILL_TREE on
|
||||
# purpose: that gates what the player may build at all and is a strategic
|
||||
# spine, while these are repeatable tuning on a vessel you already have.
|
||||
|
|
@ -553,11 +574,44 @@ func pending_orders_of(id: int, product: String) -> int:
|
|||
return 0
|
||||
var n: int = 0
|
||||
for o in b.get("orders", []):
|
||||
if o == product:
|
||||
if String(o.product) == product:
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
||||
func order_list(id: int) -> Array:
|
||||
# What the queue panel draws: product, and how far along the one being
|
||||
# built is. Everything behind it reads as simply waiting.
|
||||
var out: Array = []
|
||||
var b: Dictionary = find_building(id)
|
||||
if b.is_empty():
|
||||
return out
|
||||
for o in b.get("orders", []):
|
||||
var product: String = String(o.product)
|
||||
out.append({
|
||||
"product": product,
|
||||
"started": bool(o.started),
|
||||
"progress": clampf(float(o.elapsed) / maxf(build_time(product), 0.001), 0.0, 1.0),
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
func cancel_order(id: int, index: int) -> bool:
|
||||
# Refunds whatever was actually taken: a build that has started has its
|
||||
# ore in the building already, one still waiting never paid.
|
||||
var b: Dictionary = find_building(id)
|
||||
if b.is_empty():
|
||||
return false
|
||||
var orders: Array = b.get("orders", [])
|
||||
if index < 0 or index >= orders.size():
|
||||
return false
|
||||
var order: Dictionary = orders[index]
|
||||
if bool(order.started):
|
||||
b.stock = int(b.get("stock", 0)) + product_cost(String(order.product))
|
||||
orders.remove_at(index)
|
||||
return true
|
||||
|
||||
|
||||
func outstanding_cost(id: int) -> int:
|
||||
# What this building still needs delivered to clear everything queued.
|
||||
var b: Dictionary = find_building(id)
|
||||
|
|
@ -565,7 +619,9 @@ func outstanding_cost(id: int) -> int:
|
|||
return 0
|
||||
var need: int = 0
|
||||
for o in b.get("orders", []):
|
||||
need += product_cost(o)
|
||||
# A started build has already paid, so it is not still owed.
|
||||
if not bool(o.started):
|
||||
need += product_cost(String(o.product))
|
||||
return maxi(need - int(b.get("stock", 0)), 0)
|
||||
|
||||
|
||||
|
|
@ -592,40 +648,69 @@ func queue_order(id: int, product: String = PRODUCT_SHIP) -> bool:
|
|||
if product == PRODUCT_TRANSPORTER and not can_station_transporter():
|
||||
# Every berth is taken or spoken for (#20).
|
||||
return false
|
||||
(b.orders as Array).append(product)
|
||||
(b.orders as Array).append({"product": product, "elapsed": 0.0, "started": false})
|
||||
return true
|
||||
|
||||
|
||||
func _advance_production() -> void:
|
||||
func _advance_production(delta: float) -> void:
|
||||
for b in buildings:
|
||||
if not can_produce(b.type):
|
||||
continue
|
||||
# `while`, not `if`: a delivery big enough to cover several queued
|
||||
# orders should clear them all rather than dribbling one out per
|
||||
# frame.
|
||||
var orders: Array = b.get("orders", [])
|
||||
while not orders.is_empty():
|
||||
var product: String = String(orders[0])
|
||||
var cost: int = product_cost(product)
|
||||
if orders.is_empty():
|
||||
continue
|
||||
|
||||
# ONE at a time, in order. A queue the player can watch and cancel
|
||||
# has to have a thing being built and things waiting behind it -
|
||||
# clearing the whole queue the instant the ore lands (which is what
|
||||
# this did before #23) leaves nothing to see or cancel.
|
||||
var order: Dictionary = orders[0]
|
||||
var product: String = String(order.product)
|
||||
var cost: int = product_cost(product)
|
||||
|
||||
if not bool(order.started):
|
||||
if int(b.get("stock", 0)) < cost:
|
||||
break
|
||||
continue
|
||||
if product == PRODUCT_TRANSPORTER and transporters.size() >= transport_capacity():
|
||||
# A hub was demolished after this was ordered, so the berth it
|
||||
# was going to use is gone. Hold the order rather than
|
||||
# overfilling what is left.
|
||||
break
|
||||
# was going to use is gone. Hold rather than overfill.
|
||||
continue
|
||||
if product == PRODUCT_TRANSPORTER and nearest_hub_to(b.position).is_empty():
|
||||
# Every hub was demolished while this sat in the queue. Leave
|
||||
# it queued and the stock untouched rather than producing a
|
||||
# vessel with nowhere to live.
|
||||
break
|
||||
continue
|
||||
b.stock = int(b.stock) - cost
|
||||
orders.pop_front()
|
||||
if product == PRODUCT_TRANSPORTER:
|
||||
_station_transporter(b.position)
|
||||
else:
|
||||
vessels_owed.append(product)
|
||||
order_completed.emit(int(b.id), product)
|
||||
order.started = true
|
||||
|
||||
order.elapsed = float(order.elapsed) + delta
|
||||
if float(order.elapsed) < build_time(product):
|
||||
continue
|
||||
|
||||
orders.pop_front()
|
||||
if product == PRODUCT_TRANSPORTER:
|
||||
# Transporters are unchanged: they take themselves to the nearest
|
||||
# hub and wait for work (#19).
|
||||
_station_transporter(b.position)
|
||||
else:
|
||||
# Everything else is delivered to the mothership and waits there
|
||||
# until the player crews it (#23).
|
||||
stored_vessels.append(product)
|
||||
order_completed.emit(int(b.id), product)
|
||||
|
||||
|
||||
func claim_stored_vessel(index: int) -> String:
|
||||
# Hands one finished vessel over to be crewed. Returns its type, or ""
|
||||
# when there is nothing there - the caller decides whether the fleet
|
||||
# actually has room.
|
||||
if index < 0 or index >= stored_vessels.size():
|
||||
return ""
|
||||
return String(stored_vessels[index])
|
||||
|
||||
|
||||
func take_stored_vessel(index: int) -> String:
|
||||
var product: String = claim_stored_vessel(index)
|
||||
if product == "":
|
||||
return ""
|
||||
stored_vessels.remove_at(index)
|
||||
return product
|
||||
|
||||
|
||||
func has_mothership() -> bool:
|
||||
|
|
@ -756,7 +841,7 @@ func _process(delta: float) -> void:
|
|||
# Runs in the autoload, so extraction keeps earning - and queued orders
|
||||
# keep completing - while the player is on a planet's surface or nowhere
|
||||
# near either.
|
||||
_advance_production()
|
||||
_advance_production(delta)
|
||||
_advance_transporters(delta)
|
||||
if extractors.is_empty():
|
||||
return
|
||||
|
|
@ -827,7 +912,7 @@ func pending_transporter_orders() -> int:
|
|||
var n: int = 0
|
||||
for b in buildings:
|
||||
for o in b.get("orders", []):
|
||||
if o == PRODUCT_TRANSPORTER:
|
||||
if String(o.product) == PRODUCT_TRANSPORTER:
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue