pixospritz-core 0.10.1 → 1.0.1

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 (157) hide show
  1. package/README.md +36 -286
  2. package/dist/bundle.js +13 -3
  3. package/dist/bundle.js.map +1 -1
  4. package/dist/style.css +1 -0
  5. package/package.json +43 -44
  6. package/src/components/WebGLView.jsx +318 -0
  7. package/src/css/pixos.css +372 -0
  8. package/src/engine/actions/animate.js +41 -0
  9. package/src/engine/actions/changezone.js +135 -0
  10. package/src/engine/actions/chat.js +109 -0
  11. package/src/engine/actions/dialogue.js +90 -0
  12. package/src/engine/actions/face.js +22 -0
  13. package/src/engine/actions/greeting.js +28 -0
  14. package/src/engine/actions/interact.js +86 -0
  15. package/src/engine/actions/move.js +67 -0
  16. package/src/engine/actions/patrol.js +109 -0
  17. package/src/engine/actions/prompt.js +185 -0
  18. package/src/engine/actions/script.js +42 -0
  19. package/src/engine/core/audio/AudioSystem.js +543 -0
  20. package/src/engine/core/cutscene/PxcPlayer.js +956 -0
  21. package/src/engine/core/cutscene/manager.js +243 -0
  22. package/src/engine/core/database/index.js +75 -0
  23. package/src/engine/core/debug/index.js +371 -0
  24. package/src/engine/core/hud/index.js +765 -0
  25. package/src/engine/core/index.js +540 -0
  26. package/src/engine/core/input/gamepad/Controller.js +71 -0
  27. package/src/engine/core/input/gamepad/ControllerButtons.js +231 -0
  28. package/src/engine/core/input/gamepad/ControllerStick.js +173 -0
  29. package/src/engine/core/input/gamepad/index.js +592 -0
  30. package/src/engine/core/input/keyboard.js +196 -0
  31. package/src/engine/core/input/manager.js +485 -0
  32. package/src/engine/core/input/mouse.js +203 -0
  33. package/src/engine/core/input/touch.js +175 -0
  34. package/src/engine/core/mode/manager.js +199 -0
  35. package/src/engine/core/net/manager.js +535 -0
  36. package/src/engine/core/queue/action.js +83 -0
  37. package/src/engine/core/queue/event.js +82 -0
  38. package/src/engine/core/queue/index.js +44 -0
  39. package/src/engine/core/queue/loadable.js +33 -0
  40. package/src/engine/core/render/CameraEffects.js +494 -0
  41. package/src/engine/core/render/FrustumCuller.js +417 -0
  42. package/src/engine/core/render/LODManager.js +285 -0
  43. package/src/engine/core/render/ParticleManager.js +529 -0
  44. package/src/engine/core/render/TextureAtlas.js +465 -0
  45. package/src/engine/core/render/camera.js +338 -0
  46. package/src/engine/core/render/light.js +197 -0
  47. package/src/engine/core/render/manager.js +1079 -0
  48. package/src/engine/core/render/shaders.js +110 -0
  49. package/src/engine/core/render/skybox.js +342 -0
  50. package/src/engine/core/resource/manager.js +133 -0
  51. package/src/engine/core/resource/object.js +611 -0
  52. package/src/engine/core/resource/texture.js +103 -0
  53. package/src/engine/core/resource/tileset.js +177 -0
  54. package/src/engine/core/scene/avatar.js +215 -0
  55. package/src/engine/core/scene/speech.js +138 -0
  56. package/src/engine/core/scene/sprite.js +702 -0
  57. package/src/engine/core/scene/spritz.js +189 -0
  58. package/src/engine/core/scene/world.js +681 -0
  59. package/src/engine/core/scene/zone.js +1167 -0
  60. package/src/engine/core/store/index.js +110 -0
  61. package/src/engine/dynamic/animatedSprite.js +64 -0
  62. package/src/engine/dynamic/animatedTile.js +98 -0
  63. package/src/engine/dynamic/avatar.js +110 -0
  64. package/src/engine/dynamic/map.js +174 -0
  65. package/src/engine/dynamic/sprite.js +255 -0
  66. package/src/engine/dynamic/spritz.js +119 -0
  67. package/src/engine/events/EventSystem.js +609 -0
  68. package/src/engine/events/camera.js +142 -0
  69. package/src/engine/events/chat.js +75 -0
  70. package/src/engine/events/menu.js +186 -0
  71. package/src/engine/scripting/CallbackManager.js +514 -0
  72. package/src/engine/scripting/PixoScriptInterpreter.js +81 -0
  73. package/src/engine/scripting/PixoScriptLibrary.js +704 -0
  74. package/src/engine/shaders/effects/index.js +450 -0
  75. package/src/engine/shaders/fs.js +222 -0
  76. package/src/engine/shaders/particles/fs.js +41 -0
  77. package/src/engine/shaders/particles/vs.js +61 -0
  78. package/src/engine/shaders/picker/fs.js +34 -0
  79. package/src/engine/shaders/picker/init.js +62 -0
  80. package/src/engine/shaders/picker/vs.js +42 -0
  81. package/src/engine/shaders/pxsl/README.md +250 -0
  82. package/src/engine/shaders/pxsl/index.js +25 -0
  83. package/src/engine/shaders/pxsl/library.js +608 -0
  84. package/src/engine/shaders/pxsl/manager.js +338 -0
  85. package/src/engine/shaders/pxsl/specification.js +363 -0
  86. package/src/engine/shaders/pxsl/transpiler.js +753 -0
  87. package/src/engine/shaders/skybox/cosmic/fs.js +147 -0
  88. package/src/engine/shaders/skybox/cosmic/vs.js +23 -0
  89. package/src/engine/shaders/skybox/matrix/fs.js +127 -0
  90. package/src/engine/shaders/skybox/matrix/vs.js +23 -0
  91. package/src/engine/shaders/skybox/morning/fs.js +109 -0
  92. package/src/engine/shaders/skybox/morning/vs.js +23 -0
  93. package/src/engine/shaders/skybox/neon/fs.js +119 -0
  94. package/src/engine/shaders/skybox/neon/vs.js +23 -0
  95. package/src/engine/shaders/skybox/sky/fs.js +114 -0
  96. package/src/engine/shaders/skybox/sky/vs.js +23 -0
  97. package/src/engine/shaders/skybox/sunset/fs.js +101 -0
  98. package/src/engine/shaders/skybox/sunset/vs.js +23 -0
  99. package/src/engine/shaders/transition/blur/fs.js +42 -0
  100. package/src/engine/shaders/transition/blur/vs.js +26 -0
  101. package/src/engine/shaders/transition/cross/fs.js +36 -0
  102. package/src/engine/shaders/transition/cross/vs.js +26 -0
  103. package/src/engine/shaders/transition/crossBlur/fs.js +41 -0
  104. package/src/engine/shaders/transition/crossBlur/vs.js +25 -0
  105. package/src/engine/shaders/transition/dissolve/fs.js +78 -0
  106. package/src/engine/shaders/transition/dissolve/vs.js +24 -0
  107. package/src/engine/shaders/transition/fade/fs.js +31 -0
  108. package/src/engine/shaders/transition/fade/vs.js +27 -0
  109. package/src/engine/shaders/transition/iris/fs.js +52 -0
  110. package/src/engine/shaders/transition/iris/vs.js +24 -0
  111. package/src/engine/shaders/transition/pixelate/fs.js +44 -0
  112. package/src/engine/shaders/transition/pixelate/vs.js +24 -0
  113. package/src/engine/shaders/transition/slide/fs.js +53 -0
  114. package/src/engine/shaders/transition/slide/vs.js +24 -0
  115. package/src/engine/shaders/transition/swirl/fs.js +39 -0
  116. package/src/engine/shaders/transition/swirl/vs.js +26 -0
  117. package/src/engine/shaders/transition/wipe/fs.js +50 -0
  118. package/src/engine/shaders/transition/wipe/vs.js +24 -0
  119. package/src/engine/shaders/vs.js +60 -0
  120. package/src/engine/utils/CameraController.js +506 -0
  121. package/src/engine/utils/ObjHelper.js +551 -0
  122. package/src/engine/utils/debug-logger.js +110 -0
  123. package/src/engine/utils/enums.js +305 -0
  124. package/src/engine/utils/generator.js +156 -0
  125. package/src/engine/utils/index.js +21 -0
  126. package/src/engine/utils/loaders/ActionLoader.js +77 -0
  127. package/src/engine/utils/loaders/AudioLoader.js +157 -0
  128. package/src/engine/utils/loaders/EventLoader.js +66 -0
  129. package/src/engine/utils/loaders/ObjectLoader.js +67 -0
  130. package/src/engine/utils/loaders/SpriteLoader.js +77 -0
  131. package/src/engine/utils/loaders/TilesetLoader.js +103 -0
  132. package/src/engine/utils/loaders/index.js +21 -0
  133. package/src/engine/utils/math/matrix4.js +367 -0
  134. package/src/engine/utils/math/vector.js +458 -0
  135. package/src/engine/utils/obj/_old_js/index.js +46 -0
  136. package/src/engine/utils/obj/_old_js/layout.js +308 -0
  137. package/src/engine/utils/obj/_old_js/material.js +711 -0
  138. package/src/engine/utils/obj/_old_js/mesh.js +761 -0
  139. package/src/engine/utils/obj/_old_js/utils.js +647 -0
  140. package/src/engine/utils/obj/index.js +24 -0
  141. package/src/engine/utils/obj/js/index.js +277 -0
  142. package/src/engine/utils/obj/js/loader.js +232 -0
  143. package/src/engine/utils/obj/layout.js +246 -0
  144. package/src/engine/utils/obj/material.js +665 -0
  145. package/src/engine/utils/obj/mesh.js +657 -0
  146. package/src/engine/utils/obj/ts/index.ts +72 -0
  147. package/src/engine/utils/obj/ts/layout.ts +265 -0
  148. package/src/engine/utils/obj/ts/material.ts +760 -0
  149. package/src/engine/utils/obj/ts/mesh.ts +785 -0
  150. package/src/engine/utils/obj/ts/utils.ts +501 -0
  151. package/src/engine/utils/obj/utils.js +428 -0
  152. package/src/engine/utils/resources.js +18 -0
  153. package/src/index.jsx +55 -0
  154. package/src/spritz/player.js +18 -0
  155. package/src/spritz/readme.md +18 -0
  156. package/LICENSE +0 -437
  157. package/dist/bundle.js.LICENSE.txt +0 -31
