Store resources in generic inventories
This commit is contained in:
parent
02643c76c4
commit
1721edd123
24 changed files with 385 additions and 172 deletions
236
GameState.gd
236
GameState.gd
|
|
@ -1,5 +1,8 @@
|
|||
extends Node
|
||||
|
||||
const INVENTORY = preload("res://Inventory.gd")
|
||||
const RESOURCE_DEFINITIONS = preload("res://ResourceDefinitions.gd")
|
||||
|
||||
|
||||
# Autoload singleton - the one thing that survives change_scene_to_file()
|
||||
# scene swaps between space (Main.tscn) and a planet's surface
|
||||
|
|
@ -23,126 +26,101 @@ var explored_chunks: Dictionary = {}
|
|||
|
||||
# --- Resources -------------------------------------------------------------
|
||||
|
||||
# Ore that has been BANKED at the mothership. This is the only pool anything
|
||||
# can be bought with.
|
||||
# Starts stocked for play-testing so buildings can be tried without mining
|
||||
# first - drop this back to 0 for a real playthrough.
|
||||
var rocks: int = 500
|
||||
const RESOURCE_ORE: String = RESOURCE_DEFINITIONS.ORE_ID
|
||||
const RESOURCE_GAS: String = RESOURCE_DEFINITIONS.GAS_ID
|
||||
const RESOURCE_TYPES: PackedStringArray = RESOURCE_DEFINITIONS.ORDER
|
||||
|
||||
# Gas is a second, banked resource. It is deliberately separate from ore:
|
||||
# construction still spends ore, while future gas technologies can consume
|
||||
# gas without making the two materials silently interchangeable.
|
||||
var gas: int = 0
|
||||
# All resource-bearing locations use the same container. The old public
|
||||
# names below are computed compatibility accessors, not separate storage.
|
||||
var bank_inventory: RefCounted = INVENTORY.new({RESOURCE_ORE: 500})
|
||||
var fleet_inventory: RefCounted = INVENTORY.new()
|
||||
|
||||
# Ore the fleet is physically carrying and has not delivered yet (#14). Mining
|
||||
# fills this, not `rocks`: ore is cargo until the fleet brings it home, so a
|
||||
# long mining run is a real trip with something to lose rather than instant
|
||||
# income from anywhere on the map.
|
||||
#
|
||||
# Lives here, like everything else that has to survive Main.tscn being torn
|
||||
# down and rebuilt on every trip to a planet's surface - and cargo mined ON a
|
||||
# surface would otherwise be dropped the moment the player took off.
|
||||
var fleet_cargo: int = 0
|
||||
var fleet_gas_cargo: int = 0
|
||||
var rocks: int:
|
||||
get: return bank_inventory.amount(RESOURCE_ORE)
|
||||
set(value): bank_inventory.set_amount(RESOURCE_ORE, value)
|
||||
|
||||
# Resource-facing UI and logistics iterate this list instead of growing a
|
||||
# second set of controls and controller branches every time a material is
|
||||
# introduced. Keep presentation data here with the state it describes so a
|
||||
# resource has one name and colour everywhere it appears.
|
||||
const RESOURCE_ORE: String = "ore"
|
||||
const RESOURCE_GAS: String = "gas"
|
||||
const RESOURCE_TYPES: PackedStringArray = [RESOURCE_ORE, RESOURCE_GAS]
|
||||
const RESOURCE_NAMES: Dictionary = {
|
||||
RESOURCE_ORE: "Ore",
|
||||
RESOURCE_GAS: "Gas",
|
||||
}
|
||||
const RESOURCE_COLORS: Dictionary = {
|
||||
RESOURCE_ORE: Color(0.0, 0.72, 0.82),
|
||||
RESOURCE_GAS: Color(0.85, 0.52, 1.0),
|
||||
}
|
||||
var gas: int:
|
||||
get: return bank_inventory.amount(RESOURCE_GAS)
|
||||
set(value): bank_inventory.set_amount(RESOURCE_GAS, value)
|
||||
|
||||
var fleet_cargo: int:
|
||||
get: return fleet_inventory.amount(RESOURCE_ORE)
|
||||
set(value): fleet_inventory.set_amount(RESOURCE_ORE, value)
|
||||
|
||||
var fleet_gas_cargo: int:
|
||||
get: return fleet_inventory.amount(RESOURCE_GAS)
|
||||
set(value): fleet_inventory.set_amount(RESOURCE_GAS, value)
|
||||
|
||||
func collect_cargo(amount: int = 1) -> void:
|
||||
fleet_cargo += amount
|
||||
fleet_inventory.deposit(RESOURCE_ORE, amount)
|
||||
|
||||
func collect_gas_cargo(amount: int = 1) -> void:
|
||||
fleet_gas_cargo += amount
|
||||
fleet_inventory.deposit(RESOURCE_GAS, amount)
|
||||
|
||||
|
||||
func resource_types() -> PackedStringArray:
|
||||
return RESOURCE_DEFINITIONS.ids()
|
||||
|
||||
|
||||
func resource_definition(resource_type: String) -> Resource:
|
||||
return RESOURCE_DEFINITIONS.get_definition(resource_type)
|
||||
|
||||
|
||||
func resource_name(resource_type: String) -> String:
|
||||
return String(RESOURCE_NAMES.get(resource_type, resource_type.capitalize()))
|
||||
var definition: Resource = resource_definition(resource_type)
|
||||
return String(definition.display_name) if definition != null else resource_type.capitalize()
|
||||
|
||||
|
||||
func resource_color(resource_type: String) -> Color:
|
||||
return RESOURCE_COLORS.get(resource_type, Color.WHITE)
|
||||
var definition: Resource = resource_definition(resource_type)
|
||||
return definition.color if definition != null else Color.WHITE
|
||||
|
||||
|
||||
func banked_resource(resource_type: String) -> int:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: return rocks
|
||||
RESOURCE_GAS: return gas
|
||||
_: return 0
|
||||
return bank_inventory.amount(resource_type)
|
||||
|
||||
|
||||
func fleet_resource(resource_type: String) -> int:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: return fleet_cargo
|
||||
RESOURCE_GAS: return fleet_gas_cargo
|
||||
_: return 0
|
||||
return fleet_inventory.amount(resource_type)
|
||||
|
||||
|
||||
func _inventory_for_record(record: Dictionary) -> RefCounted:
|
||||
var inventory: RefCounted = record.get("inventory") as RefCounted
|
||||
if inventory == null:
|
||||
inventory = INVENTORY.new()
|
||||
record.inventory = inventory
|
||||
# Compatibility migration for state made before #33. Erase each legacy
|
||||
# field once absorbed so it cannot drift out of step with the inventory.
|
||||
if record.has("stock"):
|
||||
inventory.set_amount(RESOURCE_ORE, int(record.stock))
|
||||
record.erase("stock")
|
||||
if record.has("gas_stock"):
|
||||
inventory.set_amount(RESOURCE_GAS, int(record.gas_stock))
|
||||
record.erase("gas_stock")
|
||||
return inventory
|
||||
|
||||
|
||||
func building_inventory(id: int) -> RefCounted:
|
||||
var building: Dictionary = find_building(id)
|
||||
if building.is_empty():
|
||||
return null
|
||||
if building.type == BUILDING_MOTHERSHIP:
|
||||
return bank_inventory
|
||||
return _inventory_for_record(building)
|
||||
|
||||
|
||||
func building_resource(id: int, resource_type: String) -> int:
|
||||
var building: Dictionary = find_building(id)
|
||||
if building.is_empty():
|
||||
return 0
|
||||
if building.type == BUILDING_MOTHERSHIP:
|
||||
return banked_resource(resource_type)
|
||||
match resource_type:
|
||||
RESOURCE_ORE: return int(building.get("stock", 0))
|
||||
RESOURCE_GAS: return int(building.get("gas_stock", 0))
|
||||
_: return 0
|
||||
var inventory: RefCounted = building_inventory(id)
|
||||
return 0 if inventory == null else inventory.amount(resource_type)
|
||||
|
||||
|
||||
func transfer_resource_to_building(id: int, resource_type: String, amount: int) -> int:
|
||||
var building: Dictionary = find_building(id)
|
||||
if building.is_empty():
|
||||
return 0
|
||||
var moved: int = mini(maxi(amount, 0), fleet_resource(resource_type))
|
||||
if moved <= 0:
|
||||
return 0
|
||||
match resource_type:
|
||||
RESOURCE_ORE: fleet_cargo -= moved
|
||||
RESOURCE_GAS: fleet_gas_cargo -= moved
|
||||
_: return 0
|
||||
if building.type == BUILDING_MOTHERSHIP:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: rocks += moved
|
||||
RESOURCE_GAS: gas += moved
|
||||
else:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: building.stock = int(building.get("stock", 0)) + moved
|
||||
RESOURCE_GAS: building.gas_stock = int(building.get("gas_stock", 0)) + moved
|
||||
return moved
|
||||
return fleet_inventory.transfer_to(building_inventory(id), resource_type, amount)
|
||||
|
||||
|
||||
func transfer_resource_to_fleet(id: int, resource_type: String, amount: int) -> int:
|
||||
var building: Dictionary = find_building(id)
|
||||
if building.is_empty():
|
||||
return 0
|
||||
var moved: int = mini(maxi(amount, 0), building_resource(id, resource_type))
|
||||
if moved <= 0:
|
||||
return 0
|
||||
if building.type == BUILDING_MOTHERSHIP:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: rocks -= moved
|
||||
RESOURCE_GAS: gas -= moved
|
||||
else:
|
||||
match resource_type:
|
||||
RESOURCE_ORE: building.stock = int(building.get("stock", 0)) - moved
|
||||
RESOURCE_GAS: building.gas_stock = int(building.get("gas_stock", 0)) - moved
|
||||
match resource_type:
|
||||
RESOURCE_ORE: fleet_cargo += moved
|
||||
RESOURCE_GAS: fleet_gas_cargo += moved
|
||||
return moved
|
||||
var source: RefCounted = building_inventory(id)
|
||||
return 0 if source == null else source.transfer_to(fleet_inventory, resource_type, amount)
|
||||
|
||||
|
||||
# The batch size the transfer menu offers, next to "All". A batch rather
|
||||
|
|
@ -401,8 +379,8 @@ func add_building(type: String, at_position: Vector2) -> int:
|
|||
var id: int = next_building_id
|
||||
next_building_id += 1
|
||||
buildings.append({
|
||||
"id": id, "type": type, "position": at_position, "stock": 0,
|
||||
"gas_stock": 0, "orders": [],
|
||||
"id": id, "type": type, "position": at_position,
|
||||
"inventory": INVENTORY.new(), "orders": [],
|
||||
})
|
||||
return id
|
||||
|
||||
|
|
@ -655,8 +633,7 @@ func can_produce(type: String) -> bool:
|
|||
|
||||
|
||||
func building_stock(id: int) -> int:
|
||||
var b: Dictionary = find_building(id)
|
||||
return 0 if b.is_empty() else int(b.get("stock", 0))
|
||||
return building_resource(id, RESOURCE_ORE)
|
||||
|
||||
|
||||
func pending_orders(id: int) -> int:
|
||||
|
|
@ -703,7 +680,7 @@ func cancel_order(id: int, index: int) -> bool:
|
|||
return false
|
||||
var order: Dictionary = orders[index]
|
||||
if bool(order.started):
|
||||
b.stock = int(b.get("stock", 0)) + product_cost(String(order.product))
|
||||
_inventory_for_record(b).deposit(RESOURCE_ORE, product_cost(String(order.product)))
|
||||
orders.remove_at(index)
|
||||
return true
|
||||
|
||||
|
|
@ -718,7 +695,7 @@ func outstanding_cost(id: int) -> int:
|
|||
# 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)
|
||||
return maxi(need - _inventory_for_record(b).amount(RESOURCE_ORE), 0)
|
||||
|
||||
|
||||
func deposit_to_building(id: int, amount: int) -> int:
|
||||
|
|
@ -727,8 +704,7 @@ func deposit_to_building(id: int, amount: int) -> int:
|
|||
var b: Dictionary = find_building(id)
|
||||
if b.is_empty():
|
||||
return 0
|
||||
b.stock = int(b.get("stock", 0)) + amount
|
||||
return amount
|
||||
return _inventory_for_record(b).deposit(RESOURCE_ORE, amount)
|
||||
|
||||
|
||||
func queue_order(id: int, product: String = PRODUCT_SHIP) -> bool:
|
||||
|
|
@ -763,9 +739,10 @@ func _advance_production(delta: float) -> void:
|
|||
var order: Dictionary = orders[0]
|
||||
var product: String = String(order.product)
|
||||
var cost: int = product_cost(product)
|
||||
var inventory: RefCounted = _inventory_for_record(b)
|
||||
|
||||
if not bool(order.started):
|
||||
if int(b.get("stock", 0)) < cost:
|
||||
if inventory.amount(RESOURCE_ORE) < cost:
|
||||
continue
|
||||
if product == PRODUCT_TRANSPORTER and transporters.size() >= transport_capacity():
|
||||
# A hub was demolished after this was ordered, so the berth it
|
||||
|
|
@ -773,7 +750,7 @@ func _advance_production(delta: float) -> void:
|
|||
continue
|
||||
if product == PRODUCT_TRANSPORTER and nearest_hub_to(b.position).is_empty():
|
||||
continue
|
||||
b.stock = int(b.stock) - cost
|
||||
inventory.withdraw(RESOURCE_ORE, cost)
|
||||
order.started = true
|
||||
|
||||
order.elapsed = float(order.elapsed) + delta
|
||||
|
|
@ -826,10 +803,11 @@ func _advance_lab_research(delta: float) -> void:
|
|||
var order: Dictionary = orders[0]
|
||||
var id: String = String(order.id)
|
||||
var cost: int = UNLOCK_SHIELD_COST if id == UNLOCK_SHIELD_VESSEL else upgrade_cost(id)
|
||||
var inventory: RefCounted = _inventory_for_record(lab)
|
||||
if not bool(order.started):
|
||||
if int(lab.get("stock", 0)) < cost:
|
||||
if inventory.amount(RESOURCE_ORE) < cost:
|
||||
continue
|
||||
lab.stock = int(lab.stock) - cost
|
||||
inventory.withdraw(RESOURCE_ORE, cost)
|
||||
order.started = true
|
||||
order.elapsed = float(order.elapsed) + delta
|
||||
if float(order.elapsed) < lab_research_time():
|
||||
|
|
@ -918,7 +896,7 @@ func rotate_formation(radians: float) -> void:
|
|||
# Extractors mine into their OWN local stock. They do not own a magical
|
||||
# hauler: ore becomes spendable only when a hub-stationed transporter picks
|
||||
# up a full load and physically delivers it to the mothership. Entries are
|
||||
# {"position", "planet_name", "resource_type", "stock", "mining_progress"}.
|
||||
# {"position", "planet_name", "resource_type", "inventory", "mining_progress"}.
|
||||
const EXTRACTOR_RATE: float = 2.0 # ore per second
|
||||
|
||||
var extractors: Array = []
|
||||
|
|
@ -939,7 +917,7 @@ func add_extractor(planet_name: String, at_position: Vector2, resource_type: Str
|
|||
"position": at_position,
|
||||
"planet_name": planet_name,
|
||||
"resource_type": resource_type,
|
||||
"stock": 0,
|
||||
"inventory": INVENTORY.new(),
|
||||
"mining_progress": 0.0,
|
||||
})
|
||||
# An extractor is built from SPACE, with the fleet parked on the planet -
|
||||
|
|
@ -952,7 +930,21 @@ func add_extractor(planet_name: String, at_position: Vector2, resource_type: Str
|
|||
func extractor_stock(index: int) -> int:
|
||||
if index < 0 or index >= extractors.size():
|
||||
return 0
|
||||
return int(extractors[index].get("stock", 0))
|
||||
var extractor: Dictionary = extractors[index]
|
||||
return _extractor_inventory(extractor).amount(String(extractor.get("resource_type", RESOURCE_ORE)))
|
||||
|
||||
|
||||
func _extractor_inventory(extractor: Dictionary) -> RefCounted:
|
||||
var inventory: RefCounted = extractor.get("inventory") as RefCounted
|
||||
if inventory == null:
|
||||
inventory = INVENTORY.new()
|
||||
extractor.inventory = inventory
|
||||
# Old extractor state had one stock field whose material was described by
|
||||
# resource_type. Absorb it into that key on first access.
|
||||
if extractor.has("stock"):
|
||||
inventory.set_amount(String(extractor.get("resource_type", RESOURCE_ORE)), int(extractor.stock))
|
||||
extractor.erase("stock")
|
||||
return inventory
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
|
|
@ -965,7 +957,7 @@ func _process(delta: float) -> void:
|
|||
e.mining_progress = float(e.get("mining_progress", 0.0)) + delta * EXTRACTOR_RATE
|
||||
var whole: int = int(floor(float(e.mining_progress)))
|
||||
if whole > 0:
|
||||
e.stock = int(e.get("stock", 0)) + whole
|
||||
_extractor_inventory(e).deposit(String(e.get("resource_type", RESOURCE_ORE)), whole)
|
||||
e.mining_progress = float(e.mining_progress) - whole
|
||||
_advance_transporters(delta)
|
||||
|
||||
|
|
@ -1127,7 +1119,8 @@ func _dispatch_transporters() -> void:
|
|||
var load: int = transporter_load_limit()
|
||||
for i in range(extractors.size()):
|
||||
var extractor: Dictionary = extractors[i]
|
||||
if int(extractor.get("stock", 0)) < load or _transporter_inbound_to_extractor(i):
|
||||
var resource_id: String = String(extractor.get("resource_type", RESOURCE_ORE))
|
||||
if _extractor_inventory(extractor).amount(resource_id) < load or _transporter_inbound_to_extractor(i):
|
||||
continue
|
||||
var chosen_extractor: Dictionary = {}
|
||||
var best_extractor_distance: float = INF
|
||||
|
|
@ -1141,7 +1134,7 @@ func _dispatch_transporters() -> void:
|
|||
if chosen_extractor.is_empty():
|
||||
return
|
||||
chosen_extractor.extractor_index = i
|
||||
chosen_extractor.resource_type = String(extractor.get("resource_type", "ore"))
|
||||
chosen_extractor.resource_type = resource_id
|
||||
chosen_extractor.target_id = 0
|
||||
_begin_leg(chosen_extractor, transporter_position(chosen_extractor), extractor.position,
|
||||
TRANSPORTER_TO_EXTRACTOR)
|
||||
|
|
@ -1192,13 +1185,13 @@ func _arrive(t: Dictionary) -> void:
|
|||
TRANSPORTER_TO_MOTHERSHIP:
|
||||
var target: Dictionary = find_building(int(t.target_id))
|
||||
var want: int = outstanding_cost(int(t.target_id))
|
||||
var take: int = mini(mini(want, rocks), transporter_load_limit())
|
||||
var take: int = mini(mini(want, bank_inventory.amount(RESOURCE_ORE)), transporter_load_limit())
|
||||
if take <= 0 or target.is_empty():
|
||||
# Nothing to carry, or the building is gone. Go home empty.
|
||||
t.target_id = 0
|
||||
_begin_leg(t, here, hub_pos if hub_pos != null else here, TRANSPORTER_RETURNING)
|
||||
return
|
||||
rocks -= take
|
||||
bank_inventory.withdraw(RESOURCE_ORE, take)
|
||||
t.carrying = take
|
||||
_begin_leg(t, here, target.position, TRANSPORTER_TO_BUILDING)
|
||||
TRANSPORTER_TO_BUILDING:
|
||||
|
|
@ -1206,7 +1199,7 @@ func _arrive(t: Dictionary) -> void:
|
|||
if deposit_to_building(int(t.target_id), int(t.carrying)) <= 0:
|
||||
# Building demolished in transit - put the ore back rather
|
||||
# than quietly destroying it.
|
||||
rocks += int(t.carrying)
|
||||
bank_inventory.deposit(RESOURCE_ORE, int(t.carrying))
|
||||
t.carrying = 0
|
||||
t.target_id = 0
|
||||
_begin_leg(t, here, hub_pos if hub_pos != null else here, TRANSPORTER_RETURNING)
|
||||
|
|
@ -1217,20 +1210,19 @@ func _arrive(t: Dictionary) -> void:
|
|||
return
|
||||
var extractor: Dictionary = extractors[extractor_index]
|
||||
var load: int = transporter_load_limit()
|
||||
if int(extractor.get("stock", 0)) < load:
|
||||
var resource_id: String = String(extractor.get("resource_type", RESOURCE_ORE))
|
||||
var extractor_inventory: RefCounted = _extractor_inventory(extractor)
|
||||
if extractor_inventory.amount(resource_id) < load:
|
||||
t.extractor_index = -1
|
||||
_begin_leg(t, here, hub_pos if hub_pos != null else here, TRANSPORTER_RETURNING)
|
||||
return
|
||||
extractor.stock = int(extractor.stock) - load
|
||||
extractor_inventory.withdraw(resource_id, load)
|
||||
t.carrying = load
|
||||
t.resource_type = String(extractor.get("resource_type", "ore"))
|
||||
t.resource_type = resource_id
|
||||
_begin_leg(t, here, get_mothership_position(), TRANSPORTER_TO_MOTHERSHIP_FROM_EXTRACTOR)
|
||||
TRANSPORTER_TO_MOTHERSHIP_FROM_EXTRACTOR:
|
||||
# Bank only on arrival; cargo retains the extractor's resource type.
|
||||
if String(t.get("resource_type", "ore")) == "gas":
|
||||
gas += int(t.carrying)
|
||||
else:
|
||||
rocks += int(t.carrying)
|
||||
bank_inventory.deposit(String(t.get("resource_type", RESOURCE_ORE)), int(t.carrying))
|
||||
t.carrying = 0
|
||||
t.extractor_index = -1
|
||||
_begin_leg(t, here, hub_pos if hub_pos != null else here, TRANSPORTER_RETURNING)
|
||||
|
|
@ -1240,11 +1232,11 @@ func _arrive(t: Dictionary) -> void:
|
|||
|
||||
|
||||
func can_afford(cost: int) -> bool:
|
||||
return rocks >= cost
|
||||
return bank_inventory.amount(RESOURCE_ORE) >= cost
|
||||
|
||||
|
||||
func spend(cost: int) -> bool:
|
||||
if not can_afford(cost):
|
||||
return false
|
||||
rocks -= cost
|
||||
bank_inventory.withdraw(RESOURCE_ORE, cost)
|
||||
return true
|
||||
|
|
|
|||
Loading…
Reference in a new issue