@rpgjs/client 4.1.3 → 4.2.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 (67) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/browser/{React-da18564b.js → React-f6f9b1de.js} +25 -0
  3. package/browser/index-e74a289f.js +44225 -0
  4. package/browser/manifest.json +10 -1
  5. package/browser/rpg.client.js +33 -44122
  6. package/browser/rpg.client.umd.cjs +470 -341
  7. package/lib/Effects/TransitionScene.d.ts +4 -3
  8. package/lib/Effects/TransitionScene.js +4 -2
  9. package/lib/Effects/TransitionScene.js.map +1 -1
  10. package/lib/GameEngine.d.ts +1 -1
  11. package/lib/GameEngine.js +4 -1
  12. package/lib/GameEngine.js.map +1 -1
  13. package/lib/Gui/Gui.d.ts +2 -1
  14. package/lib/Gui/Gui.js +7 -5
  15. package/lib/Gui/Gui.js.map +1 -1
  16. package/lib/Gui/React.d.ts +1 -0
  17. package/lib/Gui/React.js +23 -0
  18. package/lib/Gui/React.js.map +1 -1
  19. package/lib/Gui/Vue.js +21 -2
  20. package/lib/Gui/Vue.js.map +1 -1
  21. package/lib/KeyboardControls.d.ts +40 -14
  22. package/lib/KeyboardControls.js +42 -15
  23. package/lib/KeyboardControls.js.map +1 -1
  24. package/lib/Presets/AnimationSpritesheet.d.ts +3 -3
  25. package/lib/Presets/AnimationSpritesheet.js +7 -3
  26. package/lib/Presets/AnimationSpritesheet.js.map +1 -1
  27. package/lib/Renderer.d.ts +13 -5
  28. package/lib/Renderer.js +40 -16
  29. package/lib/Renderer.js.map +1 -1
  30. package/lib/Resources.js.map +1 -1
  31. package/lib/RpgClientEngine.d.ts +10 -4
  32. package/lib/RpgClientEngine.js +26 -17
  33. package/lib/RpgClientEngine.js.map +1 -1
  34. package/lib/Scene/Map.d.ts +4 -5
  35. package/lib/Scene/Map.js +6 -4
  36. package/lib/Scene/Map.js.map +1 -1
  37. package/lib/Scene/Scene.d.ts +7 -2
  38. package/lib/Scene/Scene.js +9 -3
  39. package/lib/Scene/Scene.js.map +1 -1
  40. package/lib/Tilemap/index.d.ts +4 -3
  41. package/lib/Tilemap/index.js +7 -3
  42. package/lib/Tilemap/index.js.map +1 -1
  43. package/lib/clientEntryPoint.js +5 -5
  44. package/lib/clientEntryPoint.js.map +1 -1
  45. package/lib/index.d.ts +3 -0
  46. package/lib/index.js +3 -0
  47. package/lib/index.js.map +1 -1
  48. package/lib/inject.d.ts +23 -0
  49. package/lib/inject.js +30 -0
  50. package/lib/inject.js.map +1 -0
  51. package/package.json +6 -6
  52. package/src/Effects/TransitionScene.ts +3 -1
  53. package/src/GameEngine.ts +2 -2
  54. package/src/Gui/Gui.ts +5 -5
  55. package/src/Gui/React.ts +26 -1
  56. package/src/Gui/Vue.ts +24 -6
  57. package/src/KeyboardControls.ts +43 -15
  58. package/src/Presets/AnimationSpritesheet.ts +9 -4
  59. package/src/Renderer.ts +44 -19
  60. package/src/Resources.ts +0 -1
  61. package/src/RpgClientEngine.ts +29 -18
  62. package/src/Scene/Map.ts +8 -7
  63. package/src/Scene/Scene.ts +8 -4
  64. package/src/Tilemap/index.ts +6 -4
  65. package/src/clientEntryPoint.ts +6 -4
  66. package/src/index.ts +4 -1
  67. package/src/inject.ts +34 -0
@@ -1,4 +1,4 @@
1
- import { DefaultInput, Direction, Input, Utils } from '@rpgjs/common'
1
+ import { DefaultInput, Direction, InjectContext, Input, Utils } from '@rpgjs/common'
2
2
  import { ControlOptions, Controls } from '@rpgjs/types';
3
3
  import { RpgClientEngine } from './RpgClientEngine';
4
4
 
