Refactor recent gameplay systems after code review

This commit is contained in:
Loïc Wisniewski 2026-08-16 21:38:07 +02:00
commit d68f4d4f88
57 changed files with 530 additions and 572 deletions

View file

@ -387,7 +387,11 @@ func prerequisite_met(id: String) -> bool:
func can_unlock(id: String) -> bool:
var node: Dictionary = skill_node(id)
return not is_unlocked(id) and prerequisite_met(id) and can_afford(int(node.cost)) \
and research_points >= int(node.get("research_points", 0))
and research_points >= skill_research_point_cost(id)
func skill_research_point_cost(id: String) -> int:
return int(skill_node(id).get("research_points", 0))
func unlock(id: String) -> bool:
@ -396,7 +400,7 @@ func unlock(id: String) -> bool:
var node: Dictionary = skill_node(id)
if not spend(int(node.cost)):
return false
var points_needed: int = int(node.get("research_points", 0))
var points_needed: int = skill_research_point_cost(id)
if points_needed > 0:
research_points -= points_needed
research_points_changed.emit(research_points)
@ -446,6 +450,8 @@ func reset_new_game() -> void:
planet_id = ""
planet_definition_id = "rocky"
fleet_speed_multiplier = 1.0
_distorder_drain_progress = 0.0
boost_depleted_notice = false
research_points = 0
explored_chunks.clear()
bank_inventory = INVENTORY.new({RESOURCE_ORE: 500})
@ -520,6 +526,13 @@ func building_has_capability(building: Dictionary, capability: String) -> bool:
return definition != null and capability in definition.capabilities
func building_logistics_profile(building: Dictionary) -> Resource:
if building.is_empty():
return null
var definition: Resource = BUILDING_DEFINITIONS.get_definition(String(building.type))
return null if definition == null else definition.logistics
# --- vessel types and their upgrades (#5) ---------------------------------
# Combat vessels can join the player fleet. Transporter metadata lives in the
@ -1044,19 +1057,20 @@ func has_building_of_type(type: String) -> bool:
func remove_buildings_of_type(type: String) -> int:
var kept: Array = []
var removed_ids: Array = []
var removed_hub_ids: Array = []
var removed: int = 0
for b in buildings:
if b.type == type:
removed += 1
removed_ids.append(int(b.id))
if building_has_capability(b, "transport_hub"):
removed_hub_ids.append(int(b.id))
else:
kept.append(b)
buildings = kept
if type == BUILDING_TRANSPORT_HUB and not removed_ids.is_empty():
if not removed_hub_ids.is_empty():
var kept_transporters: Array = []
for transporter in transporters:
if int(transporter.get("hub_id", 0)) not in removed_ids:
if int(transporter.get("hub_id", 0)) not in removed_hub_ids:
kept_transporters.append(transporter)
transporters = kept_transporters
return removed
@ -1186,44 +1200,50 @@ func _extractor_inventory(extractor: Dictionary) -> RefCounted:
return inventory
# --- gas burners (#54) -----------------------------------------------------
#
# Converts gas into energy at the building's own site, simulated here
# alongside extractor mining and production so it keeps running whether or
# not the player is nearby. Deliberately withdraws only from the building's
# OWN inventory, never the bank - a burner with no local gas simply stalls,
# exactly like a lab or crafter waiting on materials, rather than conjuring
# energy for free.
# --- building logistics and resource conversion ---------------------------
const GAS_BURN_RATE: float = 1.0 # gas/second
const ENERGY_PER_GAS: int = 2
# Local gas reserve a burner tries to keep topped up. Transporters treat
# falling short of this the same way they treat a queued production/research
# order (see _dispatch_transporters()'s gas_burner_gas_deficit() use).
const GAS_BURNER_BUFFER: int = 40
func gas_burner_gas_deficit(id: int) -> int:
var b: Dictionary = find_building(id)
if not building_has_capability(b, "gas_burner"):
func building_logistics_deficit(id: int, resource_type: String) -> int:
var building: Dictionary = find_building(id)
var profile: Resource = building_logistics_profile(building)
if profile == null:
return 0
return maxi(0, GAS_BURNER_BUFFER - building_resource(id, RESOURCE_GAS))
var target: int = int(profile.input_buffers.get(resource_type, 0))
return maxi(0, target - building_resource(id, resource_type))
func _advance_gas_burners(delta: float) -> void:
for b in buildings:
if not building_has_capability(b, "gas_burner"):
func building_requested_resource(id: int, resource_type: String) -> int:
return maxi(outstanding_resource_cost(id, resource_type),
building_logistics_deficit(id, resource_type))
func _collectable_building_resource(building: Dictionary, minimum_amount: int) -> String:
var profile: Resource = building_logistics_profile(building)
if profile == null:
return ""
for resource_type in profile.collected_outputs:
if building_resource(int(building.id), resource_type) >= minimum_amount:
return resource_type
return ""
func _advance_resource_converters(delta: float) -> void:
for building in buildings:
var profile: Resource = building_logistics_profile(building)
if profile == null or profile.conversion_input == "" \
or profile.conversion_output == "" or profile.conversion_rate <= 0.0 \
or profile.conversion_yield <= 0:
continue
var inventory: RefCounted = _inventory_for_record(b)
if inventory.amount(RESOURCE_GAS) <= 0:
continue # stalled - no local gas to burn
var progress: float = float(b.get("gas_burn_progress", 0.0)) + delta * GAS_BURN_RATE
var whole: int = mini(int(floor(progress)), inventory.amount(RESOURCE_GAS))
var inventory: RefCounted = _inventory_for_record(building)
if inventory.amount(profile.conversion_input) <= 0:
continue
var progress: float = float(building.get("conversion_progress", 0.0)) \
+ delta * profile.conversion_rate
var whole: int = mini(int(floor(progress)), inventory.amount(profile.conversion_input))
if whole > 0:
inventory.withdraw(RESOURCE_GAS, whole)
inventory.deposit(RESOURCE_ENERGY, whole * ENERGY_PER_GAS)
inventory.withdraw(profile.conversion_input, whole)
inventory.deposit(profile.conversion_output, whole * profile.conversion_yield)
progress -= whole
b.gas_burn_progress = progress
building.conversion_progress = progress
func _advance_distorder_drain(delta: float) -> void:
@ -1341,7 +1361,7 @@ func _process(delta: float) -> void:
_advance_production(delta)
_advance_lab_research(delta)
_advance_vessel_deliveries(delta)
_advance_gas_burners(delta)
_advance_resource_converters(delta)
_advance_distorder_drain(delta)
for e in extractors:
e.mining_progress = float(e.get("mining_progress", 0.0)) + delta * EXTRACTOR_RATE
@ -1500,13 +1520,7 @@ func _dispatch_transporters() -> void:
for b in buildings:
var needed_resource := ""
for resource_type in resource_types():
# A gas burner's ongoing gas deficit is folded in alongside
# queued production/research costs (#54) - both are just "this
# building wants more of resource_type", so the one dispatch
# path already below serves both without any new transporter
# state.
var need: int = maxi(outstanding_resource_cost(int(b.id), resource_type),
gas_burner_gas_deficit(int(b.id)) if resource_type == RESOURCE_GAS else 0)
var need: int = building_requested_resource(int(b.id), resource_type)
if need > 0 and bank_inventory.amount(resource_type) > 0:
needed_resource = resource_type
break
@ -1559,14 +1573,11 @@ func _dispatch_transporters() -> void:
_begin_leg(chosen_extractor, transporter_position(chosen_extractor), extractor.position,
TRANSPORTER_TO_EXTRACTOR)
# Collecting a gas burner's energy output, last: queued deliveries and
# extractor collection both outrank it, same "supply what's needed
# before what's merely ready" ordering _dispatch_transporters() already
# holds for extractors over queued orders (f5c60f8).
# Collect building outputs last: queued deliveries and extractor collection
# both outrank resources that are merely ready for pickup.
for b in buildings:
if not building_has_capability(b, "gas_burner"):
continue
if building_resource(int(b.id), RESOURCE_ENERGY) < load or _transporter_inbound_for_pickup(int(b.id)):
var pickup_resource: String = _collectable_building_resource(b, load)
if pickup_resource == "" or _transporter_inbound_for_pickup(int(b.id)):
continue
var chosen_pickup: Dictionary = {}
var best_pickup_distance: float = INF
@ -1580,7 +1591,7 @@ func _dispatch_transporters() -> void:
if chosen_pickup.is_empty():
return
chosen_pickup.target_id = int(b.id)
chosen_pickup.resource_type = RESOURCE_ENERGY
chosen_pickup.resource_type = pickup_resource
chosen_pickup.extractor_index = -1
_begin_leg(chosen_pickup, transporter_position(chosen_pickup), b.position,
TRANSPORTER_TO_BUILDING_PICKUP)
@ -1610,8 +1621,7 @@ func _arrive(t: Dictionary) -> void:
TRANSPORTER_TO_MOTHERSHIP:
var target: Dictionary = find_building(int(t.target_id))
var resource_type: String = String(t.get("resource_type", RESOURCE_ORE))
var want: int = maxi(outstanding_resource_cost(int(t.target_id), resource_type),
gas_burner_gas_deficit(int(t.target_id)) if resource_type == RESOURCE_GAS else 0)
var want: int = building_requested_resource(int(t.target_id), resource_type)
var take: int = mini(mini(want, bank_inventory.amount(resource_type)), transporter_load_limit())
if take <= 0 or target.is_empty():
# Nothing to carry, or the building is gone. Go home empty.