@nice2dev/game-engine 1.0.2 → 1.0.4

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.
Files changed (188) hide show
  1. package/dist/cjs/ai/BehaviorTree.js +1215 -0
  2. package/dist/cjs/ai/BehaviorTree.js.map +1 -0
  3. package/dist/cjs/ai/StateMachine.js +783 -0
  4. package/dist/cjs/ai/StateMachine.js.map +1 -0
  5. package/dist/cjs/editor/ShaderGraph.js +1616 -0
  6. package/dist/cjs/editor/ShaderGraph.js.map +1 -0
  7. package/dist/cjs/editor/TimelineEditor.js +819 -0
  8. package/dist/cjs/editor/TimelineEditor.js.map +1 -0
  9. package/dist/cjs/export/GodotExporter.js +1102 -0
  10. package/dist/cjs/export/GodotExporter.js.map +1 -0
  11. package/dist/cjs/export/PlatformExporter.js +236 -0
  12. package/dist/cjs/export/PlatformExporter.js.map +1 -0
  13. package/dist/cjs/export/ThreeJSExporter.js +1116 -0
  14. package/dist/cjs/export/ThreeJSExporter.js.map +1 -0
  15. package/dist/cjs/export/UnityExporter.js +1193 -0
  16. package/dist/cjs/export/UnityExporter.js.map +1 -0
  17. package/dist/cjs/export/WebExporter.js +1036 -0
  18. package/dist/cjs/export/WebExporter.js.map +1 -0
  19. package/dist/cjs/export/index.js +58 -0
  20. package/dist/cjs/export/index.js.map +1 -0
  21. package/dist/cjs/import/AsepriteImporter.js +761 -0
  22. package/dist/cjs/import/AsepriteImporter.js.map +1 -0
  23. package/dist/cjs/import/DragonBonesImporter.js +499 -0
  24. package/dist/cjs/import/DragonBonesImporter.js.map +1 -0
  25. package/dist/cjs/import/GameMakerImporter.js +559 -0
  26. package/dist/cjs/import/GameMakerImporter.js.map +1 -0
  27. package/dist/cjs/import/GodotSceneImporter.js +824 -0
  28. package/dist/cjs/import/GodotSceneImporter.js.map +1 -0
  29. package/dist/cjs/import/LDtkImporter.js +481 -0
  30. package/dist/cjs/import/LDtkImporter.js.map +1 -0
  31. package/dist/cjs/import/Live2DImporter.js +553 -0
  32. package/dist/cjs/import/Live2DImporter.js.map +1 -0
  33. package/dist/cjs/import/NdgFormat.js +499 -0
  34. package/dist/cjs/import/NdgFormat.js.map +1 -0
  35. package/dist/cjs/import/OgmoImporter.js +529 -0
  36. package/dist/cjs/import/OgmoImporter.js.map +1 -0
  37. package/dist/cjs/import/RPGMakerImporter.js +520 -0
  38. package/dist/cjs/import/RPGMakerImporter.js.map +1 -0
  39. package/dist/cjs/import/SceneImporter.js +449 -0
  40. package/dist/cjs/import/SceneImporter.js.map +1 -0
  41. package/dist/cjs/import/SpineImporter.js +583 -0
  42. package/dist/cjs/import/SpineImporter.js.map +1 -0
  43. package/dist/cjs/import/SpriterImporter.js +652 -0
  44. package/dist/cjs/import/SpriterImporter.js.map +1 -0
  45. package/dist/cjs/import/TiledMapImporter.js +859 -0
  46. package/dist/cjs/import/TiledMapImporter.js.map +1 -0
  47. package/dist/cjs/import/UnitySceneImporter.js +732 -0
  48. package/dist/cjs/import/UnitySceneImporter.js.map +1 -0
  49. package/dist/cjs/import/index.js +305 -0
  50. package/dist/cjs/import/index.js.map +1 -0
  51. package/dist/cjs/index.js +201 -0
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/scripting/GraphToAST.js +567 -0
  54. package/dist/cjs/scripting/GraphToAST.js.map +1 -0
  55. package/dist/cjs/scripting/LanguageExporter.js +321 -0
  56. package/dist/cjs/scripting/LanguageExporter.js.map +1 -0
  57. package/dist/cjs/scripting/ScriptAST.js +67 -0
  58. package/dist/cjs/scripting/ScriptAST.js.map +1 -0
  59. package/dist/cjs/scripting/VisualScripting2.js +1140 -0
  60. package/dist/cjs/scripting/VisualScripting2.js.map +1 -0
  61. package/dist/cjs/scripting/exporters/CSharpExporter.js +503 -0
  62. package/dist/cjs/scripting/exporters/CSharpExporter.js.map +1 -0
  63. package/dist/cjs/scripting/exporters/GDScriptExporter.js +452 -0
  64. package/dist/cjs/scripting/exporters/GDScriptExporter.js.map +1 -0
  65. package/dist/cjs/scripting/exporters/LuaExporter.js +457 -0
  66. package/dist/cjs/scripting/exporters/LuaExporter.js.map +1 -0
  67. package/dist/cjs/scripting/exporters/PythonExporter.js +565 -0
  68. package/dist/cjs/scripting/exporters/PythonExporter.js.map +1 -0
  69. package/dist/cjs/scripting/exporters/RustExporter.js +525 -0
  70. package/dist/cjs/scripting/exporters/RustExporter.js.map +1 -0
  71. package/dist/cjs/scripting/exporters/TypeScriptExporter.js +570 -0
  72. package/dist/cjs/scripting/exporters/TypeScriptExporter.js.map +1 -0
  73. package/dist/cjs/systems/ParticleSystem2.js +1478 -0
  74. package/dist/cjs/systems/ParticleSystem2.js.map +1 -0
  75. package/dist/esm/ai/BehaviorTree.js +1186 -0
  76. package/dist/esm/ai/BehaviorTree.js.map +1 -0
  77. package/dist/esm/ai/StateMachine.js +767 -0
  78. package/dist/esm/ai/StateMachine.js.map +1 -0
  79. package/dist/esm/editor/ShaderGraph.js +1606 -0
  80. package/dist/esm/editor/ShaderGraph.js.map +1 -0
  81. package/dist/esm/editor/TimelineEditor.js +800 -0
  82. package/dist/esm/editor/TimelineEditor.js.map +1 -0
  83. package/dist/esm/export/GodotExporter.js +1100 -0
  84. package/dist/esm/export/GodotExporter.js.map +1 -0
  85. package/dist/esm/export/PlatformExporter.js +230 -0
  86. package/dist/esm/export/PlatformExporter.js.map +1 -0
  87. package/dist/esm/export/ThreeJSExporter.js +1114 -0
  88. package/dist/esm/export/ThreeJSExporter.js.map +1 -0
  89. package/dist/esm/export/UnityExporter.js +1191 -0
  90. package/dist/esm/export/UnityExporter.js.map +1 -0
  91. package/dist/esm/export/WebExporter.js +1033 -0
  92. package/dist/esm/export/WebExporter.js.map +1 -0
  93. package/dist/esm/export/index.js +44 -0
  94. package/dist/esm/export/index.js.map +1 -0
  95. package/dist/esm/import/AsepriteImporter.js +759 -0
  96. package/dist/esm/import/AsepriteImporter.js.map +1 -0
  97. package/dist/esm/import/DragonBonesImporter.js +496 -0
  98. package/dist/esm/import/DragonBonesImporter.js.map +1 -0
  99. package/dist/esm/import/GameMakerImporter.js +556 -0
  100. package/dist/esm/import/GameMakerImporter.js.map +1 -0
  101. package/dist/esm/import/GodotSceneImporter.js +822 -0
  102. package/dist/esm/import/GodotSceneImporter.js.map +1 -0
  103. package/dist/esm/import/LDtkImporter.js +479 -0
  104. package/dist/esm/import/LDtkImporter.js.map +1 -0
  105. package/dist/esm/import/Live2DImporter.js +550 -0
  106. package/dist/esm/import/Live2DImporter.js.map +1 -0
  107. package/dist/esm/import/NdgFormat.js +490 -0
  108. package/dist/esm/import/NdgFormat.js.map +1 -0
  109. package/dist/esm/import/OgmoImporter.js +526 -0
  110. package/dist/esm/import/OgmoImporter.js.map +1 -0
  111. package/dist/esm/import/RPGMakerImporter.js +517 -0
  112. package/dist/esm/import/RPGMakerImporter.js.map +1 -0
  113. package/dist/esm/import/SceneImporter.js +441 -0
  114. package/dist/esm/import/SceneImporter.js.map +1 -0
  115. package/dist/esm/import/SpineImporter.js +580 -0
  116. package/dist/esm/import/SpineImporter.js.map +1 -0
  117. package/dist/esm/import/SpriterImporter.js +649 -0
  118. package/dist/esm/import/SpriterImporter.js.map +1 -0
  119. package/dist/esm/import/TiledMapImporter.js +857 -0
  120. package/dist/esm/import/TiledMapImporter.js.map +1 -0
  121. package/dist/esm/import/UnitySceneImporter.js +730 -0
  122. package/dist/esm/import/UnitySceneImporter.js.map +1 -0
  123. package/dist/esm/import/index.js +279 -0
  124. package/dist/esm/import/index.js.map +1 -0
  125. package/dist/esm/index.js +36 -0
  126. package/dist/esm/index.js.map +1 -1
  127. package/dist/esm/scripting/GraphToAST.js +564 -0
  128. package/dist/esm/scripting/GraphToAST.js.map +1 -0
  129. package/dist/esm/scripting/LanguageExporter.js +311 -0
  130. package/dist/esm/scripting/LanguageExporter.js.map +1 -0
  131. package/dist/esm/scripting/ScriptAST.js +52 -0
  132. package/dist/esm/scripting/ScriptAST.js.map +1 -0
  133. package/dist/esm/scripting/VisualScripting2.js +1130 -0
  134. package/dist/esm/scripting/VisualScripting2.js.map +1 -0
  135. package/dist/esm/scripting/exporters/CSharpExporter.js +501 -0
  136. package/dist/esm/scripting/exporters/CSharpExporter.js.map +1 -0
  137. package/dist/esm/scripting/exporters/GDScriptExporter.js +450 -0
  138. package/dist/esm/scripting/exporters/GDScriptExporter.js.map +1 -0
  139. package/dist/esm/scripting/exporters/LuaExporter.js +455 -0
  140. package/dist/esm/scripting/exporters/LuaExporter.js.map +1 -0
  141. package/dist/esm/scripting/exporters/PythonExporter.js +563 -0
  142. package/dist/esm/scripting/exporters/PythonExporter.js.map +1 -0
  143. package/dist/esm/scripting/exporters/RustExporter.js +523 -0
  144. package/dist/esm/scripting/exporters/RustExporter.js.map +1 -0
  145. package/dist/esm/scripting/exporters/TypeScriptExporter.js +568 -0
  146. package/dist/esm/scripting/exporters/TypeScriptExporter.js.map +1 -0
  147. package/dist/esm/systems/ParticleSystem2.js +1471 -0
  148. package/dist/esm/systems/ParticleSystem2.js.map +1 -0
  149. package/dist/types/ai/BehaviorTree.d.ts +375 -0
  150. package/dist/types/ai/StateMachine.d.ts +296 -0
  151. package/dist/types/editor/ShaderGraph.d.ts +207 -0
  152. package/dist/types/editor/TimelineEditor.d.ts +393 -0
  153. package/dist/types/export/GodotExporter.d.ts +56 -0
  154. package/dist/types/export/PlatformExporter.d.ts +201 -0
  155. package/dist/types/export/ThreeJSExporter.d.ts +40 -0
  156. package/dist/types/export/UnityExporter.d.ts +69 -0
  157. package/dist/types/export/WebExporter.d.ts +58 -0
  158. package/dist/types/export/index.d.ts +19 -0
  159. package/dist/types/import/AsepriteImporter.d.ts +46 -0
  160. package/dist/types/import/DragonBonesImporter.d.ts +331 -0
  161. package/dist/types/import/GameMakerImporter.d.ts +375 -0
  162. package/dist/types/import/GodotSceneImporter.d.ts +34 -0
  163. package/dist/types/import/LDtkImporter.d.ts +177 -0
  164. package/dist/types/import/Live2DImporter.d.ts +237 -0
  165. package/dist/types/import/NdgFormat.d.ts +387 -0
  166. package/dist/types/import/OgmoImporter.d.ts +237 -0
  167. package/dist/types/import/RPGMakerImporter.d.ts +186 -0
  168. package/dist/types/import/SceneImporter.d.ts +276 -0
  169. package/dist/types/import/SpineImporter.d.ts +372 -0
  170. package/dist/types/import/SpriterImporter.d.ts +230 -0
  171. package/dist/types/import/TiledMapImporter.d.ts +57 -0
  172. package/dist/types/import/UnitySceneImporter.d.ts +87 -0
  173. package/dist/types/import/index.d.ts +59 -0
  174. package/dist/types/index.d.ts +29 -17
  175. package/dist/types/scripting/GraphToAST.d.ts +55 -0
  176. package/dist/types/scripting/LanguageExporter.d.ts +136 -0
  177. package/dist/types/scripting/ScriptAST.d.ts +312 -0
  178. package/dist/types/scripting/VisualScripting2.d.ts +353 -0
  179. package/dist/types/scripting/exporters/CSharpExporter.d.ts +44 -0
  180. package/dist/types/scripting/exporters/GDScriptExporter.d.ts +46 -0
  181. package/dist/types/scripting/exporters/LuaExporter.d.ts +46 -0
  182. package/dist/types/scripting/exporters/PythonExporter.d.ts +49 -0
  183. package/dist/types/scripting/exporters/RustExporter.d.ts +46 -0
  184. package/dist/types/scripting/exporters/TypeScriptExporter.d.ts +48 -0
  185. package/dist/types/scripting/exporters/index.d.ts +8 -0
  186. package/dist/types/scripting/index.d.ts +11 -0
  187. package/dist/types/systems/ParticleSystem2.d.ts +646 -0
  188. package/package.json +3 -3
