# skill.player_ui_cooldown —— 技能栏、快捷键、等级、冷却和禁止条件 状态:`PARTIAL` 本合同覆盖 40250 Windows 客户端的技能数据解析、技能页加点、快捷栏持久化、技能施放前检查、目标/射程预约、冷却和服务端解锁事件。判断标准是调用顺序、禁止条件、数据来源、冷却公式和网络副作用一致,而不是只看当前端是否存在同名 `use_skill` 函数。 ## 40250 参考调用链 ### 技能数据和等级 - `UserInterface/PythonSkill.cpp` - `RegisterSkillDesc`/`RegisterSkillTable` 读取技能属性、武器限制、动作、等级、目标范围、目标数量、SP/冷却公式和 `.msk` 数据。 - `SSkillData::IsCanUseSkill` 只允许非被动主动施放;`GetSkillCoolTime`、`GetTargetCount`、`GetNeedSP` 都以 `fcurEfficientPercentage` 代入公式。 - `UserInterface/PythonPlayer.cpp` - `SetSkillLevel_` 将服务端等级/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`。 - `__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` 修正,最后触发 UI `RunUseSkillEvent`。 ### 网络回包 - `PythonNetworkStreamPhaseGame.cpp` - `RecvSkillLevel`/`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 分段公式本地预测。 - `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_skill` UI 入口。 - `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.gd` fixture 可启动 GameScene,并断言 `FUNC_SKILL → CG_USE_SKILL` 动作/意图包序。 ## 已确认差异和未验证项 1. `Quickbar` 在本地 `use_skill` 返回成功后立即预测冷却,缺少服务端拒绝/动作未起播/断线后的冷却回滚证据;40250 也做客户端预测,但其状态由 `CPythonPlayer` 的 slot 冷却对象统一管理。 2. 当前 skill/group/quickslot parser 虽已存在,尚无 `GC_SKILL_LEVEL_NEW → group → quickslot → cooldown-end` 乱序、重复、跨角色和重连生命周期测试。 3. 需要建立全部技能 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,与 40250 `char.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()` 将 native `GUILD_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`。