These files were loaded only by their own test_*_parity.gd and never by the runtime (transitively checked, incl. client_phase_integration_system). Removes 76 manifest implementation/evidence references across 18 contracts; contract docs get a note that conclusions based on them are void. Live systems kept: chest, consumable, item_attr, metin_socket, whisper_chat. Full headless suite: no new failures (7 failing + 5 headless-hanging render tests are identical before the deletion). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 KiB
13 KiB
skill.player_ui_cooldown —— 技能栏、快捷键、等级、冷却和禁止条件
状态:PARTIAL
本合同覆盖 40250 Windows 客户端的技能数据解析、技能页加点、快捷栏持久化、技能施放前检查、目标/射程预约、冷却和服务端解锁事件。判断标准是调用顺序、禁止条件、数据来源、冷却公式和网络副作用一致,而不是只看当前端是否存在同名 use_skill 函数。
40250 参考调用链
技能数据和等级
UserInterface/PythonSkill.cppRegisterSkillDesc/RegisterSkillTable读取技能属性、武器限制、动作、等级、目标范围、目标数量、SP/冷却公式和.msk数据。SSkillData::IsCanUseSkill只允许非被动主动施放;GetSkillCoolTime、GetTargetCount、GetNeedSP都以fcurEfficientPercentage代入公式。
UserInterface/PythonPlayer.cppSetSkillLevel_将服务端等级/grade 转为当前 slot 的 grade、level 和LocaleService_GetSkillPower效率百分比。- 快捷栏是服务端
QUICKSLOT_MAX_NUM=36,本地显示 4×8;分页、slot/ref 映射、add/del/move 请求均以全局槽位为权威。 - affect 的 toggle 技能会调用
ActivateSkillSlot/DeactivateSkillSlot,而不是由 UI 本地猜测。
施放前检查和副作用顺序
UserInterface/PythonPlayerSkill.cpp::ClickSkillSlot- 槽位/技能数据 → 公会技能路由 → 被动静默返回 → standing/toggle-off →
__UseSkill。
- 槽位/技能数据 → 公会技能路由 → 被动静默返回 → standing/toggle-off →
__CanUseSkill- 主角存在、非观战、坐骑术等级满足、
CInstanceBase::CanUseSkill通过。
- 主角存在、非观战、坐骑术等级满足、
CInstanceBase::CanUseSkill- 拒绝变身
IsPoly、穿礼服IsWearingDress、持镐IsHoldingPickAxe、坐骑不可用技能和当前图形动作不可施法。
- 拒绝变身
__CheckSkillUsable- 骑乘/骑乘技、攻击技安全区、被动、空瓶/毒瓶、钓鱼、等级/未学习、武器类型、箭矢、冲刺 affect、冷却、HP/SP 按固定顺序检查。
__UseSkill- 私人商店静默返回 →
__CanUseSkill→ 特殊技能 → toggle-off →__CheckSkillUsable→ 施法中检查 → 目标/尸体/结盟/安全区/可攻击/射程预约 → 改朝向 → 扇形/圆形追加目标 → 清预约 →NEW_UseSkill动作 →SendUseSkillPacket→__RunCoolTime。
- 私人商店静默返回 →
__RunCoolTime- 以 skill
.msk冷却公式计算基础时间,再按POINT_CASTING_SPEED修正,最后触发 UIRunUseSkillEvent。
- 以 skill
网络回包
PythonNetworkStreamPhaseGame.cppRecvSkillLevel/RecvSkillLevelNew刷新技能和效率百分比。RecvSkillCoolTimeEnd调用EndSkillCoolTime,按技能 ID 清除本地冷却。RecvChangeSkillGroupPacket清空旧 skill data 并刷新角色窗口。GC_QUICKSLOT_ADD/DEL/MOVE更新本地 36 槽数据。
当前端实现映射
project/ui/skill_table.gd- 已解析
skilldesc.txt、属性/武器 bit、.msk的冷却/SP/目标数量/范围公式,并实现技能分类和等级加点谓词。
- 已解析
project/player_skill.gd- 已按
ClickSkillSlot → can_use_skill → check_skill_usable → use_skill分层校验,包含CInstanceBase::CanUseSkill的变身、礼服、矿镐、坐骑动作和图形动作五类门,以及安全区、骑乘、被动、瓶子、钓鱼、等级、武器、箭矢、冷却、HP/SP、尸体、结盟、目标和射程预约。 - 公会技能按
GUILD_ROUTED转入独立use_guild_skill,补齐公会战状态门和独立冷却入口。
- 已按
project/ui/quickbar.gd- 维护 36 个服务端槽位、4×8 可见页、server quickslot add/del/swap、技能图标/等级/冷却遮罩、移动端快捷栏和
GC_SKILL_COOLTIME_END解锁。- 通过
M2Client.use_skill、use_guild_skill、cast_skill和NetPlay发起技能;冷却按.msk基础值和POINT_CASTING_SPEED的 40250 分段公式本地预测。
- 通过
- 维护 36 个服务端槽位、4×8 可见页、server quickslot add/del/swap、技能图标/等级/冷却遮罩、移动端快捷栏和
project/ui/skill_ui.gd- 有主动/辅助/坐骑页、技能等级/grade、加点按钮、技能拖入快捷栏和服务端 skill/group 刷新。
extension/src/net/entity_store.cpp、classic_parser.cpp、m2_client.cpp- 已存储/转发 skill level、master、group、quickslot 和 cooldown-end 包,并暴露相应信号。
分支等价矩阵
| 参考判据/副作用 | 当前实现 | 结论 |
|---|---|---|
技能属性、武器限制、.msk 公式和等级效率 |
SkillTable 读取 skilldesc/.msk,_skill_point 有 40250 等级表 |
基本等价;全部语言、所有 .msk 和公式异常仍未建立快照 |
ClickSkillSlot 分层顺序、被动/公会/特殊/开关技 |
PlayerSkill.click_skill_slot/use_skill/use_guild_skill + Quickbar._activate_guild_skill |
基本等价;公会技已独立走 CG_GUILD_USE_SKILL,toggle active 状态主要由调用方传入,未证明与 affect slot 状态始终一致 |
CInstanceBase::CanUseSkill 变身、礼服、持镐、坐骑和图形动作门 |
can_use_skill 按相同顺序拒绝五类动作状态,NetPlay.skill_context 从实体快照注入 poly/礼服/矿镐/坐骑/图形门 |
基本等价;礼服和图形层字段依赖服务器快照是否提供 |
| 安全区、瓶子、钓鱼、等级、武器、箭矢、HP/SP、尸体和结盟 | check_skill_usable/_resolve_target |
基本等价;in_safe、持有物、HP/SP 和目标字段均依赖当前端快照,实时/乱序未证实 |
冷却公式 GetSkillCoolTime(fcurEfficientPercentage) |
SkillTable.cooldown_of |
公式主体基本等价 |
__RunCoolTime 的 POINT_CASTING_SPEED 修正 |
M2Client.get_points() 暴露点 21,Quickbar._skill_cooldown 按 40250 分段公式换算 |
基本等价;真实服务端点变更与冷却回包乱序仍待 fixture |
冷却开始时机:动作成功后 SendUseSkillPacket → __RunCoolTime |
当前先成功发送 cast_skill(FUNC_SKILL),再发送 use_skill(CG_USE_SKILL),最后写本地 cd_end |
基本等价;没有以服务端/动作成功回执确认本地预测是否应回滚 |
GC_SKILL_COOLTIME_END 按 skill ID 解锁 |
native parser/store → skill_cooldown_end → Quickbar 清理 |
基本等价;重复、过期、断线和槽位重排时序未验证 |
| 36 槽 server quickslot 与 4×8 可见页 | Quickbar 36 状态 + 32 可见槽,native 36 槽 | 基本等价;最后 4 个不可见槽的恢复、键盘输入和 UI 可达性未验证 |
| 目标/射程预约、自动索敌、扇形/圆形追加目标 | NetPlay/PlayerSkill/Quickbar |
部分等价;SetFlyTargetInstance、动作朝向、资源目标数量和发送顺序尚无完整包序证据 |
| skill/group/quickslot 回包刷新 UI | native signals → Quickbar/SkillUI | 基本等价;已有单元测试,但没有真实乱序、拒绝和重连场景 |
已执行测试
project/player_skill_test.gd:PASS;覆盖三层技能合法性校验和CanUseSkill五类动作门。project/skill_test.gd:PASS;覆盖技能表、技能页和快捷栏基础链。project/netplay_test.gd:PASS;覆盖GUILD_WAR_NONE/ON_WAR/WAIT_START到公会技状态门的映射。project/test_skill_inventory_parity.gd:45 项 PASS;覆盖技能门控、库存/装备、角色状态和 quickslot 同步。project/test_taskbar_quickslot_parity.gd:34/34 PASS;覆盖任务栏、4×8 页面和鼠标技能模式;有 Godot ObjectDB/RID/resource cleanup 警告。project/test_status_skill_reset_parity.gd:32/32 PASS;覆盖技能重置相关状态变化。project/mobile_gesture_test.gd:PASS;覆盖移动端技能拖放、瞄准和快捷栏交换。project/playable_combat_test.gd:0 failures;覆盖离线可玩技能请求、冷却、目标、资源和断线。project/guild_war_skill_test.gd:PASS;覆盖公会技能页、公会战状态和use_guild_skillUI 入口。build/extension/net_entity_test、build/extension/net_classic_session_test:PASS;覆盖 native skill/group/quickslot/cooldown-end 协议存储和会话入口。project/test_full_skill_flow.gd:PASS;共享net_client_fake.gdfixture 可启动 GameScene,并断言FUNC_SKILL → CG_USE_SKILL动作/意图包序。
已确认差异和未验证项
Quickbar在本地use_skill返回成功后立即预测冷却,缺少服务端拒绝/动作未起播/断线后的冷却回滚证据;40250 也做客户端预测,但其状态由CPythonPlayer的 slot 冷却对象统一管理。- 当前 skill/group/quickslot parser 虽已存在,尚无
GC_SKILL_LEVEL_NEW → group → quickslot → cooldown-end乱序、重复、跨角色和重连生命周期测试。 - 需要建立全部技能 ID/grade/语言/
.msk公式和动作资源索引,验证动作事件、目标追加、SendUseSkillPacket、冷却和服务端 damage 的顺序。
下一轮验证入口
- 验证
POINT_CASTING_SPEED在真实 points 包、重复点变更和冷却结束回包中的时序,并补 native/GDScript 端到端证据。 - 将
CanUseSkill五类禁止状态接到实体真实 shape/动作字段,并验证拒绝时不发CG_USE_SKILL、CG_MOVE(FUNC_SKILL)或 fly-target 包。 - 用真实
GUILD_WAR_ON_WAR回包验证公会技ONLY_FOR_GUILD_WAR门、CG_GUILD_USE_SKILL和冷却结束包序。 - 增加 skill level/group/quickslot/cooldown-end 的乱序、重复、断线、重连和换图测试,再决定是否可提升合同状态。
Active UserInterface skill-data bridge review
PythonSkill.h的TSkillData/技能描述字段包括 target range、attribute、need weapon、状态上下限、affect description、cooldown、SP、continuation SP、motion loop count、target count、duration、motion index、grade icon 和职业/等级限制;RegisterSkill/Table/Desc是资源权威入口。- 当前端有
SkillTable/quickbar/cooldown 基础,已补 casting speed、动作门和FUNC_SKILL → CG_USE_SKILL包序;loop count、grade/motion、技能描述公式和服务端拒绝后的冷却回滚尚未由同一数据链驱动,合同继续PARTIAL。
Implementation round 2026-09-21T14:45Z
本轮按 40250 CInstanceBase::CanUseSkill() 和 CPythonPlayer::__RunCoolTime() 修复两处实现差异:
PlayerSkill.can_use_skill()新增并按参考顺序执行POLYMORPHED、WEARING_DRESS、HOLDING_PICKAXE、HORSE_CANNOT_USE、GRAPHIC_CANNOT_USE五类动作门;NetPlay.skill_context()从实体poly/shape/wedding_dress/parts[weapon]及服务器动作字段注入这些状态。- native
POINT_CASTING_SPEED=21加入协议点位定义,M2Client.get_points()暴露casting_speed;Quickbar._skill_cooldown()按iSpd = 100 - casting_speed的两段公式计算本地冷却遮罩和阻止时长, 默认中性值为 100,与 40250char.cpp的初始化一致。 player_skill_test新增五类 gate 断言,skill_test新增casting_speed=20 → 180%数值断言;skill_test、player_skill_test、test_skill_inventory_parity、playable_combat_test、netplay_test和 native 目标构建通过。
仍保持 PARTIAL:服务端拒绝/动作未起播后的冷却回滚、skill/group/quickslot/cooldown-end 乱序与跨生命周期、
完整动作资源和 damage 包序尚未闭合。
Implementation round 2026-09-20T20:45Z
本轮按 40250 CPythonPlayer::__UseSkill 的真实副作用顺序修复技能发送链:
Quickbar._activate_slot()和activate_skill_direct()现在先调用cast_skill()上行FUNC_SKILL|motion + arg,动作成功后再调用use_skill()上行CG_USE_SKILL(skill, target); 本地skill_cast_started也在动作状态成功后触发,扇形/圆形追加目标仍在主技能意图之后发送。- 补充共享
project/net_client_fake.gd,替换test_full_skill_flow.gd的缺失夹具;该测试现在实际装配GameScene、执行一次技能,并断言动作包先于技能意图包。 playable_combat_test、skill_test、mobile_gesture_test、player_skill_test和test_full_skill_flow均通过。
仍保持 PARTIAL:服务端拒绝/动作未起播后的冷却回滚、skill/group/quickslot/cooldown-end
乱序与跨生命周期、完整动作资源和服务端 damage 包序仍需真实网络证据。
Implementation round 2026-09-20T20:52Z
本轮按 40250 ClickSkillSlot → UseGuildSkill 分支修复公会技能:
PlayerSkill保留GUILD_ROUTED的入口分流,并新增use_guild_skill(),执行主角动作门、技能数据、冷却和ONLY_FOR_GUILD_WAR检查。NetPlay.skill_context()将 nativeGUILD_WAR_ON_WAR=6映射为guild_war_active;其它公会战通知状态不会误放行公会战限定技能。Quickbar新增_activate_guild_skill():先上行FUNC_SKILL(loop=1),再调用M2Client.use_guild_skill(skill, 0),并复用技能冷却/动作信号;普通技能仍走CG_USE_SKILL。player_skill_test、skill_test、guild_war_skill_test均通过,覆盖非公会战拒绝、公会战独立包和 UI 公会技能页。
仍保持 PARTIAL:真实 GUILD_WAR_ON_WAR 回包、服务端拒绝/冷却回滚、跨生命周期乱序和完整动作/damage 证据尚未闭合。
2026-09-22 死代码删除
以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:test_status_skill_reset_parity.gd。