# GuildUI (P8/M4) —— 公会窗(G 键):成员 / 技能 / 公会战 三页。 # # var gu := preload("res://ui/guild_ui.gd").new() # add_child(gu) # gu.setup(m2client, canvas_parent, skill_table) # skill_table 可空(出公会技能名) # gu.toggle() # G 键 # # `guild_changed` / `guild_skill_changed` / `guild_war_changed` 刷新,`guild_marks_ready` # 到时刷会徽图。⬜ 等级页、日志页。 extends Node const TABS := ["成员", "技能", "公会战"] # EGuildWarState (wire.h) const WAR_STATE_NAMES := ["无", "已宣战", "被拒", "收到宣战", "待开始", "取消", "交战中", "结束"] var client: Node var skill_table: RefCounted # SkillTable # 返回一张 Image(16×12,或任意会被转换/缩放)用作会徽上传源;null = 不显示上传按钮。 var mark_image_provider: Callable var _root: Control var _info: Label var _mark: TextureRect var _tab := 0 var _tab_btns: Array[Button] = [] var _pages: Array[VBoxContainer] = [] var _war_name_edit: LineEdit var _upload_status: Label func setup(m2client: Node, parent: Node, table: RefCounted = null) -> void: client = m2client skill_table = table _build(parent) for sig in ["guild_changed", "guild_skill_changed", "guild_war_changed"]: if client.has_signal(sig): client.connect(sig, refresh) if client.has_signal("guild_marks_ready"): client.guild_marks_ready.connect(func(_n): refresh()) if client.has_signal("guild_mark_uploaded"): client.guild_mark_uploaded.connect(_on_mark_uploaded) func is_open() -> bool: return _root != null and _root.visible func toggle() -> void: _root.visible = not _root.visible if _root.visible: refresh() func _set_tab(i: int) -> void: _tab = i for t in _pages.size(): _pages[t].visible = (t == i) _tab_btns[t].disabled = (t == i) refresh() func refresh() -> void: if not is_open() or client == null: return var g: Dictionary = client.get_guild() var in_guild: bool = g.get("in_guild", false) _refresh_mark(int(g.get("id", 0)) if in_guild else 0) if not in_guild: _info.text = "(未加入公会)" for p in _pages: for c in p.get_children(): c.queue_free() return _info.text = "%s Lv.%d 成员 %d/%d 资金 %d" % [ g.get("name", "?"), int(g.get("level", 0)), int(g.get("member_count", 0)), int(g.get("max_member_count", 0)), int(g.get("gold", 0))] match _tab: 0: _fill_members() 1: _fill_skills() 2: _fill_war(int(g.get("id", 0))) func _fill_members() -> void: var page := _pages[0] for c in page.get_children(): c.queue_free() # 会徽上传(需要配了 mark server + 提供了图源) var srv: Dictionary = client.get_mark_server() if client.has_method("get_mark_server") else {} if mark_image_provider.is_valid() and int(srv.get("port", 0)) > 0: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 8) page.add_child(row) var ub := Button.new() ub.text = "上传会徽" ub.custom_minimum_size = Vector2(96, 26) ub.pressed.connect(_upload_mark) row.add_child(ub) _upload_status = Label.new() _upload_status.add_theme_font_size_override("font_size", 11) _upload_status.modulate = Color(0.7, 0.8, 0.7) row.add_child(_upload_status) var grades: Array = client.get_guild_grades() var members: Array = client.get_guild_members() members.sort_custom(func(a, b): return int(a.get("grade", 99)) < int(b.get("grade", 99))) for m in members: var gi := int(m.get("grade", 0)) var gname := "" if gi >= 1 and gi <= grades.size(): gname = String(grades[gi - 1].get("name", "")) var row := Label.new() row.add_theme_font_size_override("font_size", 12) row.text = "%s Lv.%d [%s] 贡献 %d%s" % [ String(m.get("name", "?")), int(m.get("level", 0)), gname if gname != "" else str(gi), int(m.get("offer", 0)), " ★" if m.get("general", false) else ""] page.add_child(row) func _guild_skill_rows() -> Array: # skill_table 的 GUILD 分类给 id(升序);按顺序配 levels[i]。返回 [{id, name}]。 var out := [] if skill_table and skill_table.has_method("for_category"): for id in skill_table.for_category("GUILD"): out.append({"id": int(id), "name": skill_table.name_of(int(id))}) return out func _fill_skills() -> void: var page := _pages[1] for c in page.get_children(): c.queue_free() var sk: Dictionary = client.get_guild_skill() if not sk.get("valid", false): var l := Label.new() l.text = "(还没收到公会技能数据)" page.add_child(l) return var head := Label.new() head.add_theme_font_size_override("font_size", 12) head.modulate = Color(0.8, 0.9, 1.0) head.text = "技能点 %d 公会点 %d / %d" % [ int(sk.get("skill_point", 0)), int(sk.get("guild_point", 0)), int(sk.get("max_guild_point", 0))] page.add_child(head) var levels: Array = sk.get("levels", []) var meta := _guild_skill_rows() for i in levels.size(): var lv := int(levels[i]) var nm := "公会技能 %d" % i var sid := -1 if i < meta.size(): nm = String(meta[i].get("name", nm)) sid = int(meta[i].get("id", -1)) var row := HBoxContainer.new() row.add_theme_constant_override("separation", 8) page.add_child(row) var lbl := Label.new() lbl.add_theme_font_size_override("font_size", 12) lbl.custom_minimum_size = Vector2(240, 0) lbl.text = "%s Lv %d" % [nm, lv] row.add_child(lbl) if sid >= 0 and lv > 0: var btn := Button.new() btn.text = "施放" btn.custom_minimum_size = Vector2(56, 24) btn.pressed.connect(func(): client.use_guild_skill(sid, 0)) row.add_child(btn) func _fill_war(my_guild_id: int) -> void: var page := _pages[2] for c in page.get_children(): c.queue_free() var cur: Dictionary = client.get_guild_war() var st := int(cur.get("state", 0)) var cur_lbl := Label.new() cur_lbl.add_theme_font_size_override("font_size", 12) cur_lbl.modulate = Color(1.0, 0.8, 0.7) if st == 0: cur_lbl.text = "当前无公会战" else: var on := String(cur.get("opp_name", "")) cur_lbl.text = "对 %s : %s" % [ on if on != "" else "#%d" % int(cur.get("opp_guild_id", 0)), WAR_STATE_NAMES[st] if st < WAR_STATE_NAMES.size() else str(st)] page.add_child(cur_lbl) # 宣战输入 var decl := HBoxContainer.new() decl.add_theme_constant_override("separation", 6) page.add_child(decl) _war_name_edit = LineEdit.new() _war_name_edit.placeholder_text = "对方公会名" _war_name_edit.custom_minimum_size = Vector2(200, 26) decl.add_child(_war_name_edit) var db := Button.new() db.text = "宣战" db.custom_minimum_size = Vector2(60, 26) db.pressed.connect(func(): var nm := _war_name_edit.text.strip_edges() if nm != "": client.declare_guild_war(nm)) decl.add_child(db) var hdr := Label.new() hdr.add_theme_font_size_override("font_size", 12) hdr.text = "— 进行中的公会战 —" page.add_child(hdr) var wars: Array = client.get_guild_wars() if wars.is_empty(): var none := Label.new() none.text = "(无)" page.add_child(none) for wpair in wars: var sn := String(wpair.get("src_name", "")) var dn := String(wpair.get("dst_name", "")) var row := Label.new() row.add_theme_font_size_override("font_size", 12) var mark_self := " ◀我方" if int(wpair.get("src", 0)) == my_guild_id or int(wpair.get("dst", 0)) == my_guild_id else "" row.text = "%s vs %s%s" % [ sn if sn != "" else "#%d" % int(wpair.get("src", 0)), dn if dn != "" else "#%d" % int(wpair.get("dst", 0)), mark_self] page.add_child(row) func _upload_mark() -> void: var g: Dictionary = client.get_guild() if not g.get("in_guild", false) or not mark_image_provider.is_valid(): return var img = mark_image_provider.call() if img == null: if _upload_status: _upload_status.text = "没有会徽图" return var srv: Dictionary = client.get_mark_server() var ok: bool = client.upload_guild_mark( String(srv.get("host", "")), int(srv.get("port", 0)), int(g.get("id", 0)), img) if _upload_status: _upload_status.text = "上传中…" if ok else "上传发起失败" func _on_mark_uploaded(ok: bool) -> void: if _upload_status: _upload_status.text = "✔ 已上传(等服务器刷新)" if ok else "✘ 上传失败" # 公会 id 变了就重新取会徽图(16x12,放大 3x 显示)。 func _refresh_mark(guild_id: int) -> void: if _mark == null: return if guild_id == 0 or client == null or not client.has_method("get_guild_mark_image"): _mark.texture = null _mark.visible = false return var img: Image = client.get_guild_mark_image(guild_id) if img == null: _mark.texture = null _mark.visible = false return _mark.texture = ImageTexture.create_from_image(img) _mark.visible = true func _build(parent: Node) -> void: _root = Control.new() _root.set_anchors_preset(Control.PRESET_CENTER) _root.position = Vector2(-190, -210) _root.size = Vector2(380, 420) _root.visible = false _root.set_meta("is_titlebar", true) parent.add_child(_root) var panel := Panel.new() panel.set_anchors_preset(Control.PRESET_FULL_RECT) var sb := StyleBoxFlat.new() sb.bg_color = Color(0.07, 0.08, 0.1, 0.97) sb.set_corner_radius_all(4) panel.add_theme_stylebox_override("panel", sb) _root.add_child(panel) var title := Label.new() title.text = "公会" title.position = Vector2(12, 8) _root.add_child(title) _mark = TextureRect.new() _mark.position = Vector2(320, 6) _mark.custom_minimum_size = Vector2(48, 36) # 16x12 放大 3x _mark.size = Vector2(48, 36) _mark.stretch_mode = TextureRect.STRETCH_SCALE _mark.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST _mark.visible = false _root.add_child(_mark) _info = Label.new() _info.position = Vector2(12, 32) _info.add_theme_font_size_override("font_size", 12) _info.modulate = Color(0.9, 0.85, 0.6) _root.add_child(_info) var tabrow := HBoxContainer.new() tabrow.position = Vector2(12, 54) tabrow.add_theme_constant_override("separation", 4) _root.add_child(tabrow) for i in TABS.size(): var b := Button.new() b.text = TABS[i] b.custom_minimum_size = Vector2(84, 26) b.pressed.connect(_set_tab.bind(i)) tabrow.add_child(b) _tab_btns.append(b) var sc := ScrollContainer.new() sc.position = Vector2(12, 86) sc.custom_minimum_size = Vector2(356, 322) _root.add_child(sc) var stack := Control.new() stack.custom_minimum_size = Vector2(350, 0) sc.add_child(stack) for i in TABS.size(): var page := VBoxContainer.new() page.custom_minimum_size = Vector2(350, 0) page.add_theme_constant_override("separation", 3) page.visible = (i == 0) stack.add_child(page) _pages.append(page) _tab_btns[0].disabled = true