Compose vessels from definitions and capabilities
This commit is contained in:
parent
0504b24d8d
commit
14081ad230
39 changed files with 705 additions and 386 deletions
|
|
@ -1,6 +1,7 @@
|
|||
extends Node2D
|
||||
|
||||
const BUILDING_DEFINITIONS = preload("res://BuildingDefinitions.gd")
|
||||
const VESSEL_DEFINITIONS = preload("res://VesselDefinitions.gd")
|
||||
|
||||
# Space-side building UI. Owns:
|
||||
# - the build menu (place a mothership, ship crafter or command centre)
|
||||
|
|
@ -32,6 +33,10 @@ const BUILDING_DEFINITIONS = preload("res://BuildingDefinitions.gd")
|
|||
get_node("../UI/LabMenu/VesselC"),
|
||||
]
|
||||
@onready var lab_vessel_label: Label = get_node("../UI/LabMenu/VesselLabel")
|
||||
@onready var lab_vessel_labels: Array = [
|
||||
get_node("../UI/LabMenu/VesselALabel"), get_node("../UI/LabMenu/VesselBLabel"),
|
||||
get_node("../UI/LabMenu/VesselCLabel"),
|
||||
]
|
||||
@onready var lab_detail: Label = get_node("../UI/LabMenu/DetailLabel")
|
||||
@onready var lab_status: Label = get_node("../UI/LabMenu/StatusLabel")
|
||||
@onready var lab_buy_button: IconButton = get_node("../UI/LabMenu/BuyButton")
|
||||
|
|
@ -98,6 +103,12 @@ var lab_building_id: int = 0
|
|||
@onready var craft_interceptor_label: Label = get_node("../UI/CraftMenu/InterceptorLabel")
|
||||
@onready var craft_transporter_label: Label = get_node("../UI/CraftMenu/TransporterLabel")
|
||||
@onready var craft_shield_label: Label = get_node("../UI/CraftMenu/ShieldLabel")
|
||||
@onready var craft_vessel_buttons: Array = [
|
||||
craft_interceptor_button, craft_shield_button, craft_transporter_button,
|
||||
]
|
||||
@onready var craft_vessel_labels: Array = [
|
||||
craft_interceptor_label, craft_shield_label, craft_transporter_label,
|
||||
]
|
||||
|
||||
# --- the transfer panel (#24) ---------------------------------------------
|
||||
# Ore used to be moved from inside whichever panel happened to be open: "load
|
||||
|
|
@ -159,7 +170,6 @@ func _ready() -> void:
|
|||
build_button.pressed.connect(_on_build_pressed)
|
||||
build_close_button.pressed.connect(_close_build_menu)
|
||||
_configure_build_menu_entries()
|
||||
craft_shield_button.pressed.connect(_on_craft_shield)
|
||||
lab_close_button.pressed.connect(func(): lab_menu.visible = false)
|
||||
lab_buy_button.pressed.connect(_on_lab_buy)
|
||||
for i in range(lab_vessel_buttons.size()):
|
||||
|
|
@ -172,8 +182,9 @@ func _ready() -> void:
|
|||
action_button.pressed.connect(_on_context_action_pressed)
|
||||
building_action_button.pressed.connect(_on_action_pressed)
|
||||
|
||||
craft_interceptor_button.pressed.connect(_on_craft_interceptor)
|
||||
craft_transporter_button.pressed.connect(_on_craft_transporter)
|
||||
for i in range(mini(craft_vessel_buttons.size(), VESSEL_DEFINITIONS.all().size())):
|
||||
var definition: Resource = VESSEL_DEFINITIONS.all()[i]
|
||||
craft_vessel_buttons[i].pressed.connect(_on_craft_vessel.bind(definition.definition_id))
|
||||
craft_close_button.pressed.connect(func(): craft_menu.visible = false)
|
||||
|
||||
transfer_button.pressed.connect(_on_transfer_pressed)
|
||||
|
|
@ -692,26 +703,19 @@ func _refresh_craft_status() -> void:
|
|||
# Prices live with the buttons they describe. The top of the screen is
|
||||
# intentionally reserved for the shared resource bar and a short action
|
||||
# message; repeating fleet, queue and transport counts here buried the UI.
|
||||
craft_interceptor_label.text = "Interceptor\n%d ore" % GameState.SHIP_COST
|
||||
craft_transporter_label.text = "Transporter\n%d ore" % GameState.TRANSPORTER_COST
|
||||
craft_shield_label.text = "Shield Vessel\n%d ore" % GameState.SHIELD_COST
|
||||
craft_status.text = ""
|
||||
# Nowhere to station one means the order would be refused, so say so
|
||||
# rather than letting the button look available and do nothing.
|
||||
craft_transporter_button.enabled = GameState.has_transport_hub() \
|
||||
and GameState.can_station_transporter()
|
||||
craft_transporter_button.queue_redraw()
|
||||
|
||||
craft_shield_button.enabled = GameState.shield_vessel_unlocked()
|
||||
craft_shield_button.queue_redraw()
|
||||
|
||||
# Fleet capacity no longer gates ORDERING (#23). A finished vessel waits
|
||||
# at the mothership until the player crews it, so a full fleet is a
|
||||
# reason not to collect one yet - not a reason the crafter cannot build.
|
||||
# This is also what stopped the crafter being inert on a fresh save,
|
||||
# where the fleet starts at exactly its cap.
|
||||
craft_interceptor_button.enabled = true
|
||||
craft_interceptor_button.queue_redraw()
|
||||
var definitions: Array = VESSEL_DEFINITIONS.all()
|
||||
for i in range(mini(definitions.size(), craft_vessel_buttons.size())):
|
||||
var definition: Resource = definitions[i]
|
||||
var button: IconButton = craft_vessel_buttons[i]
|
||||
craft_vessel_labels[i].text = "%s\n%d ore" % [definition.display_name, definition.build_cost]
|
||||
button.icon = definition.icon
|
||||
button.glow_color = definition.glow_color
|
||||
button.enabled = GameState.vessel_is_unlocked(definition.definition_id)
|
||||
if definition.has_capability("transport"):
|
||||
button.enabled = button.enabled and GameState.has_transport_hub() \
|
||||
and GameState.can_station_transporter()
|
||||
button.queue_redraw()
|
||||
_refresh_queue()
|
||||
if craft_message != "":
|
||||
if Time.get_ticks_msec() < craft_message_until:
|
||||
|
|
@ -721,40 +725,40 @@ func _refresh_craft_status() -> void:
|
|||
|
||||
|
||||
func _on_craft_interceptor() -> void:
|
||||
_on_craft_vessel(GameState.VESSEL_INTERCEPTOR)
|
||||
|
||||
|
||||
func _on_craft_vessel(vessel: String) -> void:
|
||||
if craft_building_id == 0:
|
||||
_say("No crafter selected - reopen this panel from the building")
|
||||
return
|
||||
# No affordability check: an order the crafter cannot pay for yet is
|
||||
# QUEUED, and completes by itself once ore is delivered here (#16). But
|
||||
# queueing silently looks identical to a dead button, so it says which
|
||||
# happened and what the crafter is still short of.
|
||||
GameState.queue_order(craft_building_id)
|
||||
_report_order(GameState.PRODUCT_SHIP)
|
||||
var definition: Resource = VESSEL_DEFINITIONS.get_definition(vessel)
|
||||
if definition == null:
|
||||
return
|
||||
if not GameState.queue_order(craft_building_id, vessel):
|
||||
if not GameState.vessel_is_unlocked(vessel):
|
||||
_say("%s must be unlocked at a ship lab first" % definition.display_name)
|
||||
elif definition.has_capability("transport") and not GameState.has_transport_hub():
|
||||
_say("Build a Transport Hub first - a transporter needs somewhere to be stationed")
|
||||
elif definition.has_capability("transport"):
|
||||
_say("All %d berths are taken - build another hub, or research Freight Yards" %
|
||||
GameState.transport_capacity())
|
||||
return
|
||||
_report_order(vessel)
|
||||
|
||||
|
||||
func _report_order(product: String) -> void:
|
||||
var short: int = GameState.outstanding_cost(craft_building_id)
|
||||
if short > 0:
|
||||
_say("%s queued - %d more ore must be delivered here" % [
|
||||
GameState.VESSEL_NAMES.get(product, product), short
|
||||
GameState.vessel_name(product), short
|
||||
])
|
||||
else:
|
||||
_say("%s ordered - building now" % GameState.VESSEL_NAMES.get(product, product))
|
||||
_say("%s ordered - building now" % GameState.vessel_name(product))
|
||||
|
||||
|
||||
func _on_craft_transporter() -> void:
|
||||
if craft_building_id == 0:
|
||||
_say("No crafter selected - reopen this panel from the building")
|
||||
return
|
||||
if not GameState.queue_order(craft_building_id, GameState.PRODUCT_TRANSPORTER):
|
||||
# Two different refusals, and the player needs to know WHICH.
|
||||
if not GameState.has_transport_hub():
|
||||
_say("Build a Transport Hub first - a transporter needs somewhere to be stationed")
|
||||
else:
|
||||
_say("All %d berths are taken - build another hub, or research Freight Yards" %
|
||||
GameState.transport_capacity())
|
||||
return
|
||||
_refresh_craft_status()
|
||||
_on_craft_vessel(GameState.VESSEL_TRANSPORTER)
|
||||
|
||||
|
||||
# --- ship lab (#5) --------------------------------------------------------
|
||||
|
|
@ -764,10 +768,13 @@ func _lab_options() -> Array:
|
|||
# entry, because unlocking the type and upgrading it belong in the same
|
||||
# place - and its upgrades are pointless until it exists.
|
||||
var options: Array = []
|
||||
if lab_vessel == GameState.VESSEL_SHIELD and not GameState.shield_vessel_unlocked():
|
||||
options.append({"unlock": true, "name": "Unlock Shield Vessel",
|
||||
"desc": "Lets the ship crafter build shield vessels. Its shield absorbs one lethal hit, then cycles back up.",
|
||||
"cost": GameState.UNLOCK_SHIELD_COST})
|
||||
var definition: Resource = VESSEL_DEFINITIONS.get_definition(lab_vessel)
|
||||
if definition == null:
|
||||
return options
|
||||
if not GameState.vessel_is_unlocked(lab_vessel):
|
||||
options.append({"unlock": true, "id": definition.unlock_id,
|
||||
"name": definition.unlock_name, "desc": definition.unlock_description,
|
||||
"cost": definition.unlock_cost, "icon": IconButton.Icon.UPGRADE_UNLOCK_SHIELD})
|
||||
return options
|
||||
for u in GameState.upgrades_for(lab_vessel):
|
||||
options.append({"unlock": false, "id": u.id, "name": u.name, "desc": u.desc})
|
||||
|
|
@ -775,8 +782,10 @@ func _lab_options() -> Array:
|
|||
|
||||
|
||||
func _on_lab_vessel(index: int) -> void:
|
||||
lab_vessel = [GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_SHIELD,
|
||||
GameState.VESSEL_TRANSPORTER][index]
|
||||
var definitions: Array = VESSEL_DEFINITIONS.all()
|
||||
if index < 0 or index >= definitions.size():
|
||||
return
|
||||
lab_vessel = definitions[index].definition_id
|
||||
lab_choice = 0
|
||||
_refresh_lab()
|
||||
|
||||
|
|
@ -794,7 +803,7 @@ func _on_lab_buy() -> void:
|
|||
if options.is_empty():
|
||||
return
|
||||
var choice: Dictionary = options[lab_choice]
|
||||
var id: String = GameState.UNLOCK_SHIELD_VESSEL if choice.unlock else String(choice.id)
|
||||
var id: String = String(choice.id)
|
||||
if not GameState.queue_lab_research(lab_building_id, id):
|
||||
lab_status.text = "Research is already queued or complete"
|
||||
return
|
||||
|
|
@ -803,14 +812,17 @@ func _on_lab_buy() -> void:
|
|||
|
||||
|
||||
func _refresh_lab() -> void:
|
||||
for i in range(lab_vessel_buttons.size()):
|
||||
var this_vessel: String = [GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_SHIELD,
|
||||
GameState.VESSEL_TRANSPORTER][i]
|
||||
var definitions: Array = VESSEL_DEFINITIONS.all()
|
||||
for i in range(mini(lab_vessel_buttons.size(), definitions.size())):
|
||||
var definition: Resource = definitions[i]
|
||||
var this_vessel: String = definition.definition_id
|
||||
lab_vessel_buttons[i].enabled = true
|
||||
lab_vessel_buttons[i].glow_color = _vessel_colour(this_vessel)
|
||||
lab_vessel_buttons[i].icon = definition.icon
|
||||
lab_vessel_buttons[i].glow_color = definition.glow_color
|
||||
lab_vessel_labels[i].text = definition.display_name
|
||||
lab_vessel_buttons[i].highlighted = this_vessel == lab_vessel
|
||||
lab_vessel_buttons[i].queue_redraw()
|
||||
lab_vessel_label.text = "%s research" % GameState.VESSEL_NAMES.get(lab_vessel, lab_vessel)
|
||||
lab_vessel_label.text = "%s research" % GameState.vessel_name(lab_vessel)
|
||||
|
||||
var options: Array = _lab_options()
|
||||
for i in range(lab_upgrade_buttons.size()):
|
||||
|
|
@ -820,7 +832,7 @@ func _refresh_lab() -> void:
|
|||
if not visible:
|
||||
continue
|
||||
var option: Dictionary = options[i]
|
||||
var price_text: String = "%d ore" % (GameState.UNLOCK_SHIELD_COST if bool(option.unlock) else GameState.upgrade_cost(option.id))
|
||||
var price_text: String = "%d ore" % (int(option.cost) if bool(option.unlock) else GameState.upgrade_cost(option.id))
|
||||
if not bool(option.unlock) and GameState.upgrade_maxed(option.id):
|
||||
price_text = "MAX"
|
||||
var level_note: String = "" if bool(option.unlock) else " %d/%d" % [
|
||||
|
|
@ -848,29 +860,11 @@ func _refresh_lab() -> void:
|
|||
|
||||
|
||||
func _upgrade_icon(option: Dictionary) -> IconButton.Icon:
|
||||
if bool(option.unlock):
|
||||
return IconButton.Icon.UPGRADE_UNLOCK_SHIELD
|
||||
match String(option.id):
|
||||
"int_damage": return IconButton.Icon.UPGRADE_DAMAGE
|
||||
"int_rate": return IconButton.Icon.UPGRADE_RATE
|
||||
"int_range": return IconButton.Icon.UPGRADE_RANGE
|
||||
"int_speed", "shd_speed": return IconButton.Icon.UPGRADE_THRUSTERS
|
||||
"shd_hits": return IconButton.Icon.UPGRADE_SHIELD_HITS
|
||||
"shd_recharge": return IconButton.Icon.UPGRADE_SHIELD_RECHARGE
|
||||
"trn_capacity": return IconButton.Icon.UPGRADE_CARGO
|
||||
"trn_speed": return IconButton.Icon.UPGRADE_ENGINES
|
||||
_: return IconButton.Icon.SHIP_LAB
|
||||
return int(option.get("icon", IconButton.Icon.SHIP_LAB))
|
||||
|
||||
|
||||
func _on_craft_shield() -> void:
|
||||
if craft_building_id == 0:
|
||||
_say("No crafter selected - reopen this panel from the building")
|
||||
return
|
||||
if not GameState.shield_vessel_unlocked():
|
||||
_say("Shield vessels must be unlocked at a ship lab first")
|
||||
return
|
||||
GameState.queue_order(craft_building_id, GameState.VESSEL_SHIELD)
|
||||
_report_order(GameState.VESSEL_SHIELD)
|
||||
_on_craft_vessel(GameState.VESSEL_SHIELD)
|
||||
|
||||
|
||||
# --- moving ore, in both directions, at any building (#24) ----------------
|
||||
|
|
@ -957,8 +951,8 @@ func _refresh_queue() -> void:
|
|||
if not slot.visible:
|
||||
continue
|
||||
var order: Dictionary = orders[i]
|
||||
slot.icon = IconButton.Icon.TRANSPORT_HUB if order.product == GameState.PRODUCT_TRANSPORTER \
|
||||
else IconButton.Icon.SHIP
|
||||
var definition: Resource = VESSEL_DEFINITIONS.get_definition(String(order.product))
|
||||
slot.icon = definition.icon
|
||||
slot.glow_color = _vessel_colour(String(order.product))
|
||||
# Only the one under construction reads as active; the rest are
|
||||
# waiting, and greyed says so without needing a caption each.
|
||||
|
|
@ -971,29 +965,24 @@ func _refresh_queue() -> void:
|
|||
var head: Dictionary = orders[0]
|
||||
if bool(head.started):
|
||||
queue_detail.text = "Building %s %d%%\nTap to cancel" % [
|
||||
GameState.VESSEL_NAMES.get(head.product, head.product),
|
||||
GameState.vessel_name(String(head.product)),
|
||||
int(round(float(head.progress) * 100.0)),
|
||||
]
|
||||
else:
|
||||
queue_detail.text = "%s waiting for ore\nTap to cancel" % \
|
||||
GameState.VESSEL_NAMES.get(head.product, head.product)
|
||||
GameState.vessel_name(String(head.product))
|
||||
|
||||
|
||||
func _vessel_colour(product: String) -> Color:
|
||||
match product:
|
||||
GameState.VESSEL_SHIELD:
|
||||
return Color(0.45, 0.8, 1.0)
|
||||
GameState.PRODUCT_TRANSPORTER:
|
||||
return Color(0.95, 0.55, 0.85)
|
||||
_:
|
||||
return Color(0.25, 0.85, 1.0)
|
||||
var definition: Resource = VESSEL_DEFINITIONS.get_definition(product)
|
||||
return definition.glow_color if definition != null else Color(0.25, 0.85, 1.0)
|
||||
|
||||
|
||||
func _on_queue_slot(index: int) -> void:
|
||||
var orders: Array = GameState.order_list(craft_building_id)
|
||||
if index >= orders.size():
|
||||
return
|
||||
var vessel: String = GameState.VESSEL_NAMES.get(orders[index].product, orders[index].product)
|
||||
var vessel: String = GameState.vessel_name(String(orders[index].product))
|
||||
var refunded: bool = bool(orders[index].started)
|
||||
if not GameState.cancel_order(craft_building_id, index):
|
||||
return
|
||||
|
|
@ -1018,12 +1007,12 @@ func _on_hangar_crew_type(index: int) -> void:
|
|||
return
|
||||
var stored_index: int = GameState.stored_vessels.find(type)
|
||||
if stored_index < 0:
|
||||
hangar_status.text = "No %s available" % GameState.VESSEL_NAMES.get(type, type)
|
||||
hangar_status.text = "No %s available" % GameState.vessel_name(type)
|
||||
return
|
||||
if not fleet.add_ship(GameState.take_stored_vessel(stored_index)):
|
||||
hangar_status.text = "Could not crew that vessel"
|
||||
return
|
||||
hangar_status.text = "%s joined the fleet" % GameState.VESSEL_NAMES.get(type, type)
|
||||
hangar_status.text = "%s joined the fleet" % GameState.vessel_name(type)
|
||||
_refresh_hangar()
|
||||
|
||||
|
||||
|
|
@ -1035,32 +1024,40 @@ func _on_hangar_store_type(index: int) -> void:
|
|||
ship_index = i
|
||||
break
|
||||
if ship_index < 0:
|
||||
hangar_status.text = "No %s in the fleet" % GameState.VESSEL_NAMES.get(type, type)
|
||||
hangar_status.text = "No %s in the fleet" % GameState.vessel_name(type)
|
||||
return
|
||||
var stored: String = fleet.store_ship(ship_index)
|
||||
if stored == "":
|
||||
hangar_status.text = "Keep one ship deployed"
|
||||
return
|
||||
GameState.stored_vessels.append(stored)
|
||||
hangar_status.text = "%s stored in the hangar" % GameState.VESSEL_NAMES.get(stored, stored)
|
||||
hangar_status.text = "%s stored in the hangar" % GameState.vessel_name(stored)
|
||||
_refresh_hangar()
|
||||
|
||||
|
||||
func _hangar_types() -> Array:
|
||||
return [GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_SHIELD, GameState.VESSEL_TRANSPORTER]
|
||||
var types: Array = []
|
||||
for definition in VESSEL_DEFINITIONS.all():
|
||||
types.append(definition.definition_id)
|
||||
return types
|
||||
|
||||
|
||||
func _refresh_hangar() -> void:
|
||||
var types: Array = _hangar_types()
|
||||
for i in range(types.size()):
|
||||
var type: String = types[i]
|
||||
var definition: Resource = VESSEL_DEFINITIONS.get_definition(type)
|
||||
var available: int = GameState.stored_vessels.count(type)
|
||||
var deployed: int = 0
|
||||
for ship in fleet.ships:
|
||||
if is_instance_valid(ship) and String(ship.vessel_type) == type:
|
||||
deployed += 1
|
||||
hangar_available_labels[i].text = "%s\n%d available" % [GameState.VESSEL_NAMES.get(type, type), available]
|
||||
hangar_fleet_labels[i].text = "%s\n%d in fleet" % [GameState.VESSEL_NAMES.get(type, type), deployed]
|
||||
hangar_available_labels[i].text = "%s\n%d available" % [definition.display_name, available]
|
||||
hangar_fleet_labels[i].text = "%s\n%d in fleet" % [definition.display_name, deployed]
|
||||
hangar_available_slots[i].icon = definition.icon
|
||||
hangar_fleet_slots[i].icon = definition.icon
|
||||
hangar_available_slots[i].glow_color = definition.glow_color
|
||||
hangar_fleet_slots[i].glow_color = definition.glow_color
|
||||
hangar_available_slots[i].enabled = available > 0 and fleet.ships.size() < GameState.max_fleet_size()
|
||||
hangar_fleet_slots[i].enabled = deployed > 0 and fleet.ships.size() > 1
|
||||
hangar_available_slots[i].queue_redraw()
|
||||
|
|
|
|||
Loading…
Reference in a new issue