blacktrigram 0.7.43 → 0.7.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,9 @@
3
3
  * Generic object pool implementation
4
4
  */
5
5
  var ObjectPool = class {
6
+ factory;
7
+ reset;
8
+ config;
6
9
  available = [];
7
10
  inUse = /* @__PURE__ */ new Set();
8
11
  acquisitionCount = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"AudioPool.js","names":[],"sources":["../../src/audio/AudioPool.ts"],"sourcesContent":["/**\n * AudioPool - Object pooling for audio instances\n * Reduces memory allocation overhead for frequently played sounds\n */\n\nexport interface PoolStatistics {\n readonly available: number;\n readonly inUse: number;\n readonly total: number;\n readonly acquisitions: number;\n readonly releases: number;\n readonly created: number;\n}\n\nexport interface PoolConfig {\n readonly initialSize?: number;\n readonly maxSize?: number;\n readonly autoExpand?: boolean;\n}\n\n/**\n * Generic object pool implementation\n */\nexport class ObjectPool<T> {\n private available: T[] = [];\n private inUse = new Set<T>();\n private acquisitionCount = 0;\n private releaseCount = 0;\n private createdCount = 0;\n\n constructor(\n private readonly factory: () => T,\n private readonly reset: (obj: T) => void,\n private readonly config: Required<PoolConfig> = {\n initialSize: 10,\n maxSize: 100,\n autoExpand: true,\n }\n ) {\n // Pre-populate pool\n for (let i = 0; i < config.initialSize; i++) {\n this.available.push(this.createObject());\n }\n }\n\n /**\n * Acquire an object from the pool\n */\n acquire(): T | null {\n this.acquisitionCount++;\n\n let obj = this.available.pop();\n\n if (!obj) {\n // Pool is empty\n if (this.config.autoExpand && this.canExpand()) {\n obj = this.createObject();\n } else {\n console.warn(\"Pool exhausted and cannot expand\");\n return null;\n }\n }\n\n this.inUse.add(obj);\n return obj;\n }\n\n /**\n * Release an object back to the pool\n */\n release(obj: T): void {\n if (!this.inUse.has(obj)) {\n console.warn(\"Attempted to release object not in use\");\n return;\n }\n\n this.releaseCount++;\n this.reset(obj);\n this.inUse.delete(obj);\n this.available.push(obj);\n }\n\n /**\n * Release all objects back to the pool\n * Resets all in-use objects and returns them to the available pool\n */\n releaseAll(): void {\n this.inUse.forEach((obj) => {\n this.releaseCount++;\n this.reset(obj);\n this.available.push(obj);\n });\n this.inUse.clear();\n }\n\n /**\n * Get pool statistics\n */\n getStatistics(): PoolStatistics {\n return {\n available: this.available.length,\n inUse: this.inUse.size,\n total: this.available.length + this.inUse.size,\n acquisitions: this.acquisitionCount,\n releases: this.releaseCount,\n created: this.createdCount,\n };\n }\n\n /**\n * Clear the pool and dispose all objects\n */\n clear(): void {\n this.available = [];\n this.inUse.clear();\n }\n\n private createObject(): T {\n this.createdCount++;\n return this.factory();\n }\n\n private canExpand(): boolean {\n const total = this.available.length + this.inUse.size;\n return total < this.config.maxSize;\n }\n}\n\n/**\n * Specialized audio pool for HTMLAudioElement instances\n */\nexport class AudioElementPool {\n private pools: Map<string, ObjectPool<HTMLAudioElement>> = new Map();\n private defaultConfig: Required<PoolConfig> = {\n initialSize: 5,\n maxSize: 20,\n autoExpand: true,\n };\n\n /**\n * Create a pool for a specific audio asset\n */\n createPool(\n assetId: string,\n audioSrc: string,\n config?: PoolConfig\n ): ObjectPool<HTMLAudioElement> {\n const existingPool = this.pools.get(assetId);\n if (existingPool) {\n return existingPool;\n }\n\n const poolConfig = { ...this.defaultConfig, ...config };\n\n const pool = new ObjectPool<HTMLAudioElement>(\n () => {\n const audio = new Audio(audioSrc);\n audio.preload = \"auto\";\n audio.load();\n return audio;\n },\n (audio) => {\n audio.pause();\n audio.currentTime = 0;\n },\n poolConfig\n );\n\n this.pools.set(assetId, pool);\n return pool;\n }\n\n /**\n * Get pool for an asset (creates if doesn't exist)\n */\n getPool(assetId: string): ObjectPool<HTMLAudioElement> | undefined {\n return this.pools.get(assetId);\n }\n\n /**\n * Acquire audio element from pool\n */\n acquire(assetId: string): HTMLAudioElement | null {\n const pool = this.pools.get(assetId);\n if (!pool) {\n console.warn(`No pool exists for asset: ${assetId}`);\n return null;\n }\n return pool.acquire();\n }\n\n /**\n * Release audio element back to pool\n */\n release(assetId: string, audio: HTMLAudioElement): void {\n const pool = this.pools.get(assetId);\n if (!pool) {\n console.warn(`No pool exists for asset: ${assetId}`);\n return;\n }\n pool.release(audio);\n }\n\n /**\n * Release all audio elements in all pools\n */\n releaseAll(): void {\n this.pools.forEach((pool) => pool.releaseAll());\n }\n\n /**\n * Get statistics for a specific pool\n */\n getPoolStatistics(assetId: string): PoolStatistics | undefined {\n const pool = this.pools.get(assetId);\n return pool?.getStatistics();\n }\n\n /**\n * Get statistics for all pools\n */\n getAllStatistics(): Map<string, PoolStatistics> {\n const stats = new Map<string, PoolStatistics>();\n this.pools.forEach((pool, assetId) => {\n stats.set(assetId, pool.getStatistics());\n });\n return stats;\n }\n\n /**\n * Remove a pool and dispose its resources\n * @param assetId - The ID of the asset whose pool should be removed\n * @returns true if the pool existed and was removed, false if no pool was found for the asset\n */\n removePool(assetId: string): boolean {\n const pool = this.pools.get(assetId);\n if (pool) {\n pool.clear();\n this.pools.delete(assetId);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all pools\n */\n clearAll(): void {\n this.pools.forEach((pool) => pool.clear());\n this.pools.clear();\n }\n\n /**\n * Check if pool exists for asset\n */\n hasPool(assetId: string): boolean {\n return this.pools.has(assetId);\n }\n\n /**\n * Get total number of pools\n */\n getPoolCount(): number {\n return this.pools.size;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AudioPool.js","names":[],"sources":["../../src/audio/AudioPool.ts"],"sourcesContent":["/**\n * AudioPool - Object pooling for audio instances\n * Reduces memory allocation overhead for frequently played sounds\n */\n\nexport interface PoolStatistics {\n readonly available: number;\n readonly inUse: number;\n readonly total: number;\n readonly acquisitions: number;\n readonly releases: number;\n readonly created: number;\n}\n\nexport interface PoolConfig {\n readonly initialSize?: number;\n readonly maxSize?: number;\n readonly autoExpand?: boolean;\n}\n\n/**\n * Generic object pool implementation\n */\nexport class ObjectPool<T> {\n private available: T[] = [];\n private inUse = new Set<T>();\n private acquisitionCount = 0;\n private releaseCount = 0;\n private createdCount = 0;\n\n constructor(\n private readonly factory: () => T,\n private readonly reset: (obj: T) => void,\n private readonly config: Required<PoolConfig> = {\n initialSize: 10,\n maxSize: 100,\n autoExpand: true,\n }\n ) {\n // Pre-populate pool\n for (let i = 0; i < config.initialSize; i++) {\n this.available.push(this.createObject());\n }\n }\n\n /**\n * Acquire an object from the pool\n */\n acquire(): T | null {\n this.acquisitionCount++;\n\n let obj = this.available.pop();\n\n if (!obj) {\n // Pool is empty\n if (this.config.autoExpand && this.canExpand()) {\n obj = this.createObject();\n } else {\n console.warn(\"Pool exhausted and cannot expand\");\n return null;\n }\n }\n\n this.inUse.add(obj);\n return obj;\n }\n\n /**\n * Release an object back to the pool\n */\n release(obj: T): void {\n if (!this.inUse.has(obj)) {\n console.warn(\"Attempted to release object not in use\");\n return;\n }\n\n this.releaseCount++;\n this.reset(obj);\n this.inUse.delete(obj);\n this.available.push(obj);\n }\n\n /**\n * Release all objects back to the pool\n * Resets all in-use objects and returns them to the available pool\n */\n releaseAll(): void {\n this.inUse.forEach((obj) => {\n this.releaseCount++;\n this.reset(obj);\n this.available.push(obj);\n });\n this.inUse.clear();\n }\n\n /**\n * Get pool statistics\n */\n getStatistics(): PoolStatistics {\n return {\n available: this.available.length,\n inUse: this.inUse.size,\n total: this.available.length + this.inUse.size,\n acquisitions: this.acquisitionCount,\n releases: this.releaseCount,\n created: this.createdCount,\n };\n }\n\n /**\n * Clear the pool and dispose all objects\n */\n clear(): void {\n this.available = [];\n this.inUse.clear();\n }\n\n private createObject(): T {\n this.createdCount++;\n return this.factory();\n }\n\n private canExpand(): boolean {\n const total = this.available.length + this.inUse.size;\n return total < this.config.maxSize;\n }\n}\n\n/**\n * Specialized audio pool for HTMLAudioElement instances\n */\nexport class AudioElementPool {\n private pools: Map<string, ObjectPool<HTMLAudioElement>> = new Map();\n private defaultConfig: Required<PoolConfig> = {\n initialSize: 5,\n maxSize: 20,\n autoExpand: true,\n };\n\n /**\n * Create a pool for a specific audio asset\n */\n createPool(\n assetId: string,\n audioSrc: string,\n config?: PoolConfig\n ): ObjectPool<HTMLAudioElement> {\n const existingPool = this.pools.get(assetId);\n if (existingPool) {\n return existingPool;\n }\n\n const poolConfig = { ...this.defaultConfig, ...config };\n\n const pool = new ObjectPool<HTMLAudioElement>(\n () => {\n const audio = new Audio(audioSrc);\n audio.preload = \"auto\";\n audio.load();\n return audio;\n },\n (audio) => {\n audio.pause();\n audio.currentTime = 0;\n },\n poolConfig\n );\n\n this.pools.set(assetId, pool);\n return pool;\n }\n\n /**\n * Get pool for an asset (creates if doesn't exist)\n */\n getPool(assetId: string): ObjectPool<HTMLAudioElement> | undefined {\n return this.pools.get(assetId);\n }\n\n /**\n * Acquire audio element from pool\n */\n acquire(assetId: string): HTMLAudioElement | null {\n const pool = this.pools.get(assetId);\n if (!pool) {\n console.warn(`No pool exists for asset: ${assetId}`);\n return null;\n }\n return pool.acquire();\n }\n\n /**\n * Release audio element back to pool\n */\n release(assetId: string, audio: HTMLAudioElement): void {\n const pool = this.pools.get(assetId);\n if (!pool) {\n console.warn(`No pool exists for asset: ${assetId}`);\n return;\n }\n pool.release(audio);\n }\n\n /**\n * Release all audio elements in all pools\n */\n releaseAll(): void {\n this.pools.forEach((pool) => pool.releaseAll());\n }\n\n /**\n * Get statistics for a specific pool\n */\n getPoolStatistics(assetId: string): PoolStatistics | undefined {\n const pool = this.pools.get(assetId);\n return pool?.getStatistics();\n }\n\n /**\n * Get statistics for all pools\n */\n getAllStatistics(): Map<string, PoolStatistics> {\n const stats = new Map<string, PoolStatistics>();\n this.pools.forEach((pool, assetId) => {\n stats.set(assetId, pool.getStatistics());\n });\n return stats;\n }\n\n /**\n * Remove a pool and dispose its resources\n * @param assetId - The ID of the asset whose pool should be removed\n * @returns true if the pool existed and was removed, false if no pool was found for the asset\n */\n removePool(assetId: string): boolean {\n const pool = this.pools.get(assetId);\n if (pool) {\n pool.clear();\n this.pools.delete(assetId);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all pools\n */\n clearAll(): void {\n this.pools.forEach((pool) => pool.clear());\n this.pools.clear();\n }\n\n /**\n * Check if pool exists for asset\n */\n hasPool(assetId: string): boolean {\n return this.pools.has(assetId);\n }\n\n /**\n * Get total number of pools\n */\n getPoolCount(): number {\n return this.pools.size;\n }\n}\n"],"mappings":";;;;AAuBA,IAAa,aAAb,MAA2B;CAQN;CACA;CACA;CATnB,YAAyB,EAAE;CAC3B,wBAAgB,IAAI,KAAQ;CAC5B,mBAA2B;CAC3B,eAAuB;CACvB,eAAuB;CAEvB,YACE,SACA,OACA,SAAgD;EAC9C,aAAa;EACb,SAAS;EACT,YAAY;EACb,EACD;EAPiB,KAAA,UAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EAOjB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,aAAa,KACtC,KAAK,UAAU,KAAK,KAAK,cAAc,CAAC;;;;;CAO5C,UAAoB;EAClB,KAAK;EAEL,IAAI,MAAM,KAAK,UAAU,KAAK;EAE9B,IAAI,CAAC,KAEH,IAAI,KAAK,OAAO,cAAc,KAAK,WAAW,EAC5C,MAAM,KAAK,cAAc;OACpB;GACL,QAAQ,KAAK,mCAAmC;GAChD,OAAO;;EAIX,KAAK,MAAM,IAAI,IAAI;EACnB,OAAO;;;;;CAMT,QAAQ,KAAc;EACpB,IAAI,CAAC,KAAK,MAAM,IAAI,IAAI,EAAE;GACxB,QAAQ,KAAK,yCAAyC;GACtD;;EAGF,KAAK;EACL,KAAK,MAAM,IAAI;EACf,KAAK,MAAM,OAAO,IAAI;EACtB,KAAK,UAAU,KAAK,IAAI;;;;;;CAO1B,aAAmB;EACjB,KAAK,MAAM,SAAS,QAAQ;GAC1B,KAAK;GACL,KAAK,MAAM,IAAI;GACf,KAAK,UAAU,KAAK,IAAI;IACxB;EACF,KAAK,MAAM,OAAO;;;;;CAMpB,gBAAgC;EAC9B,OAAO;GACL,WAAW,KAAK,UAAU;GAC1B,OAAO,KAAK,MAAM;GAClB,OAAO,KAAK,UAAU,SAAS,KAAK,MAAM;GAC1C,cAAc,KAAK;GACnB,UAAU,KAAK;GACf,SAAS,KAAK;GACf;;;;;CAMH,QAAc;EACZ,KAAK,YAAY,EAAE;EACnB,KAAK,MAAM,OAAO;;CAGpB,eAA0B;EACxB,KAAK;EACL,OAAO,KAAK,SAAS;;CAGvB,YAA6B;EAE3B,OADc,KAAK,UAAU,SAAS,KAAK,MAAM,OAClC,KAAK,OAAO;;;;;;AAO/B,IAAa,mBAAb,MAA8B;CAC5B,wBAA2D,IAAI,KAAK;CACpE,gBAA8C;EAC5C,aAAa;EACb,SAAS;EACT,YAAY;EACb;;;;CAKD,WACE,SACA,UACA,QAC8B;EAC9B,MAAM,eAAe,KAAK,MAAM,IAAI,QAAQ;EAC5C,IAAI,cACF,OAAO;EAKT,MAAM,OAAO,IAAI,iBACT;GACJ,MAAM,QAAQ,IAAI,MAAM,SAAS;GACjC,MAAM,UAAU;GAChB,MAAM,MAAM;GACZ,OAAO;MAER,UAAU;GACT,MAAM,OAAO;GACb,MAAM,cAAc;KAEtB;GAbmB,GAAG,KAAK;GAAe,GAAG;GAa7C,CACD;EAED,KAAK,MAAM,IAAI,SAAS,KAAK;EAC7B,OAAO;;;;;CAMT,QAAQ,SAA2D;EACjE,OAAO,KAAK,MAAM,IAAI,QAAQ;;;;;CAMhC,QAAQ,SAA0C;EAChD,MAAM,OAAO,KAAK,MAAM,IAAI,QAAQ;EACpC,IAAI,CAAC,MAAM;GACT,QAAQ,KAAK,6BAA6B,UAAU;GACpD,OAAO;;EAET,OAAO,KAAK,SAAS;;;;;CAMvB,QAAQ,SAAiB,OAA+B;EACtD,MAAM,OAAO,KAAK,MAAM,IAAI,QAAQ;EACpC,IAAI,CAAC,MAAM;GACT,QAAQ,KAAK,6BAA6B,UAAU;GACpD;;EAEF,KAAK,QAAQ,MAAM;;;;;CAMrB,aAAmB;EACjB,KAAK,MAAM,SAAS,SAAS,KAAK,YAAY,CAAC;;;;;CAMjD,kBAAkB,SAA6C;EAE7D,OADa,KAAK,MAAM,IAAI,QACrB,EAAM,eAAe;;;;;CAM9B,mBAAgD;EAC9C,MAAM,wBAAQ,IAAI,KAA6B;EAC/C,KAAK,MAAM,SAAS,MAAM,YAAY;GACpC,MAAM,IAAI,SAAS,KAAK,eAAe,CAAC;IACxC;EACF,OAAO;;;;;;;CAQT,WAAW,SAA0B;EACnC,MAAM,OAAO,KAAK,MAAM,IAAI,QAAQ;EACpC,IAAI,MAAM;GACR,KAAK,OAAO;GACZ,KAAK,MAAM,OAAO,QAAQ;GAC1B,OAAO;;EAET,OAAO;;;;;CAMT,WAAiB;EACf,KAAK,MAAM,SAAS,SAAS,KAAK,OAAO,CAAC;EAC1C,KAAK,MAAM,OAAO;;;;;CAMpB,QAAQ,SAA0B;EAChC,OAAO,KAAK,MAAM,IAAI,QAAQ;;;;;CAMhC,eAAuB;EACrB,OAAO,KAAK,MAAM"}
@@ -21,7 +21,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
21
21
  import { jsx, jsxs } from "react/jsx-runtime";
22
22
  import { Canvas } from "@react-three/fiber";
23
23
  //#region src/components/screens/intro/IntroScreen3D.tsx
24
- var APP_VERSION = "0.7.43";
24
+ var APP_VERSION = "0.7.44";
25
25
  var MENU_ITEMS = [
26
26
  {
27
27
  mode: GameMode.VERSUS,
@@ -183,7 +183,7 @@ var SplashScreen = ({ onStart, width, height }) => {
183
183
  }),
184
184
  /* @__PURE__ */ jsxs("div", {
185
185
  role: "contentinfo",
186
- "aria-label": `Application version 0.7.43`,
186
+ "aria-label": `Application version 0.7.44`,
187
187
  style: {
188
188
  position: "absolute",
189
189
  bottom: "20px",
@@ -192,7 +192,7 @@ var SplashScreen = ({ onStart, width, height }) => {
192
192
  fontSize: "10px",
193
193
  zIndex: 1
194
194
  },
195
- children: ["v", "0.7.43"]
195
+ children: ["v", "0.7.44"]
196
196
  })
197
197
  ]
198
198
  });
