@rpgjs/common 3.1.0 → 3.2.0

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.
@@ -0,0 +1,828 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.AbstractObject = void 0;
16
+ const Utils_1 = require("./Utils");
17
+ const Hit_1 = require("./Hit");
18
+ const Shape_1 = require("./Shape");
19
+ const sat_1 = __importDefault(require("sat"));
20
+ const Map_1 = require("./Map");
21
+ const Plugin_1 = require("./Plugin");
22
+ const Game_1 = require("./Game");
23
+ const Vector2d_1 = require("./Vector2d");
24
+ const types_1 = require("@rpgjs/types");
25
+ const rxjs_1 = require("rxjs");
26
+ const ACTIONS = { IDLE: 0, RUN: 1, ACTION: 2 };
27
+ class AbstractObject {
28
+ constructor(gameEngine, playerId) {
29
+ this.gameEngine = gameEngine;
30
+ this.playerId = playerId;
31
+ this.map = '';
32
+ this.height = 0;
33
+ this.width = 0;
34
+ this.direction = 3;
35
+ /*
36
+ Properties for move mode
37
+ */
38
+ this.checkCollision = true;
39
+ this.clientModeMove = types_1.MoveClientMode.ByDirection;
40
+ this.behavior = types_1.Behavior.Direction;
41
+ this.inShapes = {};
42
+ this.disableVirtualGrid = false;
43
+ this.shapes = [];
44
+ this.collisionWith = [];
45
+ this._collisionWithTiles = [];
46
+ this._collisionWithShapes = [];
47
+ this.destroyMove$ = new rxjs_1.Subject();
48
+ this._hitboxPos = new sat_1.default.Vector(0, 0);
49
+ this.setHitbox(this.width, this.height);
50
+ this.position = { x: 0, y: 0, z: 0 };
51
+ }
52
+ static get ACTIONS() {
53
+ return ACTIONS;
54
+ }
55
+ get id() {
56
+ return this.playerId;
57
+ }
58
+ set id(str) {
59
+ this.playerId = str;
60
+ }
61
+ updateInVirtualGrid() {
62
+ const map = this.mapInstance;
63
+ if (map && !this.disableVirtualGrid /*&& this.gameEngine.isWorker TODO */) {
64
+ map.grid.insertInCells(this.id, this.getSizeMaxShape());
65
+ }
66
+ }
67
+ get canMove() {
68
+ return this.clientModeMove == types_1.MoveClientMode.ByDirection;
69
+ }
70
+ set canMove(val) {
71
+ this.clientModeMove = val ? types_1.MoveClientMode.ByDirection : types_1.MoveClientMode.Disabled;
72
+ }
73
+ /**
74
+ * Get/Set position x, y and z of player
75
+ *
76
+ * z is the depth layer. By default, its value is 0. Collisions and overlays will be performed with other objects on the same z-position.
77
+ *
78
+ * @title Get/Set position
79
+ * @prop { { x: number, y: number, z: number } } position
80
+ * @memberof Player
81
+ */
82
+ set position(val) {
83
+ const { x, y, z } = val;
84
+ if (!Utils_1.isInstanceOf(val, Vector2d_1.Vector2d)) {
85
+ val = new Vector2d_1.Vector2d(x, y, z);
86
+ }
87
+ this._hitboxPos.x = x;
88
+ this._hitboxPos.y = y;
89
+ this._hitboxPos.z = z;
90
+ this.updateInVirtualGrid();
91
+ this._position = new Proxy(val, {
92
+ get: (target, prop) => target[prop],
93
+ set: (target, prop, value) => {
94
+ this._hitboxPos[prop] = value;
95
+ target[prop] = value;
96
+ return true;
97
+ }
98
+ });
99
+ }
100
+ get position() {
101
+ return this._position;
102
+ }
103
+ get worldPositionX() {
104
+ let x = this.position.x;
105
+ if (this.mapInstance) {
106
+ x += this.mapInstance.worldX;
107
+ }
108
+ return x;
109
+ }
110
+ get worldPositionY() {
111
+ let y = this.position.y;
112
+ if (this.mapInstance) {
113
+ y += this.mapInstance.worldY;
114
+ }
115
+ return y;
116
+ }
117
+ set posX(val) {
118
+ this.position.x = val;
119
+ }
120
+ set posY(val) {
121
+ this.position.y = val;
122
+ }
123
+ set posZ(val) {
124
+ this.position.z = val;
125
+ }
126
+ /** @internal */
127
+ get mapInstance() {
128
+ if (this.gameEngine.side == Game_1.GameSide.Client) {
129
+ return Map_1.RpgCommonMap.bufferClient.get(this.map);
130
+ }
131
+ return Map_1.RpgCommonMap.buffer.get(this.map);
132
+ }
133
+ /**
134
+ *
135
+ * Recovers all the colliding shapes of the current player
136
+ *
137
+ * @title Get Collision of shapes
138
+ * @since 3.2.0
139
+ * @readonly
140
+ * @prop { RpgShape[] } shapes
141
+ * @memberof Player
142
+ * @memberof RpgSpriteLogic
143
+ */
144
+ get shapesCollision() {
145
+ return this._collisionWithShapes;
146
+ }
147
+ /**
148
+ *
149
+ * Recovers all the colliding tiles of the current player
150
+ *
151
+ * @title Get Collision of tiles
152
+ * @since 3.0.0-beta.4
153
+ * @readonly
154
+ * @prop { TileInfo[] } tiles
155
+ * @memberof Player
156
+ * @memberof RpgSpriteLogic
157
+ */
158
+ get tilesCollision() {
159
+ return this._collisionWithTiles;
160
+ }
161
+ /**
162
+ *
163
+ * Recovers all other players and events colliding with the current player's hitbox
164
+ *
165
+ * @title Get Collision of other players/events
166
+ * @since 3.0.0-beta.4
167
+ * @readonly
168
+ * @prop { (RpgPlayer | RpgEvent)[] } otherPlayersCollision
169
+ * @memberof Player
170
+ * @memberof RpgSpriteLogic
171
+ */
172
+ get otherPlayersCollision() {
173
+ return this.collisionWith;
174
+ }
175
+ /**
176
+ * Define the size of the player. You can set the hitbox for collisions
177
+ *
178
+ * ```ts
179
+ * player.setSizes({
180
+ * width: 32,
181
+ * height: 32
182
+ * })
183
+ * ```
184
+ *
185
+ * and with hitbox:
186
+ *
187
+ * ```ts
188
+ * player.setSizes({
189
+ * width: 32,
190
+ * height: 32,
191
+ * hitbox: {
192
+ * width: 20,
193
+ * height: 20
194
+ * }
195
+ * })
196
+ * ```
197
+ *
198
+ * @title Set Sizes
199
+ * @method player.setSizes(key,value)
200
+ * @param { { width: number, height: number, hitbox?: { width: number, height: number } } } obj
201
+ * @deprecated
202
+ * @returns {void}
203
+ * @memberof Player
204
+ */
205
+ setSizes(obj) {
206
+ this.width = obj.width;
207
+ this.height = obj.height;
208
+ if (obj.hitbox) {
209
+ this.hitbox = new sat_1.default.Box(this._hitboxPos, obj.hitbox.width, obj.hitbox.height);
210
+ }
211
+ }
212
+ /**
213
+ * Define the hitbox of the player.
214
+ *
215
+ * ```ts
216
+ * player.setHitbox(20, 20)
217
+ * ```
218
+ *
219
+ * @title Set Hitbox
220
+ * @method player.setHitbox(width,height)
221
+ * @param {number} width
222
+ * @param {number} height
223
+ * @returns {void}
224
+ * @memberof Player
225
+ */
226
+ setHitbox(width, height) {
227
+ const map = this.mapInstance;
228
+ if (map) {
229
+ this.width = map.tileWidth;
230
+ this.height = map.tileHeight;
231
+ }
232
+ this.hitbox = new sat_1.default.Box(this._hitboxPos, width, height);
233
+ this.wHitbox = width;
234
+ this.hHitbox = height;
235
+ this.updateInVirtualGrid();
236
+ }
237
+ set wHitbox(val) {
238
+ this.hitbox.w = val;
239
+ }
240
+ set hHitbox(val) {
241
+ this.hitbox.h = val;
242
+ }
243
+ get wHitbox() {
244
+ return this.hitbox.w;
245
+ }
246
+ get hHitbox() {
247
+ return this.hitbox.h;
248
+ }
249
+ directionToAngle(direction) {
250
+ const angle = (direction < 2 ? +direction + 2 : direction - 2) * 90;
251
+ return Utils_1.toRadians(angle);
252
+ }
253
+ /** @internal */
254
+ defineNextPosition(direction, deltaTimeInt) {
255
+ const angle = this.directionToAngle(direction);
256
+ const computePosition = (prop) => {
257
+ return this.position[prop] + this.speed * deltaTimeInt
258
+ * (Math.round(Math[prop == 'x' ? 'cos' : 'sin'](angle) * 100) / 100);
259
+ };
260
+ return new Vector2d_1.Vector2d(~~computePosition('x'), ~~computePosition('y'), ~~this.position.z);
261
+ }
262
+ /** @internal */
263
+ setPosition({ x, y, tileX, tileY }, move = true) {
264
+ const { tileWidth, tileHeight } = this.mapInstance;
265
+ if (x !== undefined)
266
+ this.posX = x;
267
+ if (y !== undefined)
268
+ this.posY = y;
269
+ if (tileX !== undefined)
270
+ this.posX = tileX * tileWidth;
271
+ if (tileY !== undefined)
272
+ this.posY = tileY * tileHeight;
273
+ }
274
+ /** @internal */
275
+ triggerCollisionWith(type) {
276
+ for (let collisionWith of this.collisionWith) {
277
+ if (collisionWith instanceof Shape_1.RpgShape) {
278
+ const goMap = collisionWith.getProperty('go-map');
279
+ if (goMap && 'changeMap' in this)
280
+ this.changeMap(goMap);
281
+ }
282
+ else {
283
+ if (type == AbstractObject.ACTIONS.ACTION) {
284
+ if ('onAction' in collisionWith)
285
+ collisionWith.execMethod('onAction', [this]);
286
+ }
287
+ else if ('onPlayerTouch' in collisionWith)
288
+ collisionWith.execMethod('onPlayerTouch', [this]);
289
+ }
290
+ }
291
+ }
292
+ /** @internal */
293
+ zCollision(other) {
294
+ const z = this.position.z;
295
+ const otherZ = other.position.z;
296
+ return Utils_1.intersection([z, z + this.height], [otherZ, otherZ + other.height]);
297
+ }
298
+ /** @internal */
299
+ moveByDirection(direction, deltaTimeInt) {
300
+ const nextPosition = this.defineNextPosition(direction, deltaTimeInt);
301
+ return this.move(nextPosition);
302
+ }
303
+ /**
304
+ * Retrieves a tile and checks if the player has a collision
305
+ *
306
+ * ```ts
307
+ * const tileInfo = player.getTile(20, 30)
308
+ * console.log(tileInfo)
309
+ * ```
310
+ *
311
+ * Example of returns:
312
+ *
313
+ ```ts
314
+ {
315
+ tiles: [
316
+ {
317
+ id: 0,
318
+ terrain: [],
319
+ probability: null,
320
+ properties: [Object],
321
+ animations: [],
322
+ objectGroups: [],
323
+ image: null,
324
+ gid: 1
325
+ }
326
+ ],
327
+ hasCollision: false,
328
+ isOverlay: undefined,
329
+ objectGroups: [],
330
+ isClimbable: undefined,
331
+ tileIndex: 93
332
+ }
333
+ ```
334
+ *
335
+ * @title Get Tile
336
+ * @since 3.0.0-beta.4
337
+ * @method player.getTile(x,y,z?)
338
+ * @param {number} x
339
+ * @param {number} y
340
+ * @param {number} [z]
341
+ * @returns {object}
342
+ * @memberof Player
343
+ * @memberof RpgSpriteLogic
344
+ */
345
+ getTile(x, y, z = 0, hitbox) {
346
+ const map = this.mapInstance;
347
+ return map.getTile(hitbox || this.hitbox, x, y, [z, this.height]);
348
+ }
349
+ collisionObjects(playerSizeBox, hitbox, triggers) {
350
+ return __awaiter(this, void 0, void 0, function* () {
351
+ const map = this.mapInstance;
352
+ if (!map)
353
+ return true;
354
+ const events = this.gameEngine.world.getObjectsOfGroup(this.map, this);
355
+ const objects = map.grid.getObjectsByBox(playerSizeBox);
356
+ let boolFound = false;
357
+ for (let objectId of objects) {
358
+ // client side: read "object" propertie
359
+ if (!events[objectId])
360
+ continue;
361
+ const event = events[objectId]['object'] || events[objectId];
362
+ if (event.id == this.id)
363
+ continue;
364
+ if (!this.zCollision(event))
365
+ continue;
366
+ const collided = Hit_1.Hit.testPolyCollision(Hit_1.HitType.Box, hitbox, event.hitbox);
367
+ for (let shape of this.shapes) {
368
+ yield this.collisionWithShape(shape, event);
369
+ }
370
+ for (let shape of event.shapes) {
371
+ yield event.collisionWithShape(shape, this);
372
+ }
373
+ if (triggers === null || triggers === void 0 ? void 0 : triggers.near)
374
+ triggers.near(event);
375
+ if (collided) {
376
+ this.collisionWith.push(event);
377
+ this.triggerCollisionWith();
378
+ let throughOtherPlayer = false;
379
+ if (event.type == types_1.PlayerType.Player && this.type == types_1.PlayerType.Player) {
380
+ if (!(event.throughOtherPlayer || this.throughOtherPlayer)) {
381
+ boolFound = true;
382
+ if (!(triggers === null || triggers === void 0 ? void 0 : triggers.allSearch))
383
+ return true;
384
+ }
385
+ else {
386
+ throughOtherPlayer = true;
387
+ }
388
+ }
389
+ if (!throughOtherPlayer && (!(event.through || this.through))) {
390
+ boolFound = true;
391
+ if (!(triggers === null || triggers === void 0 ? void 0 : triggers.allSearch))
392
+ return true;
393
+ }
394
+ }
395
+ if (boolFound) {
396
+ if (triggers === null || triggers === void 0 ? void 0 : triggers.collision)
397
+ triggers.collision(event);
398
+ }
399
+ }
400
+ return boolFound;
401
+ });
402
+ }
403
+ /** @internal */
404
+ collisionWithShape(shape, player, nextPosition) {
405
+ return __awaiter(this, void 0, void 0, function* () {
406
+ const collision = shape.hasCollision;
407
+ const z = shape.z;
408
+ if (shape.isShapePosition())
409
+ return false;
410
+ if (z !== undefined && !this.zCollision({
411
+ position: { z },
412
+ height: this.mapInstance.zTileHeight
413
+ })) {
414
+ return false;
415
+ }
416
+ let position;
417
+ let { hitbox } = player;
418
+ if (nextPosition) {
419
+ position = nextPosition.copy();
420
+ }
421
+ else {
422
+ position = player.position.copy();
423
+ }
424
+ const hitboxObj = Hit_1.Hit.createObjectHitbox(position.x, position.y, position.z, hitbox.w, hitbox.h);
425
+ let collided = Hit_1.Hit.testPolyCollision(shape.type, hitboxObj, shape.hitbox);
426
+ if (collided) {
427
+ this._collisionWithShapes.push(shape);
428
+ // TODO: in shape after map load
429
+ if (!collision)
430
+ yield shape.in(this);
431
+ this.triggerCollisionWith();
432
+ if (collision)
433
+ return true;
434
+ }
435
+ else {
436
+ yield shape.out(this);
437
+ }
438
+ return false;
439
+ });
440
+ }
441
+ collisionShapes(playerSizeBox, nextPosition, triggers) {
442
+ return __awaiter(this, void 0, void 0, function* () {
443
+ const map = this.mapInstance;
444
+ if (!map)
445
+ return false;
446
+ const shapes = this.gameEngine.world.getShapesOfGroup(this.map);
447
+ const shapesInGrid = this.gameEngine.side == Game_1.GameSide.Client
448
+ ? new Set(Object.keys(shapes))
449
+ : map.gridShapes.getObjectsByBox(playerSizeBox);
450
+ let boolFound = false;
451
+ for (let shapeId of shapesInGrid) {
452
+ const shape = shapes[shapeId]['object'] || shapes[shapeId];
453
+ if (triggers === null || triggers === void 0 ? void 0 : triggers.near)
454
+ triggers.near(shape);
455
+ const bool = yield this.collisionWithShape(shape, this, nextPosition);
456
+ if (bool) {
457
+ if (triggers === null || triggers === void 0 ? void 0 : triggers.collision)
458
+ triggers.collision(shape);
459
+ boolFound = true;
460
+ if (!(triggers === null || triggers === void 0 ? void 0 : triggers.allSearch))
461
+ return true;
462
+ }
463
+ }
464
+ return boolFound;
465
+ });
466
+ }
467
+ computeNextPositionByTarget(nextPosition, target) {
468
+ return __awaiter(this, void 0, void 0, function* () {
469
+ const pullDistance = target.distanceWith(nextPosition);
470
+ if (pullDistance <= this.speed) {
471
+ return nextPosition.set(target);
472
+ }
473
+ const pull = (target.copy().subtract(nextPosition)).multiply((1 / pullDistance));
474
+ const totalPush = new Vector2d_1.Vector2dZero();
475
+ let contenders = 0;
476
+ const hitbox = Hit_1.Hit.createObjectHitbox(nextPosition.x, nextPosition.y, nextPosition.z, this.hitbox.w, this.hitbox.h);
477
+ const createObstacle = function (x, y, radius) {
478
+ const obstacle = new Vector2d_1.Vector2d(x, y);
479
+ let push = nextPosition.copy().subtract(obstacle);
480
+ let distance = (nextPosition.distanceWith(obstacle) - radius) - radius;
481
+ if (distance < radius * 2 * 10) {
482
+ ++contenders;
483
+ if (distance < 0.0001)
484
+ distance = 0.0001; // avoid div by 0
485
+ let weight = 1 / distance;
486
+ totalPush.add(push.multiply(weight));
487
+ }
488
+ return obstacle;
489
+ };
490
+ const area = this.mapInstance.tileheight * 2;
491
+ this.mapInstance.gridTiles.getCells({
492
+ minX: nextPosition.x - area,
493
+ maxX: nextPosition.x + area,
494
+ minY: nextPosition.y - area,
495
+ maxY: nextPosition.y + area
496
+ }, (index) => {
497
+ if (index < 0)
498
+ return;
499
+ const pos = this.mapInstance.getTilePosition(index);
500
+ const hitbox = Hit_1.Hit.createObjectHitbox(pos.x, pos.y, nextPosition.z, this.hitbox.w, this.hitbox.h);
501
+ const radius = this.mapInstance.tilewidth / 2;
502
+ const tile = this.getTile(pos.x, pos.y, nextPosition.z, hitbox);
503
+ if (tile.hasCollision) {
504
+ createObstacle(pos.x, pos.y, radius);
505
+ }
506
+ });
507
+ const playerSizeBox = this.getSizeMaxShape(nextPosition.x, nextPosition.y);
508
+ yield this.collisionObjects(playerSizeBox, hitbox, {
509
+ collision: (event) => {
510
+ const { x, y } = event.position;
511
+ createObstacle(x, y, event.hitbox.w);
512
+ },
513
+ allSearch: true
514
+ });
515
+ yield this.collisionShapes(playerSizeBox, nextPosition, {
516
+ collision: (shape) => {
517
+ const { x, y } = shape.position;
518
+ createObstacle(x, y, shape.hitbox.w);
519
+ },
520
+ allSearch: true
521
+ });
522
+ pull
523
+ .multiply(Math.max(1, 4 * contenders))
524
+ .add(totalPush)
525
+ .normalize();
526
+ return nextPosition.add(pull.multiply(this.speed));
527
+ });
528
+ }
529
+ isCollided(nextPosition, options = {}) {
530
+ return __awaiter(this, void 0, void 0, function* () {
531
+ this.collisionWith = [];
532
+ this._collisionWithTiles = [];
533
+ const prevMapId = this.map;
534
+ const hitbox = Hit_1.Hit.createObjectHitbox(nextPosition.x, nextPosition.y, 0, this.hitbox.w, this.hitbox.h);
535
+ const boundingMap = this.mapInstance.boundingMap(nextPosition, this.hitbox);
536
+ let collided = false;
537
+ if (boundingMap === null || boundingMap === void 0 ? void 0 : boundingMap.bounding) {
538
+ this.position.set(nextPosition);
539
+ if (!options.allSearch)
540
+ return true;
541
+ else
542
+ collided = true;
543
+ }
544
+ const tileCollision = (x, y) => {
545
+ const tile = this.getTile(x, y, nextPosition.z, hitbox);
546
+ if (tile.hasCollision) {
547
+ this._collisionWithTiles.push(tile);
548
+ return true;
549
+ }
550
+ return false;
551
+ };
552
+ if (tileCollision(nextPosition.x, nextPosition.y) ||
553
+ tileCollision(nextPosition.x + this.hitbox.w, nextPosition.y) ||
554
+ tileCollision(nextPosition.x, nextPosition.y + this.hitbox.h) ||
555
+ tileCollision(nextPosition.x + this.hitbox.w, nextPosition.y + this.hitbox.h)) {
556
+ if (!options.allSearch)
557
+ return true;
558
+ else
559
+ collided = true;
560
+ }
561
+ if (this.autoChangeMap) {
562
+ const changeMap = yield this.autoChangeMap(nextPosition);
563
+ if (changeMap) {
564
+ return true;
565
+ }
566
+ }
567
+ const playerSizeBox = this.getSizeMaxShape(nextPosition.x, nextPosition.y);
568
+ if (yield this.collisionObjects(playerSizeBox, hitbox, options)) {
569
+ if (!options.allSearch)
570
+ return true;
571
+ else
572
+ collided = true;
573
+ }
574
+ if (yield this.collisionShapes(playerSizeBox, nextPosition, options)) {
575
+ if (!options.allSearch)
576
+ return true;
577
+ else
578
+ collided = true;
579
+ }
580
+ // if there is a change of map after a move, the moves are not changed
581
+ if (prevMapId != this.map) {
582
+ return true;
583
+ }
584
+ return collided;
585
+ });
586
+ }
587
+ /**
588
+ * Attach a shape to the player (and allow interaction with it)
589
+ *
590
+ * ```ts
591
+ * import { ShapePositioning } from '@rpgjs/server'
592
+ *
593
+ * player.attachShape({
594
+ * width: 100,
595
+ * height: 100,
596
+ * positioning: ShapePositioning.Center
597
+ * })
598
+ * ```
599
+ *
600
+ * @title Attach Shape
601
+ * @method player.attachShape(parameters)
602
+ * @param { { width: number, height: number, positioning?, name?, properties?: object } } obj
603
+ * - positioning: Indicate where the shape is placed.
604
+ * - properties: An object in order to retrieve information when interacting with the shape
605
+ * - name: The name of the shape
606
+ * @since 3.0.0-beta.3
607
+ * @returns {RpgShape}
608
+ * @memberof Player
609
+ */
610
+ attachShape(obj) {
611
+ obj.name = (obj.name || Utils_1.generateUID());
612
+ const shape = new Shape_1.RpgShape(Object.assign(Object.assign({}, obj), { fixEvent: this }));
613
+ this.shapes.push(shape);
614
+ return shape;
615
+ }
616
+ /**
617
+ * Returns all shapes assigned to this player
618
+ *
619
+ * @title Get Shapes
620
+ * @method player.getShapes()
621
+ * @returns {RpgShape[]}
622
+ * @since 3.0.0-beta.3
623
+ * @memberof Player
624
+ * @memberof RpgSpriteLogic
625
+ */
626
+ getShapes() {
627
+ return this.shapes;
628
+ }
629
+ autoChangeDirection(nextPosition) {
630
+ const { x, y } = this.position;
631
+ const { x: nx, y: ny } = nextPosition;
632
+ const diff = Math.abs(x - nx) > Math.abs(y - ny);
633
+ if (diff) {
634
+ if (nx > x) {
635
+ this.changeDirection(types_1.Direction.Right);
636
+ }
637
+ else {
638
+ this.changeDirection(types_1.Direction.Left);
639
+ }
640
+ }
641
+ else {
642
+ if (ny > y) {
643
+ this.changeDirection(types_1.Direction.Down);
644
+ }
645
+ else {
646
+ this.changeDirection(types_1.Direction.Up);
647
+ }
648
+ }
649
+ }
650
+ /**
651
+ * Stops the movement of the player who moves towards his target
652
+ *
653
+ * @title Stop Move To
654
+ * @method player.stopMoveTo()
655
+ * @returns {void}
656
+ * @since 3.2.0
657
+ * @memberof MoveManager
658
+ */
659
+ stopMoveTo() {
660
+ if (this.destroyMove$.closed)
661
+ return;
662
+ this.destroyMove$.next(true);
663
+ this.destroyMove$.unsubscribe();
664
+ }
665
+ _moveTo(tick$, positionTarget, options = {}) {
666
+ let i = 0;
667
+ let count = 0;
668
+ const lastPositions = [];
669
+ this.stopMoveTo();
670
+ this.destroyMove$ = new rxjs_1.Subject();
671
+ const { infinite, onStuck, onComplete } = options;
672
+ const getPosition = () => {
673
+ let pos;
674
+ if ('x' in positionTarget) {
675
+ pos = new Vector2d_1.Vector2d(positionTarget.x, positionTarget.y);
676
+ }
677
+ else {
678
+ pos = positionTarget.position;
679
+ }
680
+ return pos;
681
+ };
682
+ return tick$
683
+ .pipe(rxjs_1.takeUntil(this.destroyMove$), rxjs_1.mergeMap(() => rxjs_1.from(this.computeNextPositionByTarget(this.position.copy(), getPosition()))), rxjs_1.map((position) => {
684
+ this.autoChangeDirection(position);
685
+ return this.position.set(position);
686
+ }), rxjs_1.tap((position) => {
687
+ lastPositions[i] = position.copy();
688
+ i++;
689
+ count++;
690
+ if (i >= 3) {
691
+ i = 0;
692
+ }
693
+ if (lastPositions[2] && lastPositions[0].isEqual(lastPositions[2])) {
694
+ onStuck === null || onStuck === void 0 ? void 0 : onStuck(count);
695
+ }
696
+ else if (this.position.isEqual(getPosition())) {
697
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete();
698
+ if (!infinite) {
699
+ this.stopMoveTo();
700
+ }
701
+ }
702
+ else {
703
+ count = 0;
704
+ }
705
+ }));
706
+ }
707
+ /** @internal */
708
+ move(nextPosition) {
709
+ return __awaiter(this, void 0, void 0, function* () {
710
+ this.autoChangeDirection(nextPosition);
711
+ const notCollided = !(yield this.isCollided(nextPosition));
712
+ if (notCollided || !this.checkCollision) {
713
+ this.position = nextPosition.copy();
714
+ yield Plugin_1.RpgPlugin.emit(Plugin_1.HookServer.PlayerMove, this);
715
+ }
716
+ return true;
717
+ });
718
+ }
719
+ /**
720
+ * Retrieves all shapes where the player is located
721
+ *
722
+ * @title Get In-Shapes
723
+ * @method player.getInShapes()
724
+ * @returns {RpgShape[]}
725
+ * @since 3.0.0-beta.3
726
+ * @memberof Player
727
+ */
728
+ getInShapes() {
729
+ return Object.values(this.inShapes);
730
+ }
731
+ /**
732
+ * Get the current direction.
733
+ *
734
+ * ```ts
735
+ * player.getDirection()
736
+ * ```
737
+ *
738
+ * @title Get Direction
739
+ * @method player.getDirection()
740
+ * @returns {Direction | number} direction
741
+ * @memberof Player
742
+ */
743
+ getDirection(direction) {
744
+ return direction || this.direction;
745
+ }
746
+ /**
747
+ * Changes the player's direction
748
+ *
749
+ * ```ts
750
+ * import { Direction } from '@rpgjs/server'
751
+ *
752
+ * player.changeDirection(Direction.Left)
753
+ * ```
754
+ *
755
+ * @title Change direction
756
+ * @method player.changeDirection(direction)
757
+ * @param {Direction} direction
758
+ * @enum {string}
759
+ *
760
+ * Direction.Left | left
761
+ * Direction.Right | right
762
+ * Direction.Up | up
763
+ * Direction.Down | down
764
+ * @returns {boolean} the direction has changed
765
+ * @memberof Player
766
+ */
767
+ changeDirection(direction) {
768
+ const dir = +this.getDirection(direction);
769
+ if (dir === undefined)
770
+ return false;
771
+ this.direction = dir;
772
+ return true;
773
+ }
774
+ /**
775
+ * Gets the necessary number of pixels to allow the player to cross a tile.
776
+ * This is the ratio between the height or width of the tile and the speed of the player.
777
+ */
778
+ get nbPixelInTile() {
779
+ const direction = this.getDirection();
780
+ switch (direction) {
781
+ case types_1.Direction.Down:
782
+ case types_1.Direction.Up:
783
+ return Math.floor(this.mapInstance.tileHeight / this.speed);
784
+ case types_1.Direction.Left:
785
+ case types_1.Direction.Right:
786
+ return Math.floor(this.mapInstance.tileWidth / this.speed);
787
+ default:
788
+ return NaN;
789
+ }
790
+ }
791
+ getSizeMaxShape(x, y) {
792
+ const _x = x || this.position.x;
793
+ const _y = y || this.position.y;
794
+ let minX = _x;
795
+ let minY = _y;
796
+ let maxX = _x + this.wHitbox;
797
+ let maxY = _y + this.hHitbox;
798
+ const shapes = this.getShapes();
799
+ for (let shape of shapes) {
800
+ if (shape.x < minX)
801
+ minX = shape.x;
802
+ if (shape.y < minY)
803
+ minY = shape.y;
804
+ const shapeMaxX = shape.x + shape.width;
805
+ const shapeMaxY = shape.y + shape.height;
806
+ if (shapeMaxX > maxX)
807
+ maxX = shapeMaxX;
808
+ if (shapeMaxY > maxY)
809
+ maxY = shapeMaxY;
810
+ }
811
+ return {
812
+ minX,
813
+ minY,
814
+ maxX,
815
+ maxY
816
+ };
817
+ }
818
+ /** @internal */
819
+ execMethod(methodName, methodData, instance) {
820
+ return __awaiter(this, void 0, void 0, function* () { });
821
+ }
822
+ /** @internal */
823
+ onAction() { }
824
+ /** @internal */
825
+ onPlayerTouch() { }
826
+ }
827
+ exports.AbstractObject = AbstractObject;
828
+ //# sourceMappingURL=AbstractObject.js.map