@rpgjs/client 3.3.2 → 4.0.0-beta.3

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 (164) hide show
  1. package/LICENSE +19 -0
  2. package/lib/Components/AbstractComponent.d.ts +3 -2
  3. package/lib/Components/AbstractComponent.js +17 -28
  4. package/lib/Components/AbstractComponent.js.map +1 -1
  5. package/lib/Components/BarComponent.d.ts +2 -1
  6. package/lib/Components/BarComponent.js +32 -33
  7. package/lib/Components/BarComponent.js.map +1 -1
  8. package/lib/Components/Component.d.ts +3 -3
  9. package/lib/Components/Component.js +84 -96
  10. package/lib/Components/Component.js.map +1 -1
  11. package/lib/Components/DebugComponent.d.ts +2 -1
  12. package/lib/Components/DebugComponent.js +9 -10
  13. package/lib/Components/DebugComponent.js.map +1 -1
  14. package/lib/Components/ImageComponent.d.ts +2 -1
  15. package/lib/Components/ImageComponent.js +5 -7
  16. package/lib/Components/ImageComponent.js.map +1 -1
  17. package/lib/Components/ShapeComponent.d.ts +2 -1
  18. package/lib/Components/ShapeComponent.js +14 -14
  19. package/lib/Components/ShapeComponent.js.map +1 -1
  20. package/lib/Components/TextComponent.d.ts +3 -2
  21. package/lib/Components/TextComponent.js +10 -9
  22. package/lib/Components/TextComponent.js.map +1 -1
  23. package/lib/Components/TileComponent.d.ts +2 -1
  24. package/lib/Components/TileComponent.js +12 -16
  25. package/lib/Components/TileComponent.js.map +1 -1
  26. package/lib/Effects/Animation.d.ts +10 -9
  27. package/lib/Effects/Animation.js +36 -36
  28. package/lib/Effects/Animation.js.map +1 -1
  29. package/lib/Effects/AnimationCharacter.js +2 -5
  30. package/lib/Effects/AnimationCharacter.js.map +1 -1
  31. package/lib/Effects/Spinner.d.ts +3 -2
  32. package/lib/Effects/Spinner.js +2 -5
  33. package/lib/Effects/Spinner.js.map +1 -1
  34. package/lib/Effects/Timeline.d.ts +1 -1
  35. package/lib/Effects/Timeline.js +6 -10
  36. package/lib/Effects/Timeline.js.map +1 -1
  37. package/lib/Effects/TransitionScene.d.ts +2 -1
  38. package/lib/Effects/TransitionScene.js +3 -7
  39. package/lib/Effects/TransitionScene.js.map +1 -1
  40. package/lib/GameEngine.js +51 -26
  41. package/lib/GameEngine.js.map +1 -1
  42. package/lib/Interfaces/Character.js +1 -2
  43. package/lib/Interfaces/Scene.js +1 -2
  44. package/lib/KeyboardControls.d.ts +2 -1
  45. package/lib/KeyboardControls.js +47 -45
  46. package/lib/KeyboardControls.js.map +1 -1
  47. package/lib/Logger.js +1 -5
  48. package/lib/Logger.js.map +1 -1
  49. package/lib/Presets/AnimationSpritesheet.js +9 -13
  50. package/lib/Presets/AnimationSpritesheet.js.map +1 -1
  51. package/lib/Presets/Scene.js +2 -5
  52. package/lib/Presets/Scene.js.map +1 -1
  53. package/lib/Renderer.d.ts +5 -3
  54. package/lib/Renderer.js +68 -60
  55. package/lib/Renderer.js.map +1 -1
  56. package/lib/Resources.js +1 -5
  57. package/lib/Resources.js.map +1 -1
  58. package/lib/RpgClient.d.ts +3 -2
  59. package/lib/RpgClient.js +1 -2
  60. package/lib/RpgClientEngine.d.ts +2 -1
  61. package/lib/RpgClientEngine.js +192 -176
  62. package/lib/RpgClientEngine.js.map +1 -1
  63. package/lib/RpgGui.js +40 -41
  64. package/lib/RpgGui.js.map +1 -1
  65. package/lib/Scene/EventLayer.d.ts +4 -0
  66. package/lib/Scene/EventLayer.js +8 -0
  67. package/lib/Scene/EventLayer.js.map +1 -0
  68. package/lib/Scene/Map.d.ts +17 -3
  69. package/lib/Scene/Map.js +157 -113
  70. package/lib/Scene/Map.js.map +1 -1
  71. package/lib/Scene/Scene.d.ts +3 -3
  72. package/lib/Scene/Scene.js +24 -20
  73. package/lib/Scene/Scene.js.map +1 -1
  74. package/lib/Scene/SceneData.js +1 -5
  75. package/lib/Scene/SceneData.js.map +1 -1
  76. package/lib/Sound/RpgSound.js +8 -11
  77. package/lib/Sound/RpgSound.js.map +1 -1
  78. package/lib/Sound/Sound.d.ts +2 -2
  79. package/lib/Sound/Sound.js +1 -5
  80. package/lib/Sound/Sound.js.map +1 -1
  81. package/lib/Sound/Sounds.js +4 -8
  82. package/lib/Sound/Sounds.js.map +1 -1
  83. package/lib/Sprite/Character.d.ts +3 -2
  84. package/lib/Sprite/Character.js +15 -16
  85. package/lib/Sprite/Character.js.map +1 -1
  86. package/lib/Sprite/Player.js +2 -9
  87. package/lib/Sprite/Player.js.map +1 -1
  88. package/lib/Sprite/Spritesheet.d.ts +4 -4
  89. package/lib/Sprite/Spritesheet.js +1 -5
  90. package/lib/Sprite/Spritesheet.js.map +1 -1
  91. package/lib/Sprite/Spritesheets.js +4 -8
  92. package/lib/Sprite/Spritesheets.js.map +1 -1
  93. package/lib/Tilemap/CommonLayer.d.ts +2 -1
  94. package/lib/Tilemap/CommonLayer.js +7 -11
  95. package/lib/Tilemap/CommonLayer.js.map +1 -1
  96. package/lib/Tilemap/ImageLayer.js +5 -7
  97. package/lib/Tilemap/ImageLayer.js.map +1 -1
  98. package/lib/Tilemap/Tile.d.ts +5 -5
  99. package/lib/Tilemap/Tile.js +19 -21
  100. package/lib/Tilemap/Tile.js.map +1 -1
  101. package/lib/Tilemap/TileLayer.d.ts +0 -7
  102. package/lib/Tilemap/TileLayer.js +27 -29
  103. package/lib/Tilemap/TileLayer.js.map +1 -1
  104. package/lib/Tilemap/TileSet.d.ts +2 -1
  105. package/lib/Tilemap/TileSet.js +9 -12
  106. package/lib/Tilemap/TileSet.js.map +1 -1
  107. package/lib/Tilemap/index.d.ts +3 -11
  108. package/lib/Tilemap/index.js +22 -61
  109. package/lib/Tilemap/index.js.map +1 -1
  110. package/lib/clientEntryPoint.js +26 -28
  111. package/lib/clientEntryPoint.js.map +1 -1
  112. package/lib/index.js +21 -75
  113. package/lib/index.js.map +1 -1
  114. package/package.json +19 -19
  115. package/src/Components/AbstractComponent.ts +120 -0
  116. package/src/Components/BarComponent.ts +179 -0
  117. package/src/Components/Component.ts +506 -0
  118. package/src/Components/DebugComponent.ts +36 -0
  119. package/src/Components/ImageComponent.ts +30 -0
  120. package/src/Components/ShapeComponent.ts +64 -0
  121. package/src/Components/TextComponent.ts +33 -0
  122. package/src/Components/TileComponent.ts +43 -0
  123. package/src/Effects/Animation.ts +297 -0
  124. package/src/Effects/AnimationCharacter.ts +7 -0
  125. package/src/Effects/Spinner.ts +19 -0
  126. package/src/Effects/Timeline.ts +294 -0
  127. package/src/Effects/TransitionScene.ts +57 -0
  128. package/src/GameEngine.ts +284 -0
  129. package/src/Interfaces/Character.ts +7 -0
  130. package/src/Interfaces/Scene.ts +9 -0
  131. package/src/KeyboardControls.ts +552 -0
  132. package/src/Logger.ts +3 -0
  133. package/src/Presets/AnimationSpritesheet.ts +36 -0
  134. package/src/Presets/Scene.ts +3 -0
  135. package/src/Renderer.ts +263 -0
  136. package/src/Resources.ts +40 -0
  137. package/src/RpgClient.ts +333 -0
  138. package/src/RpgClientEngine.ts +709 -0
  139. package/src/RpgGui.ts +553 -0
  140. package/src/RpgGuiCompiled.ts +43 -0
  141. package/src/Scene/EventLayer.ts +9 -0
  142. package/src/Scene/Map.ts +393 -0
  143. package/src/Scene/Scene.ts +270 -0
  144. package/src/Scene/SceneData.ts +13 -0
  145. package/src/Sound/RpgSound.ts +50 -0
  146. package/src/Sound/Sound.ts +91 -0
  147. package/src/Sound/Sounds.ts +7 -0
  148. package/src/Sprite/Character.ts +149 -0
  149. package/src/Sprite/Player.ts +3 -0
  150. package/src/Sprite/Spritesheet.ts +392 -0
  151. package/src/Sprite/Spritesheets.ts +8 -0
  152. package/src/Tilemap/CommonLayer.ts +20 -0
  153. package/src/Tilemap/ImageLayer.ts +20 -0
  154. package/src/Tilemap/Tile.ts +80 -0
  155. package/src/Tilemap/TileLayer.ts +142 -0
  156. package/src/Tilemap/TileSet.ts +40 -0
  157. package/src/Tilemap/index.ts +173 -0
  158. package/src/clientEntryPoint.ts +141 -0
  159. package/src/index.ts +25 -0
  160. package/src/types/howler.d.ts +73 -0
  161. package/tsconfig.json +30 -0
  162. package/lib/Components/ColorComponent.d.ts +0 -9
  163. package/lib/Components/ColorComponent.js +0 -18
  164. package/lib/Components/ColorComponent.js.map +0 -1
