canvasengine 1.3.0 → 2.0.0-beta.2

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 (96) hide show
  1. package/package.json +51 -17
  2. package/src/components/Canvas.ts +134 -0
  3. package/src/components/Container.ts +46 -0
  4. package/src/components/DisplayObject.ts +458 -0
  5. package/src/components/DrawMap/index.ts +65 -0
  6. package/src/components/Graphic.ts +147 -0
  7. package/src/components/NineSliceSprite.ts +46 -0
  8. package/src/components/ParticleEmitter.ts +39 -0
  9. package/src/components/Scene.ts +6 -0
  10. package/src/components/Sprite.ts +493 -0
  11. package/src/components/Text.ts +145 -0
  12. package/src/components/Tilemap/Tile.ts +79 -0
  13. package/src/components/Tilemap/TileGroup.ts +207 -0
  14. package/src/components/Tilemap/TileLayer.ts +163 -0
  15. package/src/components/Tilemap/TileSet.ts +41 -0
  16. package/src/components/Tilemap/index.ts +80 -0
  17. package/src/components/TilingSprite.ts +39 -0
  18. package/src/components/Viewport.ts +159 -0
  19. package/src/components/index.ts +13 -0
  20. package/src/components/types/DisplayObject.ts +69 -0
  21. package/src/components/types/MouseEvent.ts +3 -0
  22. package/src/components/types/Spritesheet.ts +389 -0
  23. package/src/components/types/index.ts +4 -0
  24. package/src/directives/Drag.ts +84 -0
  25. package/src/directives/KeyboardControls.ts +922 -0
  26. package/src/directives/Scheduler.ts +101 -0
  27. package/src/directives/Sound.ts +91 -0
  28. package/src/directives/Transition.ts +45 -0
  29. package/src/directives/ViewportCull.ts +40 -0
  30. package/src/directives/ViewportFollow.ts +26 -0
  31. package/src/directives/index.ts +7 -0
  32. package/src/engine/animation.ts +113 -0
  33. package/src/engine/bootstrap.ts +19 -0
  34. package/src/engine/directive.ts +23 -0
  35. package/src/engine/reactive.ts +379 -0
  36. package/src/engine/signal.ts +138 -0
  37. package/src/engine/trigger.ts +40 -0
  38. package/src/engine/utils.ts +135 -0
  39. package/src/hooks/addContext.ts +6 -0
  40. package/src/hooks/useProps.ts +155 -0
  41. package/src/hooks/useRef.ts +21 -0
  42. package/src/index.ts +13 -0
  43. package/src/utils/Ease.ts +33 -0
  44. package/src/utils/RadialGradient.ts +86 -0
  45. package/.gitattributes +0 -22
  46. package/.npmignore +0 -163
  47. package/canvasengine-1.3.0.all.min.js +0 -21
  48. package/canvasengine.js +0 -5802
  49. package/core/DB.js +0 -24
  50. package/core/ModelServer.js +0 -348
  51. package/core/Users.js +0 -190
  52. package/core/engine-common.js +0 -952
  53. package/doc/cocoonjs.md +0 -36
  54. package/doc/doc-lang.yml +0 -43
  55. package/doc/doc-router.yml +0 -14
  56. package/doc/doc-tuto.yml +0 -9
  57. package/doc/doc.yml +0 -39
  58. package/doc/element.md +0 -37
  59. package/doc/entity.md +0 -90
  60. package/doc/extend.md +0 -47
  61. package/doc/get_started.md +0 -19
  62. package/doc/images/entity.png +0 -0
  63. package/doc/multitouch.md +0 -58
  64. package/doc/nodejs.md +0 -142
  65. package/doc/scene.md +0 -44
  66. package/doc/text.md +0 -156
  67. package/examples/server/client.html +0 -31
  68. package/examples/server/server.js +0 -16
  69. package/examples/tiled_server/client.html +0 -52
  70. package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
  71. package/examples/tiled_server/server/map.json +0 -50
  72. package/examples/tiled_server/server/map.tmx +0 -16
  73. package/examples/tiled_server/server/server.js +0 -16
  74. package/extends/Animation.js +0 -910
  75. package/extends/Effect.js +0 -252
  76. package/extends/Gleed2d.js +0 -252
  77. package/extends/Hit.js +0 -1509
  78. package/extends/Input.js +0 -699
  79. package/extends/Marshal.js +0 -716
  80. package/extends/Scrolling.js +0 -388
  81. package/extends/Soundmanager2.js +0 -5466
  82. package/extends/Spritesheet.js +0 -196
  83. package/extends/Text.js +0 -366
  84. package/extends/Tiled.js +0 -403
  85. package/extends/Window.js +0 -575
  86. package/extends/gamepad.js +0 -397
  87. package/extends/socket.io.min.js +0 -2
  88. package/extends/swf/soundmanager2.swf +0 -0
  89. package/extends/swf/soundmanager2_debug.swf +0 -0
  90. package/extends/swf/soundmanager2_flash9.swf +0 -0
  91. package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
  92. package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
  93. package/extends/workers/transition.js +0 -43
  94. package/index.js +0 -46
  95. package/license.txt +0 -19
  96. package/readme.md +0 -483