@@ -556,6 +556,7 @@ var DEFAULT_ANIMATION_CONFIGS = new Map([
556
556
  * @korean 플레이어애니메이션상태머신
557
557
  */
558
558
  var PlayerAnimationStateMachine = class PlayerAnimationStateMachine {
559
+ events;
559
560
  /**
560
561
  * Static mapping from TrigramStance to guard AnimationState
561
562
  * Prevents repeated object allocation in transitionToStanceGuard()
@@ -1 +1 @@
1
- {"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4BACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAED,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;EAChB,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;EACxB,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EACf,QAAQ;EACT,CACF;CACD,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAED,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EAChB,CACF;CACD,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EAChB,CACF;CAED,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAGD,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EA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"}
1
+ {"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4BACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAED,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;EAChB,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;EACxB,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;EACT,CACF;CACD,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EACf,QAAQ;EACT,CACF;CACD,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAED,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CAED,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EAChB,CACF;CACD,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,KAAK;EAChB,CACF;CAED,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CACD,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;EACf,CACF;CAGD,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACX,CACF;CACD,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EA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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacktrigram",
3
- "version": "0.7.43",
3
+ "version": "0.7.44",
4
4
  "description": "Black Trigram (흑괘) - Korean Martial Arts Combat Simulator. Reusable game systems, combat mechanics, animation framework, and Korean martial arts data built with React, Three.js, and TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -201,8 +201,8 @@
201
201
  "@types/react-dom": "19.2.3",
202
202
  "@types/three": "0.184.1",
203
203
  "@vitejs/plugin-react": "6.0.1",
204
- "@vitest/coverage-v8": "4.1.5",
205
- "@vitest/ui": "4.1.5",
204
+ "@vitest/coverage-v8": "4.1.6",
205
+ "@vitest/ui": "4.1.6",
206
206
  "cypress": "15.14.2",
207
207
  "cypress-junit-reporter": "1.3.1",
208
208
  "cypress-multi-reporters": "2.0.5",
@@ -226,7 +226,7 @@
226
226
  "postprocessing": "6.39.1",
227
227
  "react": "19.2.6",
228
228
  "react-dom": "19.2.6",
229
- "start-server-and-test": "3.0.2",
229
+ "start-server-and-test": "3.0.4",
230
230
  "three": "0.184.0",
231
231
  "ts-morph": "28.0.0",
232
232
  "ts-node": "10.9.2",
@@ -238,10 +238,10 @@
238
238
  "typedoc-plugin-missing-exports": "4.1.3",
239
239
  "typescript": "6.0.3",
240
240
  "typescript-eslint": "8.59.2",
241
- "vite": "8.0.11",
241
+ "vite": "8.0.12",
242
242
  "vite-bundle-analyzer": "1.3.8",
243
243
  "vite-tsconfig-paths": "6.1.1",
244
- "vitest": "4.1.5"
244
+ "vitest": "4.1.6"
245
245
  },
246
246
  "overrides": {
247
247
  "eslint-plugin-react-hooks": {