# CubeUI (첫 버전) —— 도깨비 방주 / 제작 (Cube) 窗。 # # var cu := preload("res://ui/cube_ui.gd").new() # add_child(cu) # cu.setup(m2client, canvas_parent, proto) # proto 可空 # # 服务器通过 GC_CHAT/COMMAND 的 "cube ..." 行驱动(见 EntityStore::apply_server_command): # cube_opened(npc) → 显示 + 请求配方列表 # cube_changed → 刷新配方 / 材料 / 金币 # cube_result(vnum,count,ok) → 结果提示 # cube_closed → 隐藏 # 选中一个配方 → [制作] → M2Client.cube_make(index)。 extends Node var client: Node var proto: Node var _root: Control var _title: Label var _status: Label var _list: VBoxContainer var _mat: VBoxContainer var _make_btn: Button var _sel := -1 func setup(m2client: Node, parent: Node, proto_node: Node = null) -> void: client = m2client proto = proto_node _build(parent) if client.has_signal("cube_opened"): client.cube_opened.connect(_on_open) if client.has_signal("cube_closed"): client.cube_closed.connect(func() -> void: _root.visible = false) if client.has_signal("cube_changed"): client.cube_changed.connect(refresh) if client.has_signal("cube_result"): client.cube_result.connect(_on_result) func is_open() -> bool: return _root != null and _root.visible func _on_open(npc_vnum: int) -> void: _sel = -1 _root.visible = true _title.text = "제작 (Cube) · NPC #%d" % npc_vnum _status.text = "" if client.has_method("cube_request_result_list"): client.cube_request_result_list(npc_vnum) refresh() func _on_result(vnum: int, count: int, ok: bool) -> void: if ok: _status.text = "제작 성공: %s ×%d" % [_name_of(vnum), count] else: _status.text = "제작 실패" func _name_of(vnum: int) -> String: if proto and proto.has_method("item"): var d: Dictionary = proto.item(vnum) var n := String(d.get("locale_name", d.get("name", ""))) if n != "": return n return "#%d" % vnum func refresh() -> void: if client == null or _root == null or not _root.visible: return var cube: Dictionary = client.get_cube() var recipes: Array = cube.get("recipes", []) var results: Array = cube.get("results", []) for c in _list.get_children(): c.queue_free() var n: int = maxi(recipes.size(), results.size()) if n == 0: var e := Label.new() e.text = "(이 NPC의 제작 목록 없음)" _list.add_child(e) for i in n: var vnum := 0 if i < recipes.size(): vnum = int((recipes[i] as Dictionary).get("result_vnum", 0)) if vnum == 0 and i < results.size(): vnum = int((results[i] as Dictionary).get("vnum", 0)) var b := Button.new() b.toggle_mode = true b.button_pressed = (i == _sel) b.text = "%d. %s" % [i + 1, _name_of(vnum)] var idx := i b.pressed.connect(func() -> void: _select(idx)) _list.add_child(b) _refresh_materials() func _select(i: int) -> void: _sel = i for k in _list.get_child_count(): var b := _list.get_child(k) if b is Button: b.button_pressed = (k == i) # 材料信息可能未拉过 → 请求 if client.has_method("cube_request_materials"): client.cube_request_materials(i, 1) _refresh_materials() func _refresh_materials() -> void: for c in _mat.get_children(): c.queue_free() var cube: Dictionary = client.get_cube() var recipes: Array = cube.get("recipes", []) _make_btn.disabled = _sel < 0 if _sel < 0 or _sel >= recipes.size(): _mat.add_child(_lbl("재료 정보를 불러오는 중…", 12)) return var r: Dictionary = recipes[_sel] _mat.add_child(_lbl("필요 골드: %d" % int(r.get("gold", 0)), 12)) var groups: Array = r.get("material_groups", []) if groups.is_empty(): _mat.add_child(_lbl("(재료 없음)", 12)) for g in groups: var parts: Array = [] for s in (g as Array): parts.append("%s ×%d" % [_name_of(int((s as Dictionary).get("vnum", 0))), int((s as Dictionary).get("count", 1))]) _mat.add_child(_lbl("• " + " / ".join(parts), 12)) func _lbl(t: String, sz: int) -> Label: var l := Label.new() l.text = t l.add_theme_font_size_override("font_size", sz) return l func _build(parent: Node) -> void: _root = Panel.new() _root.set_anchors_preset(Control.PRESET_CENTER) _root.position = Vector2(-200, -190) _root.custom_minimum_size = Vector2(400, 380) _root.size = Vector2(400, 380) _root.visible = false parent.add_child(_root) var box := VBoxContainer.new() box.position = Vector2(16, 14) box.custom_minimum_size = Vector2(368, 0) box.add_theme_constant_override("separation", 6) _root.add_child(box) _title = _lbl("제작 (Cube)", 16) box.add_child(_title) var split := HBoxContainer.new() split.add_theme_constant_override("separation", 10) box.add_child(split) var lsc := ScrollContainer.new() lsc.custom_minimum_size = Vector2(180, 250) split.add_child(lsc) _list = VBoxContainer.new() _list.add_theme_constant_override("separation", 3) lsc.add_child(_list) _mat = VBoxContainer.new() _mat.custom_minimum_size = Vector2(178, 250) _mat.add_theme_constant_override("separation", 3) split.add_child(_mat) _status = _lbl("", 12) _status.add_theme_color_override("font_color", Color(1, 0.9, 0.5)) box.add_child(_status) var brow := HBoxContainer.new() brow.add_theme_constant_override("separation", 12) box.add_child(brow) _make_btn = Button.new() _make_btn.text = "제작" _make_btn.disabled = true _make_btn.pressed.connect(func() -> void: if _sel >= 0 and client.has_method("cube_make"): client.cube_make(_sel)) brow.add_child(_make_btn) var close := Button.new() close.text = "닫기" close.pressed.connect(func() -> void: _root.visible = false) brow.add_child(close)