@@ -0,0 +1,393 @@
1
+ import { RpgCommonMap, RpgPlugin, HookClient, RpgShape, Utils, RpgCommonPlayer } from '@rpgjs/common'
2
+ import TileMap from '../Tilemap'
3
+ import { Viewport } from 'pixi-viewport'
4
+ import { Scene, SceneObservableData, SceneSpriteLogic } from './Scene'
5
+ import { spritesheets } from '../Sprite/Spritesheets'
6
+ import { RpgSound } from '../Sound/RpgSound'
7
+ import { GameEngineClient } from '../GameEngine'
8
+ import { TiledLayerType, TiledMap } from '@rpgjs/tiled'
9
+ import { RpgComponent } from '../Components/Component'
10
+ import { CameraOptions } from '@rpgjs/types'
11
+ import { Assets, Container, Point, IRenderer, DisplayObjectEvents, utils, FederatedPointerEvent } from 'pixi.js'
12
+ import { EventLayer } from './EventLayer'
13
+
14
+ interface MapObject extends TiledMap {
15
+ id: number
16
+ sounds: string | string[] | undefined
17
+ }
18
+
19
+ export class SceneMap extends Scene {
20
+ static readonly EVENTS_LAYER_DEFAULT: string = 'events-layer-default'
21
+
22
+ /**
23
+ * Get the tilemap
24
+ *
25
+ * @prop {PIXI.Container} [tilemap]
26
+ * @memberof RpgSceneMap
27
+ * */
28
+ public tilemap: TileMap
29
+
30
+ /**
31
+ * The viewport of the map
32
+ *
33
+ * It automatically follows the sprite representing the player but you can attach it to something else
34
+ *
35
+ * > Do not change the size of the viewport
36
+ *
37
+ * @prop {PIXI.Viewport} viewport
38
+ * @memberof RpgSceneMap
39
+ * */
40
+ public viewport: Viewport | undefined
41
+ private players: object = {}
42
+ private isLoaded: boolean = false
43
+ private gameMap: RpgCommonMap | undefined
44
+ private eventsLayers: {
45
+ [eventLayerName: string]: Container
46
+ } = {}
47
+ private defaultLayer: Container | undefined
48
+
49
+ shapes = {}
50
+
51
+ constructor(
52
+ public game: GameEngineClient,
53
+ private renderer: IRenderer,
54
+ private options: { screenWidth?: number, screenHeight?: number, drawMap?: boolean } = {}) {
55
+ super(game)
56
+ if (options.drawMap === undefined) this.options.drawMap = true
57
+ this.onInit()
58
+ }
59
+
60
+ private constructMethods() {
61
+ [
62
+ 'getTileIndex',
63
+ 'getTileByIndex',
64
+ 'getTileOriginPosition',
65
+ 'getTileByPosition',
66
+ 'getLayerByName'
67
+ ].forEach(method => this[method] = (this.gameMap as any)[method].bind(this.gameMap));
68
+ [
69
+ 'heightPx',
70
+ 'widthPx',
71
+ 'zTileHeight',
72
+ 'tileHeight',
73
+ 'tileWidth',
74
+ 'data',
75
+ 'layers'
76
+ ].forEach(prop => this[prop] = (this.gameMap as any)[prop])
77
+
78
+ }
79
+
80
+ /** @internal */
81
+ async load(obj: MapObject, prevObj: MapObject, isUpdate = false): Promise<Viewport> {
82
+ let { sounds } = obj
83
+ const { clientEngine } = this.game
84
+
85
+ if (sounds) {
86
+ if (!Utils.isArray(sounds)) sounds = obj.sounds = <string[]>[sounds]
87
+ }
88
+
89
+ this.gameMap = new RpgCommonMap()
90
+ this.gameMap.load(obj)
91
+ this.gameMap.clearCacheTilesets()
92
+ this.constructMethods()
93
+
94
+ RpgCommonMap.bufferClient.set(obj.id, this.gameMap)
95
+
96
+ this.tilemap = new TileMap(this.gameMap.getData(), this.game.renderer)
97
+
98
+ // TODO: Remove this
99
+ Assets.reset()
100
+
101
+ let nbLoad = 0
102
+
103
+ const objects = this.game.world.getObjectsOfGroup()
104
+
105
+ for (let { object } of Object.values(objects) as any[]) {
106
+ if (Utils.isInstanceOf(object, RpgCommonPlayer) && object) {
107
+ (object as RpgCommonPlayer).updateInVirtualGrid()
108
+ }
109
+ }
110
+
111
+ const assets: string[] = []
112
+ for (let tileset of this.tilemap.tilesets) {
113
+ let spritesheet = spritesheets.get(tileset.name)
114
+ if (!spritesheet) {
115
+ clientEngine.addSpriteSheet(tileset.image.source, tileset.name)
116
+ spritesheet = spritesheets.get(tileset.name)
117
+ }
118
+ if (spritesheet?.resource) {
119
+ continue
120
+ }
121
+ Assets.add(tileset.name, spritesheet.image)
122
+ assets.push(tileset.name)
123
+ nbLoad++
124
+ }
125
+
126
+ if (nbLoad > 0) {
127
+ const assetsLoaded = await Assets.load(assets)
128
+ for (let assetName in assetsLoaded) {
129
+ if (!assetsLoaded[assetName]) console.log(assetName, assetsLoaded[assetName])
130
+ const spritesheet = spritesheets.get(assetName)
131
+ if (spritesheet) spritesheet.resource = assetsLoaded[assetName]
132
+ }
133
+ }
134
+
135
+ RpgPlugin.emit(HookClient.SceneMapLoading, Assets)
136
+
137
+ this.tilemap.load({
138
+ drawTiles: this.options.drawMap,
139
+ isUpdate
140
+ })
141
+ this.viewport = new Viewport({
142
+ screenWidth: this.options.screenWidth,
143
+ screenHeight: this.options.screenHeight,
144
+ worldWidth: obj.width * obj.tilewidth,
145
+ worldHeight: obj.height * obj.tileheight,
146
+ noTicker: true,
147
+ events: this.renderer.events
148
+ })
149
+ this.tilemap.addChild(this.animationLayer)
150
+ this.viewport.clamp({ direction: 'all' })
151
+ this.viewport.addChild(this.tilemap, ...this.createEventLayers(obj))
152
+ this.isLoaded = true
153
+ if (prevObj.sounds && prevObj.sounds instanceof Array) {
154
+ prevObj.sounds.forEach(soundId => {
155
+ const continueSound = (<string[]>obj.sounds || []).find(id => id == soundId)
156
+ if (!continueSound) RpgSound.stop(soundId)
157
+ })
158
+ }
159
+ if (sounds) (<string[]>sounds).forEach(soundId => RpgSound.play(soundId))
160
+ if (this.onLoad) this.onLoad()
161
+
162
+ return this.viewport
163
+ }
164
+
165
+ createEventLayers(map: MapObject): Container[] {
166
+ const containers: Container[] = []
167
+ map.layers.forEach((layerData) => {
168
+ if (layerData.type !== TiledLayerType.ObjectGroup) return
169
+ if (this.eventsLayers[layerData.name]) {
170
+ containers.push(this.eventsLayers[layerData.name])
171
+ return
172
+ }
173
+ const layer = new EventLayer()
174
+ this.defaultLayer = this.eventsLayers[layerData.name] = layer
175
+ containers.push(layer)
176
+ })
177
+ if (containers.length == 0) {
178
+ if (!this.defaultLayer) {
179
+ this.defaultLayer = new EventLayer()
180
+ }
181
+ containers.push(this.defaultLayer)
182
+ }
183
+ this.cameraFollowSprite(this.game.playerId)
184
+ return containers
185
+ }
186
+
187
+ getEventLayer(objectName?: string): Container | undefined {
188
+ for (let layerData of this.data.layers) {
189
+ if (layerData.type != TiledLayerType.ObjectGroup) {
190
+ continue
191
+ }
192
+ if (!layerData.objects) {
193
+ continue
194
+ }
195
+ for (let object of layerData.objects) {
196
+ if (object.name == objectName) {
197
+ return this.eventsLayers[layerData.name]
198
+ }
199
+ }
200
+ }
201
+ return this.defaultLayer
202
+ }
203
+
204
+ /** @internal */
205
+ changeTile(x: number, y: number, layers: {
206
+ [layerName: string]: object
207
+ }) {
208
+ for (let layerName in layers) {
209
+ const layerInfo = layers[layerName]
210
+ this.gameMap?.setTile(x, y, layerName, layerInfo)
211
+ this.tilemap.changeTile(x, y, layerName)
212
+ }
213
+ }
214
+
215
+ /** @internal */
216
+ draw(t: number, deltaTime: number, deltaRatio: number, frame: number) {
217
+ if (!this.isLoaded) {
218
+ return
219
+ }
220
+ super.draw(t, deltaTime, deltaRatio, frame)
221
+ this.tilemap.drawAnimateTile(frame)
222
+ this.viewport?.update(deltaTime)
223
+ }
224
+
225
+ // @internal
226
+ updateTilesOverlayAllSprites() {
227
+ const objects = this.objects
228
+ for (let [id, sprite] of objects) {
229
+ this.updateTilesOverlay(sprite)
230
+ }
231
+ }
232
+
233
+ private updateTilesOverlay(sprite: RpgComponent) {
234
+ if (!this.gameMap) return sprite
235
+ const { tileWidth, tileHeight } = this.gameMap
236
+ const { tilesOverlay }: any = sprite
237
+ const bounds = sprite.parent.getLocalBounds()
238
+ const width = Math.ceil(bounds.width / tileWidth) * tileWidth
239
+ const height = Math.ceil(bounds.height / tileHeight) * tileHeight
240
+ const _x = bounds.x
241
+ const _y = bounds.y
242
+
243
+ const addTile = (x, y) => {
244
+ const tiles = this.tilemap.createOverlayTiles(x, y, sprite)
245
+ if (tiles.length) tilesOverlay.addChild(...tiles)
246
+ }
247
+
248
+ tilesOverlay.removeChildren()
249
+
250
+ for (let i = _x; i <= _x + width; i += tileWidth) {
251
+ for (let j = _y; j <= _y + height; j += tileHeight) {
252
+ addTile(i, j)
253
+ }
254
+ }
255
+ return sprite
256
+ }
257
+
258
+ onUpdateObject(logic: SceneSpriteLogic, sprite: RpgComponent, moving: boolean): RpgComponent {
259
+ const { paramsChanged } = logic
260
+ if (!this.gameMap) return sprite
261
+ if (moving || (paramsChanged && (paramsChanged.width || paramsChanged.height))) {
262
+ this.updateTilesOverlay(sprite)
263
+ }
264
+ return sprite
265
+ }
266
+
267
+ /** @internal */
268
+ setPlayerPosition(id: string, { x, y }: { x: number, y: number }) {
269
+ this.players[id].x = x
270
+ this.players[id].y = y
271
+ }
272
+
273
+ /** @internal */
274
+ updateScene(obj: SceneObservableData) { }
275
+
276
+ addObject(obj: RpgCommonPlayer | RpgShape, id: string): RpgComponent {
277
+ const wrapper = new Container()
278
+ const inner = new Container()
279
+ const tilesOverlay = new Container()
280
+ const component = new RpgComponent(obj, this)
281
+
282
+ component.tilesOverlay = tilesOverlay
283
+ inner.addChild(component)
284
+ wrapper.addChild(inner, tilesOverlay)
285
+
286
+ this.objects.set(id, component)
287
+ this.getEventLayer(obj.id)?.addChild(wrapper)
288
+ if (component.isCurrentPlayer) this.cameraFollowSprite(id)
289
+ component.onInit()
290
+ return component
291
+ }
292
+
293
+ removeObject(id: string) {
294
+ let sprite = this.objects.get(id)
295
+ if (sprite) {
296
+ this.objects.delete(id)
297
+ RpgPlugin.emit(HookClient.SceneRemoveSprite, [this, sprite], true)
298
+ RpgPlugin.emit(HookClient.RemoveSprite, sprite)
299
+ sprite.destroy()
300
+ }
301
+ }
302
+
303
+ getShape(name: string): RpgShape | undefined {
304
+ return this.game.getShape(name)?.object
305
+ }
306
+
307
+ getShapes(): RpgShape[] {
308
+ const shapes = Object.values(this.game.getShapes())
309
+ return shapes.map(shape => shape.object)
310
+ }
311
+
312
+ cameraFollowSprite(id: string, options: CameraOptions = {}) {
313
+ const sprite = this.getSprite(id)
314
+ const follow = () => {
315
+ if (sprite) this.viewport?.follow(sprite)
316
+ }
317
+ if (options.smoothMove) {
318
+ this.viewport?.plugins.remove('follow')
319
+ let moreOptions = {}
320
+ if (typeof options.smoothMove != 'boolean') {
321
+ moreOptions = options.smoothMove
322
+ }
323
+ this.viewport?.animate({
324
+ position: new Point(sprite?.x, sprite?.y),
325
+ ...moreOptions,
326
+ callbackOnComplete: follow
327
+ })
328
+ }
329
+ else {
330
+ follow()
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Listen to the events of the smile on the stage
336
+ *
337
+ * @title Listen mouse event
338
+ * @method on(eventName,callback)
339
+ * @since 3.0.0-beta.4
340
+ * @param {string} eventName Name of the event (see PIXI documentation). Name often used in the codes
341
+ * - click
342
+ * - mousedown
343
+ * - mouseup
344
+ * - mousemove
345
+ * - pointerdown
346
+ * - pointermove
347
+ * - pointerup
348
+ * - (etc...)
349
+ * @param {(position: { x: number, y: number }, ev?: PIXI.InteractionEvent ) => any} callback
350
+ * @example
351
+ * ```ts
352
+ * sceneMap.on('pointerdown', (position) => {
353
+ * console.log(position)
354
+ * })
355
+ * ```
356
+ * @returns {void}
357
+ * @memberof RpgSceneMap
358
+ */
359
+ on(eventName: keyof DisplayObjectEvents, cb: (position: { x: number, y: number }, ev?: any) => any) {
360
+ if (!this.viewport) return
361
+ this.viewport.eventMode = 'static'
362
+ this.viewport.on(eventName, (...args) => {
363
+ const ev: FederatedPointerEvent = args[0] as any
364
+ const pos = ev.getLocalPosition(this.viewport as Viewport)
365
+ cb(pos, ev)
366
+ })
367
+ }
368
+ }
369
+
370
+ export interface SceneMap {
371
+ data: any;
372
+ tileWidth: number;
373
+ tileHeight: number;
374
+ layers: any[];
375
+ widthPx: number;
376
+ heightPx: number;
377
+ zTileHeight: number
378
+ getShapes(): RpgShape[];
379
+ getShape(name: string): RpgShape | undefined;
380
+ getPositionByShape(filter: (shape: RpgShape) => {}): {
381
+ x: number;
382
+ y: number;
383
+ z: number;
384
+ } | null;
385
+ getTileIndex(x: number, y: number, [z]?: [number]): number;
386
+ getTileByIndex(tileIndex: number, zPlayer?: [number, number]): any;
387
+ getTileOriginPosition(x: number, y: number): {
388
+ x: number;
389
+ y: number;
390
+ };
391
+ getTileByPosition(x: number, y: number, z?: [number, number]): any;
392
+ getLayerByName(name: string): any
393
+ }
@@ -0,0 +1,270 @@
1
+ import { RpgPlugin, HookClient, DefaultInput } from '@rpgjs/common'
2
+ import { KeyboardControls } from '../KeyboardControls'
3
+ import RpgSprite from '../Sprite/Character'
4
+ import { Animation } from '../Effects/Animation'
5
+ import { BehaviorSubject, Observable } from 'rxjs'
6
+ import { RpgGui } from '../RpgGui'
7
+ import { GameEngineClient } from '../GameEngine'
8
+ import { RpgComponent } from '../Components/Component'
9
+ import { Controls } from '@rpgjs/types'
10
+ import { Container } from 'pixi.js'
11
+
12
+ export type SceneObservableData = {
13
+ data: {
14
+ [key: string]: any
15
+ },
16
+ partial: {
17
+ [key: string]: any
18
+ }
19
+ }
20
+
21
+ export interface SceneSpriteLogic {
22
+ paramsChanged: {
23
+ [key: string]: any
24
+ } | null,
25
+ prevParamsChanged: object
26
+ }
27
+
28
+ export abstract class Scene {
29
+ protected objects: Map<string, RpgComponent> = new Map()
30
+ protected animationLayer: Container = new Container()
31
+
32
+ private controls: KeyboardControls
33
+ private animations: Animation[] = []
34
+
35
+ private _data: BehaviorSubject<SceneObservableData> = new BehaviorSubject({
36
+ data: {},
37
+ partial: {}
38
+ })
39
+
40
+ constructor(public game: GameEngineClient) {
41
+ const { globalConfig } = this.game.clientEngine
42
+ this.controls = this.game.clientEngine.controls
43
+ const mergeInputs = {
44
+ ...DefaultInput,
45
+ ...(globalConfig.inputs || {})
46
+ }
47
+ this.controls.setInputs(this.inputs || mergeInputs)
48
+ }
49
+
50
+ /**
51
+ * Listen to all the synchronized values of the scene with the server
52
+ *
53
+ * ```ts
54
+ * import { RpgClient, RpgModule, RpgSceneMap, RpgSprite } from '@rpgjs/client'
55
+ *
56
+ * @RpgModule<RpgClient>({
57
+ scenes: {
58
+ map: {
59
+ onAfterLoading(scene: RpgSceneMap, sprite: RpgSprite) {
60
+ scene.valuesChange.subscribe((obj) => {
61
+ console.log(obj.data, obj.partial)
62
+ })
63
+ }
64
+ }
65
+ }
66
+ })
67
+ export default class RpgClientModuleEngine {}
68
+ * ```
69
+ *
70
+ * - `data` represents all the current data of the scene (`users`, `events` and others)
71
+ * - `partial` represents only the data that has changed on the scene
72
+ *
73
+ * > In the class, you can also use the onChanges hook
74
+ *
75
+ *
76
+ * @prop {Observable<{ data: object, partial: object }>} [valuesChange]
77
+ * @readonly
78
+ * @memberof RpgScene
79
+ */
80
+ get valuesChange(): Observable<SceneObservableData> {
81
+ return this._data.asObservable()
82
+ }
83
+
84
+ private triggerSpriteChanges(logic: SceneSpriteLogic, sprite: RpgComponent, moving: boolean) {
85
+ if (this.onUpdateObject) this.onUpdateObject(logic, sprite, moving)
86
+ RpgPlugin.emit(HookClient.UpdateSprite, [sprite, logic], true)
87
+ }
88
+
89
+ /** @internal */
90
+ update(obj?: SceneObservableData) {
91
+ if (!obj) {
92
+ this.updateScene(this._data.value)
93
+ return
94
+ }
95
+ this.updateScene(obj)
96
+ RpgPlugin.emit(HookClient.SceneOnChanges, [this, obj], true)
97
+ this._data.next(obj)
98
+ }
99
+
100
+ /** @internal */
101
+ draw(t: number, deltaTime: number, deltaRatio: number, frame: number) {
102
+ const logicObjects = {
103
+ ...this.game.world.getObjects(),
104
+ ...this.game.events,
105
+ ...this.game.getShapes()
106
+ }
107
+ const renderObjects = this.objects
108
+ const sizeLogic = Object.values(logicObjects).length
109
+ for (let key in logicObjects) {
110
+ const val: SceneSpriteLogic = logicObjects[key].object
111
+ const valueChanged = logicObjects[key].paramsChanged
112
+ if (!renderObjects.has(key)) {
113
+ const sprite = this.addObject(val, key)
114
+ this.triggerSpriteChanges(val, sprite, true)
115
+ }
116
+ else {
117
+ const object = renderObjects.get(key)
118
+ if (!object?.update) return
119
+ const ret = object.update(val, valueChanged, t, deltaRatio)
120
+ this.triggerSpriteChanges(val, object, ret.moving)
121
+ }
122
+ }
123
+ if (sizeLogic < renderObjects.size) {
124
+ renderObjects.forEach((val, key) => {
125
+ if (!logicObjects[key]) {
126
+ this.removeObject(key)
127
+ }
128
+ })
129
+ }
130
+ for (let animation of this.animations) {
131
+ animation.update(deltaRatio)
132
+ }
133
+ this.onDraw(t)
134
+ RpgGui.update(logicObjects)
135
+ RpgPlugin.emit(HookClient.SceneDraw, this)
136
+ }
137
+
138
+ abstract onUpdateObject(logic: SceneSpriteLogic, sprite: RpgComponent, moving: boolean): void
139
+ abstract addObject(obj, id: string): RpgComponent
140
+ abstract removeObject(id: string)
141
+
142
+ /**
143
+ * Display an animation on the scene
144
+ *
145
+ * The object is the following:
146
+ * * `graphic`: Spritesheet id
147
+ * * `animationName`: The name of the animation
148
+ * * `attachTo`: Define a sprite. The animation will follow this sprite (optional)
149
+ * * `x`: Position X (0 by default)
150
+ * * `y`: Position Y (0 by default)
151
+ * * `loop`: Display the animation in a loop (false by default)
152
+ *
153
+ * ```ts
154
+ * import { RpgClient, RpgModule, RpgSceneMap, RpgSprite } from '@rpgjs/client'
155
+ *
156
+
157
+ * @RpgModule<RpgClient>({
158
+ scenes: {
159
+ map: {
160
+ onAfterLoading(scene: RpgSceneMap, sprite: RpgSprite) {
161
+ const animation = scene.showAnimation({
162
+ graphic: 'my-spritesheet',
163
+ animationName: 'my-anim'
164
+ })
165
+ }
166
+ }
167
+ }
168
+ })
169
+ export default class RpgClientModuleEngine {}
170
+ * ```
171
+ *
172
+ * The return is an animation containing two methods:
173
+ * * `play()`: Play the animation (Already the case when calling the method)
174
+ * * `stop()`: Stop the animation
175
+ *
176
+ * They have a hook:
177
+ *
178
+ * `onFinish`: Triggered when the animation is finished
179
+ *
180
+ * ```ts
181
+ * animation.onFinish = () => {
182
+ * console.log('finish !')
183
+ * }
184
+ * ```
185
+ *
186
+ * @title Show Animation
187
+ * @method scene.showAnimation(object)
188
+ * @param {object} object
189
+ * @returns {Animation}
190
+ * @memberof RpgScene
191
+ */
192
+ showAnimation({
193
+ graphic,
194
+ animationName,
195
+ attachTo,
196
+ x = 0,
197
+ y = 0,
198
+ loop = false,
199
+ replaceGraphic = false
200
+ }: {
201
+ graphic: string,
202
+ animationName: string,
203
+ attachTo?: RpgComponent,
204
+ x?: number,
205
+ y?: number,
206
+ loop?: boolean,
207
+ replaceGraphic?: boolean
208
+ }): Animation | undefined {
209
+ if (replaceGraphic && attachTo) {
210
+ attachTo.showAnimation(graphic, animationName)
211
+ return
212
+ }
213
+ const animation = new Animation(graphic)
214
+ this.animationLayer.addChild(animation)
215
+ if (!loop) {
216
+ animation.onFinish = () => {
217
+ animation.stop()
218
+ }
219
+ }
220
+ if (attachTo) {
221
+ animation.attachTo = attachTo
222
+ }
223
+ else {
224
+ animation.x = x
225
+ animation.y = y
226
+ }
227
+ animation.play(animationName)
228
+ this.animations.push(animation)
229
+ return animation
230
+ }
231
+
232
+ /**
233
+ * Retrieve a sprite according to its identifier
234
+ *
235
+ * @title Get Sprite
236
+ * @method scene.getSprite(id)
237
+ * @param {string} id
238
+ * @returns {RpgSprite | undefined}
239
+ * @memberof RpgScene
240
+ */
241
+ getSprite(id: string) { return this.getPlayer(id) }
242
+ getPlayer(id: string): RpgComponent | undefined {
243
+ return this.objects.get(id)
244
+ }
245
+
246
+ /**
247
+ * Retrieve a sprite that the player controls
248
+ *
249
+ * @title Get Current Player
250
+ * @method scene.getCurrentPlayer()
251
+ * @returns {RpgSprite | undefined}
252
+ * @memberof RpgScene
253
+ */
254
+ getCurrentPlayer(): RpgSprite | RpgComponent | undefined {
255
+ return this.objects.get(this.game.playerId)
256
+ }
257
+
258
+ // Hooks
259
+ onInit() {}
260
+ onLoad() {}
261
+ onChanges(obj) {}
262
+ onDraw(t: number) {}
263
+ onAddSprite(sprite: RpgSprite) {}
264
+ onRemoveSprite(sprite: RpgSprite) {}
265
+ }
266
+
267
+ export interface Scene {
268
+ inputs: Controls,
269
+ updateScene(obj: SceneObservableData)
270
+ }
@@ -0,0 +1,13 @@
1
+ export interface SceneDataOptions {
2
+ inputs?: {
3
+
4
+ }
5
+ }
6
+
7
+ export function SceneData(options) {
8
+ return (target) => {
9
+ for (let key in options) {
10
+ target.prototype[key] = options[key]
11
+ }
12
+ }
13
+ }