@rpgjs/common 4.3.0 → 5.0.0-alpha.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 (148) hide show
  1. package/dist/Physic.d.ts +619 -0
  2. package/dist/Player.d.ts +198 -0
  3. package/{lib → dist}/Utils.d.ts +19 -2
  4. package/dist/database/Item.d.ts +10 -0
  5. package/dist/database/index.d.ts +1 -0
  6. package/dist/index.d.ts +9 -0
  7. package/dist/index.js +16741 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/modules.d.ts +92 -0
  10. package/dist/movement/MovementManager.d.ts +84 -0
  11. package/dist/movement/MovementStrategy.d.ts +39 -0
  12. package/dist/movement/index.d.ts +12 -0
  13. package/dist/movement/strategies/CompositeMovement.d.ts +76 -0
  14. package/dist/movement/strategies/Dash.d.ts +52 -0
  15. package/dist/movement/strategies/IceMovement.d.ts +87 -0
  16. package/dist/movement/strategies/Knockback.d.ts +50 -0
  17. package/dist/movement/strategies/LinearMove.d.ts +43 -0
  18. package/dist/movement/strategies/LinearRepulsion.d.ts +55 -0
  19. package/dist/movement/strategies/Oscillate.d.ts +60 -0
  20. package/dist/movement/strategies/PathFollow.d.ts +78 -0
  21. package/dist/movement/strategies/ProjectileMovement.d.ts +138 -0
  22. package/dist/movement/strategies/SeekAvoid.d.ts +27 -0
  23. package/dist/rooms/Map.d.ts +109 -0
  24. package/dist/services/updateMap.d.ts +7 -0
  25. package/package.json +17 -17
  26. package/src/Physic.ts +1644 -0
  27. package/src/Player.ts +262 -26
  28. package/src/{gui/PrebuiltGui.ts → PrebuiltGui.ts} +1 -1
  29. package/src/Utils.ts +184 -123
  30. package/src/database/Item.ts +19 -0
  31. package/src/database/index.ts +1 -0
  32. package/src/index.ts +9 -25
  33. package/src/modules.ts +230 -0
  34. package/src/movement/MovementManager.ts +142 -0
  35. package/src/movement/MovementStrategy.ts +42 -0
  36. package/src/movement/index.ts +15 -0
  37. package/src/movement/strategies/CompositeMovement.ts +173 -0
  38. package/src/movement/strategies/Dash.ts +82 -0
  39. package/src/movement/strategies/IceMovement.ts +158 -0
  40. package/src/movement/strategies/Knockback.ts +81 -0
  41. package/src/movement/strategies/LinearMove.ts +58 -0
  42. package/src/movement/strategies/LinearRepulsion.ts +128 -0
  43. package/src/movement/strategies/Oscillate.ts +144 -0
  44. package/src/movement/strategies/PathFollow.ts +156 -0
  45. package/src/movement/strategies/ProjectileMovement.ts +322 -0
  46. package/src/movement/strategies/SeekAvoid.ts +123 -0
  47. package/src/rooms/Map.ts +272 -0
  48. package/src/services/updateMap.ts +9 -0
  49. package/tests/physic.spec.ts +454 -0
  50. package/tsconfig.json +8 -3
  51. package/vite.config.ts +21 -0
  52. package/CHANGELOG.md +0 -160
  53. package/LICENSE +0 -19
  54. package/browser/manifest.json +0 -7
  55. package/browser/rpg.common.js +0 -11357
  56. package/browser/rpg.common.umd.cjs +0 -11358
  57. package/lib/AbstractObject.d.ts +0 -322
  58. package/lib/AbstractObject.js +0 -872
  59. package/lib/AbstractObject.js.map +0 -1
  60. package/lib/Color.d.ts +0 -1
  61. package/lib/Color.js +0 -25
  62. package/lib/Color.js.map +0 -1
  63. package/lib/DefaultInput.d.ts +0 -2
  64. package/lib/DefaultInput.js +0 -26
  65. package/lib/DefaultInput.js.map +0 -1
  66. package/lib/Event.d.ts +0 -3
  67. package/lib/Event.js +0 -4
  68. package/lib/Event.js.map +0 -1
  69. package/lib/EventEmitter.d.ts +0 -10
  70. package/lib/EventEmitter.js +0 -61
  71. package/lib/EventEmitter.js.map +0 -1
  72. package/lib/Game.d.ts +0 -28
  73. package/lib/Game.js +0 -127
  74. package/lib/Game.js.map +0 -1
  75. package/lib/Hit.d.ts +0 -16
  76. package/lib/Hit.js +0 -65
  77. package/lib/Hit.js.map +0 -1
  78. package/lib/Inject.d.ts +0 -9
  79. package/lib/Inject.js +0 -17
  80. package/lib/Inject.js.map +0 -1
  81. package/lib/Logger.d.ts +0 -2
  82. package/lib/Logger.js +0 -7
  83. package/lib/Logger.js.map +0 -1
  84. package/lib/Map.d.ts +0 -174
  85. package/lib/Map.js +0 -263
  86. package/lib/Map.js.map +0 -1
  87. package/lib/Module.d.ts +0 -16
  88. package/lib/Module.js +0 -139
  89. package/lib/Module.js.map +0 -1
  90. package/lib/Player.d.ts +0 -26
  91. package/lib/Player.js +0 -19
  92. package/lib/Player.js.map +0 -1
  93. package/lib/Plugin.d.ts +0 -67
  94. package/lib/Plugin.js +0 -92
  95. package/lib/Plugin.js.map +0 -1
  96. package/lib/Scheduler.d.ts +0 -26
  97. package/lib/Scheduler.js +0 -90
  98. package/lib/Scheduler.js.map +0 -1
  99. package/lib/Shape.d.ts +0 -127
  100. package/lib/Shape.js +0 -261
  101. package/lib/Shape.js.map +0 -1
  102. package/lib/Utils.js +0 -181
  103. package/lib/Utils.js.map +0 -1
  104. package/lib/Vector2d.d.ts +0 -20
  105. package/lib/Vector2d.js +0 -63
  106. package/lib/Vector2d.js.map +0 -1
  107. package/lib/VirtualGrid.d.ts +0 -26
  108. package/lib/VirtualGrid.js +0 -68
  109. package/lib/VirtualGrid.js.map +0 -1
  110. package/lib/Worker.d.ts +0 -7
  111. package/lib/Worker.js +0 -13
  112. package/lib/Worker.js.map +0 -1
  113. package/lib/WorldMaps.d.ts +0 -105
  114. package/lib/WorldMaps.js +0 -184
  115. package/lib/WorldMaps.js.map +0 -1
  116. package/lib/gui/PrebuiltGui.js +0 -29
  117. package/lib/gui/PrebuiltGui.js.map +0 -1
  118. package/lib/index.d.ts +0 -25
  119. package/lib/index.js +0 -26
  120. package/lib/index.js.map +0 -1
  121. package/lib/transports/io.d.ts +0 -22
  122. package/lib/transports/io.js +0 -82
  123. package/lib/transports/io.js.map +0 -1
  124. package/lib/workers/move.d.ts +0 -1
  125. package/lib/workers/move.js +0 -57
  126. package/lib/workers/move.js.map +0 -1
  127. package/rpg.toml +0 -11
  128. package/src/AbstractObject.ts +0 -973
  129. package/src/Color.ts +0 -29
  130. package/src/DefaultInput.ts +0 -26
  131. package/src/Event.ts +0 -3
  132. package/src/EventEmitter.ts +0 -65
  133. package/src/Game.ts +0 -159
  134. package/src/Hit.ts +0 -70
  135. package/src/Inject.ts +0 -22
  136. package/src/Logger.ts +0 -7
  137. package/src/Map.ts +0 -335
  138. package/src/Module.ts +0 -144
  139. package/src/Plugin.ts +0 -100
  140. package/src/Scheduler.ts +0 -95
  141. package/src/Shape.ts +0 -302
  142. package/src/Vector2d.ts +0 -70
  143. package/src/VirtualGrid.ts +0 -78
  144. package/src/Worker.ts +0 -17
  145. package/src/WorldMaps.ts +0 -204
  146. package/src/transports/io.ts +0 -91
  147. package/src/workers/move.ts +0 -61
  148. /package/{lib/gui → dist}/PrebuiltGui.d.ts +0 -0