@@ -0,0 +1,819 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @file TimelineEditor.ts
5
+ * @description Timeline System for Cutscenes, Cinematics, and Sequencing
6
+ * PRO-1.4: Professional Editor Features
7
+ *
8
+ * Features:
9
+ * - Multi-track timeline
10
+ * - Track types: animation, audio, event, property, activation
11
+ * - Keyframe animation with curves
12
+ * - Clips with blending
13
+ * - Markers and events
14
+ * - Playback control
15
+ * - Recording mode
16
+ * - Debugger integration
17
+ */
18
+ // ============================================================
19
+ // Easing Implementation
20
+ // ============================================================
21
+ const easingFunctions = {
22
+ linear: (t) => t,
23
+ easeIn: (t) => t * t,
24
+ easeOut: (t) => t * (2 - t),
25
+ easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
26
+ easeInQuad: (t) => t * t,
27
+ easeOutQuad: (t) => t * (2 - t),
28
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
29
+ easeInCubic: (t) => t * t * t,
30
+ easeOutCubic: (t) => --t * t * t + 1,
31
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
32
+ easeInQuart: (t) => t * t * t * t,
33
+ easeOutQuart: (t) => 1 - --t * t * t * t,
34
+ easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
35
+ easeInQuint: (t) => t * t * t * t * t,
36
+ easeOutQuint: (t) => 1 + --t * t * t * t * t,
37
+ easeInOutQuint: (t) => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t),
38
+ easeInSine: (t) => 1 - Math.cos((t * Math.PI) / 2),
39
+ easeOutSine: (t) => Math.sin((t * Math.PI) / 2),
40
+ easeInOutSine: (t) => -(Math.cos(Math.PI * t) - 1) / 2,
41
+ easeInExpo: (t) => (t === 0 ? 0 : Math.pow(2, 10 * t - 10)),
42
+ easeOutExpo: (t) => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t)),
43
+ easeInOutExpo: (t) => {
44
+ if (t === 0)
45
+ return 0;
46
+ if (t === 1)
47
+ return 1;
48
+ if (t < 0.5)
49
+ return Math.pow(2, 20 * t - 10) / 2;
50
+ return (2 - Math.pow(2, -20 * t + 10)) / 2;
51
+ },
52
+ easeInCirc: (t) => 1 - Math.sqrt(1 - t * t),
53
+ easeOutCirc: (t) => Math.sqrt(1 - --t * t),
54
+ easeInOutCirc: (t) => t < 0.5 ? (1 - Math.sqrt(1 - 4 * t * t)) / 2 : (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 2,
55
+ easeInBack: (t) => {
56
+ const c1 = 1.70158;
57
+ const c3 = c1 + 1;
58
+ return c3 * t * t * t - c1 * t * t;
59
+ },
60
+ easeOutBack: (t) => {
61
+ const c1 = 1.70158;
62
+ const c3 = c1 + 1;
63
+ return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
64
+ },
65
+ easeInOutBack: (t) => {
66
+ const c1 = 1.70158;
67
+ const c2 = c1 * 1.525;
68
+ return t < 0.5
69
+ ? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
70
+ : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
71
+ },
72
+ easeInElastic: (t) => {
73
+ if (t === 0)
74
+ return 0;
75
+ if (t === 1)
76
+ return 1;
77
+ const c4 = (2 * Math.PI) / 3;
78
+ return -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4);
79
+ },
80
+ easeOutElastic: (t) => {
81
+ if (t === 0)
82
+ return 0;
83
+ if (t === 1)
84
+ return 1;
85
+ const c4 = (2 * Math.PI) / 3;
86
+ return Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
87
+ },
88
+ easeInOutElastic: (t) => {
89
+ if (t === 0)
90
+ return 0;
91
+ if (t === 1)
92
+ return 1;
93
+ const c5 = (2 * Math.PI) / 4.5;
94
+ return t < 0.5
95
+ ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
96
+ : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
97
+ },
98
+ easeInBounce: (t) => 1 - easingFunctions.easeOutBounce(1 - t),
99
+ easeOutBounce: (t) => {
100
+ const n1 = 7.5625;
101
+ const d1 = 2.75;
102
+ if (t < 1 / d1) {
103
+ return n1 * t * t;
104
+ }
105
+ else if (t < 2 / d1) {
106
+ return n1 * (t -= 1.5 / d1) * t + 0.75;
107
+ }
108
+ else if (t < 2.5 / d1) {
109
+ return n1 * (t -= 2.25 / d1) * t + 0.9375;
110
+ }
111
+ else {
112
+ return n1 * (t -= 2.625 / d1) * t + 0.984375;
113
+ }
114
+ },
115
+ easeInOutBounce: (t) => t < 0.5
116
+ ? (1 - easingFunctions.easeOutBounce(1 - 2 * t)) / 2
117
+ : (1 + easingFunctions.easeOutBounce(2 * t - 1)) / 2,
118
+ };
119
+ function applyEasing(t, easing = 'linear') {
120
+ return easingFunctions[easing](t);
121
+ }
122
+ // ============================================================
123
+ // Curve Evaluation
124
+ // ============================================================
125
+ function evaluateCurve(curve, time) {
126
+ const keyframes = curve.keyframes;
127
+ if (keyframes.length === 0)
128
+ return 0;
129
+ if (keyframes.length === 1)
130
+ return keyframes[0].value;
131
+ // Handle pre/post infinity
132
+ const firstKey = keyframes[0];
133
+ const lastKey = keyframes[keyframes.length - 1];
134
+ if (time <= firstKey.time) {
135
+ return firstKey.value;
136
+ }
137
+ if (time >= lastKey.time) {
138
+ return lastKey.value;
139
+ }
140
+ // Find surrounding keyframes
141
+ let prevKey = firstKey;
142
+ let nextKey = lastKey;
143
+ for (let i = 0; i < keyframes.length - 1; i++) {
144
+ if (time >= keyframes[i].time && time <= keyframes[i + 1].time) {
145
+ prevKey = keyframes[i];
146
+ nextKey = keyframes[i + 1];
147
+ break;
148
+ }
149
+ }
150
+ // Calculate local t
151
+ const duration = nextKey.time - prevKey.time;
152
+ const localT = duration > 0 ? (time - prevKey.time) / duration : 0;
153
+ // Apply easing
154
+ const easedT = applyEasing(localT, prevKey.easing || 'linear');
155
+ // Interpolate
156
+ if (prevKey.tangentMode === 'constant') {
157
+ return prevKey.value;
158
+ }
159
+ // Bezier interpolation if tangents are specified
160
+ if (prevKey.outTangent && nextKey.inTangent) {
161
+ return bezierInterpolate(prevKey.value, prevKey.value + prevKey.outTangent.y * duration, nextKey.value + nextKey.inTangent.y * duration, nextKey.value, easedT);
162
+ }
163
+ // Linear interpolation
164
+ return prevKey.value + (nextKey.value - prevKey.value) * easedT;
165
+ }
166
+ function bezierInterpolate(p0, p1, p2, p3, t) {
167
+ const mt = 1 - t;
168
+ return mt * mt * mt * p0 + 3 * mt * mt * t * p1 + 3 * mt * t * t * p2 + t * t * t * p3;
169
+ }
170
+ // ============================================================
171
+ // Timeline Player
172
+ // ============================================================
173
+ class TimelinePlayer {
174
+ constructor(definition) {
175
+ this.state = 'stopped';
176
+ this.currentTime = 0;
177
+ this.speed = 1;
178
+ this.direction = 1; // 1 = forward, -1 = backward
179
+ /** Active clips per track */
180
+ this.activeClips = new Map();
181
+ /** Previous time for detecting clip transitions */
182
+ this.previousTime = 0;
183
+ /** Event listeners */
184
+ this.listeners = new Map();
185
+ /** Property bindings */
186
+ this.propertyBindings = new Map();
187
+ this.audioNodes = new Map();
188
+ /** Debug mode */
189
+ this.debugMode = false;
190
+ this.history = [];
191
+ this.definition = definition;
192
+ }
193
+ // ─── Playback Control ────────────────────────────
194
+ play() {
195
+ if (this.state === 'playing')
196
+ return;
197
+ this.state = 'playing';
198
+ this.emit('play');
199
+ }
200
+ pause() {
201
+ if (this.state !== 'playing')
202
+ return;
203
+ this.state = 'paused';
204
+ this.emit('pause');
205
+ }
206
+ stop() {
207
+ this.state = 'stopped';
208
+ this.currentTime = 0;
209
+ this.previousTime = 0;
210
+ this.activeClips.clear();
211
+ this.stopAllAudio();
212
+ this.emit('stop');
213
+ }
214
+ seek(time) {
215
+ this.previousTime = this.currentTime;
216
+ this.currentTime = Math.max(0, Math.min(time, this.definition.duration));
217
+ this.emit('seek', { time: this.currentTime });
218
+ }
219
+ seekToMarker(markerName) {
220
+ const marker = this.definition.markers.find((m) => m.name === markerName);
221
+ if (marker) {
222
+ this.seek(marker.time);
223
+ return true;
224
+ }
225
+ return false;
226
+ }
227
+ setSpeed(speed) {
228
+ this.speed = speed;
229
+ this.direction = speed >= 0 ? 1 : -1;
230
+ }
231
+ getSpeed() {
232
+ return this.speed;
233
+ }
234
+ // ─── Update ──────────────────────────────────────
235
+ update(dt) {
236
+ if (this.state !== 'playing')
237
+ return;
238
+ this.previousTime = this.currentTime;
239
+ this.currentTime += dt * this.speed;
240
+ // Handle wrap modes
241
+ if (this.currentTime >= this.definition.duration) {
242
+ switch (this.definition.wrapMode) {
243
+ case 'once':
244
+ this.currentTime = this.definition.duration;
245
+ this.state = 'stopped';
246
+ this.emit('complete');
247
+ break;
248
+ case 'loop':
249
+ this.currentTime = this.currentTime % this.definition.duration;
250
+ this.emit('loop');
251
+ this.activeClips.clear();
252
+ break;
253
+ case 'pingPong':
254
+ this.direction *= -1;
255
+ this.currentTime =
256
+ this.definition.duration - (this.currentTime - this.definition.duration);
257
+ this.emit('loop');
258
+ break;
259
+ case 'clampForever':
260
+ this.currentTime = this.definition.duration;
261
+ break;
262
+ }
263
+ }
264
+ else if (this.currentTime < 0) {
265
+ switch (this.definition.wrapMode) {
266
+ case 'once':
267
+ this.currentTime = 0;
268
+ this.state = 'stopped';
269
+ this.emit('complete');
270
+ break;
271
+ case 'loop':
272
+ this.currentTime = this.definition.duration + this.currentTime;
273
+ this.emit('loop');
274
+ this.activeClips.clear();
275
+ break;
276
+ case 'pingPong':
277
+ this.direction *= -1;
278
+ this.currentTime = -this.currentTime;
279
+ this.emit('loop');
280
+ break;
281
+ case 'clampForever':
282
+ this.currentTime = 0;
283
+ break;
284
+ }
285
+ }
286
+ // Process tracks
287
+ this.processTracks();
288
+ // Check markers
289
+ this.processMarkers();
290
+ }
291
+ processTracks() {
292
+ for (const track of this.definition.tracks) {
293
+ if (track.muted)
294
+ continue;
295
+ this.processTrack(track);
296
+ }
297
+ }
298
+ processTrack(track) {
299
+ let activeSet = this.activeClips.get(track.id);
300
+ if (!activeSet) {
301
+ activeSet = new Set();
302
+ this.activeClips.set(track.id, activeSet);
303
+ }
304
+ for (const clip of track.clips) {
305
+ if (clip.muted)
306
+ continue;
307
+ const clipEnd = clip.startTime + clip.duration;
308
+ const wasActive = activeSet.has(clip.id);
309
+ const isActive = this.currentTime >= clip.startTime && this.currentTime < clipEnd;
310
+ // Clip enter
311
+ if (isActive && !wasActive) {
312
+ activeSet.add(clip.id);
313
+ this.onClipEnter(track, clip);
314
+ }
315
+ // Clip exit
316
+ if (!isActive && wasActive) {
317
+ activeSet.delete(clip.id);
318
+ this.onClipExit(track, clip);
319
+ }
320
+ // Clip update
321
+ if (isActive) {
322
+ const localTime = (this.currentTime - clip.startTime) * (clip.speed || 1);
323
+ this.updateClip(track, clip, localTime);
324
+ }
325
+ }
326
+ }
327
+ onClipEnter(track, clip) {
328
+ this.emit('clip-enter', { track, clip });
329
+ // Handle audio clips
330
+ if (clip.type === 'audio') {
331
+ this.playAudioClip(clip);
332
+ }
333
+ }
334
+ onClipExit(track, clip) {
335
+ this.emit('clip-exit', { track, clip });
336
+ // Handle event clips with on-exit fire mode
337
+ if (clip.type === 'event' && clip.fireMode === 'on-exit') {
338
+ this.fireEventClip(clip);
339
+ }
340
+ // Stop audio
341
+ if (clip.type === 'audio') {
342
+ this.stopAudioClip(clip.id);
343
+ }
344
+ }
345
+ updateClip(track, clip, localTime) {
346
+ switch (clip.type) {
347
+ case 'animation':
348
+ this.updateAnimationClip(clip, localTime);
349
+ break;
350
+ case 'property':
351
+ this.updatePropertyClip(clip, localTime);
352
+ break;
353
+ case 'event':
354
+ this.updateEventClip(clip, localTime);
355
+ break;
356
+ case 'transform':
357
+ this.updateTransformClip(clip, localTime);
358
+ break;
359
+ case 'camera':
360
+ this.updateCameraClip(clip, localTime);
361
+ break;
362
+ case 'audio':
363
+ this.updateAudioClip(clip, localTime);
364
+ break;
365
+ }
366
+ }
367
+ updateAnimationClip(clip, localTime) {
368
+ for (const curve of clip.curves) {
369
+ const value = evaluateCurve(curve, localTime);
370
+ this.applyProperty(curve.property, value);
371
+ }
372
+ }
373
+ updatePropertyClip(clip, localTime) {
374
+ for (const curve of clip.curves) {
375
+ const value = evaluateCurve(curve, localTime);
376
+ this.applyProperty(clip.propertyPath + '.' + curve.property, value);
377
+ }
378
+ }
379
+ updateEventClip(clip, localTime) {
380
+ if (clip.fireMode === 'every-frame') {
381
+ this.fireEventClip(clip);
382
+ }
383
+ else if (clip.fireMode === 'once' && localTime < 0.016) {
384
+ // Fire once at the start (within one frame)
385
+ this.fireEventClip(clip);
386
+ }
387
+ }
388
+ updateTransformClip(clip, localTime) {
389
+ if (clip.positionCurve) {
390
+ const x = evaluateCurve(clip.positionCurve.x, localTime);
391
+ const y = evaluateCurve(clip.positionCurve.y, localTime);
392
+ const z = evaluateCurve(clip.positionCurve.z, localTime);
393
+ this.applyProperty(`${clip.trackId}.position`, { x, y, z });
394
+ }
395
+ if (clip.rotationCurve) {
396
+ const x = evaluateCurve(clip.rotationCurve.x, localTime);
397
+ const y = evaluateCurve(clip.rotationCurve.y, localTime);
398
+ const z = evaluateCurve(clip.rotationCurve.z, localTime);
399
+ this.applyProperty(`${clip.trackId}.rotation`, { x, y, z });
400
+ }
401
+ if (clip.scaleCurve) {
402
+ const x = evaluateCurve(clip.scaleCurve.x, localTime);
403
+ const y = evaluateCurve(clip.scaleCurve.y, localTime);
404
+ const z = evaluateCurve(clip.scaleCurve.z, localTime);
405
+ this.applyProperty(`${clip.trackId}.scale`, { x, y, z });
406
+ }
407
+ }
408
+ updateCameraClip(clip, localTime) {
409
+ if (clip.fieldOfView) {
410
+ const fov = evaluateCurve(clip.fieldOfView, localTime);
411
+ this.applyProperty(`${clip.trackId}.fov`, fov);
412
+ }
413
+ if (clip.dof) {
414
+ const focusDistance = evaluateCurve(clip.dof.focusDistance, localTime);
415
+ const aperture = evaluateCurve(clip.dof.aperture, localTime);
416
+ const focalLength = evaluateCurve(clip.dof.focalLength, localTime);
417
+ this.applyProperty(`${clip.trackId}.dof`, { focusDistance, aperture, focalLength });
418
+ }
419
+ if (clip.shake) {
420
+ const amplitude = evaluateCurve(clip.shake.amplitude, localTime);
421
+ const frequency = evaluateCurve(clip.shake.frequency, localTime);
422
+ this.applyProperty(`${clip.trackId}.shake`, { amplitude, frequency });
423
+ }
424
+ }
425
+ updateAudioClip(clip, localTime) {
426
+ if (clip.volumeCurve) {
427
+ evaluateCurve(clip.volumeCurve, localTime);
428
+ // Apply volume to audio node
429
+ this.audioNodes.get(clip.id);
430
+ // Volume adjustment would go here
431
+ }
432
+ }
433
+ fireEventClip(clip) {
434
+ this.emit('event', {
435
+ eventName: clip.eventName,
436
+ payload: clip.payload,
437
+ });
438
+ }
439
+ // ─── Audio ───────────────────────────────────────
440
+ playAudioClip(clip) {
441
+ // Audio playback would be implemented with Web Audio API
442
+ // This is a placeholder for the interface
443
+ }
444
+ stopAudioClip(clipId) {
445
+ const node = this.audioNodes.get(clipId);
446
+ if (node) {
447
+ node.stop();
448
+ this.audioNodes.delete(clipId);
449
+ }
450
+ }
451
+ stopAllAudio() {
452
+ for (const node of this.audioNodes.values()) {
453
+ node.stop();
454
+ }
455
+ this.audioNodes.clear();
456
+ }
457
+ // ─── Markers ─────────────────────────────────────
458
+ processMarkers() {
459
+ for (const marker of this.definition.markers) {
460
+ // Check if we crossed the marker
461
+ const crossedForward = this.previousTime < marker.time && this.currentTime >= marker.time;
462
+ const crossedBackward = this.previousTime > marker.time && this.currentTime <= marker.time;
463
+ if (crossedForward || crossedBackward) {
464
+ this.emit('marker', { marker });
465
+ if (marker.type === 'signal' && marker.signalName) {
466
+ this.emit('signal', {
467
+ signalName: marker.signalName,
468
+ payload: marker.signalPayload,
469
+ });
470
+ }
471
+ if (marker.type === 'jump' && marker.jumpTarget) {
472
+ const targetMarker = this.definition.markers.find((m) => m.name === marker.jumpTarget);
473
+ if (targetMarker) {
474
+ // Check condition if present
475
+ if (!marker.jumpCondition || this.evaluateCondition(marker.jumpCondition)) {
476
+ this.seek(targetMarker.time);
477
+ }
478
+ }
479
+ }
480
+ }
481
+ }
482
+ }
483
+ evaluateCondition(condition) {
484
+ // Simple condition evaluation - could be extended
485
+ return true;
486
+ }
487
+ // ─── Property Binding ────────────────────────────
488
+ bindProperty(path, setter) {
489
+ this.propertyBindings.set(path, setter);
490
+ }
491
+ unbindProperty(path) {
492
+ this.propertyBindings.delete(path);
493
+ }
494
+ applyProperty(path, value) {
495
+ const binding = this.propertyBindings.get(path);
496
+ if (binding) {
497
+ binding(value);
498
+ }
499
+ }
500
+ // ─── Events ──────────────────────────────────────
501
+ on(event, listener) {
502
+ let listeners = this.listeners.get(event);
503
+ if (!listeners) {
504
+ listeners = [];
505
+ this.listeners.set(event, listeners);
506
+ }
507
+ listeners.push(listener);
508
+ }
509
+ off(event, listener) {
510
+ const listeners = this.listeners.get(event);
511
+ if (listeners) {
512
+ const idx = listeners.indexOf(listener);
513
+ if (idx >= 0) {
514
+ listeners.splice(idx, 1);
515
+ }
516
+ }
517
+ }
518
+ emit(type, data) {
519
+ const event = {
520
+ type,
521
+ time: this.currentTime,
522
+ data,
523
+ };
524
+ if (this.debugMode) {
525
+ this.history.push(event);
526
+ if (this.history.length > 100) {
527
+ this.history.shift();
528
+ }
529
+ }
530
+ const listeners = this.listeners.get(type);
531
+ if (listeners) {
532
+ for (const listener of listeners) {
533
+ listener(event);
534
+ }
535
+ }
536
+ }
537
+ // ─── State Queries ───────────────────────────────
538
+ getState() {
539
+ return this.state;
540
+ }
541
+ getCurrentTime() {
542
+ return this.currentTime;
543
+ }
544
+ getDuration() {
545
+ return this.definition.duration;
546
+ }
547
+ getProgress() {
548
+ return this.definition.duration > 0 ? this.currentTime / this.definition.duration : 0;
549
+ }
550
+ isPlaying() {
551
+ return this.state === 'playing';
552
+ }
553
+ isPaused() {
554
+ return this.state === 'paused';
555
+ }
556
+ isStopped() {
557
+ return this.state === 'stopped';
558
+ }
559
+ getDefinition() {
560
+ return this.definition;
561
+ }
562
+ // ─── Debugging ───────────────────────────────────
563
+ getHistory() {
564
+ return [...this.history];
565
+ }
566
+ clearHistory() {
567
+ this.history = [];
568
+ }
569
+ getDebugInfo() {
570
+ let activeClipCount = 0;
571
+ for (const set of this.activeClips.values()) {
572
+ activeClipCount += set.size;
573
+ }
574
+ return {
575
+ state: this.state,
576
+ time: this.currentTime,
577
+ duration: this.definition.duration,
578
+ progress: this.getProgress(),
579
+ speed: this.speed,
580
+ activeClipCount,
581
+ };
582
+ }
583
+ }
584
+ class TimelineRecorder {
585
+ constructor(player, options) {
586
+ this.recording = false;
587
+ this.recordedKeyframes = new Map();
588
+ this.lastSampleTime = 0;
589
+ this.player = player;
590
+ this.options = options;
591
+ }
592
+ startRecording() {
593
+ this.recording = true;
594
+ this.recordedKeyframes.clear();
595
+ this.lastSampleTime = 0;
596
+ }
597
+ stopRecording() {
598
+ this.recording = false;
599
+ return new Map(this.recordedKeyframes);
600
+ }
601
+ isRecording() {
602
+ return this.recording;
603
+ }
604
+ recordValue(property, value) {
605
+ if (!this.recording)
606
+ return;
607
+ const time = this.player.getCurrentTime();
608
+ const sampleRate = this.options.sampleRate || 30;
609
+ const minInterval = 1 / sampleRate;
610
+ // Check if enough time has passed
611
+ if (time - this.lastSampleTime < minInterval)
612
+ return;
613
+ let keyframes = this.recordedKeyframes.get(property);
614
+ if (!keyframes) {
615
+ keyframes = [];
616
+ this.recordedKeyframes.set(property, keyframes);
617
+ }
618
+ keyframes.push({
619
+ id: `key_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
620
+ time,
621
+ value,
622
+ easing: 'linear',
623
+ });
624
+ this.lastSampleTime = time;
625
+ }
626
+ getRecordedKeyframes() {
627
+ return new Map(this.recordedKeyframes);
628
+ }
629
+ }
630
+ // ============================================================
631
+ // Factory Functions
632
+ // ============================================================
633
+ function createTimelineDefinition(name, duration = 10) {
634
+ return {
635
+ id: `timeline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
636
+ name,
637
+ description: '',
638
+ duration,
639
+ fps: 60,
640
+ wrapMode: 'once',
641
+ tracks: [],
642
+ markers: [],
643
+ };
644
+ }
645
+ function createTimelinePlayer(definition) {
646
+ return new TimelinePlayer(definition);
647
+ }
648
+ function createTimelineRecorder(player, options) {
649
+ return new TimelineRecorder(player, options);
650
+ }
651
+ function createTrack(name, type) {
652
+ const baseTrack = {
653
+ id: `track_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
654
+ name,
655
+ type,
656
+ muted: false,
657
+ locked: false,
658
+ collapsed: false,
659
+ clips: [],
660
+ };
661
+ switch (type) {
662
+ case 'animation':
663
+ return { ...baseTrack, type: 'animation' };
664
+ case 'audio':
665
+ return { ...baseTrack, type: 'audio', volume: 1 };
666
+ case 'event':
667
+ return { ...baseTrack, type: 'event' };
668
+ case 'property':
669
+ return {
670
+ ...baseTrack,
671
+ type: 'property',
672
+ targetEntity: '',
673
+ propertyPath: '',
674
+ };
675
+ case 'activation':
676
+ return {
677
+ ...baseTrack,
678
+ type: 'activation',
679
+ targetEntity: '',
680
+ };
681
+ case 'transform':
682
+ return {
683
+ ...baseTrack,
684
+ type: 'transform',
685
+ targetEntity: '',
686
+ space: 'local',
687
+ };
688
+ case 'camera':
689
+ return {
690
+ ...baseTrack,
691
+ type: 'camera',
692
+ cameraEntity: '',
693
+ };
694
+ case 'group':
695
+ return { ...baseTrack, type: 'group', children: [] };
696
+ default:
697
+ return baseTrack;
698
+ }
699
+ }
700
+ function createAnimationClip(trackId, startTime, duration) {
701
+ return {
702
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
703
+ name: 'Animation Clip',
704
+ type: 'animation',
705
+ trackId,
706
+ startTime,
707
+ duration,
708
+ curves: [],
709
+ };
710
+ }
711
+ function createAudioClip(trackId, startTime, duration, audioSource) {
712
+ return {
713
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
714
+ name: 'Audio Clip',
715
+ type: 'audio',
716
+ trackId,
717
+ startTime,
718
+ duration,
719
+ audioSource,
720
+ volume: 1,
721
+ };
722
+ }
723
+ function createEventClip(trackId, time, eventName) {
724
+ return {
725
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
726
+ name: eventName,
727
+ type: 'event',
728
+ trackId,
729
+ startTime: time,
730
+ duration: 0.01, // Instant
731
+ eventName,
732
+ fireMode: 'once',
733
+ };
734
+ }
735
+ function createPropertyClip(trackId, startTime, duration, propertyPath) {
736
+ return {
737
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
738
+ name: 'Property Clip',
739
+ type: 'property',
740
+ trackId,
741
+ startTime,
742
+ duration,
743
+ propertyPath,
744
+ curves: [],
745
+ };
746
+ }
747
+ function createTransformClip(trackId, startTime, duration) {
748
+ return {
749
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
750
+ name: 'Transform Clip',
751
+ type: 'transform',
752
+ trackId,
753
+ startTime,
754
+ duration,
755
+ };
756
+ }
757
+ function createCameraClip(trackId, startTime, duration) {
758
+ return {
759
+ id: `clip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
760
+ name: 'Camera Clip',
761
+ type: 'camera',
762
+ trackId,
763
+ startTime,
764
+ duration,
765
+ };
766
+ }
767
+ function createAnimationCurve(property) {
768
+ return {
769
+ id: `curve_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
770
+ property,
771
+ keyframes: [],
772
+ };
773
+ }
774
+ function createKeyframe(time, value, easing = 'linear') {
775
+ return {
776
+ id: `key_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
777
+ time,
778
+ value,
779
+ easing,
780
+ };
781
+ }
782
+ function createMarker(name, time) {
783
+ return {
784
+ id: `marker_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
785
+ name,
786
+ time,
787
+ type: 'marker',
788
+ };
789
+ }
790
+ function createSignalMarker(name, time, signalName, payload) {
791
+ return {
792
+ id: `marker_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
793
+ name,
794
+ time,
795
+ type: 'signal',
796
+ signalName,
797
+ signalPayload: payload,
798
+ };
799
+ }
800
+
801
+ exports.TimelinePlayer = TimelinePlayer;
802
+ exports.TimelineRecorder = TimelineRecorder;
803
+ exports.applyEasing = applyEasing;
804
+ exports.createAnimationClip = createAnimationClip;
805
+ exports.createAnimationCurve = createAnimationCurve;
806
+ exports.createAudioClip = createAudioClip;
807
+ exports.createCameraClip = createCameraClip;
808
+ exports.createEventClip = createEventClip;
809
+ exports.createKeyframe = createKeyframe;
810
+ exports.createMarker = createMarker;
811
+ exports.createPropertyClip = createPropertyClip;
812
+ exports.createSignalMarker = createSignalMarker;
813
+ exports.createTimelineDefinition = createTimelineDefinition;
814
+ exports.createTimelinePlayer = createTimelinePlayer;
815
+ exports.createTimelineRecorder = createTimelineRecorder;
816
+ exports.createTrack = createTrack;
817
+ exports.createTransformClip = createTransformClip;
818
+ exports.evaluateCurve = evaluateCurve;
819
+ //# sourceMappingURL=TimelineEditor.js.map