装备校验链/快捷栏/公会/好友情侣/交易仓库五项差距修复(增量 129)
- 装备校验:can_equip 按 CanEquipNow→FindEquipCell→EquipItem 精确重排, 新增 find_equip_cell() 1:1 复刻(RING/UNIQUE 占用翻转、COSTUME/BELT/DS), 帝国 antiflag 降级为 tooltip 提示,LIMIT_PCBANG/REAL_TIME* 语义与倒计时文案 - 快捷栏:死亡全槽禁用+激活守卫、变身(affect 220)技能置灰、 物品槽失配置灰(服务器回收/换武器后刷新) - 公会:GUILD_AUTH_* 权限位门控踢人/公告/技能升级,/gskillup、 被宣战应答弹窗(/war·/nowar)、资金存取行(40250 服务端已禁用仍保留 1:1) - 好友/情侣:messenger 三固定组(好友/公会/情侣),lover_login/logout/ near/far/divorce 命令事件→在线/在身边状态与离婚隐藏 - 交易/仓库:ExchangeState.last_notice(ALREADY/LESS_GOLD)结束浮层提示; 仓库改密 /safebox_change_password 三段输入;关窗补 /safebox_close·/mall_close; 仓库金钱经核实 40250 无 wire 协议,UI 定界只读 - app_flow:重连时 guild_marks_ready 重复连接加 is_connected 守卫 - 相机 look_at 零向量守卫(首帧/传送落点) - 测试:equip/love/guild 断言更新,79 项中 78 绿(game_camera_test 为 HEAD 既有失败);真服冒烟 LIVE_SMOKE PASS(192.168.21.203 全链路) - docs/CLIENT-GAP.md 同步增量 129 Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018kUFvQGYusuY3dkfGitmAe
This commit is contained in:
+56
-1
@@ -65,7 +65,19 @@ func setup(m2client: Node, skill_table: RefCounted, parent: Node, player_getter:
|
||||
client.quickslots_changed.connect(restore_from_server)
|
||||
if client.has_signal("skills_changed"):
|
||||
client.skills_changed.connect(_refresh_page)
|
||||
restore_from_server()
|
||||
# 物品槽引用的是背包 cell:装备/销毁/丢弃后服务器 SyncQuickslot 会推
|
||||
# quickslots_changed;这里再挂 inventory_changed 兜住时序窗口(置灰失效引用)。
|
||||
if client.has_signal("inventory_changed"):
|
||||
client.inventory_changed.connect(func(_w, _c): _refresh_page())
|
||||
# 死亡/复活:死亡中槽位全部置灰(对齐原版 POS_DEAD 全链路禁用)。
|
||||
if client.has_signal("entity_dead"):
|
||||
client.entity_dead.connect(_on_entity_dead)
|
||||
# 变身(AFFECT_POLYMORPH=220):技能槽整体遮罩,结束(re-broadcast)后解除。
|
||||
if client.has_signal("affect_added"):
|
||||
client.affect_added.connect(_on_affect_added)
|
||||
if client.has_signal("affect_removed"):
|
||||
client.affect_removed.connect(_on_affect_removed)
|
||||
restore_from_server()
|
||||
|
||||
# 从服务器 GC_QUICKSLOT_* 恢复全部 36 个快捷栏槽位。
|
||||
# 参考 ESlotType:1 道具 / 2 技能 / 3 表情 / 4 商店(不可执行)。
|
||||
@@ -140,6 +152,9 @@ func _activate_slot(slot: int, screen_direction: Vector2) -> void:
|
||||
return
|
||||
if _now() < s.cd_end:
|
||||
return
|
||||
# 死亡中不可使用(服务器 POS_DEAD 拦截一切使用;这里提前本地拦截)。
|
||||
if _player_dead():
|
||||
return
|
||||
if s.kind == "skill":
|
||||
var mi: int = table.motion_idx_of(s.id) if table else 0
|
||||
var p: Node3D = _player_getter.call() if _player_getter.is_valid() else null
|
||||
@@ -220,6 +235,35 @@ func _aim_heading(player_node: Node3D, screen_direction: Vector2, fallback: floa
|
||||
var camera_delta := rad_to_deg(camera_heading - player_node.rotation.y)
|
||||
return fposmod(fallback + camera_delta + angle_from_up, 360.0)
|
||||
|
||||
# 服务器权威的死亡态(GC add chr state DEAD);复活后 entity_info 会再触发刷新。
|
||||
func _player_dead() -> bool:
|
||||
if client == null or not client.has_method("get_main_vid"):
|
||||
return false
|
||||
var me: Dictionary = client.get_entity(client.get_main_vid())
|
||||
return me != null and bool(me.get("dead", false))
|
||||
|
||||
# 变身态(POINT_POLYMORPH / affect 220):技能槽遮罩的数据源。
|
||||
func _polymorphed() -> bool:
|
||||
if client == null or not client.has_method("get_main_vid"):
|
||||
return false
|
||||
var me: Dictionary = client.get_entity(client.get_main_vid())
|
||||
return me != null and (int(me.get("polymorph", 0)) != 0 or int(me.get("poly_vnum", 0)) != 0)
|
||||
|
||||
func _on_entity_dead(vid: int) -> void:
|
||||
if client and client.has_method("get_main_vid") and vid == client.get_main_vid():
|
||||
_refresh_page()
|
||||
|
||||
# 只关心 AFFECT_POLYMORPH(220) 的增减,避免每次 buff 变化都整页重刷。
|
||||
const AFFECT_POLYMORPH := 220
|
||||
|
||||
func _on_affect_added(affect: Dictionary) -> void:
|
||||
if int(affect.get("type", 0)) == AFFECT_POLYMORPH:
|
||||
_refresh_page()
|
||||
|
||||
func _on_affect_removed(type: int) -> void:
|
||||
if type == AFFECT_POLYMORPH:
|
||||
_refresh_page()
|
||||
|
||||
# net_play 的 MODE_USE_SKILL 预约进射程后回调(use_skill_hook)——slot 为全局快捷栏
|
||||
# 索引。返回是否确实发起了施法(对齐参考端 `if (__UseSkill(slot)) __ClearReservedAction()`)。
|
||||
func activate_reserved(global_slot: int) -> bool:
|
||||
@@ -422,9 +466,20 @@ func _refresh_slot(slot: int) -> void:
|
||||
grade_text = _skill_grade_text(int(s.id))
|
||||
elif s.kind == "item":
|
||||
txt = "#%d" % s.id
|
||||
# 引用失效(物品已装备/销毁/掉落,服务器 SyncQuickslot 尚未推或已删)→ 置灰。
|
||||
var dim := false
|
||||
if s.kind == "item" and client and client.has_method("get_item"):
|
||||
var it: Dictionary = client.get_item(1, s.id)
|
||||
if it.is_empty() or int(it.get("vnum", 0)) == 0:
|
||||
dim = true
|
||||
txt = "×%d" % s.id
|
||||
# 变身中技能槽整体遮罩(服务器只放行箭矢类物品);死亡中全部置灰。
|
||||
if (s.kind == "skill" and _polymorphed()) or _player_dead():
|
||||
dim = true
|
||||
var icon: TextureRect = _slots[slot].icon
|
||||
icon.texture = tex
|
||||
icon.visible = tex != null
|
||||
_slots[slot].btn.modulate = Color(0.42, 0.42, 0.42) if dim else Color(1, 1, 1)
|
||||
_slots[slot].lbl.text = txt if tex == null else ""
|
||||
_slots[slot].grade.text = grade_text
|
||||
_slots[slot].grade.visible = grade_text != "" and tex != null
|
||||
|
||||
Reference in New Issue
Block a user