364 lines
16 KiB
GDScript
364 lines
16 KiB
GDScript
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
|
||
|
||
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:
|
||
_tab = section
|
||
deck.selected = section
|
||
_refresh()
|
||
|
||
func _close_section() -> void:
|
||
_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:
|
||
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)
|
||
_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():
|
||
progress.repair(captain)
|
||
_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))
|
||
_refresh()
|
||
|
||
func _equip(kind: String, uid: int) -> void:
|
||
GEAR.equip(captain,kind,uid)
|
||
_refresh()
|