@@ -0,0 +1,493 @@
1
+ import { computed, effect, isSignal, Signal, WritableSignal } from "@signe/reactive";
2
+ import {
3
+ Assets,
4
+ Container,
5
+ Sprite as PixiSprite,
6
+ Rectangle,
7
+ Texture,
8
+ } from "pixi.js";
9
+ import { Subscription } from "rxjs";
10
+ import {
11
+ Element,
12
+ createComponent,
13
+ registerComponent,
14
+ } from "../engine/reactive";
15
+ import { arrayEquals, isFunction } from "../engine/utils";
16
+ import { DisplayObject } from "./DisplayObject";
17
+ import {
18
+ AnimationFrames,
19
+ FrameOptions,
20
+ SpritesheetOptions,
21
+ TextureOptions,
22
+ TransformOptions,
23
+ } from "./types/Spritesheet";
24
+ import { ComponentFunction } from "../engine/signal";
25
+ import { DisplayObjectProps } from "./types/DisplayObject";
26
+ import { AnimatedSignal, isAnimatedSignal } from "../engine/animation";
27
+
28
+ const log = console.log;
29
+
30
+ type Image = { image: string };
31
+
32
+ type TextureOptionsMerging = TextureOptions & {
33
+ spriteWidth: number;
34
+ spriteHeight: number;
35
+ sound?: string;
36
+ } & Image &
37
+ TransformOptions;
38
+
39
+ type FrameOptionsMerging = TextureOptionsMerging & FrameOptions;
40
+ type SpritesheetOptionsMerging = TextureOptionsMerging & SpritesheetOptions;
41
+ type TransformOptionsAsArray = Pick<
42
+ TransformOptions,
43
+ "anchor" | "scale" | "skew" | "pivot"
44
+ >;
45
+
46
+ type AnimationDataFrames = {
47
+ sprites: FrameOptionsMerging[];
48
+ frames: Texture[][];
49
+ name: string;
50
+ animations: AnimationFrames;
51
+ params: any[];
52
+ data: TextureOptionsMerging;
53
+ };
54
+
55
+ export enum StandardAnimation {
56
+ Stand = "stand",
57
+ Walk = "walk",
58
+ }
59
+
60
+ export class CanvasSprite extends DisplayObject(PixiSprite) {
61
+ public hitbox: { w: number; h: number };
62
+ public applyTransform: (
63
+ frame: FrameOptionsMerging,
64
+ data: TextureOptionsMerging,
65
+ spritesheet: SpritesheetOptionsMerging
66
+ ) => Partial<FrameOptionsMerging>;
67
+ private spritesheet: SpritesheetOptionsMerging;
68
+ private currentAnimation: AnimationDataFrames | null = null;
69
+ private time: number = 0;
70
+ private frameIndex: number = 0;
71
+ private animations: Map<string, AnimationDataFrames> = new Map();
72
+ private subscriptionTick: Subscription;
73
+ private subscriptionSheet: Subscription[] = [];
74
+ private sheetParams: any = {};
75
+ private sheetCurrentAnimation: string = StandardAnimation.Stand;
76
+ onFinish: () => void;
77
+
78
+ private currentAnimationContainer: Container | null = null;
79
+
80
+ private async createTextures(
81
+ options: Required<TextureOptionsMerging>
82
+ ): Promise<Texture[][]> {
83
+ const { width, height, framesHeight, framesWidth, image, offset } = options;
84
+ const texture = await Assets.load(image);
85
+ const spriteWidth = options.spriteWidth;
86
+ const spriteHeight = options.spriteHeight;
87
+ const frames: Texture[][] = [];
88
+ const offsetX = (offset && offset.x) || 0;
89
+ const offsetY = (offset && offset.y) || 0;
90
+ for (let i = 0; i < framesHeight; i++) {
91
+ frames[i] = [];
92
+ for (let j = 0; j < framesWidth; j++) {
93
+ const rectX = j * spriteWidth + offsetX;
94
+ const rectY = i * spriteHeight + offsetY;
95
+ if (rectY > height) {
96
+ throw log(
97
+ `Warning, there is a problem with the height of the "${this.id}" spritesheet. When cutting into frames, the frame exceeds the height of the image.`
98
+ );
99
+ }
100
+ if (rectX > width) {
101
+ throw log(
102
+ `Warning, there is a problem with the width of the "${this.id}" spritesheet. When cutting into frames, the frame exceeds the width of the image.`
103
+ );
104
+ }
105
+ frames[i].push(
106
+ new Texture({
107
+ source: texture.source,
108
+ frame: new Rectangle(rectX, rectY, spriteWidth, spriteHeight),
109
+ })
110
+ );
111
+ }
112
+ }
113
+ return frames;
114
+ }
115
+
116
+ private async createAnimations() {
117
+ const { textures } = this.spritesheet;
118
+ if (!textures) {
119
+ return;
120
+ }
121
+ for (let animationName in textures) {
122
+ const props: (keyof TextureOptionsMerging)[] = [
123
+ "width",
124
+ "height",
125
+ "framesHeight",
126
+ "framesWidth",
127
+ "rectWidth",
128
+ "rectHeight",
129
+ "offset",
130
+ "image",
131
+ "sound",
132
+ ];
133
+ const parentObj = props.reduce(
134
+ (prev, val) => ({ ...prev, [val]: this.spritesheet[val] }),
135
+ {}
136
+ );
137
+ const optionsTextures: TextureOptionsMerging = {
138
+ ...parentObj,
139
+ ...textures[animationName],
140
+ } as any;
141
+ const {
142
+ rectWidth,
143
+ width = 0,
144
+ framesWidth = 1,
145
+ rectHeight,
146
+ height = 0,
147
+ framesHeight = 1,
148
+ } = optionsTextures;
149
+ optionsTextures.spriteWidth = rectWidth ? rectWidth : width / framesWidth;
150
+ optionsTextures.spriteHeight = rectHeight
151
+ ? rectHeight
152
+ : height / framesHeight;
153
+ this.animations.set(animationName, {
154
+ frames: await this.createTextures(
155
+ optionsTextures as Required<TextureOptionsMerging>
156
+ ),
157
+ name: animationName,
158
+ animations: textures[animationName].animations,
159
+ params: [],
160
+ data: optionsTextures,
161
+ sprites: [],
162
+ });
163
+ }
164
+ }
165
+
166
+ async onMount(params: Element<CanvasSprite>) {
167
+ const { props, propObservables } = params;
168
+ const tick: Signal = props.context.tick;
169
+ const sheet = props.sheet ?? {};
170
+ if (sheet?.onFinish) {
171
+ this.onFinish = sheet.onFinish;
172
+ }
173
+ this.subscriptionTick = tick.observable.subscribe((value) => {
174
+ this.update(value);
175
+ });
176
+ if (props.sheet?.definition) {
177
+ this.spritesheet = props.sheet.definition;
178
+ await this.createAnimations();
179
+ }
180
+ if (sheet.params) {
181
+ for (let key in propObservables?.sheet["params"]) {
182
+ const value = propObservables?.sheet["params"][key] as Signal;
183
+ if (isSignal(value)) {
184
+ this.subscriptionSheet.push(
185
+ value.observable.subscribe((value) => {
186
+ if (this.animations.size == 0) return;
187
+ this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
188
+ })
189
+ );
190
+ } else {
191
+ this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
192
+ }
193
+ }
194
+ }
195
+
196
+ const isMoving = computed(() => {
197
+ const { x, y } = propObservables ?? {};
198
+ if (!x || !y) return false;
199
+ const xSignal = x as AnimatedSignal<any>;
200
+ const ySignal = y as AnimatedSignal<any>;
201
+ const isMovingX =
202
+ isAnimatedSignal(xSignal) &&
203
+ xSignal.animatedState().current !== xSignal.animatedState().end;
204
+ const isMovingY =
205
+ isAnimatedSignal(ySignal) &&
206
+ ySignal.animatedState().current !== ySignal.animatedState().end;
207
+ return isMovingX || isMovingY;
208
+ });
209
+
210
+ effect(() => {
211
+ const _isMoving = isMoving();
212
+
213
+ if (!this.isMounted) return;
214
+
215
+ if (_isMoving) {
216
+ this.sheetCurrentAnimation = StandardAnimation.Walk;
217
+ } else {
218
+ this.sheetCurrentAnimation = StandardAnimation.Stand;
219
+ }
220
+
221
+ this.play(this.sheetCurrentAnimation, [this.sheetParams]);
222
+ });
223
+
224
+ super.onMount(params);
225
+ }
226
+
227
+ async onUpdate(props) {
228
+ super.onUpdate(props);
229
+
230
+ const sheet = props.sheet;
231
+ if (sheet?.params) this.sheetParams = sheet?.params;
232
+
233
+ if (sheet?.playing && this.isMounted) {
234
+ this.sheetCurrentAnimation = sheet?.playing;
235
+ this.play(this.sheetCurrentAnimation, [this.sheetParams]);
236
+ }
237
+
238
+ if (props.hitbox) this.hitbox = props.hitbox;
239
+
240
+ if (props.scaleMode) this.baseTexture.scaleMode = props.scaleMode;
241
+ else if (props.image && this.fullProps.rectangle === undefined) {
242
+ this.texture = await Assets.load(this.fullProps.image);
243
+ } else if (props.texture) {
244
+ this.texture = props.texture;
245
+ }
246
+
247
+ if (props.rectangle !== undefined) {
248
+ const { x, y, width, height } = props.rectangle?.value ?? props.rectangle;
249
+ const texture = await Assets.load(this.fullProps.image);
250
+ this.texture = new Texture({
251
+ source: texture.source,
252
+ frame: new Rectangle(x, y, width, height),
253
+ });
254
+ }
255
+ }
256
+
257
+ onDestroy(): void {
258
+ super.onDestroy();
259
+ this.subscriptionSheet.forEach((sub) => sub.unsubscribe());
260
+ this.subscriptionTick.unsubscribe();
261
+ if (this.currentAnimationContainer && this.parent instanceof Container) {
262
+ this.parent.removeChild(this.currentAnimationContainer);
263
+ }
264
+ }
265
+
266
+ has(name: string): boolean {
267
+ return this.animations.has(name);
268
+ }
269
+
270
+ get(name: string): AnimationDataFrames {
271
+ return this.animations.get(name) as AnimationDataFrames;
272
+ }
273
+
274
+ isPlaying(name?: string): boolean {
275
+ if (!name) return !!this.currentAnimation;
276
+ if (this.currentAnimation == null) return false;
277
+ return this.currentAnimation.name == name;
278
+ }
279
+
280
+ stop() {
281
+ this.currentAnimation = null;
282
+ this.destroy();
283
+ }
284
+
285
+ play(name: string, params: any[] = []) {
286
+ const animParams = this.currentAnimation?.params;
287
+
288
+ if (this.isPlaying(name) && arrayEquals(params, animParams || [])) return;
289
+
290
+ const animation = this.get(name);
291
+
292
+ if (!animation) {
293
+ throw new Error(
294
+ `Impossible to play the ${name} animation because it doesn't exist on the "${this.id}" spritesheet`
295
+ );
296
+ }
297
+
298
+ const cloneParams = structuredClone(params);
299
+
300
+ this.removeChildren();
301
+ animation.sprites = [];
302
+ this.currentAnimation = animation;
303
+ this.currentAnimation.params = cloneParams;
304
+ this.time = 0;
305
+ this.frameIndex = 0;
306
+ let animations: any = animation.animations;
307
+ animations = isFunction(animations)
308
+ ? (animations as Function)(...cloneParams)
309
+ : animations;
310
+
311
+ this.currentAnimationContainer = new Container();
312
+
313
+ for (let container of animations as FrameOptionsMerging[][]) {
314
+ const sprite = new PixiSprite();
315
+ for (let frame of container) {
316
+ this.currentAnimation.sprites.push(frame);
317
+ }
318
+ this.currentAnimationContainer.addChild(sprite);
319
+ }
320
+
321
+ const sound = this.currentAnimation.data.sound;
322
+
323
+ if (sound) {
324
+ //RpgSound.get(sound).play()
325
+ }
326
+
327
+ // Updates immediately to avoid flickering
328
+ this.update({
329
+ deltaRatio: 1,
330
+ });
331
+ }
332
+
333
+ update({ deltaRatio }) {
334
+ if (
335
+ !this.isPlaying() ||
336
+ !this.currentAnimation ||
337
+ !this.currentAnimationContainer
338
+ )
339
+ return;
340
+
341
+ const self = this;
342
+ const { frames, sprites, data } = this.currentAnimation;
343
+ let frame = sprites[this.frameIndex];
344
+ const nextFrame = sprites[this.frameIndex + 1];
345
+
346
+ for (let _sprite of this.currentAnimationContainer.children) {
347
+ const sprite = _sprite as PixiSprite;
348
+
349
+ if (!frame || frame.frameY == undefined || frame.frameX == undefined) {
350
+ continue;
351
+ }
352
+
353
+ this.texture = frames[frame.frameY][frame.frameX];
354
+
355
+ const getVal = <T extends keyof TransformOptions>(
356
+ prop: T
357
+ ): TransformOptions[T] | undefined => {
358
+ return frame[prop] ?? data[prop] ?? this.spritesheet[prop];
359
+ };
360
+
361
+ const applyTransform = <T extends keyof TransformOptionsAsArray>(
362
+ prop: T
363
+ ): void => {
364
+ const val = getVal<T>(prop);
365
+ if (val) {
366
+ this[prop as string].set(...val!);
367
+ }
368
+ };
369
+
370
+ function applyTransformValue<T extends keyof TransformOptions>(prop: T);
371
+ function applyTransformValue<T extends keyof TransformOptions>(
372
+ prop: string,
373
+ alias: T
374
+ );
375
+ function applyTransformValue<T extends keyof TransformOptions>(
376
+ prop: T,
377
+ alias?: T
378
+ ): void {
379
+ const optionProp = alias || prop;
380
+ const val = getVal<T>(optionProp);
381
+ if (val !== undefined) {
382
+ self[prop as string] = val;
383
+ }
384
+ }
385
+
386
+ if (this.applyTransform) {
387
+ frame = {
388
+ ...frame,
389
+ ...this.applyTransform(frame, data, this.spritesheet),
390
+ };
391
+ }
392
+
393
+ const realSize = getVal<"spriteRealSize">("spriteRealSize");
394
+ const heightOfSprite =
395
+ typeof realSize == "number" ? realSize : realSize?.height;
396
+ const widthOfSprite =
397
+ typeof realSize == "number" ? realSize : realSize?.width;
398
+
399
+ const applyAnchorBySize = () => {
400
+ if (heightOfSprite && this.hitbox) {
401
+ const { spriteWidth, spriteHeight } = data;
402
+ const w = (spriteWidth - this.hitbox.w) / 2 / spriteWidth;
403
+ const gap = (spriteHeight - heightOfSprite) / 2;
404
+ const h = (spriteHeight - this.hitbox.h - gap) / spriteHeight;
405
+ this.anchor.set(w, h);
406
+ }
407
+ };
408
+
409
+ if (frame.sound) {
410
+ //RpgSound.get(frame.sound).play()
411
+ }
412
+
413
+ applyAnchorBySize();
414
+
415
+ applyTransform("anchor");
416
+ applyTransform("scale");
417
+ applyTransform("skew");
418
+ applyTransform("pivot");
419
+
420
+ applyTransformValue("alpha", "opacity");
421
+ applyTransformValue("x");
422
+ applyTransformValue("y");
423
+ applyTransformValue("angle");
424
+ applyTransformValue("rotation");
425
+ applyTransformValue("visible");
426
+ }
427
+
428
+ if (!nextFrame) {
429
+ this.time = 0;
430
+ this.frameIndex = 0;
431
+ if (this.onFinish && sprites.length > 1) this.onFinish();
432
+ return;
433
+ }
434
+
435
+ this.time += deltaRatio ?? 1;
436
+
437
+ if (this.time >= nextFrame.time) {
438
+ this.frameIndex++;
439
+ }
440
+ }
441
+ }
442
+
443
+ export interface CanvasSprite extends PixiSprite {}
444
+
445
+ registerComponent("Sprite", CanvasSprite);
446
+
447
+ // Define the props interface for Sprite
448
+ export interface SpriteProps extends DisplayObjectProps {
449
+ sheet?: {
450
+ definition?: SpritesheetOptionsMerging;
451
+ playing?: string;
452
+ params?: any;
453
+ onFinish?: () => void;
454
+ };
455
+ scaleMode?: number;
456
+ image?: string;
457
+ rectangle?: {
458
+ x: number;
459
+ y: number;
460
+ width: number;
461
+ height: number;
462
+ };
463
+ context?: {
464
+ tick: Signal;
465
+ };
466
+ }
467
+
468
+ export interface SpritePropsWithImage extends Omit<SpriteProps, "sheet"> {
469
+ image: string;
470
+ rectangle?: {
471
+ x: number;
472
+ y: number;
473
+ width: number;
474
+ height: number;
475
+ };
476
+ }
477
+
478
+ export interface SpritePropsWithSheet
479
+ extends Omit<SpriteProps, "image" | "rectangle"> {
480
+ sheet: {
481
+ definition: SpritesheetOptionsMerging;
482
+ playing?: string;
483
+ params?: any;
484
+ onFinish?: () => void;
485
+ };
486
+ }
487
+
488
+ export type SpritePropTypes = SpritePropsWithImage | SpritePropsWithSheet;
489
+
490
+ // Update the Sprite function to use the props interface
491
+ export const Sprite: ComponentFunction<SpritePropTypes> = (props) => {
492
+ return createComponent("Sprite", props);
493
+ };
@@ -0,0 +1,145 @@
1
+ import { Text as PixiText, TextStyle } from "pixi.js";
2
+ import { createComponent, registerComponent } from "../engine/reactive";
3
+ import { DisplayObject } from "./DisplayObject";
4
+ import { DisplayObjectProps } from "./types/DisplayObject";
5
+ import { Signal } from "@signe/reactive";
6
+ import { on } from "../engine/trigger";
7
+
8
+ enum TextEffect {
9
+ Typewriter = "typewriter",
10
+ }
11
+
12
+ interface TextProps extends DisplayObjectProps {
13
+ text?: string;
14
+ style?: Partial<TextStyle>;
15
+ color?: string;
16
+ size?: string;
17
+ fontFamily?: string;
18
+ typewriter?: {
19
+ speed?: number;
20
+ start?: () => void;
21
+ onComplete?: () => void;
22
+ skip?: () => void;
23
+ };
24
+ }
25
+
26
+ class CanvasText extends DisplayObject(PixiText) {
27
+ private subscriptionTick: any;
28
+ private fullText: string = "";
29
+ private currentIndex: number = 0;
30
+ private typewriterSpeed: number = 1; // Default speed
31
+ private _wordWrapWidth: number = 0;
32
+ private typewriterOptions: any = {};
33
+ private skipSignal?: () => void;
34
+
35
+ onMount(args) {
36
+ super.onMount(args);
37
+ const { props } = args;
38
+ const tick: Signal = props.context.tick;
39
+
40
+ if (props.text && props.typewriter) {
41
+ this.fullText = props.text;
42
+ this.text = "";
43
+ this.currentIndex = 0;
44
+ // Set typewriter options
45
+ if (props.typewriter) {
46
+ this.typewriterOptions = props.typewriter;
47
+ if (this.typewriterOptions.skip) {
48
+ on(this.typewriterOptions.skip, () => {
49
+ this.skipTypewriter();
50
+ });
51
+ }
52
+ }
53
+ }
54
+ this.subscriptionTick = tick.observable.subscribe(() => {
55
+ if (props.typewriter) {
56
+ this.typewriterEffect();
57
+ }
58
+ });
59
+ }
60
+
61
+ onUpdate(props: TextProps) {
62
+ super.onUpdate(props);
63
+ if (props.typewriter) {
64
+ if (props.typewriter) {
65
+ this.typewriterOptions = props.typewriter;
66
+ }
67
+ }
68
+ if (props.text) {
69
+ this.text = props.text;
70
+ }
71
+ if (props.text !== undefined && props.text !== this.fullText && this.fullProps.typewriter) {
72
+ this.text = "";
73
+ this.currentIndex = 0;
74
+ this.fullText = props.text;
75
+ }
76
+ if (props.style) {
77
+ for (const key in props.style) {
78
+ this.style[key] = props.style[key];
79
+ }
80
+ if (props.style.wordWrapWidth) {
81
+ this._wordWrapWidth = props.style.wordWrapWidth;
82
+ }
83
+ }
84
+ if (props.color) {
85
+ this.style.fill = props.color;
86
+ }
87
+ if (props.size) {
88
+ this.style.fontSize = props.size;
89
+ }
90
+ if (props.fontFamily) {
91
+ this.style.fontFamily = props.fontFamily;
92
+ }
93
+ if (this._wordWrapWidth) {
94
+ this.setWidth(this._wordWrapWidth);
95
+ } else {
96
+ this.setWidth(this.width);
97
+ }
98
+ this.setHeight(this.height);
99
+ }
100
+
101
+ get onCompleteCallback() {
102
+ return this.typewriterOptions.onComplete;
103
+ }
104
+
105
+ private typewriterEffect() {
106
+ if (this.currentIndex < this.fullText.length) {
107
+ const nextIndex = Math.min(
108
+ this.currentIndex + (this.typewriterOptions.speed ?? 1),
109
+ this.fullText.length
110
+ );
111
+ this.text = this.fullText.slice(0, nextIndex);
112
+ this.currentIndex = nextIndex;
113
+
114
+ // Check if typewriter effect is complete
115
+ if (
116
+ this.currentIndex === this.fullText.length &&
117
+ this.onCompleteCallback
118
+ ) {
119
+ this.onCompleteCallback();
120
+ }
121
+ }
122
+ }
123
+
124
+ // Add a method to skip the typewriter effect
125
+ private skipTypewriter() {
126
+ if (this.skipSignal) {
127
+ this.skipSignal();
128
+ }
129
+ this.text = this.fullText;
130
+ this.currentIndex = this.fullText.length;
131
+ }
132
+
133
+ onDestroy(): void {
134
+ super.onDestroy();
135
+ this.subscriptionTick.unsubscribe();
136
+ }
137
+ }
138
+
139
+ interface CanvasText extends PixiText {}
140
+
141
+ registerComponent("Text", CanvasText);
142
+
143
+ export function Text(props: TextProps) {
144
+ return createComponent("Text", props);
145
+ }
@@ -0,0 +1,79 @@
1
+ import { CompositeTilemap } from "@pixi/tilemap";
2
+ import { Tile as TiledTileClass } from '@rpgjs/tiled';
3
+ import { AnimatedSprite, Texture, groupD8 } from "pixi.js";
4
+ import { TileSet } from "./TileSet";
5
+
6
+ export class Tile extends AnimatedSprite {
7
+ static getTextures(tile: TiledTileClass, tileSet: TileSet) {
8
+ const textures: Texture[] = [];
9
+
10
+ if (tile.animations && tile.animations.length) {
11
+ tile.animations.forEach(frame => {
12
+ textures.push(tileSet.textures[frame.tileid])
13
+ });
14
+ } else {
15
+ textures.push(tileSet.textures[tile.gid - tileSet.firstgid])
16
+ }
17
+
18
+ return textures;
19
+ }
20
+
21
+ animations: { tileid: number, duration: number }[] = []
22
+ _x: number = 0
23
+ _y: number = 0
24
+ pointsBufIndex: number
25
+ properties: any = {}
26
+
27
+ constructor(
28
+ private tile: TiledTileClass,
29
+ private tileSet: TileSet
30
+ ) {
31
+ super(Tile.getTextures(tile, tileSet));
32
+ this.animations = tile.animations || []
33
+ this.properties = tile.properties
34
+ this.textures = Tile.getTextures(tile, tileSet)
35
+ this.texture = this.textures[0] as Texture
36
+ this.flip()
37
+ }
38
+
39
+ get gid() {
40
+ return this.tile.gid
41
+ }
42
+
43
+ setAnimation(frame: CompositeTilemap) {
44
+ const size = this.animations.length
45
+ if (size > 1) {
46
+ const offset = (this.animations[1].tileid - this.animations[0].tileid) * this.width
47
+ frame.tileAnimX(offset, size)
48
+ }
49
+ }
50
+
51
+ flip() {
52
+ let symmetry
53
+ let i = 0
54
+ const add = (symmetrySecond) => {
55
+ i++
56
+ if (symmetry) symmetry = groupD8.add(symmetry, symmetrySecond)
57
+ else symmetry = symmetrySecond
58
+ }
59
+
60
+ if (this.tile.horizontalFlip) {
61
+ add(groupD8.MIRROR_HORIZONTAL)
62
+ }
63
+
64
+ if (this.tile.verticalFlip) {
65
+ add(groupD8.MIRROR_VERTICAL)
66
+ }
67
+
68
+ if (this.tile.diagonalFlip) {
69
+ if (i % 2 == 0) {
70
+ add(groupD8.MAIN_DIAGONAL)
71
+ }
72
+ else {
73
+ add(groupD8.REVERSE_DIAGONAL)
74
+ }
75
+ }
76
+
77
+ //if (symmetry) this.texture.rotate = symmetry
78
+ }
79
+ }