274 lines
10 KiB
GDScript
274 lines
10 KiB
GDScript
extends PanelContainer
|
|
|
|
@export var slot_kind: String = "cargo"
|
|
@export var slot_index: int = 0
|
|
@export var overlay_slot: bool = false
|
|
|
|
@onready var slot_label: Label = $SlotLabel
|
|
|
|
var _inventory: ShipInventory
|
|
var _icon: TextureRect
|
|
var _delete_button: Button
|
|
|
|
func _ready() -> void:
|
|
if slot_kind == "cargo":
|
|
var overlay := Control.new()
|
|
overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
add_child(overlay)
|
|
_delete_button = Button.new()
|
|
_delete_button.text = "x"
|
|
_delete_button.z_index = 5
|
|
_delete_button.tooltip_text = "Rozłóż przedmiot: +10 części"
|
|
_delete_button.flat = true
|
|
_delete_button.add_theme_font_size_override("font_size", 13)
|
|
_delete_button.add_theme_color_override("font_color", Color("ff8989"))
|
|
overlay.add_child(_delete_button)
|
|
_delete_button.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
|
|
# Overlay follows the panel's content rect (5 px top, 18 px right padding).
|
|
_delete_button.offset_left = -4
|
|
_delete_button.offset_right = 16
|
|
_delete_button.offset_top = -3
|
|
_delete_button.offset_bottom = 17
|
|
_delete_button.custom_minimum_size = Vector2(20, 20)
|
|
_delete_button.pressed.connect(_delete_item)
|
|
_inventory = get_tree().get_first_node_in_group("ship_inventory") as ShipInventory
|
|
if _inventory:
|
|
_inventory.inventory_changed.connect(_refresh)
|
|
_inventory.ammunition_changed.connect(_refresh)
|
|
custom_minimum_size = Vector2(72, 72) if slot_kind == "cargo" else Vector2(64, 64)
|
|
slot_label.custom_minimum_size = Vector2.ZERO
|
|
slot_label.add_theme_font_size_override("font_size", 9)
|
|
_icon = TextureRect.new()
|
|
_icon.name = "ItemIcon"
|
|
_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
add_child(_icon)
|
|
if overlay_slot:
|
|
var binding := Label.new()
|
|
binding.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
binding.text = (["LPM", "PPM", "ŚPM"][slot_index]) if slot_kind == "weapon" else (str(slot_index + 1) if slot_kind == "extension" else ShipInventory.MODULE_NAMES[slot_index])
|
|
binding.position = Vector2(2, -14)
|
|
binding.add_theme_font_size_override("font_size", 9)
|
|
binding.add_theme_color_override("font_color", Color("a0b2c4"))
|
|
# Use a plain overlay to keep the binding outside the slot's content layout.
|
|
var marker := Control.new()
|
|
marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
add_child(marker)
|
|
marker.add_child(binding)
|
|
_refresh()
|
|
|
|
func _process(_delta: float) -> void:
|
|
if slot_kind == "weapon" and _has_missile():
|
|
queue_redraw()
|
|
|
|
func _draw() -> void:
|
|
if slot_kind != "weapon" or not _has_missile() or _inventory == null:
|
|
return
|
|
var weapons := _inventory.ship.get_node("WeaponController")
|
|
var remaining := float(weapons.get("missile_cooldown_remaining"))
|
|
var duration := float(weapons.get("missile_cooldown_duration"))
|
|
if remaining > 0.0:
|
|
var height := (size.y - 4.0) * clampf(remaining / duration, 0.0, 1.0)
|
|
draw_rect(Rect2(2.0, size.y - 2.0 - height, size.x - 4.0, height), Color(1.0, 0.5, 0.1, 0.45))
|
|
|
|
func _refresh() -> void:
|
|
if not is_node_ready() or not _inventory:
|
|
return
|
|
var item: ItemInstance = _inventory.get_item(slot_kind, slot_index)
|
|
_apply_slot_style(item)
|
|
_icon.texture = item.definition.icon if item != null and item.definition != null else _empty_icon()
|
|
_icon.modulate = Color(1, 1, 1, 1.0 if item != null else 0.16)
|
|
slot_label.visible = _icon.texture == null
|
|
tooltip_text = item.get_tooltip_text() if item != null else _empty_slot_text()
|
|
if item != null and slot_kind == "weapon" and _has_missile():
|
|
tooltip_text += "\nCooldown po addonach: %.2f s\nZapas rakiet: %d" % [_inventory.get_missile_cooldown(item), _inventory.get_missile_count()]
|
|
if _delete_button:
|
|
_delete_button.visible = item != null
|
|
if item == null or item.definition == null:
|
|
slot_label.add_theme_color_override("font_color", Color("82909e"))
|
|
slot_label.text = "—"
|
|
return
|
|
var definition := item.definition
|
|
slot_label.add_theme_color_override("font_color", Color("f1f4f8"))
|
|
slot_label.text = definition.display_name
|
|
|
|
func _apply_slot_style(item: ItemInstance) -> void:
|
|
var style := StyleBoxFlat.new()
|
|
style.bg_color = Color("101925")
|
|
style.border_color = Color("354455")
|
|
style.set_border_width_all(1)
|
|
style.set_content_margin_all(5)
|
|
style.set_corner_radius_all(4)
|
|
if slot_kind == "cargo":
|
|
style.content_margin_right = 5
|
|
if item != null and item.definition != null:
|
|
style.border_color = item.get_type_color()
|
|
style.bg_color = item.get_background_color()
|
|
style.set_border_width_all(2)
|
|
if overlay_slot:
|
|
style.bg_color.a = 0.86 if item != null else 0.48
|
|
add_theme_stylebox_override("panel", style)
|
|
|
|
func _delete_item() -> void:
|
|
if _inventory:
|
|
_inventory.delete_cargo_item(slot_index)
|
|
|
|
func _make_custom_tooltip(for_text: String) -> Object:
|
|
var panel := PanelContainer.new()
|
|
var background := StyleBoxFlat.new()
|
|
background.bg_color = Color("101925")
|
|
background.border_color = Color("52677e")
|
|
background.set_border_width_all(1)
|
|
background.set_content_margin_all(12)
|
|
panel.add_theme_stylebox_override("panel", background)
|
|
var label := RichTextLabel.new()
|
|
label.fit_content = true
|
|
label.scroll_active = false
|
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
label.custom_minimum_size.x = 330
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
label.add_theme_font_size_override("normal_font_size", 13)
|
|
var item := _inventory.get_item(slot_kind, slot_index)
|
|
if item:
|
|
background.bg_color = item.get_background_color()
|
|
background.border_color = item.get_type_color()
|
|
label.add_theme_color_override("default_color", Color("f1f4f8"))
|
|
for line in for_text.split("\n"):
|
|
var quality := 0
|
|
if item:
|
|
for modifier in item.additional_modifiers:
|
|
if modifier != null and line == modifier.get_display_text():
|
|
quality = modifier.get_roll_quality(item.item_level)
|
|
break
|
|
if quality > 0:
|
|
label.push_color(Color("ffd166") if quality == 2 else Color("79e6a0"))
|
|
label.add_text(("★ " if quality == 2 else "") + line + "\n")
|
|
if quality > 0:
|
|
label.pop()
|
|
var columns := HBoxContainer.new()
|
|
columns.add_theme_constant_override("separation", 16)
|
|
panel.add_child(columns)
|
|
columns.add_child(label)
|
|
if slot_kind == "cargo" and item != null:
|
|
_add_comparison_column(columns, item)
|
|
return panel
|
|
|
|
func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void:
|
|
var module_index := ShipInventory.MODULE_CATEGORIES.find(item.definition.category)
|
|
if module_index < 0:
|
|
return
|
|
columns.add_child(VSeparator.new())
|
|
var comparison := RichTextLabel.new()
|
|
comparison.name = "EquipmentComparison"
|
|
comparison.fit_content = true
|
|
comparison.scroll_active = false
|
|
comparison.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
comparison.custom_minimum_size.x = 290
|
|
comparison.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
comparison.add_theme_font_size_override("normal_font_size", 13)
|
|
comparison.add_theme_color_override("default_color", Color("f1f4f8"))
|
|
columns.add_child(comparison)
|
|
var equipped := _inventory.get_item("module", module_index)
|
|
comparison.add_text("ZMIANY PO ZAŁOŻENIU\n")
|
|
comparison.add_text("Slot: %s\n" % ShipInventory.MODULE_NAMES[module_index])
|
|
comparison.add_text("Obecnie: %s\n\n" % ("%s • Poz. %d" % [equipped.definition.display_name, equipped.item_level] if equipped != null else "pusty"))
|
|
comparison.add_text("Zmiana statystyk statku:\n")
|
|
var rows := _inventory.get_item_comparison(item, equipped)
|
|
if rows.is_empty():
|
|
comparison.add_text("Bez zmian w bonusach.\n")
|
|
for row in rows:
|
|
comparison.push_color(Color("79e6a0") if row["delta"] > 0.0 else Color("ff8989"))
|
|
var name: String = row["name"]
|
|
comparison.add_text("%s: %+.2f %s\n" % [name, row["delta"], row["unit"]])
|
|
comparison.pop()
|
|
comparison.add_text("\nUwzględnia utratę bonusów zdejmowanego przedmiotu.")
|
|
|
|
func _get_drag_data(_at_position: Vector2) -> Variant:
|
|
if not _inventory:
|
|
return null
|
|
var item: ItemInstance = _inventory.get_item(slot_kind, slot_index)
|
|
if item == null:
|
|
return null
|
|
if item.definition.icon != null:
|
|
var preview := TextureRect.new()
|
|
preview.texture = item.definition.icon
|
|
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
preview.custom_minimum_size = Vector2(64, 64)
|
|
set_drag_preview(preview)
|
|
else:
|
|
var preview := Label.new()
|
|
preview.text = item.definition.display_name
|
|
set_drag_preview(preview)
|
|
return {"from_kind": slot_kind, "from_index": slot_index}
|
|
|
|
func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
|
|
return (
|
|
_inventory != null
|
|
and data is Dictionary
|
|
and _inventory.can_move_item(
|
|
str(data.get("from_kind", "")),
|
|
int(data.get("from_index", -1)),
|
|
slot_kind,
|
|
slot_index
|
|
)
|
|
)
|
|
|
|
func _drop_data(_at_position: Vector2, data: Variant) -> void:
|
|
_inventory.move_item(
|
|
str(data.get("from_kind", "")),
|
|
int(data.get("from_index", -1)),
|
|
slot_kind,
|
|
slot_index
|
|
)
|
|
|
|
func _has_missile() -> bool:
|
|
if _inventory == null or slot_kind != "weapon":
|
|
return false
|
|
var item := _inventory.get_item(slot_kind, slot_index)
|
|
return item != null and item.definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE
|
|
|
|
func _empty_slot_text() -> String:
|
|
if slot_kind == "weapon":
|
|
return "BROŃ [%s]" % ["LPM", "PPM", "ŚPM"][slot_index]
|
|
if slot_kind == "module":
|
|
return ShipInventory.MODULE_NAMES[slot_index].to_upper()
|
|
if slot_kind == "extension":
|
|
return "ROZSZ. [%d]" % (slot_index + 1)
|
|
return "PUSTY"
|
|
|
|
func _category_name(category: int) -> String:
|
|
match category:
|
|
ItemDefinition.Category.WEAPON:
|
|
return "BROŃ"
|
|
ItemDefinition.Category.SHIELD:
|
|
return "TARCZA"
|
|
ItemDefinition.Category.ARMOR:
|
|
return "PANCERZ"
|
|
ItemDefinition.Category.REACTOR:
|
|
return "REAKTOR"
|
|
ItemDefinition.Category.ENGINE:
|
|
return "SILNIK"
|
|
ItemDefinition.Category.COMPUTER:
|
|
return "KOMPUTER"
|
|
ItemDefinition.Category.ACTIVE_EXTENSION:
|
|
return "ROZSZERZENIE"
|
|
return "PRZEDMIOT"
|
|
|
|
func _stat_text(item: ItemInstance) -> String:
|
|
var definition := item.definition
|
|
if definition.category == ItemDefinition.Category.WEAPON:
|
|
return "%s · Poz. %d" % [item.get_damage_range_text(), item.item_level]
|
|
if definition.primary_stat.is_empty():
|
|
return "Poz. %d" % item.item_level
|
|
var total_value := item.get_stat_total(definition.primary_stat)
|
|
var sign_text := "+" if total_value >= 0.0 else ""
|
|
return "%s%.0f%s · Poz. %d" % [sign_text, total_value, definition.stat_suffix, item.item_level]
|
|
|
|
func _empty_icon() -> Texture2D:
|
|
if slot_kind != "module":
|
|
return null
|
|
var names := ["shield", "armor", "engine", "reactor", "computer"]
|
|
return load("res://assets/icons/%s.png" % names[slot_index]) as Texture2D
|