# Animation Scene — Editor Tooling This document tracks **Editor-only tooling** used with the runtime animation test workflow described in **`AnimationSceneSetup.md`**. **Scene docs (runtime hierarchy, managers, APIs):** see `Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md`. **How to extend this file:** whenever you add or plan a new piece of Editor UI / automation for the animation scene, append a **`### Feature`** block under **[Feature log](#feature-log)** using the **[Feature template](#feature-template)** below. Keep one feature per numbered section so history stays searchable. --- ## Goals | Goal | Notes | |---|---| | Faster iteration | Load and validate character models **from Play Mode**, driven by Editor menus where appropriate — **no Edit Mode `.ecm` preview** without a dedicated preview pipeline | | Stable bootstrap | Respect the same prerequisites as runtime (`NPCManager`, `elementdataman`, `CECPlayer.InitStaticRes()`, etc.—see §Prerequisites below) | | Traceability | Features and acceptance criteria live here **before or while** implementing the Editor scripts | Suggested implementation locations (when you create them): - Menu: e.g. `Tools / PerfectWorld / Animation Scene / …` - Scripts: under `Assets/PerfectWorld/Scripts/Editor/` (or project convention for Editor assemblies) --- ## Prerequisites (runtime + Editor) Anything the Editor tool drives must satisfy the same constraints as a Play Mode session: | Prerequisite | Why | |---|---| | `AutoInitializer` in the open scene | Bootstraps `IAutoInitialize` types including **`ElementDataManProvider`**; runs **`EC_Game.Init()`**, **`SkillStubs.Init()`** | | **`elementdataman` loaded** | **`CECPlayer.InitStaticRes()`** → **`BuildActionList()`** uses **`ElementDataManProvider.GetElementDataMan()`** | | **`CECPlayer.InitStaticRes()`** called once **after** element data is usable | **`InitializePlayer`** does **not** register **`IAutoInitialize`** (interface commented); **not** invoked by **`CECGameRun.Init()`** alone. Normal login hits it via **`CECGameRun.InitCharacter`**. Animation scene tooling must arrange this explicitly if you rely on skill / action maps | | `NPCManager` in scene | **`SetPlayerModel`** → **`NPCManager.Instance.GetModelPlayer(profession, gender)`** | | `CECAttacksMan` in scene | **`PlayAttackEffect`**, **`SkillGfxMan.InstanceSub.Tick`** path (see **`AnimationSceneSetup.md` §12**) | | `EC_ManMessageMono` in scene | **`SkillGfxMan`** resolves **`EC_ManMessageMono.Instance`** references | | `CECHostPlayer` reference (selection or serialized) | **`await player.SetPlayerModel(byte profession, byte gender)`** is **`CECPlayer`** API; host implementation is **`CECHostPlayer`** | | **`InGameGraphicOption`** in scene *(optional)* | **`EC_Game.InitSetting()`** adjusts render scale / MSAA via **`InGameGraphicOption`**. If omitted, **`MonoSingleton`** auto-creates a host component; **`InGameGraphicOption`** resolves URP from **`GraphicsSettings.defaultRenderPipeline`**. Fully custom scenes should still assign URP (+ optional Cinemachine) on **`InGameGraphicOption`** for predictable behavior. **Editor caveat:** **`CECHostPlayer.LoadResources`** uses **`UnityGameSession.Instance.GetRoleInfo()`**. For offline Editor-driven loads, prefer calling **`SetPlayerModel`** directly (and/or setting **`m_iProfession`**, **`m_iGender`** on the player instance as your tool requires) rather than invoking full **`InitCharacter`** unless session is mocked. --- ## Feature log ### Feature 1 — Editor / Play Mode: bootstrap player model **Status:** Planned (spec captured here; implementation optional). **Problem:** In the animation test scene you need a repeatable way to load the `.ecm` player model **by profession and gender** without going through the server login flow. **Proposed behavior:** 1. User opens the animation test scene (or any scene that contains the required managers + a **`CECHostPlayer`** instance). 2. **Play Mode required** for real **`SetPlayerModel`** / Addressables — Editor UI only **queues** work once Unity is playing (same constraint as Feature 2–3). 3. Tool or scene **`MonoBehaviour`** exposes **`Profession`** / **`Gender`** mapped to **`CECPlayer.SetPlayerModel(byte, byte)`**. 4. Sequence (recommended): wait until **`elementdataman`** load has succeeded → **`CECPlayer.InitStaticRes()`** once → **`await SetPlayerModel(profession, gender)`**. 5. Success: visible `.ecm` under **`Player`** per **`parentModel`** rules (**`CECPlayer.SetPlayerModel`** — first child if present, else root); Console filter **`AnimSceneBootstrap`** shows **`SetPlayerModel` BEGIN/END** when those logs exist. **Out of scope:** Full **`InitCharacter`** / **`UnityGameSession`** parity unless explicitly mocked elsewhere. **Acceptance criteria:** - Model appears after bootstrap when **`NPCManager`**, Addressables, and element data are healthy. - Failures are visible (**`NPCManager`** null, **`GetModelPlayer`** null, **`InvalidOperationException`** from **`SetPlayerModel`**). - **`InitStaticRes`** either runs before skill/action-dependent tests or is intentionally skipped with a clear warning. **Primary APIs:** **`CECPlayer.SetPlayerModel`**, **`CECPlayer.InitStaticRes`** — see **`AnimationSceneSetup.md`** §1, §10–11. --- ### Feature 2 — Editor: trigger Play Action (animation probe) **Status:** Planned. **Problem:** Quickly verify **`CECPlayerActionController` / Animancer** wiring without hunting hotkeys or server-driven triggers. **Proposed behavior:** 1. Menu entry e.g. **`Tools / PerfectWorld / Animation Scene / …`** opens a small **EditorWindow** (or equivalent). 2. Window active **only in Play Mode**; references **`CECHostPlayer`** (object field or auto-find). 3. User enters **`PLAYER_ACTION_TYPE`** index (same **`int`** as **`CECPlayer.PlayAction(int iAction, bool bRestart)`** — **`ACT_STAND = 0`**, etc.; see **`CECPlayer`** enum). 4. Button **Play action** invokes **`host.PlayAction(index, true)`** on the live instance. **Out of scope:** Driving **`PlaySkillCastAction`** / **`PlayAttackEffect`** from this panel (those belong to combat/skill tooling or separate Feature entries). **Acceptance criteria:** - With model loaded and action maps built (**`InitStaticRes`**), **`ACT_STAND`** / **`ACT_RUN`**-class actions behave like runtime calls. - **`ACT_ATTACK_1..4`** may still refuse **`PlayAction`** by design (**`CECPlayer.PlayActionWithConfig`** blocks normal melee slots — use documented skill APIs instead). **Primary API:** **`CECPlayer.PlayAction(int iAction, bool bRestart)`** on **`CECHostPlayer`**. --- ### Feature 3 — Offline host init: god stats + synthetic skills (below character level 80, human form) **Status:** Planned. **Problem:** **`OnMsgHstSkillData`** never runs offline, so **`m_aPtSkills`** / **`SkillWrapper`** stay empty — **`GetNormalSkill`** / cast checks cannot mirror a real character. You still want **most** combat skills usable for VFX/animation testing **without** shape-change prerequisites. **Design summary:** After **`InitStaticRes`** and **`SetPlayerModel`**, synthesize a **`cmd_skill_data`-equivalent** payload in memory and apply the **same conceptual pipeline** as **`CECHostPlayer.OnMsgHstSkillData`**: **`ElementSkill.LoadSkillData`** → **`SkillWrapper.Instance.LoadData`** → instantiate **`CECSkill`** rows into **`m_aPtSkills`** / **`m_aPsSkills`** by skill **type** (positive vs passive/production/life). Optionally align **`m_iProfession` / `m_iGender`** with the loaded model before filtering. **Simulated character limits:** | Constraint | Intent | |---|---| | Player level ceiling **below 80** | Treat **maximum simulated level as 79** (or equivalent) when choosing skill **rank**: for each skill id, pick the **highest** rank whose **`SkillStub.GetRequiredLevel(skill)`** is **≤** that simulated level (exact comparison uses live **`Skill`** instance at each candidate rank). | | Realm | Set **`m_RealmLevel`** (and any extend props you rely on) high enough so **`GetRequiredRealmLevel`** does not fail for included ranks. | | SP / Mana / AP / Vigour / HP | Raise **`ROLEBASICPROP`** (**`iCurMP`**, **`iCurAP`**, **`iSP`**, **`iVigour`**, **`iCurHP`**, etc.) and matching **`ROLEEXTPROP`** caps (**`max_hp`**, **`max_mp`**, **`max_ap`**) so **`CheckSkillCastCondition`** resource checks succeed for typical casts. | | **Exclude transform-shape-only skills** | **`ElementSkill.Condition`** tests **`(allow_forms & (1 << form_type))`** where **`form_type`** comes from **`m_iShape`** high bits (**`FORM_MASK_HIGH`**). Base human **`m_iShape = 0`** ⇒ **`form_type = 0`** ⇒ skill must allow **bit 0** in **`SkillStub.allow_forms`**. **Exclude** stubs where **`(allow_forms & 1) == 0`** (human/base form disallowed). Also **exclude** stubs flagged **`restrict_change`** when they denote transformation-only restrictions in data. | | **Exclude combo-chain prerequisites** | Skip stubs with **`combosk_preskill != 0`** unless you also simulate combo state (**`CECComboSkillState`**). | | **Exclude item / ammo gated skills** | Skip **`itemcost > 0`** or **`arrowcost > 0`** unless you populate inventory accordingly. | | **Exclude goblin line** | Skip **`cls == 258`**. | | **Profession filter** | Include **`stub.cls == playerProfession`** **or** **`cls == 255`** (general skills). | **Caveats (document in tooltips):** - **`restrict_weapons`**: skills may still return condition **invalid weapon** if no matching weapon sits in **`m_pEquipPack`** — either equip a dummy weapon per profession or accept skipped casts. - **`cmd_skill_data.SKILL.id_skill`** is a **`short`** in **`GPDataType`**: skill ids **> 32767** cannot be represented; skip or warn. - Passive / prerequisite graphs are imperfectly simulated — goal is **animation/VFX iteration**, not authoritative balance. **Acceptance criteria:** - **`GetPositiveSkillByID`** / **`GetNormalSkill`** resolve for a large subset of profession skills after injection. - **`CheckSkillCastCondition`** returns **0** for a sampled skill **without** shape-change, assuming weapon/item caveats addressed. - Transform-only skills (**`allow_forms`** missing human bit or **`restrict_change`**) do **not** appear in the injected list. **Primary references:** **`CECHostPlayer.OnMsgHstSkillData`** (**`CECHostPlayer.Skill.cs`**), **`ElementSkill.Condition`**, **`SkillStub.allow_forms`**, **`ROLEBASICPROP`**, **`SkillWrapper`**. --- ### Feature 4 — `AnimScenePlayerBootstrap` inspector: play action + weapon slots (`action_type` index) **Status:** Done. **Problem:** Drive **`PlayAction`** and test **weapon-mesh + `action_weapon_suffix`** alignment without a separate EditorWindow; **`GetShowingWeaponType`** ignores attached meshes when **`m_uAttackType == DEFAULT_ACTION_TYPE`**, so animation tests must set **`m_uAttackType`** the same way as **`CECPlayer_Inventory.ShowEquipments`** (from **`WEAPON_SUB_TYPE.action_type`**). **Implemented behavior:** 1. **`AnimScenePlayerBootstrapEditor`** (Play Mode): **Play action** (`int` + **Restart** → **`CECHostPlayer.PlayAction`**), **Re-apply active weapon slot**. 2. **`AnimScenePlayerBootstrap`**: **`AnimSceneWeaponSlot[15]`** (`rightHandModelPrefab` / `leftHandModelPrefab` per row **`i === action_type`**), **`activeWeaponActionTypeIndex`**, **`applyWeaponAfterModelLoad`**; after **`SetPlayerModel`**, **`ApplyWeaponForActiveSlot()`** runs when enabled. 3. **`CECPlayer.AnimSceneAttachWeaponPrefabs`**: clears **`_currentRightHandWeapon` / `_currentLeftHandWeapon`**, parents instances under **`HH_righthandweapon` / `HH_lefthandweapon`** (same as **`CECPlayer_Inventory.ShowEquipments`**), sets **`m_uAttackType`** when any side attaches; empty slot → **`DEFAULT_ACTION_TYPE`** and **`AttachWeapon()`**. **Primary API / hooks:** **`CECPlayer.AnimSceneAttachWeaponPrefabs`**, **`AnimScenePlayerBootstrap.ApplyWeaponForActiveSlot`**, **`Assets/PerfectWorld/Scripts/Editor/AnimScenePlayerBootstrapEditor.cs`** --- ### Feature 1 legacy API snippet (reference only) Use when implementing Feature 1 bootstrap: ```csharp await player.SetPlayerModel((byte)profession, (byte)gender); CECPlayer.InitStaticRes(); ``` **Related runtime doc sections:** **`AnimationSceneSetup.md`** §1 (model loading), §9–11 (scene objects / init order). --- ### Feature template (copy for Feature 4, 5, …) Paste and fill whenever you scope a **new** Editor capability: ``` ### Feature N — **Status:** Planned | In progress | Done **Problem:** **Proposed behavior:** 1. 2. **Out of scope:** **Acceptance criteria:** - - <…> **Primary API / hooks:** `` **Related docs / code paths:** `` ``` --- ## Maintaining this doc | When | Do | |---|---| | You start designing an Editor capability | Add a **`### Feature N`** section with **Status: Planned** and acceptance criteria | | You implement or change behavior | Update **Status**, **Primary API**, and point to concrete script paths under `Assets/.../Editor/` | | Behaviour diverges from runtime | Refresh **Prerequisites** and cross-check **`AnimationSceneSetup.md`** | --- ## Play Mode — Bootstrap log tag `[AnimSceneBootstrap]` Scripts log a shared prefix **`[AnimSceneBootstrap]`** so you can filter the Unity Console while verifying initialization order: | Typical order | Location | |---|---| | Scene `Awake` boot | **`AutoInitializer`**: BEGIN → `IAutoInitialize` count → `EC_Game.Init` → `SkillStubs.Init` → END | | Element data finished (async after above) | **`ElementDataManProvider`**: SUCCESS or FAILED after `load_data` | | Attacks/GFX composers | **`CECAttacksMan`**: `Awake` → `Start` → `SetupAttacksMan` → optional GFX preload kick | | Action/skill tables (when you call it) | **`CECPlayer.InitStaticRes`**: BEGIN / END | **`SetPlayerModel`** also logs BEGIN / END with the same prefix when loading the player `.ecm`. **Filter:** Console search **`AnimSceneBootstrap`**.