@@ -0,0 +1,372 @@
1
+ /* *** ----------------------------------------------- **
2
+ ** Calliope - Site Generator **
3
+ ** ----------------------------------------------- **
4
+ ** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
5
+ ** **
6
+ ** Any unauthorized distribution or transfer **
7
+ ** of this work is strictly prohibited. **
8
+ ** **
9
+ ** All Rights Reserved. **
10
+ ** ----------------------------------------------- **
11
+ \* */
12
+
13
+ :root {
14
+ --color-bg: #0a0a0f;
15
+ --color-primary: #00ff88;
16
+ --color-secondary: #00ccff;
17
+ --color-accent: #ff0055;
18
+ --color-border: #2a2a3e;
19
+ --glow-primary: rgba(0, 255, 136, 0.5);
20
+ --glow-secondary: rgba(0, 204, 255, 0.5);
21
+ }
22
+
23
+ .pixos {
24
+ font-family: 'Press Start 2P', 'Courier New', monospace;
25
+ font-size: 16px;
26
+ width: 100%;
27
+ text-shadow: #000 2px 2px 0px;
28
+ position: relative;
29
+ align-items: center;
30
+ align-content: center;
31
+ }
32
+
33
+ .renderSurface {
34
+ height: 100%;
35
+ left: 0;
36
+ position: absolute;
37
+ top: 0;
38
+ width: 100%;
39
+ }
40
+
41
+ .materialSelector {
42
+ background: rgba(10, 10, 15, 0.95);
43
+ margin: auto;
44
+ position: relative;
45
+ border: 2px solid var(--color-border);
46
+ border-radius: 4px;
47
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
48
+ }
49
+
50
+ .materialSelector tr {
51
+ height: 70px;
52
+ }
53
+
54
+ .materialSelector td {
55
+ background-position: 0px 0px;
56
+ cursor: pointer;
57
+ margin: 0;
58
+ opacity: 0.5;
59
+ padding: 0;
60
+ width: 70px;
61
+ transition: opacity 0.2s;
62
+ }
63
+
64
+ .materialSelector td:hover {
65
+ opacity: 1;
66
+ box-shadow: 0 0 10px var(--glow-primary);
67
+ }
68
+
69
+ .nickname {
70
+ color: var(--color-primary);
71
+ cursor: default;
72
+ left: 42%;
73
+ position: absolute;
74
+ top: 40%;
75
+ width: 300px;
76
+ text-shadow: 0 0 10px var(--glow-primary);
77
+ }
78
+
79
+ .nickname input {
80
+ background: rgba(10, 10, 15, 0.8);
81
+ border-bottom: 2px solid var(--color-primary);
82
+ border: 1px solid var(--color-border);
83
+ border-radius: 4px;
84
+ color: var(--color-primary);
85
+ font-family: 'Press Start 2P', 'Courier New', monospace;
86
+ font-size: 16px;
87
+ outline: none;
88
+ width: 100%;
89
+ padding: 8px;
90
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
91
+ }
92
+
93
+ .nickname input:focus {
94
+ border-color: var(--color-primary);
95
+ box-shadow: 0 0 15px var(--glow-primary);
96
+ }
97
+
98
+ .joininfo {
99
+ color: var(--color-primary);
100
+ cursor: default;
101
+ font-size: 16px;
102
+ position: absolute;
103
+ text-align: center;
104
+ top: 42%;
105
+ width: 99%;
106
+ text-shadow: 0 0 10px var(--glow-primary);
107
+ }
108
+
109
+ .chatbox {
110
+ background: rgba(10, 10, 15, 0.95);
111
+ border: 2px solid var(--color-border);
112
+ border-radius: 8px;
113
+ bottom: 55px;
114
+ color: var(--color-primary);
115
+ cursor: default;
116
+ height: 195px;
117
+ left: 20px;
118
+ overflow: hidden;
119
+ padding: 10px;
120
+ position: absolute;
121
+ width: 600px;
122
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.8), inset 0 0 10px rgba(0, 0, 0, 0.5);
123
+ }
124
+
125
+ .chatbox_text {
126
+ bottom: 8px;
127
+ position: absolute;
128
+ text-shadow: none;
129
+ font-size: 12px;
130
+ line-height: 1.6;
131
+ }
132
+
133
+ .chatbox_entry {
134
+ background: rgba(10, 10, 15, 0.95);
135
+ border: 2px solid var(--color-border);
136
+ border-radius: 4px;
137
+ bottom: 18px;
138
+ color: var(--color-primary);
139
+ font-family: 'Press Start 2P', 'Courier New', monospace;
140
+ font-size: 12px;
141
+ height: 30px;
142
+ left: 20px;
143
+ outline: none;
144
+ padding: 8px 10px;
145
+ position: absolute;
146
+ width: 610px;
147
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
148
+ }
149
+
150
+ .chatbox_entry:focus {
151
+ border-color: var(--color-primary);
152
+ box-shadow: 0 0 15px var(--glow-primary);
153
+ }
154
+
155
+ html {
156
+ height: 100%;
157
+ }
158
+
159
+ .pixos-body {
160
+ height: 100%;
161
+ margin: 0;
162
+ overflow: hidden;
163
+ background: linear-gradient(to bottom, #202020, #111119);
164
+ }
165
+
166
+ .rain {
167
+ position: absolute;
168
+ left: 0;
169
+ width: 100%;
170
+ height: 100%;
171
+ z-index: 2;
172
+ }
173
+
174
+ .snow {
175
+ position: absolute;
176
+ left: 0;
177
+ width: 100%;
178
+ height: 100%;
179
+ z-index: 2;
180
+ }
181
+
182
+ .rain.back-row {
183
+ z-index: 1;
184
+ bottom: 60px;
185
+ opacity: 0.5;
186
+ }
187
+
188
+ .snow.back-row {
189
+ z-index: 1;
190
+ bottom: 60px;
191
+ opacity: 0.8;
192
+ }
193
+
194
+ body.back-row-toggle .rain.back-row .snow.back-row {
195
+ display: block;
196
+ }
197
+
198
+ .drop {
199
+ position: absolute;
200
+ bottom: 100%;
201
+ width: 15px;
202
+ height: 120px;
203
+ pointer-events: none;
204
+ animation: drop 0.5s linear infinite;
205
+ }
206
+
207
+ .flake {
208
+ position: absolute;
209
+ bottom: 100%;
210
+ width: 24px;
211
+ height: 24px;
212
+ pointer-events: none;
213
+ animation: snow 2.5s linear infinite;
214
+ }
215
+
216
+ @keyframes drop {
217
+ 0% {
218
+ transform: translateY(0vh);
219
+ }
220
+
221
+ 75% {
222
+ transform: translateY(90vh);
223
+ }
224
+
225
+ 100% {
226
+ transform: translateY(90vh);
227
+ }
228
+ }
229
+
230
+ @keyframes snow {
231
+ 0% {
232
+ transform: translateY(0vh);
233
+ }
234
+
235
+ 70% {
236
+ transform: translateY(50vh);
237
+ }
238
+
239
+ 85% {
240
+ transform: translateY(85vh);
241
+ }
242
+
243
+ 75% {
244
+ transform: translateY(75vh);
245
+ }
246
+
247
+ 100% {
248
+ transform: translateY(95vh);
249
+ transform: translateX(30vw);
250
+ }
251
+ }
252
+
253
+ .stem {
254
+ width: 1px;
255
+ height: 60%;
256
+ margin-left: 7px;
257
+ background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.25));
258
+ animation: stem 0.5s linear infinite;
259
+ }
260
+
261
+ @keyframes stem {
262
+ 0% {
263
+ opacity: 1;
264
+ }
265
+
266
+ 65% {
267
+ opacity: 1;
268
+ }
269
+
270
+ 75% {
271
+ opacity: 0;
272
+ }
273
+
274
+ 100% {
275
+ opacity: 0;
276
+ }
277
+ }
278
+
279
+ .splat {
280
+ width: 15px;
281
+ height: 10px;
282
+ border-top: 2px dotted rgba(255, 255, 255, 0.5);
283
+ border-radius: 50%;
284
+ opacity: 1;
285
+ transform: scale(0);
286
+ animation: splat 0.5s linear infinite;
287
+ }
288
+
289
+ body.splat-toggle .splat {
290
+ display: block;
291
+ }
292
+
293
+ @keyframes splat {
294
+ 0% {
295
+ opacity: 1;
296
+ transform: scale(0);
297
+ }
298
+
299
+ 80% {
300
+ opacity: 1;
301
+ transform: scale(0);
302
+ }
303
+
304
+ 90% {
305
+ opacity: 0.5;
306
+ transform: scale(1);
307
+ }
308
+
309
+ 100% {
310
+ opacity: 0;
311
+ transform: scale(1.5);
312
+ }
313
+ }
314
+
315
+ .toggles {
316
+ position: absolute;
317
+ top: 0;
318
+ left: 0;
319
+ z-index: 3;
320
+ }
321
+
322
+ .toggle {
323
+ position: absolute;
324
+ left: 20px;
325
+ width: 50px;
326
+ height: 50px;
327
+ line-height: 51px;
328
+ box-sizing: border-box;
329
+ text-align: center;
330
+ font-family: sans-serif;
331
+ font-size: 10px;
332
+ font-weight: bold;
333
+ background-color: rgba(255, 255, 255, 0.2);
334
+ color: rgba(0, 0, 0, 0.5);
335
+ border-radius: 50%;
336
+ cursor: pointer;
337
+ transition: background-color 0.3s;
338
+ }
339
+
340
+ .toggle:hover {
341
+ background-color: rgba(255, 255, 255, 0.25);
342
+ }
343
+
344
+ .toggle:active {
345
+ background-color: rgba(255, 255, 255, 0.3);
346
+ }
347
+
348
+ .toggle.active {
349
+ background-color: rgba(255, 255, 255, 0.4);
350
+ }
351
+
352
+ .splat-toggle {
353
+ top: 20px;
354
+ }
355
+
356
+ .back-row-toggle {
357
+ top: 90px;
358
+ line-height: 12px;
359
+ padding-top: 14px;
360
+ }
361
+
362
+ .single-toggle {
363
+ top: 160px;
364
+ }
365
+
366
+ body.single-toggle .drop {
367
+ display: none;
368
+ }
369
+
370
+ body.single-toggle .drop:nth-child(10) {
371
+ display: block;
372
+ }
@@ -0,0 +1,41 @@
1
+ /* *\
2
+ ** ----------------------------------------------- **
3
+ ** Calliope - Pixos Game Engine **
4
+ ** ----------------------------------------------- **
5
+ ** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
6
+ ** **
7
+ ** Any unauthorized distribution or transfer **
8
+ ** of this work is strictly prohibited. **
9
+ ** **
10
+ ** All Rights Reserved. **
11
+ ** ----------------------------------------------- **
12
+ \* */
13
+
14
+ import { debug } from '@Engine/utils/debug-logger.js';
15
+
16
+ // Animate a sprite without moving them
17
+ export default {
18
+ init: function (length, untilFrame, finish) {
19
+ debug('Animate', 'initializing animation', { length, untilFrame });
20
+ this.length = length;
21
+ this.untilFrame = untilFrame;
22
+ this.finish = finish;
23
+ },
24
+ tick: function (time) {
25
+ if (!this.loaded) return;
26
+ // Set facing
27
+ if (this.facing && this.facing != this.sprite.facing) this.sprite.setFacing(this.facing);
28
+ // Transition & Move
29
+ let endTime = this.startTime + this.length;
30
+ let frac = (time - this.startTime) / this.length;
31
+ if (time >= endTime) {
32
+ frac = 1;
33
+ if (this.finish) this.finish(true);
34
+ }
35
+ // Get next frame
36
+ let newFrame = Math.floor(frac * this.untilFrame);
37
+ if (newFrame != this.sprite.animFrame) this.sprite.setFrame(newFrame);
38
+
39
+ return time >= endTime;
40
+ }
41
+ };
@@ -0,0 +1,135 @@
1
+ /* *\
2
+ ** ----------------------------------------------- **
3
+ ** Calliope - Pixos Game Engine **
4
+ ** ----------------------------------------------- **
5
+ ** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
6
+ ** **
7
+ ** Any unauthorized distribution or transfer **
8
+ ** of this work is strictly prohibited. **
9
+ ** **
10
+ ** All Rights Reserved. **
11
+ ** ----------------------------------------------- **
12
+ \* */
13
+
14
+ import { Vector, set, lerp } from '@Engine/utils/math/vector.js';
15
+ import { Direction } from '@Engine/utils/enums.js';
16
+ import { debug } from '@Engine/utils/debug-logger.js';
17
+
18
+ export default {
19
+ init: async function (fromZoneId, from, toZoneId, to, length) {
20
+ // When changing zones we fade out of the current zone, load the new zone(s)
21
+ // and then fade back in. This makes the transition smoother and hides
22
+ // asset loading. The duration can be tweaked if needed.
23
+ const engine = this.sprite.zone?.world?.engine;
24
+ if (engine?.renderManager) {
25
+ // fade out
26
+ debug('ChangeZone', 'fading out...');
27
+ await engine.renderManager.startTransition({ effect: 'cross', direction: 'out', duration: 500 });
28
+ }
29
+
30
+ // When changing zones we load both zones without triggering the default
31
+ // transition defined in World.loadZone. We handle the fade out/in around
32
+ // these calls ourselves in this action. Passing `null` as the transition
33
+ // parameter disables the automatic transition in loadZone.
34
+ this.fromZone = await this.sprite.zone.world.loadZone(fromZoneId, false, false, null);
35
+ this.toZone = await this.sprite.zone.world.loadZone(toZoneId, false, false, null);
36
+ this.from = new Vector(...from);
37
+ this.to = new Vector(...to);
38
+ this.facing = Direction.fromOffset([Math.round(to.x - from.x), Math.round(to.y - from.y)]);
39
+ this.length = length;
40
+ // Determine if the transition should preserve height (e.g., portals/doors)
41
+ try {
42
+ const spritesAtDest = this.toZone ? this.toZone.spriteList.filter((s) => s.pos.x === this.to.x && s.pos.y === this.to.y) : [];
43
+ this.preserveHeight = spritesAtDest.some((s) => s.preserveHeightOnWalk === true);
44
+ if (this.preserveHeight && (this.from && (this.from.z === null || this.from.z === undefined))) {
45
+ const fHx = this.from.x + (this.sprite?.hotspotOffset?.x ?? 0);
46
+ const fHy = this.from.y + (this.sprite?.hotspotOffset?.y ?? 0);
47
+ this.from.z = typeof this.fromZone.getHeight === 'function' ? this.fromZone.getHeight(fHx, fHy) : 0;
48
+ this.preserveHeightSourceZ = this.from.z;
49
+ }
50
+ } catch (e) {
51
+ // ignore and continue
52
+ }
53
+ if (this.preserveHeight && this.sprite?.zone?.engine?.debug) {
54
+ debug('ChangeZone', 'preserveHeight true for transition from', this.from.toArray?.() ?? this.from, 'to', this.to.toArray?.() ?? this.to);
55
+ }
56
+ // Compute the z height for from/to so we interpolate vertically across zones
57
+ try {
58
+ const fHx = this.from.x + (this.sprite ? this.sprite.hotspotOffset.x : 0);
59
+ const fHy = this.from.y + (this.sprite ? this.sprite.hotspotOffset.y : 0);
60
+ const tHx = this.to.x + (this.sprite ? this.sprite.hotspotOffset.x : 0);
61
+ const tHy = this.to.y + (this.sprite ? this.sprite.hotspotOffset.y : 0);
62
+ if (this.from && (this.from.z === null || this.from.z === undefined)) this.from.z = typeof this.fromZone.getHeight === 'function' ? this.fromZone.getHeight(fHx, fHy) : 0;
63
+ if (this.to && (this.to.z === null || this.to.z === undefined)) this.to.z = typeof this.toZone.getHeight === 'function' ? this.toZone.getHeight(tHx, tHy) : 0;
64
+ } catch (e) {
65
+ if (this.sprite?.zone?.engine?.debug) console.warn('changezone.init failed to compute z for from/to', e?.message || e);
66
+ }
67
+
68
+ debug('ChangeZone', 'init', { fromZoneId, toZoneId, from, to, length });
69
+ if (engine?.renderManager) {
70
+ debug('ChangeZone', 'fading in...');
71
+ // fade in once the new zones are ready
72
+ await engine.renderManager.startTransition({ effect: 'cross', direction: 'in', duration: 500 });
73
+ }
74
+ },
75
+ tick: function (time) {
76
+ if (!this.toZone.loaded || !this.fromZone.loaded) return;
77
+ // Set facing
78
+ if (this.facing && this.facing != this.sprite.facing) {
79
+ this.sprite.facing = this.facing;
80
+ this.sprite.setFrame(0);
81
+ }
82
+ // Time Animation
83
+ let endTime = this.startTime + this.length;
84
+ let frac = (time - this.startTime) / this.length;
85
+ if (time >= endTime) {
86
+ set(this.to, this.sprite.pos);
87
+ frac = 1;
88
+ } else {
89
+ // Lerp X and Y only, handle Z separately
90
+ this.sprite.pos.x = this.from.x + frac * (this.to.x - this.from.x);
91
+ this.sprite.pos.y = this.from.y + frac * (this.to.y - this.from.y);
92
+ // Calculate height based on current zone and position
93
+ let hx = this.sprite.pos.x + this.sprite.hotspotOffset.x;
94
+ let hy = this.sprite.pos.y + this.sprite.hotspotOffset.y;
95
+ let zLerp = (typeof this.from.z === 'number' && typeof this.to.z === 'number') ? this.from.z + frac * (this.to.z - this.from.z) : null;
96
+ if (!this.switchRenderZone && !this.fromZone.isInZone(hx, hy)) {
97
+ this.switchRenderZone = true;
98
+ }
99
+ if (this.preserveHeight) {
100
+ this.sprite.pos.z = this.preserveHeightSourceZ ?? this.sprite.pos.z;
101
+ if (this.sprite.zone.engine?.debug && !this.__preserveLog) {
102
+ this.__preserveLog = true;
103
+ debug('ChangeZone', 'preserveHeight applied for sprite', this.sprite.id, 'sourceZ=', this.preserveHeightSourceZ);
104
+ }
105
+ } else {
106
+ const zZone = (this.switchRenderZone ? this.toZone : this.fromZone).getHeight(hx, hy);
107
+ if (zLerp !== null) {
108
+ this.sprite.pos.z = zLerp;
109
+ } else {
110
+ this.sprite.pos.z = zZone;
111
+ }
112
+ if (this.sprite.zone.engine?.debug && this.__tickLogCount < 3) {
113
+ if (!this.__tickLogCount) this.__tickLogCount = 0;
114
+ this.__tickLogCount++;
115
+ debug('ChangeZone', 'tick sprite', this.sprite.id, 'frac=', frac.toFixed(2), 'hx,hy=', hx.toFixed(2), hy.toFixed(2), 'zLerp=', zLerp?.toFixed(2), 'zZone=', zZone?.toFixed(2), 'pos.z=', this.sprite.pos.z.toFixed(2));
116
+ }
117
+ }
118
+ }
119
+ // New Frame
120
+ let newFrame = Math.floor(frac * 4);
121
+ if (newFrame != this.sprite.animFrame) this.sprite.setFrame(newFrame);
122
+ // Move into the new zone
123
+ if (!this.sprite.zone.isInZone(this.sprite.pos.x, this.sprite.pos.y)) {
124
+ this.fromZone.removeSprite(this.sprite.id);
125
+ // Defer until aftertick to stop the sprite being ticked twice
126
+ this.sprite.zone.world.runAfterTick(
127
+ function () {
128
+ this.toZone.addSprite(this.sprite);
129
+ }.bind(this)
130
+ );
131
+ }
132
+
133
+ return time >= endTime;
134
+ },
135
+ };
@@ -0,0 +1,109 @@
1
+ /* *\
2
+ ** ----------------------------------------------- **
3
+ ** Calliope - Pixos Game Engine **
4
+ ** ----------------------------------------------- **
5
+ ** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
6
+ ** **
7
+ ** Any unauthorized distribution or transfer **
8
+ ** of this work is strictly prohibited. **
9
+ ** **
10
+ ** All Rights Reserved. **
11
+ ** ----------------------------------------------- **
12
+ \* */
13
+
14
+ /**
15
+ * @fileoverview Chat action for Pixos game engine.
16
+ * Handles dialogue input and display for sprites.
17
+ */
18
+
19
+ /**
20
+ * @typedef {object} ChatOptions
21
+ * @property {boolean} [autoclose=false] - Whether to auto-close the chat after a time.
22
+ * @property {number} [endTime] - Timestamp when to auto-close.
23
+ */
24
+
25
+ /**
26
+ * Chat action object for handling dialogue interactions.
27
+ */
28
+ export default {
29
+ /**
30
+ * Initializes the chat action.
31
+ * @param {string} prompt - The initial prompt text.
32
+ * @param {boolean} [scrolling=true] - Whether the text scrolls.
33
+ * @param {ChatOptions} [options={}] - Additional options.
34
+ */
35
+ init: function (prompt, scrolling = true, options = {}) {
36
+ /** @type {import('../core/index.js').default} */
37
+ this.engine = this.sprite.engine;
38
+ /** @type {string} */
39
+ this.text = '';
40
+ /** @type {string} */
41
+ this.prompt = prompt;
42
+ /** @type {boolean} */
43
+ this.scrolling = scrolling;
44
+ /** @type {number} */
45
+ this.line = 0;
46
+ /** @type {ChatOptions} */
47
+ this.options = options;
48
+ /** @type {boolean} */
49
+ this.completed = false;
50
+ /** @type {number} */
51
+ this.lastKey = new Date().getTime();
52
+ },
53
+
54
+ /**
55
+ * Updates and scrolls the chat.
56
+ * @param {number} time - The current time.
57
+ * @returns {boolean} True if the chat is completed.
58
+ */
59
+ tick: function (time) {
60
+ if (!this.loaded) return false;
61
+ if (this.options && this.options.autoclose) {
62
+ this.endTime = this.endTime ? this.endTime : this.options.endTime ?? new Date().getTime() + 10000;
63
+ if (time > this.endTime) {
64
+ this.completed = true;
65
+ }
66
+ }
67
+ this.checkInput(time);
68
+ this.textbox = this.engine.hud.scrollText(this.prompt + this.text, this.scrolling, this.options);
69
+ return this.completed;
70
+ },
71
+
72
+ /**
73
+ * Handles keyboard input for the chat.
74
+ * @param {number} time - The current time.
75
+ */
76
+ checkInput: function (time) {
77
+ if (time > this.lastKey + 100) {
78
+ let skipChar = false;
79
+ switch (this.engine.keyboard.lastPressedCode()) {
80
+ case 'Escape':
81
+ this.completed = true;
82
+ skipChar = true;
83
+ break;
84
+ case 'Backspace':
85
+ let arr = this.text.split('');
86
+ arr.pop();
87
+ this.text = arr.join('');
88
+ this.lastKey = time;
89
+ skipChar = true;
90
+ break;
91
+ case 'Enter':
92
+ this.sprite.setGreeting(this.text);
93
+ if (this.sprite.speech.clearHud) this.sprite.speech.clearHud();
94
+ this.speechbox = this.sprite.speech.scrollText(this.text);
95
+ this.sprite.speech.loadImage();
96
+ this.completed = true;
97
+ skipChar = true;
98
+ break;
99
+ }
100
+ if (!skipChar) {
101
+ let char = this.engine.keyboard.lastPressedKey();
102
+ if (char) {
103
+ this.lastKey = time;
104
+ this.text += '' + char;
105
+ }
106
+ }
107
+ }
108
+ },
109
+ };