Add gas bomber skill-shot vessel

This commit is contained in:
Loïc Wisniewski 2026-08-15 04:45:15 +02:00
commit 1d9afcbc2e
28 changed files with 882 additions and 85 deletions

View file

@ -30,12 +30,12 @@ const VESSEL_DEFINITIONS = preload("res://VesselDefinitions.gd")
@onready var lab_menu: Control = get_node("../UI/LabMenu")
@onready var lab_vessel_buttons: Array = [
get_node("../UI/LabMenu/VesselA"), get_node("../UI/LabMenu/VesselB"),
get_node("../UI/LabMenu/VesselC"),
get_node("../UI/LabMenu/VesselC"), get_node("../UI/LabMenu/VesselD"),
]
@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"),
get_node("../UI/LabMenu/VesselCLabel"), get_node("../UI/LabMenu/VesselDLabel"),
]
@onready var lab_detail: Label = get_node("../UI/LabMenu/DetailLabel")
@onready var lab_status: Label = get_node("../UI/LabMenu/StatusLabel")
@ -62,18 +62,22 @@ const VESSEL_DEFINITIONS = preload("res://VesselDefinitions.gd")
@onready var hangar_available_slots: Array = [
get_node("../UI/HangarMenu/AvailableInterceptor"),
get_node("../UI/HangarMenu/AvailableShield"),
get_node("../UI/HangarMenu/AvailableGasBomber"),
]
@onready var hangar_available_labels: Array = [
get_node("../UI/HangarMenu/AvailableInterceptorLabel"),
get_node("../UI/HangarMenu/AvailableShieldLabel"),
get_node("../UI/HangarMenu/AvailableGasBomberLabel"),
]
@onready var hangar_fleet_slots: Array = [
get_node("../UI/HangarMenu/FleetInterceptor"),
get_node("../UI/HangarMenu/FleetShield"),
get_node("../UI/HangarMenu/FleetGasBomber"),
]
@onready var hangar_fleet_labels: Array = [
get_node("../UI/HangarMenu/FleetInterceptorLabel"),
get_node("../UI/HangarMenu/FleetShieldLabel"),
get_node("../UI/HangarMenu/FleetGasBomberLabel"),
]
var build_menu_entries: Array = []
@ -97,11 +101,13 @@ var lab_building_id: int = 0
@onready var craft_status: Label = get_node("../UI/CraftMenu/StatusLabel")
@onready var craft_interceptor_label: Label = get_node("../UI/CraftMenu/InterceptorLabel")
@onready var craft_shield_label: Label = get_node("../UI/CraftMenu/ShieldLabel")
@onready var craft_gas_bomber_button: IconButton = get_node("../UI/CraftMenu/GasBomberButton")
@onready var craft_gas_bomber_label: Label = get_node("../UI/CraftMenu/GasBomberLabel")
@onready var craft_vessel_buttons: Array = [
craft_interceptor_button, craft_shield_button,
craft_interceptor_button, craft_shield_button, craft_gas_bomber_button,
]
@onready var craft_vessel_labels: Array = [
craft_interceptor_label, craft_shield_label,
craft_interceptor_label, craft_shield_label, craft_gas_bomber_label,
]
# --- the transfer panel (#24) ---------------------------------------------
@ -723,6 +729,25 @@ func _say(message: String) -> void:
_refresh_craft_status()
func _format_costs(costs: Dictionary) -> String:
var parts: PackedStringArray = PackedStringArray()
for resource_type in GameState.resource_types():
var amount: int = int(costs.get(resource_type, 0))
if amount > 0:
parts.append("%d %s" % [amount, GameState.resource_name(resource_type).to_lower()])
return " · ".join(parts) if not parts.is_empty() else "free"
func _format_missing_costs(costs: Dictionary) -> String:
var parts: PackedStringArray = PackedStringArray()
for resource_type in GameState.resource_types():
var amount: int = int(costs.get(resource_type, 0))
if amount > 0:
parts.append("%d more %s" % [amount,
GameState.resource_name(resource_type).to_lower()])
return " · ".join(parts)
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
@ -732,7 +757,8 @@ func _refresh_craft_status() -> void:
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]
craft_vessel_labels[i].text = "%s\n%s" % [definition.display_name,
_format_costs(GameState.product_costs(definition.definition_id))]
button.icon = definition.icon
button.glow_color = definition.glow_color
button.enabled = GameState.vessel_is_unlocked(definition.definition_id)
@ -764,11 +790,10 @@ func _on_craft_vessel(vessel: String) -> void:
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_name(product), short
])
var missing: Dictionary = GameState.outstanding_costs(craft_building_id)
if not missing.is_empty():
_say("%s queued - deliver %s here" % [GameState.vessel_name(product),
_format_missing_costs(missing)])
else:
_say("%s ordered - building now" % GameState.vessel_name(product))
@ -786,7 +811,8 @@ func _lab_options() -> Array:
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})
"costs": GameState.research_costs(definition.unlock_id),
"icon": definition.unlock_icon if definition.unlock_icon >= 0 else definition.icon})
return options
for u in GameState.upgrades_for(lab_vessel):
options.append({"unlock": false, "id": u.id, "name": u.name, "desc": u.desc})
@ -819,7 +845,8 @@ func _on_lab_buy() -> void:
if not GameState.queue_lab_research(lab_building_id, id):
lab_status.text = "Research is already queued or complete"
return
lab_status.text = "%s queued - deliver ore to this lab" % choice.name
lab_status.text = "%s queued - deliver %s to this lab" % [choice.name,
_format_costs(GameState.research_costs(id))]
_refresh_lab()
@ -844,7 +871,8 @@ func _refresh_lab() -> void:
if not visible:
continue
var option: Dictionary = options[i]
var price_text: String = "%d ore" % (int(option.cost) if bool(option.unlock) else GameState.upgrade_cost(option.id))
var price_text: String = _format_costs(option.costs if bool(option.unlock) \
else GameState.research_costs(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" % [
@ -867,7 +895,7 @@ func _refresh_lab() -> void:
var queued: Dictionary = GameState.lab_research_order(lab_building_id)
lab_buy_button.enabled = queued.is_empty() and (choice.unlock or not GameState.upgrade_maxed(choice.id))
if not queued.is_empty():
lab_status.text = "%s %d%%" % ["Researching" if bool(queued.started) else "Waiting for ore", int(100.0 * float(queued.elapsed) / maxf(GameState.lab_research_time(), 0.001))]
lab_status.text = "%s %d%%" % ["Researching" if bool(queued.started) else "Waiting for resources", int(100.0 * float(queued.elapsed) / maxf(GameState.lab_research_time(), 0.001))]
lab_buy_button.queue_redraw()
@ -989,7 +1017,7 @@ func _refresh_queue() -> void:
int(round(float(head.progress) * 100.0)),
]
else:
queue_detail.text = "%s waiting for ore\nTap to cancel" % \
queue_detail.text = "%s waiting for resources\nTap to cancel" % \
GameState.vessel_name(String(head.product))
@ -1006,7 +1034,7 @@ func _on_queue_slot(index: int) -> void:
var refunded: bool = bool(orders[index].started)
if not GameState.cancel_order(craft_building_id, index):
return
_say("%s cancelled%s" % [vessel, " - ore refunded here" if refunded else ""])
_say("%s cancelled%s" % [vessel, " - resources refunded here" if refunded else ""])
_refresh_queue()