@@ -1,973 +0,0 @@
1
- import { intersection, generateUID, toRadians, isInstanceOf, round } from './Utils'
2
- import { Hit, HitType } from './Hit'
3
- import { RpgShape } from './Shape'
4
- import SAT from 'sat'
5
- import { TileInfo, RpgCommonMap } from './Map'
6
- import { RpgPlugin, HookServer } from './Plugin'
7
- import { GameSide, RpgCommonGame } from './Game'
8
- import { Vector2d, Vector2dZero } from './Vector2d'
9
- import { Box } from './VirtualGrid'
10
- import { Behavior, ClientMode, Direction, MoveClientMode, MoveTo, PlayerType, Position, PositionXY, Tick } from '@rpgjs/types'
11
- import { from, map, mergeMap, Observable, Subject, tap, takeUntil, filter } from 'rxjs'
12
- import { RpgCommonPlayer } from './Player'
13
-
14
- const ACTIONS = { IDLE: 0, RUN: 1, ACTION: 2 }
15
-
16
- type CollisionOptions = {
17
- collision?: (event: AbstractObject) => void
18
- near?: (event: AbstractObject) => void,
19
- allSearch?: boolean
20
- }
21
-
22
- export class AbstractObject {
23
- map: string = ''
24
- height: number = 0
25
- width: number = 0
26
- speed: number
27
- direction: number = 3
28
- moving: boolean = false
29
-
30
- /*
31
- Properties for move mode
32
- */
33
- checkCollision: boolean = true
34
- clientModeMove: ClientMode = MoveClientMode.ByDirection
35
- behavior: Behavior = Behavior.Direction
36
-
37
- hitbox: SAT.Box
38
-
39
- inShapes: {
40
- [shapeId: string]: RpgShape
41
- } = {}
42
-
43
- disableVirtualGrid: boolean = false
44
-
45
- private shapes: RpgShape[] = []
46
- private _position: Vector2d
47
- private _hitboxPos: SAT.Vector
48
- private collisionWith: AbstractObject[] = []
49
- private _collisionWithTiles: TileInfo[] = []
50
- private _collisionWithShapes: RpgShape[] = []
51
-
52
- private destroyMove$: Subject<boolean> = new Subject<boolean>()
53
- // notifier for destroy
54
- _destroy$: Subject<void> = new Subject()
55
-
56
- static get ACTIONS() {
57
- return ACTIONS
58
- }
59
-
60
- constructor(public gameEngine: RpgCommonGame, public playerId: string) {
61
- this._hitboxPos = new SAT.Vector(0, 0)
62
- this.setHitbox(this.width, this.height)
63
- this.position = { x: 0, y: 0, z: 0 }
64
- }
65
-
66
- get id() {
67
- return this.playerId
68
- }
69
-
70
- set id(str: string) {
71
- this.playerId = str
72
- }
73
-
74
- updateInVirtualGrid() {
75
- const map = this.mapInstance
76
- if (map && !this.disableVirtualGrid /*&& this.gameEngine.isWorker TODO */) {
77
- map.grid.insertInCells(this.id, this.getSizeMaxShape())
78
- }
79
- }
80
-
81
- get canMove(): boolean {
82
- return this.clientModeMove == MoveClientMode.ByDirection
83
- }
84
-
85
- set canMove(val: boolean) {
86
- this.clientModeMove = val ? MoveClientMode.ByDirection : MoveClientMode.Disabled
87
- }
88
-
89
- /**
90
- * Get/Set position x, y and z of player
91
- *
92
- * 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.
93
- *
94
- * @title Get/Set position
95
- * @prop { { x: number, y: number, z: number } } position
96
- * @memberof Player
97
- */
98
- set position(val: Position | Vector2d) {
99
- if (this.isDestroyed) return
100
- const { x, y, z } = val
101
- if (!isInstanceOf(val, Vector2d)) {
102
- val = new Vector2d(x, y, z)
103
- }
104
- this._hitboxPos.x = x
105
- this._hitboxPos.y = y
106
- this._hitboxPos.z = z
107
- this.updateInVirtualGrid()
108
- this._position = new Proxy<Vector2d>(val as Vector2d, {
109
- get: (target, prop: string) => target[prop],
110
- set: (target, prop, value) => {
111
- this._hitboxPos[prop] = value
112
- target[prop] = value
113
- this.updateInVirtualGrid()
114
- return true
115
- }
116
- })
117
- }
118
-
119
- get position(): Vector2d {
120
- return this._position
121
- }
122
-
123
- get worldPositionX(): number {
124
- let x = this.position.x
125
- if (this.mapInstance) {
126
- x += this.mapInstance.worldX
127
- }
128
- return x
129
- }
130
-
131
- get worldPositionY(): number {
132
- let y = this.position.y
133
- if (this.mapInstance) {
134
- y += this.mapInstance.worldY
135
- }
136
- return y
137
- }
138
-
139
- set posX(val) {
140
- this.position.x = val
141
- }
142
-
143
- set posY(val) {
144
- this.position.y = val
145
- }
146
-
147
- set posZ(val) {
148
- this.position.z = val
149
- }
150
-
151
- /** @internal */
152
- get mapInstance(): RpgCommonMap {
153
- if (this.gameEngine.side == GameSide.Client) {
154
- return RpgCommonMap.bufferClient.get(this.map)
155
- }
156
- return RpgCommonMap.buffer.get(this.map)
157
- }
158
-
159
- /**
160
- *
161
- * Recovers all the colliding shapes of the current player
162
- *
163
- * @title Get Collision of shapes
164
- * @since 3.2.0
165
- * @readonly
166
- * @prop { RpgShape[] } shapes
167
- * @memberof Player
168
- * @memberof RpgSpriteLogic
169
- */
170
- get shapesCollision(): RpgShape[] {
171
- return this._collisionWithShapes
172
- }
173
-
174
- /**
175
- *
176
- * Recovers all the colliding tiles of the current player
177
- *
178
- * @title Get Collision of tiles
179
- * @since 3.0.0-beta.4
180
- * @readonly
181
- * @prop { TileInfo[] } tiles
182
- * @memberof Player
183
- * @memberof RpgSpriteLogic
184
- */
185
- get tilesCollision(): TileInfo[] {
186
- return this._collisionWithTiles
187
- }
188
-
189
- /**
190
- *
191
- * Recovers all other players and events colliding with the current player's hitbox
192
- *
193
- * @title Get Collision of other players/events
194
- * @since 3.0.0-beta.4
195
- * @readonly
196
- * @prop { (RpgPlayer | RpgEvent)[] } otherPlayersCollision
197
- * @memberof Player
198
- * @memberof RpgSpriteLogic
199
- */
200
- get otherPlayersCollision(): AbstractObject[] {
201
- return this.collisionWith
202
- }
203
-
204
- /**
205
- * Define the size of the player. You can set the hitbox for collisions
206
- *
207
- * ```ts
208
- * player.setSizes({
209
- * width: 32,
210
- * height: 32
211
- * })
212
- * ```
213
- *
214
- * and with hitbox:
215
- *
216
- * ```ts
217
- * player.setSizes({
218
- * width: 32,
219
- * height: 32,
220
- * hitbox: {
221
- * width: 20,
222
- * height: 20
223
- * }
224
- * })
225
- * ```
226
- *
227
- * @title Set Sizes
228
- * @method player.setSizes(key,value)
229
- * @param { { width: number, height: number, hitbox?: { width: number, height: number } } } obj
230
- * @deprecated
231
- * @returns {void}
232
- * @memberof Player
233
- */
234
- setSizes(obj: { width: number, height: number, hitbox?: { width: number, height: number } }): void {
235
- this.width = obj.width
236
- this.height = obj.height
237
- if (obj.hitbox) {
238
- this.hitbox = new SAT.Box(this._hitboxPos, obj.hitbox.width, obj.hitbox.height)
239
- }
240
- }
241
-
242
- /**
243
- * Define the hitbox of the player.
244
- *
245
- * ```ts
246
- * player.setHitbox(20, 20)
247
- * ```
248
- *
249
- * @title Set Hitbox
250
- * @method player.setHitbox(width,height)
251
- * @param {number} width
252
- * @param {number} height
253
- * @returns {void}
254
- * @memberof Player
255
- */
256
- setHitbox(width: number, height: number): void {
257
- const map = this.mapInstance
258
- if (map) {
259
- this.width = map.tileWidth
260
- this.height = map.tileHeight
261
- }
262
- this.hitbox = new SAT.Box(this._hitboxPos, width, height)
263
- this.wHitbox = width
264
- this.hHitbox = height
265
- this.updateInVirtualGrid()
266
- }
267
-
268
- set wHitbox(val) {
269
- this.hitbox.w = val
270
- }
271
-
272
- set hHitbox(val) {
273
- this.hitbox.h = val
274
- }
275
-
276
- get wHitbox() {
277
- return this.hitbox.w
278
- }
279
-
280
- get hHitbox() {
281
- return this.hitbox.h
282
- }
283
-
284
- private directionToAngle(direction: number): number {
285
- const angle = (direction < 2 ? +direction + 2 : direction - 2) * 90
286
- return toRadians(angle)
287
- }
288
-
289
- /** @internal */
290
- defineNextPosition(direction: number, deltaTimeInt: number): Vector2d {
291
- const angle = this.directionToAngle(direction)
292
- const computePosition = (prop: string) => {
293
- return this.position[prop] + this.speed * deltaTimeInt
294
- * (Math.round(Math[prop == 'x' ? 'cos' : 'sin'](angle) * 100) / 100)
295
- }
296
- // If it's greater than 1, round value to reduces bandwidth
297
- const x = this.speed < 1 ? computePosition('x') : round(computePosition('x'))
298
- const y = this.speed < 1 ? computePosition('y') : round(computePosition('y'))
299
- return new Vector2d(x, y, ~~this.position.z)
300
- }
301
-
302
- /** @internal */
303
- setPosition({ x, y, tileX, tileY }, move = true) {
304
- const { tileWidth, tileHeight } = this.mapInstance
305
- if (x !== undefined) this.posX = x
306
- if (y !== undefined) this.posY = y
307
- if (tileX !== undefined) this.posX = tileX * tileWidth
308
- if (tileY !== undefined) this.posY = tileY * tileHeight
309
- }
310
-
311
- /** @internal */
312
- async triggerCollisionWith(type?: number) {
313
- let doChanges = false
314
- for (let collisionWith of this.collisionWith) {
315
- if (collisionWith.isDestroyed) continue
316
- if (collisionWith instanceof RpgShape) {
317
- const goMap = collisionWith.getProperty<string>('go-map')
318
- if (goMap && 'changeMap' in this) await this.changeMap(goMap)
319
- }
320
- else {
321
- if (type == AbstractObject.ACTIONS.ACTION) {
322
- if ('onAction' in collisionWith) {
323
- await collisionWith.execMethod('onAction', [this])
324
- doChanges = true
325
- }
326
- }
327
- else if ('onPlayerTouch' in collisionWith) {
328
- await collisionWith.execMethod('onPlayerTouch', [this])
329
- doChanges = true
330
- }
331
- }
332
- }
333
- if (this.syncChanges && doChanges) this.syncChanges()
334
- }
335
-
336
- /** @internal */
337
- zCollision(other: Pick<AbstractObject, 'height'> & { position: Pick<AbstractObject['position'], 'z'> }): boolean {
338
- const z = this.position.z
339
- const otherZ = other.position.z
340
- return intersection([z, z + this.height], [otherZ, otherZ + other.height])
341
- }
342
-
343
- /** @internal */
344
- moveByDirection(direction: Direction, deltaTimeInt: number): Promise<boolean> {
345
- const nextPosition = this.defineNextPosition(direction, deltaTimeInt)
346
- return this.move(nextPosition)
347
- }
348
-
349
- /**
350
- * Retrieves a tile and checks if the player has a collision
351
- *
352
- * ```ts
353
- * const tileInfo = player.getTile(20, 30)
354
- * console.log(tileInfo)
355
- * ```
356
- *
357
- * Example of returns:
358
- *
359
- * ```ts
360
- * {
361
- * tiles: [
362
- * {
363
- * id: 0,
364
- * terrain: [],
365
- * probability: null,
366
- * properties: [Object],
367
- * animations: [],
368
- * objectGroups: [],
369
- * image: null,
370
- * gid: 1
371
- * }
372
- * ],
373
- * hasCollision: false,
374
- * isOverlay: undefined,
375
- * objectGroups: [],
376
- * isClimbable: undefined,
377
- * tileIndex: 93
378
- * }
379
- * ```
380
- *
381
- * @title Get Tile
382
- * @since 3.0.0-beta.4
383
- * @method player.getTile(x,y,z?)
384
- * @param {number} x
385
- * @param {number} y
386
- * @param {number} [z]
387
- * @returns {object}
388
- * @memberof Player
389
- * @memberof RpgSpriteLogic
390
- */
391
- getTile(x: number, y: number, z: number = 0, hitbox?: SAT.Box): TileInfo {
392
- const map = this.mapInstance
393
- return map.getTile(hitbox || this.hitbox, x, y, [z, this.height])
394
- }
395
-
396
- private async collisionObjects(
397
- playerSizeBox: Box,
398
- hitbox: SAT.Box,
399
- triggers?: CollisionOptions
400
- ): Promise<boolean> {
401
- const map = this.mapInstance
402
-
403
- if (!map) return true
404
-
405
- const events: { [id: string]: AbstractObject } = this.gameEngine.world.getObjectsOfGroup(this.map, this)
406
- const objects = map.grid.getObjectsByBox(playerSizeBox)
407
- let boolFound = false
408
-
409
- for (let objectId of objects) {
410
- // client side: read "object" property
411
- if (!events[objectId]) continue
412
- const event = events[objectId]['object'] || events[objectId]
413
-
414
- if (event.id == this.id) continue
415
- if (!this.zCollision(event)) continue
416
-
417
- const collided = Hit.testPolyCollision(HitType.Box, hitbox, event.hitbox)
418
-
419
- for (let shape of this.shapes) {
420
- await this.collisionWithShape(shape, event)
421
- }
422
-
423
- for (let shape of event.shapes) {
424
- await event.collisionWithShape(shape, this)
425
- }
426
-
427
- if (triggers?.near) triggers.near(event)
428
-
429
- if (collided) {
430
- this.collisionWith.push(event)
431
- this.triggerCollisionWith()
432
- let throughOtherPlayer = false
433
- if (event.type == PlayerType.Player && this.type == PlayerType.Player) {
434
- if (!(event.throughOtherPlayer || this.throughOtherPlayer)) {
435
- boolFound = true
436
- if (!triggers?.allSearch) return true
437
- }
438
- else {
439
- throughOtherPlayer = true
440
- }
441
- }
442
- if (!throughOtherPlayer && (!(event.through || this.through))) {
443
- boolFound = true
444
- if (!triggers?.allSearch) return true
445
- }
446
- }
447
-
448
- if (boolFound) {
449
- if (triggers?.collision) triggers.collision(event)
450
- }
451
- }
452
-
453
- return boolFound
454
- }
455
-
456
- /** @internal */
457
- private async collisionWithShape(shape: RpgShape, player: AbstractObject, nextPosition?: Vector2d): Promise<boolean> {
458
- const collision = shape.hasCollision
459
- const z = shape.z
460
- if (shape.isShapePosition()) return false
461
- if (z !== undefined && !this.zCollision({
462
- position: { z },
463
- height: this.mapInstance.zTileHeight
464
- })) {
465
- return false
466
- }
467
- let position: Vector2d
468
- let { hitbox } = player
469
- if (nextPosition) {
470
- position = nextPosition.copy()
471
- }
472
- else {
473
- position = player.position.copy()
474
- }
475
- const hitboxObj = Hit.createObjectHitbox(
476
- position.x,
477
- position.y,
478
- position.z,
479
- hitbox.w,
480
- hitbox.h
481
- )
482
- let collided = Hit.testPolyCollision(shape.type, hitboxObj, shape.hitbox)
483
- const playerPositionSaved = player.position.copy()
484
-
485
- // Position can changed after enter or exit shape. So, we need to verify if position changed and update if z is changed
486
- // If X or Y changed, we need to return true, it means that stop the current movement, and apply the new position
487
- const verifyIfPositionChanged = (): boolean | undefined => {
488
- if (this.position.z != playerPositionSaved.z && nextPosition) {
489
- nextPosition.z = this.position.z
490
- }
491
- if (this.position.x != playerPositionSaved.x || this.position.y != playerPositionSaved.y) {
492
- return true
493
- }
494
- }
495
-
496
- if (collided) {
497
- this._collisionWithShapes.push(shape)
498
- // TODO: in shape after map load
499
- if (!collision) await shape.in(player)
500
- if (verifyIfPositionChanged() === true) return true
501
- this.triggerCollisionWith()
502
- if (collision) return true
503
- }
504
- else {
505
- await shape.out(player)
506
- if (verifyIfPositionChanged() === true) return true
507
- }
508
-
509
- return false
510
- }
511
-
512
- private async collisionShapes(playerSizeBox: Box, nextPosition?: Vector2d, triggers?: CollisionOptions): Promise<boolean> {
513
- const map = this.mapInstance
514
- if (!map) return false
515
- const shapes: { [id: string]: RpgShape } = this.gameEngine.world.getShapesOfGroup(this.map)
516
- const shapesInGrid = this.gameEngine.side == GameSide.Client
517
- ? new Set(Object.keys(shapes))
518
- : map.gridShapes.getObjectsByBox(playerSizeBox)
519
- let boolFound = false
520
-
521
- for (let shapeId of shapesInGrid) {
522
- const shape = shapes[shapeId]['object'] || shapes[shapeId]
523
- if (triggers?.near) triggers.near(shape)
524
- const bool = await this.collisionWithShape(shape, this, nextPosition)
525
- if (bool) {
526
- if (triggers?.collision) triggers.collision(shape)
527
- boolFound = true
528
- if (!triggers?.allSearch) return true
529
- }
530
- }
531
- return boolFound
532
- }
533
-
534
- async computeNextPositionByTarget(nextPosition: Vector2d, target: Vector2d): Promise<Vector2d> {
535
- const pullDistance = target.distanceWith(nextPosition)
536
- if (pullDistance <= this.speed) {
537
- return nextPosition.set(target)
538
- }
539
- const pull = (target.copy().subtract(nextPosition)).multiply((1 / pullDistance))
540
- const totalPush = new Vector2dZero()
541
- let contenders = 0
542
- const hitbox = Hit.createObjectHitbox(nextPosition.x, nextPosition.y, nextPosition.z, this.hitbox.w, this.hitbox.h)
543
-
544
- const createObstacle = function (x: number, y: number, radius: number): Vector2d {
545
- const obstacle = new Vector2d(x, y)
546
- let push = nextPosition.copy().subtract(obstacle)
547
- let distance = (nextPosition.distanceWith(obstacle) - radius) - radius;
548
- if (distance < radius * 2 * 10) {
549
- ++contenders
550
- if (distance < 0.0001) distance = 0.0001 // avoid div by 0
551
- let weight = 1 / distance;
552
- totalPush.add(push.multiply(weight))
553
- }
554
- return obstacle
555
- }
556
-
557
- const area = this.mapInstance.tileheight * 2
558
- this.mapInstance.gridTiles.getCells({
559
- minX: nextPosition.x - area,
560
- maxX: nextPosition.x + area,
561
- minY: nextPosition.y - area,
562
- maxY: nextPosition.y + area
563
- }, (index) => {
564
- if (index < 0) return
565
- const pos = this.mapInstance.getTilePosition(index)
566
- const hitbox = Hit.createObjectHitbox(pos.x, pos.y, nextPosition.z, this.hitbox.w, this.hitbox.h)
567
- const radius = this.mapInstance.tilewidth / 2
568
- const tile = this.getTile(pos.x, pos.y, nextPosition.z, hitbox)
569
- if (tile.hasCollision) {
570
- createObstacle(pos.x, pos.y, radius)
571
- }
572
- })
573
-
574
-
575
- const playerSizeBox = this.getSizeMaxShape(nextPosition.x, nextPosition.y)
576
-
577
- await this.collisionObjects(playerSizeBox, hitbox, {
578
- collision: (event: AbstractObject) => {
579
- const { x, y } = event.position
580
- createObstacle(x, y, event.hitbox.w)
581
- },
582
- allSearch: true
583
- })
584
-
585
- await this.collisionShapes(playerSizeBox, nextPosition, {
586
- collision: (shape) => {
587
- const { x, y } = shape.position
588
- createObstacle(x, y, shape.hitbox.w)
589
- },
590
- allSearch: true
591
- })
592
-
593
- pull
594
- .multiply(Math.max(1, 4 * contenders))
595
- .add(totalPush)
596
- .normalize()
597
-
598
- return nextPosition.add(pull.multiply(this.speed))
599
- }
600
-
601
- async isCollided(nextPosition: Vector2d, options: CollisionOptions = {}): Promise<boolean> {
602
- this.collisionWith = []
603
- this._collisionWithTiles = []
604
- const prevMapId = this.map
605
- const hitbox = Hit.createObjectHitbox(nextPosition.x, nextPosition.y, 0, this.hitbox.w, this.hitbox.h)
606
- const boundingMap = this.mapInstance?.boundingMap(nextPosition, this.hitbox)
607
- let collided = false
608
-
609
- if (boundingMap?.bounding) {
610
- this.position.set(nextPosition)
611
- if (!options.allSearch) return true
612
- else collided = true
613
- }
614
-
615
- const tileCollision = (x: number, y: number): boolean => {
616
- const tile = this.getTile(x, y, nextPosition.z, hitbox)
617
- if (tile.hasCollision) {
618
- this._collisionWithTiles.push(tile)
619
- return true
620
- }
621
- return false
622
- }
623
-
624
- if (
625
- tileCollision(nextPosition.x, nextPosition.y) ||
626
- tileCollision(nextPosition.x + this.hitbox.w, nextPosition.y) ||
627
- tileCollision(nextPosition.x, nextPosition.y + this.hitbox.h) ||
628
- tileCollision(nextPosition.x + this.hitbox.w, nextPosition.y + this.hitbox.h)
629
- ) {
630
- if (!options.allSearch) return true
631
- else collided = true
632
- }
633
-
634
- if (this.autoChangeMap && this.type == PlayerType.Player) {
635
- const changeMap = await this.autoChangeMap(nextPosition)
636
- if (changeMap) {
637
- return true
638
- }
639
- }
640
-
641
- const playerSizeBox = this.getSizeMaxShape(nextPosition.x, nextPosition.y)
642
-
643
- if (await this.collisionObjects(playerSizeBox, hitbox, options)) {
644
- if (!options.allSearch) return true
645
- else collided = true
646
- }
647
-
648
- if (await this.collisionShapes(playerSizeBox, nextPosition, options)) {
649
- if (!options.allSearch) return true
650
- else collided = true
651
- }
652
-
653
- // if there is a change of map after a move, the moves are not changed
654
- if (prevMapId != this.map) {
655
- return true
656
- }
657
-
658
- return collided
659
- }
660
-
661
- /**
662
- * Attach a shape to the player (and allow interaction with it)
663
- *
664
- * ```ts
665
- * import { ShapePositioning } from '@rpgjs/server'
666
- *
667
- * player.attachShape({
668
- * width: 100,
669
- * height: 100,
670
- * positioning: ShapePositioning.Center
671
- * })
672
- * ```
673
- *
674
- * @title Attach Shape
675
- * @method player.attachShape(parameters)
676
- * @param { { width: number, height: number, positioning?, name?, properties?: object } } obj
677
- * - positioning: Indicate where the shape is placed.
678
- * - properties: An object in order to retrieve information when interacting with the shape
679
- * - name: The name of the shape
680
- * @since 3.0.0-beta.3
681
- * @returns {RpgShape}
682
- * @memberof Player
683
- */
684
- attachShape(obj: {
685
- width: number,
686
- height: number
687
- positioning?: string
688
- name?: string
689
- properties?: object
690
- }): RpgShape {
691
- obj.name = (obj.name || generateUID()) as string
692
- const shape = new RpgShape({
693
- ...obj,
694
- fixEvent: this
695
- } as any)
696
- this.shapes.push(shape)
697
- return shape
698
- }
699
-
700
- /**
701
- * Returns all shapes assigned to this player
702
- *
703
- * @title Get Shapes
704
- * @method player.getShapes()
705
- * @returns {RpgShape[]}
706
- * @since 3.0.0-beta.3
707
- * @memberof Player
708
- * @memberof RpgSpriteLogic
709
- */
710
- getShapes(): RpgShape[] {
711
- return this.shapes
712
- }
713
-
714
- private autoChangeDirection(nextPosition: Vector2d) {
715
- const { x, y } = this.position
716
- const { x: nx, y: ny } = nextPosition
717
- const diff = Math.abs(x - nx) > Math.abs(y - ny)
718
- if (diff) {
719
- if (nx > x) {
720
- this.changeDirection(Direction.Right)
721
- }
722
- else {
723
- this.changeDirection(Direction.Left)
724
- }
725
- }
726
- else {
727
- if (ny > y) {
728
- this.changeDirection(Direction.Down)
729
- }
730
- else {
731
- this.changeDirection(Direction.Up)
732
- }
733
- }
734
- }
735
-
736
- // @internal
737
- /**
738
- * We need to know if the event is deleted. Because when the event is deleted, you don't update the positions and you don't send the positions back to the client.
739
- */
740
- get isDestroyed(): boolean {
741
- return !!this._destroy$['_closed']
742
- }
743
-
744
- /**
745
- * Stops the movement of the player who moves towards his target
746
- *
747
- * @title Stop Move To
748
- * @method player.stopMoveTo()
749
- * @returns {void}
750
- * @since 3.2.0
751
- * @memberof MoveManager
752
- */
753
- stopMoveTo() {
754
- if (this.destroyMove$.closed) return
755
- this.moving = false
756
- this.destroyMove$.next(true)
757
- this.destroyMove$.unsubscribe()
758
- }
759
-
760
- private _lookToward(player: Vector2d, otherPlayer: Vector2d): Direction {
761
- const { x, y } = player;
762
- const { x: ox, y: oy } = otherPlayer;
763
-
764
- // Calculate the differences between the x and y coordinates.
765
- const dx = ox - x;
766
- const dy = oy - y;
767
-
768
- // Determine the primary direction based on the relative magnitude
769
- // of the x and y differences.
770
- if (Math.abs(dx) > Math.abs(dy)) {
771
- if (dx > 0) {
772
- return Direction.Right;
773
- } else {
774
- return Direction.Left;
775
- }
776
- } else {
777
- if (dy > 0) {
778
- return Direction.Down;
779
- } else {
780
- return Direction.Up;
781
- }
782
- }
783
- }
784
-
785
- _moveTo(tick$: Observable<Tick>, positionTarget: AbstractObject | RpgShape | PositionXY, options: MoveTo = {}): Observable<Vector2d> {
786
- let i = 0
787
- let count = 0
788
- const lastPositions: Vector2d[] = []
789
- this.stopMoveTo()
790
- this.moving = true
791
- this.destroyMove$ = new Subject()
792
- const { infinite, onStuck, onComplete } = options
793
- const getPosition = (): Vector2d => {
794
- let pos
795
- if ('x' in positionTarget) {
796
- pos = new Vector2d(positionTarget.x, positionTarget.y)
797
- }
798
- else {
799
- pos = positionTarget.position
800
- }
801
- return pos
802
- }
803
- return tick$
804
- .pipe(
805
- takeUntil(this.destroyMove$),
806
- takeUntil(this._destroy$),
807
- mergeMap(() => from(this.computeNextPositionByTarget(this.position.copy(), getPosition()))),
808
- filter(() => {
809
- return this.isDestroyed === false
810
- }),
811
- map((position) => {
812
- this.autoChangeDirection(position)
813
- return this.position.set(position)
814
- }),
815
- tap((position: Vector2d) => {
816
- lastPositions[i] = position.copy()
817
- i++
818
- count++
819
- if (i >= 3) {
820
- i = 0
821
- }
822
- if (
823
- lastPositions[2] &&
824
- (
825
- lastPositions[0].isEqual(lastPositions[2]) ||
826
- lastPositions[1].isEqual(lastPositions[2]) ||
827
- lastPositions[0].isEqual(lastPositions[1])
828
- )
829
- ) {
830
- this.direction = this._lookToward(this.position, getPosition())
831
- onStuck?.(count)
832
- this.moving = false
833
- }
834
- else if (this.position.isEqual(getPosition())) {
835
- onComplete?.()
836
- if (!infinite) {
837
- this.stopMoveTo()
838
- }
839
- }
840
- else {
841
- count = 0
842
- this.moving = true
843
- }
844
- })
845
- )
846
- }
847
-
848
- /** @internal */
849
- async move(nextPosition: Vector2d): Promise<boolean> {
850
- this.autoChangeDirection(nextPosition)
851
-
852
- const notCollided = !(await this.isCollided(nextPosition))
853
-
854
- if ((notCollided || !this.checkCollision) && !this.isDestroyed) {
855
- this.position = nextPosition.copy()
856
- await RpgPlugin.emit(HookServer.PlayerMove, this)
857
- }
858
-
859
- return true
860
- }
861
-
862
- /**
863
- * Retrieves all shapes where the player is located
864
- *
865
- * @title Get In-Shapes
866
- * @method player.getInShapes()
867
- * @returns {RpgShape[]}
868
- * @since 3.0.0-beta.3
869
- * @memberof Player
870
- */
871
- getInShapes(): RpgShape[] {
872
- return Object.values(this.inShapes)
873
- }
874
-
875
- /**
876
- * Get the current direction.
877
- *
878
- * ```ts
879
- * player.getDirection()
880
- * ```
881
- *
882
- * @title Get Direction
883
- * @method player.getDirection()
884
- * @returns {Direction | number} direction
885
- * @memberof Player
886
- */
887
- getDirection(direction?: Direction | number): string | number {
888
- return direction || this.direction
889
- }
890
-
891
- /**
892
- * Changes the player's direction
893
- *
894
- * ```ts
895
- * import { Direction } from '@rpgjs/server'
896
- *
897
- * player.changeDirection(Direction.Left)
898
- * ```
899
- *
900
- * @title Change direction
901
- * @method player.changeDirection(direction)
902
- * @param {Direction} direction
903
- * @enum {string}
904
- *
905
- * Direction.Left | left
906
- * Direction.Right | right
907
- * Direction.Up | up
908
- * Direction.Down | down
909
- * @returns {boolean} the direction has changed
910
- * @memberof Player
911
- */
912
- changeDirection(direction: Direction): boolean {
913
- const dir = +this.getDirection(direction)
914
- if (dir === undefined) return false
915
- this.direction = dir
916
- return true
917
- }
918
-
919
- /**
920
- * Gets the necessary number of pixels to allow the player to cross a tile.
921
- * This is the ratio between the height or width of the tile and the speed of the player.
922
- */
923
- get nbPixelInTile(): any {
924
- const direction = this.getDirection()
925
- switch (direction) {
926
- case Direction.Down:
927
- case Direction.Up:
928
- return Math.floor(this.mapInstance.tileHeight / this.speed)
929
- case Direction.Left:
930
- case Direction.Right:
931
- return Math.floor(this.mapInstance.tileWidth / this.speed)
932
- default:
933
- return NaN
934
- }
935
- }
936
-
937
- getSizeMaxShape(x?: number, y?: number): Box {
938
- const _x = x || this.position.x
939
- const _y = y || this.position.y
940
- let minX = _x
941
- let minY = _y
942
- let maxX = _x + this.wHitbox
943
- let maxY = _y + this.hHitbox
944
- const shapes = this.getShapes()
945
- for (let shape of shapes) {
946
- if (shape.x < minX) minX = shape.x
947
- if (shape.y < minY) minY = shape.y
948
- const shapeMaxX = shape.x + shape.width
949
- const shapeMaxY = shape.y + shape.height
950
- if (shapeMaxX > maxX) maxX = shapeMaxX
951
- if (shapeMaxY > maxY) maxY = shapeMaxY
952
- }
953
- return {
954
- minX,
955
- minY,
956
- maxX,
957
- maxY
958
- }
959
- }
960
-
961
- /** @internal */
962
- async execMethod(methodName: string, methodData?, instance?) { }
963
- }
964
-
965
- export interface AbstractObject {
966
- readonly type: string
967
- through: boolean
968
- throughOtherPlayer: boolean
969
- autoChangeMap?(nextPosition: Position): Promise<boolean>
970
- execMethod(methodName: string, methodData?, instance?)
971
- changeMap(mapName: string)
972
- syncChanges()
973
- }