123 lines
3.8 KiB
GDScript
123 lines
3.8 KiB
GDScript
extends Control
|
||
|
||
## NEO-74: scrollable prototype recipe list with per-row Craft buttons.
|
||
|
||
signal craft_requested(recipe_id: String)
|
||
|
||
const PrototypeHudTheme := preload("res://scripts/prototype_hud_theme.gd")
|
||
const HudThemeConstants := preload("res://scripts/prototype_hud_theme_constants.gd")
|
||
|
||
var _item_defs_client: Node = null
|
||
var _craft_buttons: Array[Button] = []
|
||
|
||
@onready var _scroll: ScrollContainer = $ScrollContainer
|
||
@onready var _vbox: VBoxContainer = $ScrollContainer/VBoxContainer
|
||
|
||
|
||
func _ready() -> void:
|
||
if is_instance_valid(_scroll):
|
||
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||
_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
|
||
resized.connect(_sync_vbox_width)
|
||
|
||
|
||
func setup(item_defs_client: Node) -> void:
|
||
_item_defs_client = item_defs_client
|
||
|
||
|
||
func populate(recipes: Array) -> void:
|
||
if not is_instance_valid(_vbox):
|
||
return
|
||
for child in _vbox.get_children():
|
||
child.queue_free()
|
||
_craft_buttons.clear()
|
||
for row_variant in recipes:
|
||
if not row_variant is Dictionary:
|
||
continue
|
||
var recipe: Dictionary = row_variant
|
||
var recipe_id: String = str(recipe.get("id", ""))
|
||
if recipe_id.is_empty():
|
||
continue
|
||
var row := VBoxContainer.new()
|
||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_theme_constant_override("separation", 4)
|
||
var info := Label.new()
|
||
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
info.text = _format_recipe_line(recipe)
|
||
_apply_recipe_row_label(info)
|
||
row.add_child(info)
|
||
var btn_row := HBoxContainer.new()
|
||
btn_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
btn_row.alignment = BoxContainer.ALIGNMENT_END
|
||
var craft_btn := Button.new()
|
||
craft_btn.text = "Craft"
|
||
PrototypeHudTheme.apply_to_button(craft_btn, false)
|
||
craft_btn.pressed.connect(_on_craft_pressed.bind(recipe_id))
|
||
btn_row.add_child(craft_btn)
|
||
row.add_child(btn_row)
|
||
_vbox.add_child(row)
|
||
_craft_buttons.append(craft_btn)
|
||
call_deferred("_reset_scroll_after_populate")
|
||
|
||
|
||
func set_craft_busy(busy: bool) -> void:
|
||
for btn in _craft_buttons:
|
||
if is_instance_valid(btn):
|
||
btn.disabled = busy
|
||
|
||
|
||
func _format_recipe_line(recipe: Dictionary) -> String:
|
||
var recipe_id: String = str(recipe.get("id", ""))
|
||
var display_name: String = str(recipe.get("displayName", recipe_id))
|
||
var inputs_line: String = _format_inputs(recipe.get("inputs", []))
|
||
return "%s (%s)\nneeds: %s" % [display_name, recipe_id, inputs_line]
|
||
|
||
|
||
func _format_inputs(raw_inputs: Variant) -> String:
|
||
if raw_inputs == null or not raw_inputs is Array:
|
||
return "—"
|
||
var parts: PackedStringArray = PackedStringArray()
|
||
for row_variant in raw_inputs as Array:
|
||
if not row_variant is Dictionary:
|
||
continue
|
||
var row: Dictionary = row_variant
|
||
var item_id: String = str(row.get("itemId", ""))
|
||
var qty: int = int(row.get("quantity", 0))
|
||
if item_id.is_empty() or qty <= 0:
|
||
continue
|
||
parts.append("%d× %s" % [qty, _item_display_name(item_id)])
|
||
if parts.is_empty():
|
||
return "—"
|
||
return ", ".join(parts)
|
||
|
||
|
||
func _item_display_name(item_id: String) -> String:
|
||
if is_instance_valid(_item_defs_client) and _item_defs_client.has_method("display_name_for"):
|
||
return str(_item_defs_client.call("display_name_for", item_id))
|
||
return item_id
|
||
|
||
|
||
func _on_craft_pressed(recipe_id: String) -> void:
|
||
craft_requested.emit(recipe_id)
|
||
|
||
|
||
func _apply_recipe_row_label(label: Label) -> void:
|
||
label.add_theme_font_size_override("font_size", HudThemeConstants.FONT_SIZE_SECONDARY)
|
||
label.add_theme_constant_override("outline_size", HudThemeConstants.OUTLINE_SIZE)
|
||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
|
||
|
||
func _sync_vbox_width() -> void:
|
||
if not is_instance_valid(_scroll) or not is_instance_valid(_vbox):
|
||
return
|
||
var width: float = _scroll.size.x
|
||
if width <= 0.0:
|
||
return
|
||
_vbox.custom_minimum_size.x = width
|
||
|
||
|
||
func _reset_scroll_after_populate() -> void:
|
||
_sync_vbox_width()
|
||
if is_instance_valid(_scroll):
|
||
_scroll.scroll_vertical = 0
|