Randoloot/scripts/planets/freighter_screen.gd
2026-10-18 15:11:06 +02:00

372 lines
16 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Control
signal closed
const UI := preload("res://scripts/planets/expedition_ui.gd")
const GEAR := preload("res://scripts/planets/captain_equipment.gd")
const CATALOG := preload("res://scripts/game/ship_catalog.gd")
var captain: Resource
var inventory: Node
var _content: VBoxContainer
var _tab := "overview"
var _drawer: PanelContainer
var _drawer_title: Label
var _wallet: Label
var _hint: Label
var _owned_open := false
@onready var deck: Control = $Deck
@onready var _audio: Node = $FreighterAudio
func _ready() -> void:
if captain == null:
captain = GameState.captain
deck.sync_rooms(captain.expedition.freighter_rooms)
deck.section_selected.connect(_select_section)
deck.section_hovered.connect(_hover_section)
var chassis := CATALOG.for_path(GameState.selected_ship_path)
deck.ship_name = str(chassis.name)
var sprite: Sprite2D
if is_instance_valid(inventory):
var ship: Node = inventory.get_parent()
sprite = ship.get_node_or_null("Sprite2D") as Sprite2D
deck.ship_texture = sprite.texture if sprite != null else load(CATALOG.texture_path(chassis))
if sprite != null:
deck.ship_name = str(CATALOG.for_path(sprite.texture.resource_path).name)
_build_header()
_build_footer()
_build_drawer()
resized.connect(_layout)
captain.changed.connect(_sync_header)
_sync_header()
_layout()
_refresh()
func _build_header() -> void:
var header := HBoxContainer.new()
header.name = "Header"
header.set_anchors_and_offsets_preset(Control.PRESET_TOP_WIDE)
header.offset_left = 26
header.offset_right = -26
header.offset_top = 20
header.add_theme_constant_override("separation",22)
add_child(header)
var heading := VBoxContainer.new()
heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL
header.add_child(heading)
UI.label(heading,"FREGATA / POKŁAD OPERACYJNY",24,Color("e3e9e8"))
UI.label(heading,"01 / POKŁAD GŁÓWNY · POŁĄCZENIE ZABEZPIECZONE",11,Color("77969f"))
_wallet = UI.label(header,"",13,Color("c9aa7c"))
_wallet.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
UI.button(header,"MAGAZYN",func(): _select_section("storage"))
UI.button(header,"ODLOT [L]",func(): closed.emit())
func _build_footer() -> void:
var footer := HBoxContainer.new()
footer.name = "Footer"
footer.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_WIDE)
footer.offset_left = 26
footer.offset_right = -26
footer.offset_top = -43
footer.offset_bottom = -12
add_child(footer)
_hint = UI.label(footer,"Wybierz pomieszczenie na pokładzie. · Warsztat i hangar są dostępne.",12,Color("91a9ae"))
_hint.size_flags_horizontal = Control.SIZE_EXPAND_FILL
footer.add_theme_constant_override("separation",18)
UI.label(footer,"● OPERACYJNE",11,Color("79d9d8"))
UI.label(footer,"● W REMONCIE",11,Color("bca078"))
func _build_drawer() -> void:
_drawer = UI.panel(self)
_drawer.name = "SectionPanel"
var stack := VBoxContainer.new()
stack.add_theme_constant_override("separation",14)
_drawer.add_child(stack)
var row := HBoxContainer.new()
stack.add_child(row)
_drawer_title = UI.label(row,"",20,UI.CYAN)
_drawer_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
UI.button(row,"×",_close_section)
var scroll := ScrollContainer.new()
scroll.name = "SectionScroll"
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
stack.add_child(scroll)
_content = VBoxContainer.new()
_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_content.add_theme_constant_override("separation",14)
scroll.add_child(_content)
func _layout() -> void:
if not is_instance_valid(_drawer): return
var viewport_size := size
var width := minf(760 if _tab == "storage" or (_tab == "expansion_3" and deck.is_built(_tab)) else 460,viewport_size.x-40)
_drawer.position = Vector2(20 if _tab == "hangar" else viewport_size.x-width-20,94)
_drawer.size = Vector2(width,maxf(240,viewport_size.y-154))
func _sync_header() -> void:
if is_instance_valid(_wallet):
_wallet.text = "%d KREDYTÓW\n%d CZĘŚCI" % [captain.credits,floori(captain.parts)]
func _hover_section(section: String) -> void:
var data: Dictionary = deck.room(section)
_hint.text = "%s / %s" % [data.title,data.status] if not data.is_empty() else "Wybierz pomieszczenie na pokładzie. · Warsztat i hangar są dostępne."
func _select_section(section: String) -> void:
var room: Dictionary = deck.room("expansion_3" if section == "storage" else section)
var pan: float = 0.0 if room.is_empty() else ((room.rect as Rect2).get_center().x / deck.DESIGN_SIZE.x - 0.5)*1.4
_audio.enter_section(section,pan)
_tab = section
deck.selected = section
_refresh()
func _close_section() -> void:
_audio.close_section()
_tab = "overview"
deck.selected = ""
_drawer.hide()
deck.queue_redraw()
func _input(event: InputEvent) -> void:
if not event is InputEventKey or not event.pressed or event.echo:
return
if event.keycode not in [KEY_L,KEY_ESCAPE]:
return
# Docking removes this scene immediately, so consume input before emitting closed.
get_viewport().set_input_as_handled()
if event.keycode == KEY_L:
closed.emit()
elif _drawer.visible:
_close_section()
else:
closed.emit()
func _label(text: String, size_value := 14, color := Color("c5d3d7")) -> Label:
var node := UI.label(_content,text,size_value,color)
node.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
return node
func _refresh() -> void:
_audio.set_maintenance_needed(captain.expedition.lander_hp < 30)
for child in _content.get_children():
_content.remove_child(child)
child.queue_free()
_sync_header()
if _tab == "overview":
_drawer.hide()
return
_drawer.show()
_layout()
_content.get_parent().set("scroll_vertical",0)
if _tab == "workshop":
_drawer_title.text = "WARSZTAT"
_build_workshop()
elif _tab == "hangar":
_drawer_title.text = "HANGAR / OBSŁUGA"
_build_hangar()
elif _tab == "command":
_drawer_title.text = "CENTRUM DOWODZENIA"
_build_command()
elif _tab == "storage" or (_tab == "expansion_3" and deck.is_built(_tab)):
_drawer_title.text = "MAGAZYN FRACHTOWCA"
_build_storage()
else:
_build_room()
func _build_room() -> void:
var section: Dictionary = deck.room(_tab)
_drawer_title.text = str(section.get("title","SEKCJA ROZWOJOWA"))
if not deck.is_buildable(_tab):
_label("Wybierz pomieszczenie na pokładzie.")
return
var plan: Dictionary = deck.plan(_tab)
_label("ZBUDOWANO" if deck.is_built(_tab) else "W REMONCIE",21,UI.CYAN if deck.is_built(_tab) else Color("e0b16a"))
_label(str(plan.description))
if deck.is_built(_tab):
_label("Pomieszczenie jest wyposażone. Jego mechanika zostanie dodana w kolejnym etapie.",14,Color("849da5"))
else:
_label("Budowa jest teraz bezpłatna. Wyposażenie pojawi się od razu na pokładzie.",14,Color("849da5"))
UI.button(_content,"ZBUDUJ · BEZ KOSZTÓW",_construct_room.bind(_tab))
UI.button(_content,"Wróć na pokład",_close_section)
func _construct_room(id: String) -> void:
if not deck.is_buildable(id) or deck.is_built(id): return
captain.expedition.freighter_rooms[id] = str(deck.plan(id).kind)
captain.expedition.emit_changed()
deck.sync_rooms(captain.expedition.freighter_rooms)
_audio.service_completed()
_hover_section(id)
_refresh()
func _build_hangar() -> void:
var progress: Resource = captain.expedition
_build_slot_upgrades()
_label("ZADOKOWANO / "+deck.ship_name.to_upper(),15,Color("8bd9d0"))
_label("Twój statek stoi na stanowisku serwisowym. Obsługa lądownika i odbiór odzyskanych modułów są dostępne poniżej.")
_label("Lądownik: %d / 100 HP" % ceili(progress.lander_hp),18)
var bar := ProgressBar.new()
bar.value = progress.lander_hp
bar.custom_minimum_size.y = 17
_content.add_child(bar)
var cost: int = progress.repair_cost()
_label("Części: %d · potrzebne do naprawy: %d" % [captain.parts,cost],13)
var repair := UI.button(_content,"NAPRAW DO 100%% · %d części" % cost,func():
if progress.repair(captain): _audio.service_completed(0.65)
_refresh())
repair.disabled = cost == 0 or captain.parts < cost
_label("1 część przywraca 1 punkt procentowy HP lądownika. Do następnego lądowania wymagany jest pełny kadłub.",13,Color("819ca5"))
_label("MODUŁY DO ODEBRANIA: %d" % progress.delivered_modules.size(),16,UI.CYAN)
var delivery := UI.button(_content,"ODBIERZ DO ŁADOWNI STATKU",func():
if is_instance_valid(inventory): progress.deliver_modules(inventory)
_refresh())
delivery.disabled = progress.delivered_modules.is_empty() or not is_instance_valid(inventory)
_label("Moduły bez miejsca w ładowni pozostają bezpiecznie na pokładzie.",13,Color("819ca5"))
func _build_slot_upgrades() -> void:
_label("ROZBUDOWA SLOTÓW STATKU",17,UI.CYAN)
if not is_instance_valid(inventory):
_label("Zadokuj swoim statkiem, aby rozbudować sloty.")
return
_label("Żelazo: %d · koszt kolejnego slotu: 10" % int(captain.minerals.get("iron",0)),14)
_label("Ładownia: %d / %d miejsc · rzędy %d / %d" % [inventory.get_cargo_capacity(),inventory.CARGO_SIZE,inventory.unlocked_cargo_rows,inventory.CARGO_ROW_COUNT],14)
if inventory.unlocked_cargo_rows < inventory.CARGO_ROW_COUNT:
var cargo_unlock := UI.button(_content,"ODBLOKUJ RZĄD ŁADOWNI · 10 żelaza",func():
inventory.unlock_cargo_row(captain)
_refresh())
cargo_unlock.disabled = not inventory.can_unlock_cargo_row(captain)
else:
_label("Wszystkie rzędy ładowni odblokowane.",13,Color("849da5"))
for kind in ["weapon","extension"]:
var count: int = inventory.unlocked_weapon_slots if kind == "weapon" else inventory.unlocked_extension_slots
var limit: int = inventory.WEAPON_SLOT_COUNT if kind == "weapon" else inventory.EXTENSION_SLOT_COUNT
var title := "BRONI" if kind == "weapon" else "ROZSZERZENIA"
_label("Sloty %s: %d / %d" % [title.to_lower(),count,limit],14)
if count < limit:
var unlock := UI.button(_content,"ODBLOKUJ SLOT %s · 10 żelaza" % title,_unlock_ship_slot.bind(kind))
unlock.disabled = not inventory.can_unlock_slot(kind,captain)
else:
_label("Wszystkie sloty odblokowane.",13,Color("849da5"))
func _unlock_ship_slot(kind: String) -> void:
if is_instance_valid(inventory): inventory.unlock_slot(kind,captain)
_refresh()
func _build_storage() -> void:
if not deck.is_built("expansion_3"):
_label("SKŁAD MODUŁÓW W REMONCIE",19,Color("e0b16a"))
_label("Zbuduj skład modułów, aby przechowywać wyposażenie na frachtowcu. Budowa jest teraz bezpłatna.")
UI.button(_content,"ZBUDUJ SKŁAD · BEZ KOSZTÓW",_construct_room.bind("expansion_3"))
return
if not is_instance_valid(inventory):
_label("Zadokuj swoim statkiem, aby przenosić wyposażenie.")
return
_label("Przechowuj moduły, broń i rozszerzenia. Wybierz przedmiot, aby przenieść go między ładownią a magazynem. Zamontowane wyposażenie najpierw zdejmij do ładowni.",13)
var columns := HBoxContainer.new()
columns.add_theme_constant_override("separation",18)
_content.add_child(columns)
var cargo_column := VBoxContainer.new()
cargo_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
cargo_column.size_flags_stretch_ratio = 1
columns.add_child(cargo_column)
var storage_column := VBoxContainer.new()
storage_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
storage_column.size_flags_stretch_ratio = 1
columns.add_child(storage_column)
UI.label(cargo_column,"STATEK · %d / %d" % [inventory.get_cargo_used(),inventory.get_cargo_capacity()],16,UI.CYAN)
var storage: Array = captain.expedition.freighter_storage
UI.label(storage_column,"FRACHTOWIEC · %d" % storage.size(),16,UI.CYAN)
var used := 0
for index in inventory.get_cargo_capacity():
var item: ItemInstance = inventory.get_item("cargo",index)
if item == null: continue
used += 1
_storage_card(cargo_column,item,"ODŁÓŻ →",_transfer_storage.bind(false,index,item))
if used == 0: UI.label(cargo_column,"Ładownia jest pusta.",13,Color("849da5"))
for index in storage.size():
var item: ItemInstance = storage[index]
var button := _storage_card(storage_column,item,"← ODBIERZ",_transfer_storage.bind(true,index,item))
button.disabled = inventory.get_cargo_used() >= inventory.get_cargo_capacity()
if storage.is_empty(): UI.label(storage_column,"Magazyn jest pusty.",13,Color("849da5"))
if inventory.get_cargo_used() >= inventory.get_cargo_capacity():
_label("Ładownia statku jest pełna. Odłóż przedmiot lub odblokuj kolejny rząd w hangarze.",13,Color("e0b16a"))
func _storage_card(parent: Control, item: ItemInstance, action: String, callback: Callable) -> Button:
var card := UI.panel(parent)
var stack := VBoxContainer.new()
card.add_child(stack)
var row := HBoxContainer.new()
stack.add_child(row)
var icon := TextureRect.new()
icon.custom_minimum_size = Vector2(38,38)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.texture = item.definition.icon
row.add_child(icon)
var title := UI.label(row,"%s · +%d" % [item.definition.display_name,item.item_level],13,item.get_type_color())
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
card.tooltip_text = item.get_tooltip_text()
return UI.button(stack,action,callback)
func _transfer_storage(retrieve: bool, index: int, expected: ItemInstance) -> void:
if not is_instance_valid(inventory): return
if retrieve: inventory.retrieve_from_freighter(index,expected,captain)
else: inventory.store_on_freighter(index,expected,captain)
_refresh()
func _build_workshop() -> void:
_label("FABRYKATOR / WYPOSAŻENIE KAPITANA",13,Color("8bd9d0"))
_label("Każdy poziom wyposażenia usuwa 2 pp odpowiadającego mu zagrożenia. L50 zapewnia pełną ochronę tej składowej.",13)
for kind in GEAR.KINDS:
var card := UI.panel(_content)
var row := HBoxContainer.new()
row.add_theme_constant_override("separation",12)
card.add_child(row)
var icon := TextureRect.new()
icon.texture = GEAR.icon(kind)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.custom_minimum_size = Vector2(46,46)
row.add_child(icon)
var column := VBoxContainer.new()
column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_child(column)
var title := UI.label(column,"%s · L%d" % [GEAR.NAMES[kind],GEAR.next_level(captain,kind)],15,Color("e4e8e5"))
title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
var recipe := UI.label(column,GEAR.recipe_text(captain,kind),12,Color("a1b3b8"))
recipe.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
var build := UI.button(column,"WYTWÓRZ I ZAŁÓŻ",_craft.bind(kind))
build.add_theme_font_size_override("font_size",13)
build.disabled = not GEAR.can_craft(captain,kind)
UI.button(_content,"POSIADANE WYPOSAŻENIE "+("−" if _owned_open else "+"),func():
_owned_open = not _owned_open
_refresh())
if _owned_open:
for gear in captain.gear_items:
var equipped: bool = captain.gear_slots.get(gear.kind,-1) == gear.uid
var label_text := "%s · L%d%s" % [GEAR.NAMES[gear.kind],gear.level," [ZAŁOŻONE]" if equipped else ""]
var button := UI.button(_content,label_text,_equip.bind(gear.kind,gear.uid))
button.add_theme_font_size_override("font_size",13)
button.disabled = equipped
_label("ZASOBY: "+UI.material_text(captain.minerals),12,Color("819ca5"))
func _build_command() -> void:
var progress: Resource = captain.expedition
_label("MOSTEK / SYSTEMY NAWIGACYJNE",13,Color("8bd9d0"))
_label("Łączność z twoim statkiem jest stabilna. Fregata pozostaje gotowa do przejścia do kolejnego układu.")
_label("Zeskanowane planety: %d" % progress.scans.size(),16)
_label("ODZYSKANE ARCHIWA",15,UI.CYAN)
if progress.research_records.is_empty():
_label("Brak odzyskanych zapisów.",13,Color("819ca5"))
for key in progress.research_records:
_label("%s ×%d" % [key,progress.research_records[key]],13)
_label("Rozbudowa centrum dowodzenia oraz pozostałych sekcji będzie dostępna później.",13,Color("bc9e72"))
func _craft(kind: String) -> void:
var gear: Dictionary = GEAR.craft(captain,kind)
if not gear.is_empty():
GEAR.equip(captain,kind,int(gear.uid))
_audio.service_completed(-0.48)
_refresh()
func _equip(kind: String, uid: int) -> void:
GEAR.equip(captain,kind,uid)
_refresh()