@@ -191,6 +191,8 @@ const inverseKeyCodeTable = inverse(keyCodeTable)
191
191
  type BoundKey = { actionName: string, options: ControlOptions, parameters?: any }
192
192
 
193
193
  export class KeyboardControls {
194
+ private clientEngine: RpgClientEngine = this.context.inject(RpgClientEngine)
195
+
194
196
  private keyState: {
195
197
  [keyName: string]: {
196
198
  isDown: boolean,
@@ -204,8 +206,8 @@ export class KeyboardControls {
204
206
  private lastKeyPressed: number | null = null
205
207
  private _controlsOptions: Controls = {}
206
208
 
207
- constructor(private clientEngine: RpgClientEngine) {
208
- const { globalConfig } = clientEngine
209
+ constructor(private context: InjectContext) {
210
+ const { globalConfig } = this.clientEngine
209
211
  this.setupListeners();
210
212
  this.setInputs({
211
213
  ...DefaultInput,
@@ -340,10 +342,11 @@ export class KeyboardControls {
340
342
  * From the name of the entry, we retrieve the control information
341
343
  *
342
344
  * ```ts
343
- * import { Input } from '@rpgjs/client'
345
+ * import { Input, inject, KeyboardControls } from '@rpgjs/client'
344
346
  *
345
- * // In method hooks, client is RpgClientEngine
346
- * client.controls.getControl(Input.Enter)
347
+ * const controls = inject(KeyboardControls)
348
+ * controls.getControl(Input.Enter)
349
+
347
350
  * if (control) {
348
351
  * console.log(control.actionName) // action
349
352
  * }
@@ -358,23 +361,36 @@ export class KeyboardControls {
358
361
  return this.boundKeys[inputName]
359
362
  }
360
363
 
364
+ /**
365
+ * Returns all controls
366
+ *
367
+ * @method getControls()
368
+ * @since 4.2.0
369
+ * @returns { { [key: string]: BoundKey } }
370
+ * @memberof KeyboardControls
371
+ */
372
+ getControls(): { [key: string]: BoundKey } {
373
+ return this.boundKeys
374
+ }
375
+
361
376
  /**
362
377
  * Triggers an input according to the name of the control
363
378
  *
364
379
  * ```ts
365
- * import { Control } from '@rpgjs/client'
380
+ * import { Control, inject, KeyboardControls } from '@rpgjs/client'
366
381
  *
367
- * // In method hooks, client is RpgClientEngine
368
- * client.controls.applyControl(Control.Action)
382
+ * const controls = inject(KeyboardControls)
383
+ * controls.applyControl(Control.Action)
369
384
  * ```
370
385
  *
371
386
  * You can put a second parameter or indicate on whether the key is pressed or released
372
387
  *
373
388
  * ```ts
374
- * import { Control } from '@rpgjs/client'
389
+ * import { Control, inject, KeyboardControls } from '@rpgjs/client'
375
390
  *
376
- * client.controls.applyControl(Control.Up, true) // keydown
377
- * client.controls.applyControl(Control.Up, false) // keyup
391
+ * const controls = inject(KeyboardControls)
392
+ * controls.applyControl(Control.Up, true) // keydown
393
+ * controls.applyControl(Control.Up, false) // keyup
378
394
  * ```
379
395
  * @title Apply Control
380
396
  * @method applyControl(controlName,isDown)
@@ -439,10 +455,10 @@ export class KeyboardControls {
439
455
  * * delay.otherControls {string | string[]} Indicates the other controls that will also have the delay at the same time
440
456
  *
441
457
  * ```ts
442
- * import { Control, Input } from '@rpgjs/client'
458
+ * import { Control, Input, inject, KeyboardControls } from '@rpgjs/client'
443
459
  *
444
- * // In method hooks, client is RpgClientEngine
445
- * client.controls.setInputs({
460
+ * const controls = inject(KeyboardControls)
461
+ * controls.setInputs({
446
462
  [Control.Up]: {
447
463
  repeat: true,
448
464
  bind: Input.Up
@@ -505,6 +521,18 @@ export class KeyboardControls {
505
521
  * Control.Action | action
506
522
  * Control.Back | back
507
523
  *
524
+ * @enum {string} Mouse Event
525
+ *
526
+ * click | Click
527
+ * dblclick | Double Click
528
+ * mousedown | Mouse Down
529
+ * mouseup | Mouse Up
530
+ * mouseover | Mouse Over
531
+ * mousemove | Mouse Move
532
+ * mouseout | Mouse Out
533
+ * contextmenu | Context Menu
534
+ *
535
+ *
508
536
  * @enum {string} Input
509
537
  *
510
538
  * break | Pause
@@ -1,14 +1,19 @@
1
1
  import { Direction } from '@rpgjs/common'
2
2
  import { Animation } from '../Effects/AnimationCharacter'
3
3
 
4
- export const RMSpritesheet = (framesWidth, framesHeight, frameStand = 1) => {
4
+ export const RMSpritesheet = (framesWidth: number, framesHeight: number, frameStand: number = 1) => {
5
+
6
+ if (framesWidth <= frameStand) {
7
+ frameStand = framesWidth - 1
8
+ }
5
9
 
6
10
  const frameY = direction => {
11
+ const gap = Math.max(4 - framesHeight, 0)
7
12
  return {
8
13
  [Direction.Down]: 0,
9
- [Direction.Left]: 1,
10
- [Direction.Right]: 2,
11
- [Direction.Up]: 3
14
+ [Direction.Left]: Math.max(0, 1 - gap),
15
+ [Direction.Right]: Math.max(0, 2 - gap),
16
+ [Direction.Up]: Math.max(0, 3 - gap)
12
17
  }[direction]
13
18
  }
14
19
 
package/src/Renderer.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RpgPlugin, HookClient, Utils } from '@rpgjs/common'
1
+ import { RpgPlugin, HookClient, Utils, InjectContext } from '@rpgjs/common'
2
2
  import { SceneMap } from './Scene/Map'
3
3
  import { Scene } from './Scene/Scene'
4
4
  import { Scene as PresetScene } from './Presets/Scene'
@@ -10,6 +10,7 @@ import { Subject, forkJoin } from 'rxjs'
10
10
  import { GameEngineClient } from './GameEngine'
11
11
  import { SpinnerGraphic } from './Effects/Spinner'
12
12
  import { autoDetectRenderer, Container, EventBoundary, FederatedEvent, FederatedPointerEvent, Graphics, ICanvas, IRenderer } from 'pixi.js'
13
+ import { KeyboardControls } from './KeyboardControls'
13
14
 
14
15
  const { elementToPositionAbsolute } = Utils
15
16
 
@@ -22,7 +23,17 @@ enum ContainerName {
22
23
  Map = 'map'
23
24
  }
24
25
 
26
+ export const EVENTS_MAP = {
27
+ MouseEvent: ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'contextmenu', 'wheel'],
28
+ KeyboardEvent: ['keydown', 'keyup', 'keypress', 'keydownoutside', 'keyupoutside', 'keypressoutside'],
29
+ PointerEvent: ['pointerdown', 'pointerup', 'pointermove', 'pointerover', 'pointerout', 'pointerenter', 'pointerleave', 'pointercancel'],
30
+ TouchEvent: ['touchstart', 'touchend', 'touchmove', 'touchcancel']
31
+ };
32
+
25
33
  export class RpgRenderer {
34
+ private gameEngine: GameEngineClient = this.context.inject(GameEngineClient)
35
+ private clientEngine: RpgClientEngine = this.context.inject(RpgClientEngine)
36
+
26
37
  public vm: ComponentPublicInstance
27
38
  public app: App
28
39
  public readonly stage: Container = new Container()
@@ -38,7 +49,6 @@ export class RpgRenderer {
38
49
  private _height: number = 400
39
50
  private canvasEl: HTMLElement
40
51
  private selector: HTMLElement
41
- private gameEngine: GameEngineClient = this.clientEngine.gameEngine
42
52
  private loadingScene = {
43
53
  transitionIn: new Subject(),
44
54
  transitionOut: new Subject()
@@ -47,7 +57,7 @@ export class RpgRenderer {
47
57
  private prevObjectScene: any = {}
48
58
  public transitionMode: TransitionMode = TransitionMode.Fading
49
59
 
50
- constructor(private clientEngine: RpgClientEngine) {
60
+ constructor(private context: InjectContext) {
51
61
  this.clientEngine.tick.subscribe(({ timestamp, deltaRatio, frame, deltaTime }) => {
52
62
  this.draw(timestamp, deltaTime, deltaRatio, frame)
53
63
  })
@@ -81,8 +91,8 @@ export class RpgRenderer {
81
91
  this.spinner.y = h * 0.5
82
92
  }
83
93
 
84
- get canvas(): ICanvas {
85
- return this.renderer.view
94
+ get canvas(): HTMLCanvasElement {
95
+ return this.renderer.view as HTMLCanvasElement
86
96
  }
87
97
 
88
98
  get height(): number {
@@ -135,9 +145,24 @@ export class RpgRenderer {
135
145
  this.fadeContainer.visible = false
136
146
  this.fadeContainer.alpha = 0
137
147
 
138
- await RpgGui._initialize(this.clientEngine, this.guiEl)
148
+ await RpgGui._initialize(this.context, this.guiEl)
139
149
 
140
150
  this.resize()
151
+ this.bindMouseControls()
152
+
153
+ }
154
+
155
+ private bindMouseControls() {
156
+ const controlInstance = this.context.inject(KeyboardControls)
157
+ const controls = controlInstance.getControls()
158
+ for (let key in controls) {
159
+ const { actionName } = controls[key]
160
+ if (EVENTS_MAP.MouseEvent.includes(key)) {
161
+ this.canvas.addEventListener(key, (e) => {
162
+ controlInstance.applyControl(actionName)
163
+ })
164
+ }
165
+ }
141
166
  }
142
167
 
143
168
  /** @internal */
@@ -205,7 +230,7 @@ export class RpgRenderer {
205
230
  this.loadingScene.transitionOut.complete()
206
231
  }
207
232
  if (this.transitionMode == TransitionMode.Fading) {
208
- new TransitionScene(this.clientEngine, this.fadeContainer)
233
+ new TransitionScene(this.context, this.fadeContainer)
209
234
  .addFadeOut()
210
235
  .onComplete(finish)
211
236
  .start()
@@ -232,7 +257,7 @@ export class RpgRenderer {
232
257
  switch (name) {
233
258
  case PresetScene.Map:
234
259
  const sceneClass = scenes[PresetScene.Map] || SceneMap
235
- this.scene = new sceneClass(this.gameEngine, this.renderer, {
260
+ this.scene = new sceneClass(this.context, this.renderer, {
236
261
  screenWidth: this.renderer.screen.width,
237
262
  screenHeight: this.renderer.screen.height,
238
263
  drawMap: this.options.drawMap
@@ -248,7 +273,7 @@ export class RpgRenderer {
248
273
  RpgPlugin.emit(HookClient.AfterSceneLoading, this.scene)
249
274
  }
250
275
  if (this.transitionMode == TransitionMode.Fading) {
251
- new TransitionScene(this.clientEngine, this.fadeContainer)
276
+ new TransitionScene(this.context, this.fadeContainer)
252
277
  .addFadeIn()
253
278
  .onComplete(finish)
254
279
  .start()
@@ -273,11 +298,18 @@ export class RpgRenderer {
273
298
  * @returns {void}
274
299
  */
275
300
  propagateEvent(ev: MouseEvent) {
301
+ const rect = this.canvas.getBoundingClientRect();
302
+ const canvasX = rect.left + window.scrollX;
303
+ const canvasY = rect.top + window.scrollY;
304
+ const realX = ev.clientX - canvasX;
305
+ const realY = ev.clientY - canvasY;
276
306
  const boundary = new EventBoundary(this.stage);
277
307
  const event = new FederatedPointerEvent(boundary)
278
- event.global.set(ev.clientX, ev.clientY);
308
+ event.global.set(realX, realY);
279
309
  event.type = ev.type;
280
- this.getScene<SceneMap>()?.viewport?.emit(event.type as any, event)
310
+ const hitTestTarget = boundary.hitTest(realX, realY);
311
+ hitTestTarget?.dispatchEvent(event)
312
+ this.canvas.dispatchEvent(new MouseEvent(ev.type, ev))
281
313
  }
282
314
 
283
315
  /***
@@ -290,14 +322,7 @@ export class RpgRenderer {
290
322
  * @returns {void}
291
323
  */
292
324
  addPropagateEventsFrom(el: HTMLElement) {
293
- const eventMap = {
294
- MouseEvent: ['click', 'mousedown', 'mouseup', 'mousemove', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'contextmenu', 'wheel'],
295
- KeyboardEvent: ['keydown', 'keyup', 'keypress', 'keydownoutside', 'keyupoutside', 'keypressoutside'],
296
- PointerEvent: ['pointerdown', 'pointerup', 'pointermove', 'pointerover', 'pointerout', 'pointerenter', 'pointerleave', 'pointercancel'],
297
- TouchEvent: ['touchstart', 'touchend', 'touchmove', 'touchcancel']
298
- };
299
-
300
- for (let [_Constructor, events] of Object.entries(eventMap)) {
325
+ for (let [_Constructor, events] of Object.entries(EVENTS_MAP)) {
301
326
  for (let type of events) {
302
327
  el.addEventListener(type, (e) => {
303
328
  const _class = window[_Constructor] ?? MouseEvent
package/src/Resources.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { RpgClientEngine } from "./RpgClientEngine"
2
- import { Utils } from '@rpgjs/common'
3
2
 
4
3
  /**
5
4
  * Get/Set images in resources
@@ -15,6 +15,7 @@ import {
15
15
  RpgCommonMap,
16
16
  Scheduler,
17
17
  Control,
18
+ InjectContext,
18
19
  } from '@rpgjs/common'
19
20
  import { RpgSound } from './Sound/RpgSound'
20
21
  import { SceneMap } from './Scene/Map'
@@ -48,6 +49,7 @@ export class RpgClientEngine {
48
49
  *
49
50
  * @prop {RpgRenderer} [renderer]
50
51
  * @readonly
52
+ * @deprecated Use `inject(RpgRenderer)` instead. Will be removed in v5
51
53
  * @memberof RpgClientEngine
52
54
  * */
53
55
  public renderer: RpgRenderer
@@ -74,6 +76,7 @@ export class RpgClientEngine {
74
76
  * Get the class managing the keyboard
75
77
  *
76
78
  * @prop {KeyboardControls} [controls]
79
+ * @deprecated Use `inject(KeyboardControls)` instead. Will be removed in v5
77
80
  * @readonly
78
81
  * @memberof RpgClientEngine
79
82
  * */
@@ -107,6 +110,10 @@ export class RpgClientEngine {
107
110
  private serverFps: number = 60
108
111
  private scheduler: Scheduler = new Scheduler()
109
112
  private _serverUrl: string = ''
113
+ /**
114
+ * * @deprecated Use `inject(GameEngineClient)` instead. Will be removed in v5
115
+ */
116
+ public gameEngine = this.context.inject(GameEngineClient)
110
117
 
111
118
  /**
112
119
  * Read objects synchronized with the server
@@ -124,7 +131,7 @@ export class RpgClientEngine {
124
131
 
125
132
  envs?: object = {}
126
133
 
127
- constructor(public gameEngine: GameEngineClient, private options) {
134
+ constructor(private context: InjectContext, private options) {
128
135
  this.envs = options.envs || {}
129
136
  this.tick.subscribe(({ timestamp, deltaTime }) => {
130
137
  if (timestamp != -1) this.step(timestamp, deltaTime)
@@ -132,9 +139,9 @@ export class RpgClientEngine {
132
139
  }
133
140
 
134
141
  private async _init() {
135
- this.renderer = new RpgRenderer(this)
142
+ this.renderer = this.context.inject(RpgRenderer)
136
143
 
137
- const pluginLoadRessource = async (hookName: string, type: string) => {
144
+ const pluginLoadResource = async (hookName: string, type: string) => {
138
145
  const resource = this.options[type] || []
139
146
  this.options[type] = [
140
147
  ...Utils.arrayFlat(await RpgPlugin.emit(hookName, resource)) || [],
@@ -142,9 +149,9 @@ export class RpgClientEngine {
142
149
  ]
143
150
  }
144
151
 
145
- await pluginLoadRessource(HookClient.AddSpriteSheet, 'spritesheets')
146
- await pluginLoadRessource(HookClient.AddGui, 'gui')
147
- await pluginLoadRessource(HookClient.AddSound, 'sounds')
152
+ await pluginLoadResource(HookClient.AddSpriteSheet, 'spritesheets')
153
+ await pluginLoadResource(HookClient.AddGui, 'gui')
154
+ await pluginLoadResource(HookClient.AddSound, 'sounds')
148
155
 
149
156
  this.renderer.options = {
150
157
  selector: '#rpg',
@@ -170,8 +177,8 @@ export class RpgClientEngine {
170
177
  const id: any = isString(sound) ? extractId(sound) : undefined
171
178
  this.addSound(sound, id)
172
179
  })
173
-
174
- // obsolete
180
+
181
+ // deprecated
175
182
  if (typeof __RPGJS_PRODUCTION__ != 'undefined' && __RPGJS_PRODUCTION__) {
176
183
  if ('serviceWorker' in navigator) {
177
184
  window.addEventListener('load', () => {
@@ -180,7 +187,7 @@ export class RpgClientEngine {
180
187
  }
181
188
  }
182
189
 
183
- this.controls = new KeyboardControls(this)
190
+ this.controls = this.context.inject(KeyboardControls)
184
191
  }
185
192
 
186
193
  private addResource(resourceClass, cb) {
@@ -333,7 +340,7 @@ export class RpgClientEngine {
333
340
  }
334
341
  // @ts-ignore
335
342
  const envUrl = this.envs.VITE_SERVER_URL
336
- this.connection(
343
+ await this.connection(
337
344
  serverUri.url ? serverUri.url + ':' + serverUri.port :
338
345
  envUrl ? envUrl : undefined
339
346
  )
@@ -420,7 +427,7 @@ export class RpgClientEngine {
420
427
  * @returns {void}
421
428
  * @memberof RpgClientEngine
422
429
  */
423
- connection(uri?: string) {
430
+ async connection(uri?: string) {
424
431
  const { standalone } = this.gameEngine
425
432
 
426
433
  this._serverUrl = uri || ''
@@ -451,12 +458,16 @@ export class RpgClientEngine {
451
458
  RpgPlugin.emit(HookClient.ConnectedError, [this, err, this.socket], true)
452
459
  })
453
460
 
454
- this.socket.on('preLoadScene', (name: string) => {
455
- if (this.lastScene == name) {
461
+ this.socket.on('preLoadScene', ({ id, reconnect }: { id: string, reconnect?: boolean }) => {
462
+ if (this.lastScene == id) {
456
463
  return
457
464
  }
458
- this.lastScene = name
459
- this.renderer.transitionScene(name)
465
+ this.lastScene = id
466
+ this.renderer.transitionScene(id)
467
+ if (reconnect) {
468
+ this.roomJoin.next('')
469
+ this.roomJoin.complete()
470
+ }
460
471
  })
461
472
 
462
473
  this.socket.on(SocketEvents.GameReload, () => {
@@ -467,7 +478,7 @@ export class RpgClientEngine {
467
478
  this.renderer.loadScene(name, data)
468
479
  })
469
480
 
470
- this.socket.on(SocketEvents.ChangeServer, ({ url, port }) => {
481
+ this.socket.on(SocketEvents.ChangeServer, async({ url, port }) => {
471
482
  const connection = url + ':' + port
472
483
  if (this.lastConnection == connection) {
473
484
  return
@@ -617,7 +628,7 @@ export class RpgClientEngine {
617
628
  paramsChanged,
618
629
  isShape
619
630
  })
620
-
631
+
621
632
  // perform actions on the sprite after creation/update
622
633
  callAction(key, paramsChanged)
623
634
  }
@@ -651,7 +662,7 @@ export class RpgClientEngine {
651
662
  RpgGui._setSocket(this.socket)
652
663
 
653
664
  if (standalone) {
654
- this.socket.connection({
665
+ await this.socket.connection({
655
666
  auth: {
656
667
  token: this.session
657
668
  }
package/src/Scene/Map.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { RpgCommonMap, RpgPlugin, HookClient, RpgShape, Utils, RpgCommonPlayer } from '@rpgjs/common'
1
+ import { RpgCommonMap, RpgPlugin, HookClient, RpgShape, Utils, RpgCommonPlayer, InjectContext } from '@rpgjs/common'
2
2
  import TileMap from '../Tilemap'
3
3
  import * as _PixiViewport from 'pixi-viewport'
4
4
  import { type Viewport } from 'pixi-viewport'
5
5
  import { Scene, SceneObservableData, SceneSpriteLogic } from './Scene'
6
6
  import { spritesheets } from '../Sprite/Spritesheets'
7
7
  import { RpgSound } from '../Sound/RpgSound'
8
- import { GameEngineClient } from '../GameEngine'
9
8
  import { TiledLayerType, TiledMap } from '@rpgjs/tiled'
10
9
  import { RpgComponent } from '../Components/Component'
11
- import { CameraOptions } from '@rpgjs/types'
10
+ import { type CameraOptions } from '@rpgjs/types'
12
11
  import { Assets, Container, Point, IRenderer, DisplayObjectEvents, utils, FederatedPointerEvent } from 'pixi.js'
13
12
  import { EventLayer } from './EventLayer'
14
13
 
@@ -53,10 +52,11 @@ export class SceneMap extends Scene {
53
52
  shapes = {}
54
53
 
55
54
  constructor(
56
- public game: GameEngineClient,
55
+ protected context: InjectContext,
57
56
  private renderer: IRenderer,
58
- private options: { screenWidth?: number, screenHeight?: number, drawMap?: boolean } = {}) {
59
- super(game)
57
+ private options: { screenWidth?: number, screenHeight?: number, drawMap?: boolean } = {}
58
+ ) {
59
+ super(context)
60
60
  if (options.drawMap === undefined) this.options.drawMap = true
61
61
  this.onInit()
62
62
  }
@@ -97,7 +97,7 @@ export class SceneMap extends Scene {
97
97
 
98
98
  RpgCommonMap.bufferClient.set(obj.id, this.gameMap)
99
99
 
100
- this.tilemap = new TileMap(this.gameMap.getData(), this.game.renderer)
100
+ this.tilemap = new TileMap(this.context, this.gameMap.getData())
101
101
 
102
102
  // TODO: Remove this
103
103
  Assets.reset()
@@ -370,6 +370,7 @@ export class SceneMap extends Scene {
370
370
  this.viewport.on(eventName, (...args) => {
371
371
  const ev: FederatedPointerEvent = args[0] as any
372
372
  const pos = ev.getLocalPosition(this.viewport as Viewport)
373
+ if (ev.defaultPrevented) return
373
374
  cb(pos, ev)
374
375
  })
375
376
  }
@@ -1,4 +1,4 @@
1
- import { RpgPlugin, HookClient, DefaultInput } from '@rpgjs/common'
1
+ import { RpgPlugin, HookClient, DefaultInput, InjectContext } from '@rpgjs/common'
2
2
  import { KeyboardControls } from '../KeyboardControls'
3
3
  import RpgSprite from '../Sprite/Character'
4
4
  import { Animation } from '../Effects/Animation'
@@ -29,7 +29,7 @@ export abstract class Scene {
29
29
  protected objects: Map<string, RpgComponent> = new Map()
30
30
  protected animationLayer: Container = new Container()
31
31
 
32
- private controls: KeyboardControls
32
+ private controls: KeyboardControls = this.context.inject(KeyboardControls)
33
33
  private animations: Animation[] = []
34
34
 
35
35
  private _data: BehaviorSubject<SceneObservableData> = new BehaviorSubject({
@@ -37,6 +37,11 @@ export abstract class Scene {
37
37
  partial: {}
38
38
  })
39
39
 
40
+ /**
41
+ * @deprecated Use `inject(GameEngineClient)` instead. Will be removed in v5
42
+ */
43
+ public game: GameEngineClient = this.context.inject(GameEngineClient)
44
+
40
45
  /**
41
46
  * Listen to the movement of objects on stage
42
47
  *
@@ -63,9 +68,8 @@ export abstract class Scene {
63
68
  [key: string]: any
64
69
  }> = new Subject()
65
70
 
66
- constructor(public game: GameEngineClient) {
71
+ constructor(protected context: InjectContext) {
67
72
  const { globalConfig } = this.game.clientEngine
68
- this.controls = this.game.clientEngine.controls
69
73
  const mergeInputs = {
70
74
  ...DefaultInput,
71
75
  ...(globalConfig.inputs || {})
@@ -1,4 +1,4 @@
1
- import { Utils } from '@rpgjs/common'
1
+ import { InjectContext, Utils } from '@rpgjs/common'
2
2
  import ImageLayer from './ImageLayer'
3
3
  import TileLayer from './TileLayer'
4
4
  import TileSet from './TileSet'
@@ -25,9 +25,9 @@ export default class TileMap extends Container {
25
25
  } = {}
26
26
  private tilesLayer: Container = new Container()
27
27
  private frameTile: number = 0
28
+ private renderer: RpgRenderer = this.context.inject(RpgRenderer)
28
29
 
29
-
30
- constructor(private data: MapInfo, private renderer: RpgRenderer) {
30
+ constructor(private context: InjectContext, private data: MapInfo) {
31
31
  super()
32
32
  this.x = 0
33
33
  this.y = 0
@@ -108,7 +108,9 @@ export default class TileMap extends Container {
108
108
  // if (yObject - tile.y >= height) {
109
109
  // return false
110
110
  // }
111
- const zIntersection = intersection([zObject, zObject + height], [realZ, realZ + tile.height])
111
+ // Always get tile.height for height. TODO: Fix this the height of the character. To be seen according to future implementations...
112
+ // Discussion: https://community.rpgjs.dev/d/250-rpgjs-v420/6
113
+ const zIntersection = intersection([zObject, zObject + tile.height /** Old code: height */], [realZ, realZ + tile.height])
112
114
  if (!zIntersection) {
113
115
  return true
114
116
  }
@@ -1,6 +1,7 @@
1
- import { HookClient, loadModules, ModuleType } from '@rpgjs/common'
1
+ import { InjectContext, HookClient, loadModules, ModuleType } from '@rpgjs/common'
2
2
  import { GameEngineClient } from './GameEngine'
3
3
  import { RpgClientEngine } from './RpgClientEngine'
4
+ import { setInject } from './inject'
4
5
 
5
6
  interface RpgClientEntryPointOptions {
6
7
  /**
@@ -142,7 +143,8 @@ export default (modules: ModuleType[], options: RpgClientEntryPointOptions): Rpg
142
143
  }
143
144
  })
144
145
 
145
- const gameEngine = new GameEngineClient()
146
- const clientEngine = new RpgClientEngine(gameEngine, options)
147
- return clientEngine
146
+ const context = new InjectContext()
147
+ setInject(context)
148
+
149
+ return context.inject(RpgClientEngine, [options])
148
150
  }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Direction, Control, Input, PrebuiltGui, HookServer, HookClient, RpgPlugin, RpgModule, RpgCommonPlayer as RpgSpriteLogic } from '@rpgjs/common'
2
2
  export { default as entryPoint } from './clientEntryPoint'
3
3
  export type { RpgClient, RpgSceneHooks, RpgSceneMapHooks, RpgSpriteHooks, RpgClientEngineHooks } from './RpgClient'
4
+ export { RpgRenderer } from './Renderer'
4
5
  export { Scene as RpgScene } from './Scene/Scene'
5
6
  export { RpgClientEngine } from './RpgClientEngine'
6
7
  export { Spritesheet } from './Sprite/Spritesheet'
@@ -16,6 +17,7 @@ export { SceneMap as RpgSceneMap } from './Scene/Map'
16
17
  export { RpgGui } from './Gui/Gui';
17
18
  export { Timeline, Ease } from './Effects/Timeline';
18
19
  export { RpgComponent, RpgComponent as RpgSprite } from './Components/Component'
20
+ export { KeyboardControls } from './KeyboardControls'
19
21
  export { World, room } from 'simple-room-client'
20
22
 
21
23
  import { spritesheets } from './Sprite/Spritesheets'
@@ -23,4 +25,5 @@ import { sounds } from './Sound/Sounds'
23
25
  export const RpgResource = {
24
26
  spritesheets,
25
27
  sounds
26
- }
28
+ }
29
+ export { inject } from './inject'
package/src/inject.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { InjectContext } from "@rpgjs/common";
2
+
3
+ let instanceContext: InjectContext | null = null
4
+
5
+ /**
6
+ * Dependency injection function for RPGJS client side.
7
+ *
8
+ * This client-side `inject` function is used to retrieve instances of various classes within the RPGJS framework,
9
+ * specifically for client-side modules. It enables developers to access singleton instances of key classes such as
10
+ * `RpgClientEngine`, `KeyboardControls`, and `RpgRenderer`. Utilizing `inject` on the client side promotes modular
11
+ * and maintainable code by simplifying dependency management.
12
+ *
13
+ * @template T The class type that you want to retrieve an instance of, relevant to client-side modules.
14
+ * @returns {T} Returns the singleton instance of the specified class, ensuring only one instance is used client-side.
15
+ * @since 4.2.0
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // Example of injecting the RpgClientEngine
20
+ * import { inject, RpgClientEngine } from '@rpgjs/client'
21
+ * const client = inject(RpgClientEngine)
22
+ * ```
23
+ */
24
+ export function inject<T>(service: new (...args: any[]) => T, args: any[] = []): T {
25
+ return instanceContext!.inject(service, args);
26
+ }
27
+
28
+ export function setInject(context: InjectContext) {
29
+ instanceContext = context;
30
+ }
31
+
32
+ export function clearInject() {
33
+ instanceContext = null
34
+ }