@rpgjs/server 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 (219) hide show
  1. package/{lib → dist}/Gui/DialogGui.d.ts +1 -2
  2. package/{lib → dist}/Gui/Gui.d.ts +1 -2
  3. package/dist/Player/BattleManager.d.ts +22 -0
  4. package/dist/Player/ClassManager.d.ts +18 -0
  5. package/dist/Player/Event.d.ts +0 -0
  6. package/dist/Player/ItemManager.d.ts +17 -0
  7. package/dist/Player/MoveManager.d.ts +177 -0
  8. package/dist/Player/ParameterManager.d.ts +42 -0
  9. package/dist/Player/Player.d.ts +73 -0
  10. package/dist/Player/SkillManager.d.ts +23 -0
  11. package/dist/Player/StateManager.d.ts +39 -0
  12. package/{lib → dist}/RpgServer.d.ts +32 -15
  13. package/dist/RpgServerEngine.d.ts +5 -0
  14. package/dist/core/context.d.ts +2 -0
  15. package/dist/core/inject.d.ts +5 -0
  16. package/dist/core/setup.d.ts +6 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.js +29673 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/module.d.ts +2 -0
  21. package/dist/rooms/lobby.d.ts +6 -0
  22. package/dist/rooms/map.d.ts +110 -0
  23. package/package.json +15 -37
  24. package/src/Gui/DialogGui.ts +3 -4
  25. package/src/Gui/Gui.ts +4 -6
  26. package/src/Player/BattleManager.ts +108 -99
  27. package/src/Player/ClassManager.ts +47 -46
  28. package/src/Player/ComponentManager.ts +26 -535
  29. package/src/Player/EffectManager.ts +44 -59
  30. package/src/Player/ElementManager.ts +70 -72
  31. package/src/Player/Event.ts +0 -0
  32. package/src/Player/GoldManager.ts +24 -6
  33. package/src/Player/GuiManager.ts +106 -97
  34. package/src/Player/ItemFixture.ts +22 -17
  35. package/src/Player/ItemManager.ts +234 -225
  36. package/src/Player/MoveManager.ts +1047 -457
  37. package/src/Player/ParameterManager.ts +59 -37
  38. package/src/Player/Player.ts +272 -1058
  39. package/src/Player/SkillManager.ts +146 -137
  40. package/src/Player/StateManager.ts +124 -98
  41. package/src/Player/VariableManager.ts +23 -3
  42. package/src/RpgServer.ts +36 -12
  43. package/src/RpgServerEngine.ts +7 -0
  44. package/src/core/context.ts +3 -0
  45. package/src/core/inject.ts +17 -0
  46. package/src/core/setup.ts +20 -0
  47. package/src/index.ts +9 -31
  48. package/src/module.ts +32 -0
  49. package/src/presets/index.ts +1 -3
  50. package/src/rooms/lobby.ts +24 -0
  51. package/src/rooms/map.ts +311 -0
  52. package/tsconfig.json +26 -23
  53. package/vite.config.ts +21 -0
  54. package/CHANGELOG.md +0 -174
  55. package/LICENSE +0 -19
  56. package/browser/manifest.json +0 -7
  57. package/browser/rpg.server.js +0 -22379
  58. package/browser/rpg.server.umd.cjs +0 -22410
  59. package/lib/Game/EventManager.d.ts +0 -54
  60. package/lib/Game/EventManager.js +0 -107
  61. package/lib/Game/EventManager.js.map +0 -1
  62. package/lib/Game/Map.d.ts +0 -191
  63. package/lib/Game/Map.js +0 -419
  64. package/lib/Game/Map.js.map +0 -1
  65. package/lib/Game/WorldMaps.d.ts +0 -17
  66. package/lib/Game/WorldMaps.js +0 -28
  67. package/lib/Game/WorldMaps.js.map +0 -1
  68. package/lib/Gui/DialogGui.js +0 -57
  69. package/lib/Gui/DialogGui.js.map +0 -1
  70. package/lib/Gui/Gui.js +0 -37
  71. package/lib/Gui/Gui.js.map +0 -1
  72. package/lib/Gui/MenuGui.js +0 -23
  73. package/lib/Gui/MenuGui.js.map +0 -1
  74. package/lib/Gui/NotificationGui.js +0 -8
  75. package/lib/Gui/NotificationGui.js.map +0 -1
  76. package/lib/Gui/ShopGui.js +0 -40
  77. package/lib/Gui/ShopGui.js.map +0 -1
  78. package/lib/Gui/index.js +0 -7
  79. package/lib/Gui/index.js.map +0 -1
  80. package/lib/Interfaces/Gui.d.ts +0 -4
  81. package/lib/Interfaces/Gui.js +0 -2
  82. package/lib/Interfaces/Gui.js.map +0 -1
  83. package/lib/Interfaces/StateStore.d.ts +0 -5
  84. package/lib/Interfaces/StateStore.js +0 -2
  85. package/lib/Interfaces/StateStore.js.map +0 -1
  86. package/lib/MatchMaker.d.ts +0 -24
  87. package/lib/MatchMaker.js +0 -42
  88. package/lib/MatchMaker.js.map +0 -1
  89. package/lib/Monitor/index.d.ts +0 -17
  90. package/lib/Monitor/index.js +0 -72
  91. package/lib/Monitor/index.js.map +0 -1
  92. package/lib/Player/BattleManager.d.ts +0 -36
  93. package/lib/Player/BattleManager.js +0 -101
  94. package/lib/Player/BattleManager.js.map +0 -1
  95. package/lib/Player/ClassManager.d.ts +0 -47
  96. package/lib/Player/ClassManager.js +0 -65
  97. package/lib/Player/ClassManager.js.map +0 -1
  98. package/lib/Player/ComponentManager.d.ts +0 -397
  99. package/lib/Player/ComponentManager.js +0 -505
  100. package/lib/Player/ComponentManager.js.map +0 -1
  101. package/lib/Player/EffectManager.d.ts +0 -46
  102. package/lib/Player/EffectManager.js +0 -75
  103. package/lib/Player/EffectManager.js.map +0 -1
  104. package/lib/Player/ElementManager.d.ts +0 -108
  105. package/lib/Player/ElementManager.js +0 -120
  106. package/lib/Player/ElementManager.js.map +0 -1
  107. package/lib/Player/GoldManager.d.ts +0 -17
  108. package/lib/Player/GoldManager.js +0 -27
  109. package/lib/Player/GoldManager.js.map +0 -1
  110. package/lib/Player/GuiManager.d.ts +0 -247
  111. package/lib/Player/GuiManager.js +0 -291
  112. package/lib/Player/GuiManager.js.map +0 -1
  113. package/lib/Player/ItemFixture.d.ts +0 -7
  114. package/lib/Player/ItemFixture.js +0 -19
  115. package/lib/Player/ItemFixture.js.map +0 -1
  116. package/lib/Player/ItemManager.d.ts +0 -331
  117. package/lib/Player/ItemManager.js +0 -475
  118. package/lib/Player/ItemManager.js.map +0 -1
  119. package/lib/Player/MoveManager.d.ts +0 -333
  120. package/lib/Player/MoveManager.js +0 -450
  121. package/lib/Player/MoveManager.js.map +0 -1
  122. package/lib/Player/ParameterManager.d.ts +0 -316
  123. package/lib/Player/ParameterManager.js +0 -408
  124. package/lib/Player/ParameterManager.js.map +0 -1
  125. package/lib/Player/Player.d.ts +0 -828
  126. package/lib/Player/Player.js +0 -927
  127. package/lib/Player/Player.js.map +0 -1
  128. package/lib/Player/SkillManager.d.ts +0 -163
  129. package/lib/Player/SkillManager.js +0 -223
  130. package/lib/Player/SkillManager.js.map +0 -1
  131. package/lib/Player/StateManager.d.ts +0 -185
  132. package/lib/Player/StateManager.js +0 -228
  133. package/lib/Player/StateManager.js.map +0 -1
  134. package/lib/Player/VariableManager.d.ts +0 -46
  135. package/lib/Player/VariableManager.js +0 -52
  136. package/lib/Player/VariableManager.js.map +0 -1
  137. package/lib/Query.d.ts +0 -127
  138. package/lib/Query.js +0 -159
  139. package/lib/Query.js.map +0 -1
  140. package/lib/RpgServer.js +0 -2
  141. package/lib/RpgServer.js.map +0 -1
  142. package/lib/Scenes/Map.d.ts +0 -136
  143. package/lib/Scenes/Map.js +0 -273
  144. package/lib/Scenes/Map.js.map +0 -1
  145. package/lib/decorators/event.d.ts +0 -43
  146. package/lib/decorators/event.js +0 -13
  147. package/lib/decorators/event.js.map +0 -1
  148. package/lib/decorators/map.d.ts +0 -178
  149. package/lib/decorators/map.js +0 -43
  150. package/lib/decorators/map.js.map +0 -1
  151. package/lib/entry-point.d.ts +0 -34
  152. package/lib/entry-point.js +0 -70
  153. package/lib/entry-point.js.map +0 -1
  154. package/lib/express/api.d.ts +0 -3
  155. package/lib/express/api.js +0 -105
  156. package/lib/express/api.js.map +0 -1
  157. package/lib/express/errors/NotAuthorized.d.ts +0 -4
  158. package/lib/express/errors/NotAuthorized.js +0 -7
  159. package/lib/express/errors/NotAuthorized.js.map +0 -1
  160. package/lib/express/errors/NotFound.d.ts +0 -4
  161. package/lib/express/errors/NotFound.js +0 -7
  162. package/lib/express/errors/NotFound.js.map +0 -1
  163. package/lib/express/server.d.ts +0 -18
  164. package/lib/express/server.js +0 -70
  165. package/lib/express/server.js.map +0 -1
  166. package/lib/index.d.ts +0 -20
  167. package/lib/index.js +0 -19
  168. package/lib/index.js.map +0 -1
  169. package/lib/inject.d.ts +0 -22
  170. package/lib/inject.js +0 -29
  171. package/lib/inject.js.map +0 -1
  172. package/lib/logs/index.js +0 -6
  173. package/lib/logs/index.js.map +0 -1
  174. package/lib/logs/item.js +0 -34
  175. package/lib/logs/item.js.map +0 -1
  176. package/lib/logs/log.js +0 -7
  177. package/lib/logs/log.js.map +0 -1
  178. package/lib/logs/skill.js +0 -19
  179. package/lib/logs/skill.js.map +0 -1
  180. package/lib/logs/state.js +0 -13
  181. package/lib/logs/state.js.map +0 -1
  182. package/lib/models/Item.d.ts +0 -10
  183. package/lib/models/Item.js +0 -2
  184. package/lib/models/Item.js.map +0 -1
  185. package/lib/presets/index.js +0 -65
  186. package/lib/presets/index.js.map +0 -1
  187. package/lib/server.d.ts +0 -137
  188. package/lib/server.js +0 -443
  189. package/lib/server.js.map +0 -1
  190. package/rpg.toml +0 -14
  191. package/src/Game/EventManager.ts +0 -125
  192. package/src/Game/Map.ts +0 -500
  193. package/src/Game/WorldMaps.ts +0 -45
  194. package/src/Interfaces/Gui.ts +0 -4
  195. package/src/Interfaces/StateStore.ts +0 -5
  196. package/src/MatchMaker.ts +0 -65
  197. package/src/Monitor/index.ts +0 -78
  198. package/src/Query.ts +0 -172
  199. package/src/Scenes/Map.ts +0 -310
  200. package/src/decorators/event.ts +0 -58
  201. package/src/decorators/map.ts +0 -226
  202. package/src/entry-point.ts +0 -111
  203. package/src/express/api.ts +0 -118
  204. package/src/express/errors/NotAuthorized.ts +0 -6
  205. package/src/express/errors/NotFound.ts +0 -6
  206. package/src/express/server.ts +0 -94
  207. package/src/inject.ts +0 -33
  208. package/src/models/Item.ts +0 -11
  209. package/src/server.ts +0 -459
  210. /package/{lib → dist}/Gui/MenuGui.d.ts +0 -0
  211. /package/{lib → dist}/Gui/NotificationGui.d.ts +0 -0
  212. /package/{lib → dist}/Gui/ShopGui.d.ts +0 -0
  213. /package/{lib → dist}/Gui/index.d.ts +0 -0
  214. /package/{lib → dist}/logs/index.d.ts +0 -0
  215. /package/{lib → dist}/logs/item.d.ts +0 -0
  216. /package/{lib → dist}/logs/log.d.ts +0 -0
  217. /package/{lib → dist}/logs/skill.d.ts +0 -0
  218. /package/{lib → dist}/logs/state.d.ts +0 -0
  219. /package/{lib → dist}/presets/index.d.ts +0 -0
@@ -1,1065 +1,279 @@
1
- import { RpgCommonPlayer, Utils, RpgPlugin, RpgCommonGame, RpgCommonMap, Direction } from '@rpgjs/common'
2
- import { Room, WorldClass } from 'simple-room'
3
- import { RpgMap, EventPosOption } from '../Game/Map'
4
- import { Query } from '../Query'
5
- import merge from 'lodash.merge'
6
- import { ItemManager } from './ItemManager'
7
- import { GoldManager } from './GoldManager'
8
- import { StateManager } from './StateManager';
9
- import { SkillManager } from './SkillManager'
10
- import { ParameterManager } from './ParameterManager';
11
- import { EffectManager } from './EffectManager';
12
- import { ClassManager } from './ClassManager';
13
- import { ElementManager } from './ElementManager'
14
- import { GuiManager } from './GuiManager'
15
- import { VariableManager } from './VariableManager'
16
- import { Frequency, MoveManager, Speed } from './MoveManager'
17
- import { BattleManager } from './BattleManager'
18
-
19
1
  import {
20
- MAXHP,
21
- MAXSP,
22
- STR,
23
- INT,
24
- DEX,
25
- AGI,
26
- MAXHP_CURVE,
27
- MAXSP_CURVE,
28
- STR_CURVE,
29
- INT_CURVE,
30
- DEX_CURVE,
31
- AGI_CURVE
32
- } from '../presets'
33
- import { RpgServerEngine } from '../server'
34
- import { RpgClassMap } from '../Scenes/Map'
35
- import { RpgTiledWorldMap } from '../Game/WorldMaps'
36
- import { CameraOptions, PositionXY_OptionalZ, SocketEvents, SocketMethods, LayoutObject } from '@rpgjs/types'
37
- import { ComponentManager } from './ComponentManager'
38
- import { Subject } from 'rxjs'
39
- import { EventManager, EventMode } from '../Game/EventManager'
40
- import { inject } from '../inject'
41
-
42
- const {
43
- isPromise,
44
- applyMixins,
45
- isString
46
- } = Utils
47
-
48
- export interface Position { x: number, y: number, z: number }
49
-
50
- const commonSchemaFeature = {
51
- name: {
52
- $permanent: false
53
- },
54
- description: {
55
- $permanent: false
56
- },
57
- id: String
58
- }
59
-
60
- const itemSchemas = {
61
- price: {
62
- $permanent: false
63
- },
64
- consumable: {
65
- $permanent: false
66
- },
67
- ...commonSchemaFeature
68
- }
69
-
70
- export const componentSchema = { id: String, value: String }
71
- export const layoutSchema = {
72
- width: Number,
73
- height: Number,
74
- marginBottom: Number,
75
- marginTop: Number,
76
- marginLeft: Number,
77
- marginRight: Number,
78
- lines: [{
79
- col: [componentSchema]
80
- }]
81
- }
82
-
83
- const playerSchemas = {
84
- position: {
85
- x: Number,
86
- y: Number,
87
- z: Number
88
- },
89
- direction: Number,
90
-
91
- teleported: {
92
- $permanent: false
93
- },
94
-
95
- deleted: {
96
- $permanent: false
97
- },
98
-
99
- moving: {
100
- $permanent: false
101
- },
102
-
103
- param: Object,
104
- hp: Number,
105
- sp: Number,
106
- gold: Number,
107
- level: {
108
- $effects: ['$this.expForNextlevel', '$this.param']
109
- },
110
- expForNextlevel: {
111
- $permanent: false
112
- },
113
- exp: Number,
114
- name: String,
115
- items: [{ nb: Number, item: itemSchemas }],
116
- _class: commonSchemaFeature,
117
- equipments: [itemSchemas],
118
- skills: [
119
- {
120
- spCost: {
121
- $permanent: false
122
- },
123
- ...commonSchemaFeature
124
- }
125
- ],
126
- states: [commonSchemaFeature],
127
- effects: [String],
128
-
129
- layout: {
130
- top: layoutSchema,
131
- bottom: layoutSchema,
132
- left: layoutSchema,
133
- right: layoutSchema,
134
- center: layoutSchema
135
- },
136
-
137
- action: Number,
138
- map: String,
139
-
140
- speed: Number,
141
- frequency: Number,
142
- canMove: Boolean,
143
- through: Boolean,
144
- throughOtherPlayer: Boolean,
145
-
146
- width: Number,
147
- height: Number,
148
- wHitbox: Number,
149
- hHitbox: Number,
150
-
151
- // only for server
152
-
153
- _statesEfficiency: [{
154
- rate: {
155
- $syncWithClient: false
156
- },
157
- state: {
158
- $syncWithClient: false
159
- }
160
- }],
161
- tmpPositions: {
162
- $syncWithClient: false
163
- },
164
- initialLevel: {
165
- $syncWithClient: false
166
- },
167
- finalLevel: {
168
- $syncWithClient: false
169
- },
170
- }
171
-
172
- const layoutObject = {
173
- lines: []
174
- }
175
-
176
- export class RpgPlayer extends RpgCommonPlayer {
177
- public readonly type: string = 'player'
178
- static schemas = {
179
- ...playerSchemas,
180
- events: [playerSchemas]
181
- }
182
-
183
- layout: LayoutObject<any> = {
184
- top: layoutObject,
185
- bottom: layoutObject,
186
- left: layoutObject,
187
- right: layoutObject,
188
- center: layoutObject
189
- }
190
-
191
- private _name
192
- public events: any = {}
193
- public param: any
194
- public _rooms = []
195
- public session: string | null = null
196
- public prevMap: string = ''
197
-
198
- /**
199
- * ```ts
200
- * retreive the server instance
201
- * ```
202
- * @title Server Instance
203
- * @prop {RpgServerEngine} player.server
204
- * @deprecated Use `inject(RpgServerEngine)` instead. Will be removed in v5
205
- * @memberof Player
206
- * */
207
- public server: RpgServerEngine
208
- private touchSide: boolean = false
209
-
210
- /** @internal */
211
- public tmpPositions: Position | string | null = null
212
- public otherPossessedPlayer: RpgPlayer | RpgEvent | null = null
213
- public followingId: string | null = null
214
-
215
- // Indicates whether to load data with load(). In this case, hooks are not triggered.
216
- private _dataLoading: boolean = false
217
-
218
- _lastFramePositions: {
219
- frame: number
220
- position: Position
221
- } | undefined
222
-
223
- /**
224
- * @deprecated Use `inject(RpgCommonGame)` instead. Will be removed in v5
225
- */
226
- public gameEngine: RpgCommonGame = inject(RpgCommonGame)
227
-
228
- // redefine type (as RpgPlayer)
229
- get otherPlayersCollision(): RpgPlayer[] {
230
- return super.otherPlayersCollision as RpgPlayer[]
231
- }
232
-
233
- get world(): WorldClass | undefined {
234
- return this.server?.world
235
- }
236
-
237
- // As soon as a teleport has been made, the value is changed to force the client to change the positions on the map without making a move.
238
- teleported: number = 0
239
-
240
- // a flag that lets the client know if the event is suppressed. The client can, for example, end animations before completely deleting the object (client side).
241
- deleted: boolean = false
242
-
243
- constructor(public playerId: string) {
244
- super(inject(RpgCommonGame), playerId)
245
- this.initialize()
246
- }
247
-
248
- /** @internal */
249
- initialize() {
250
- this.server = inject(RpgServerEngine)
251
- this.expCurve = {
252
- basis: 30,
253
- extra: 20,
254
- accelerationA: 30,
255
- accelerationB: 30
256
- }
257
- this.parameters = new Map()
258
- this.variables = new Map()
259
- this.states = []
260
- this.equipments = []
261
- this._effects = []
262
- this.items = []
263
- this.skills = []
264
- this.gold = 0
265
- this.exp = 0
266
- this.speed = Speed.Normal
267
- this.frequency = Frequency.None
268
- this.canMove = true
269
- this.through = false
270
- this.throughOtherPlayer = true
271
- this.initialLevel = 1
272
- this.finalLevel = 99
273
- this.level = this.initialLevel
274
- this._gui = {}
275
- this._elementsEfficiency = []
276
- this._statesEfficiency = []
277
- this.moving = false
278
-
279
- this.addParameter(MAXHP, MAXHP_CURVE)
280
- this.addParameter(MAXSP, MAXSP_CURVE)
281
- this.addParameter(STR, STR_CURVE)
282
- this.addParameter(INT, INT_CURVE)
283
- this.addParameter(DEX, DEX_CURVE)
284
- this.addParameter(AGI, AGI_CURVE)
285
- this.allRecovery()
286
- }
287
-
288
- _init() {
289
- this._socket.on('gui.interaction', ({ guiId, name, data }) => {
290
- if (this._gui[guiId]) {
291
- this._gui[guiId].emit(name, data)
292
- this.syncChanges()
293
- }
294
- })
295
- this._socket.on('gui.exit', ({ guiId, data }) => {
296
- this.removeGui(guiId, data)
297
- })
298
- }
299
-
300
- private get schema() {
301
- return {
302
- ...RpgPlayer.schemas,
303
- ...this.server['playerProps']
304
- }
305
- }
306
-
307
- /**
308
- * ```ts
309
- * player.name = 'Link'
310
- * ```
311
- * @title Read/Give a name
312
- * @prop {string} player.name
313
- * @memberof Player
314
- * */
315
- get name(): string {
316
- return this._name
317
- }
318
-
319
- set name(val: string) {
320
- this._name = val
321
- }
322
-
323
- /**
324
- * Change your map. Indicate the positions to put the player at a place on the map
325
- *
326
- * > The map must be added to RpgServer beforehand. Guide: [Create Map](/guide/create-map.html)
327
- *
328
- * You don't have to give positions but you can put a starting position in the TMX file. Guide: [Start Position](/guide/player-start.html)
329
- *
330
- * @title Change Map
331
- * @method player.changeMap(mapId,positions)
332
- * @param {string} mapId
333
- * @param { {x: number, y: number, z?: number} | string } [positions]
334
- * @returns {Promise<RpgMap | null>} null if map not exists
335
- * @memberof Player
336
- */
337
- changeMap(mapId: string, positions?: { x: number, y: number, z?: number } | string): Promise<RpgMap | null | boolean> {
338
- return this.server.sceneMap.changeMap(mapId, this, positions)
339
- }
340
-
341
- async autoChangeMap(nextPosition: Position): Promise<boolean> {
342
- const map = this.getCurrentMap()
343
- const worldMaps = map?.getInWorldMaps()
344
- let ret: boolean = false
345
- if (worldMaps && map) {
346
- const direction = this.getDirection()
347
- const marginLeftRight = map.tileWidth / 2
348
- const marginTopDown = map.tileHeight / 2
349
-
350
- const changeMap = async (adjacent, to) => {
351
- if (this.touchSide) {
352
- return false
353
- }
354
- this.touchSide = true
355
- const [nextMap] = worldMaps.getAdjacentMaps(map, adjacent) as RpgClassMap<RpgMap>[]
356
- if (!nextMap) return false
357
- const id = nextMap.id as string
358
- const nextMapInfo = worldMaps.getMapInfo(id) as RpgTiledWorldMap
359
- return !!(await this.changeMap(id, to(nextMapInfo)))
360
- }
361
-
362
- if (nextPosition.x < marginLeftRight && direction == Direction.Left) {
363
- ret = await changeMap({
364
- x: map.worldX - 1,
365
- y: this.worldPositionY + 1
366
- }, nextMapInfo => ({
367
- x: (nextMapInfo.width) - this.wHitbox - marginLeftRight,
368
- y: map.worldY - nextMapInfo.y + nextPosition.y
369
- }))
370
- }
371
- else if (nextPosition.x > map.widthPx - this.wHitbox - marginLeftRight && direction == Direction.Right) {
372
- ret = await changeMap({
373
- x: map.worldX + map.widthPx + 1,
374
- y: this.worldPositionY + 1
375
- }, nextMapInfo => ({
376
- x: marginLeftRight,
377
- y: map.worldY - nextMapInfo.y + nextPosition.y
378
- }))
379
- }
380
- else if (nextPosition.y < marginTopDown && direction == Direction.Up) {
381
- ret = await changeMap({
382
- x: this.worldPositionX + 1,
383
- y: map.worldY - 1
384
- }, nextMapInfo => ({
385
- x: map.worldX - nextMapInfo.x + nextPosition.x,
386
- y: (nextMapInfo.height) - this.hHitbox - marginTopDown,
387
- }))
388
- }
389
- else if (nextPosition.y > map.heightPx - this.hHitbox - marginTopDown && direction == Direction.Down) {
390
- ret = await changeMap({
391
- x: this.worldPositionX + 1,
392
- y: map.worldY + map.heightPx + 1
393
- }, nextMapInfo => ({
394
- x: map.worldX - nextMapInfo.x + nextPosition.x,
395
- y: marginTopDown,
396
- }))
397
- }
398
- else {
399
- this.touchSide = false
400
- }
401
- }
402
- return ret
403
- }
404
-
405
- /**
406
- * Dynamically create an event in Scenario mode on the current map
407
- *
408
- * ```ts
409
- * @EventData({
410
- * name: 'EV-1'
411
- * })
412
- * class MyEvent extends RpgEvent {
413
- * onAction() {
414
- * console.log('ok')
415
- * }
416
- * }
417
- *
418
- * player.createDynamicEvent({
419
- * x: 100,
420
- * y: 100,
421
- * event: MyEvent
422
- * })
423
- * ```
424
- *
425
- * You can also put an array of objects to create several events at once
426
- *
427
- * @title Create Dynamic Event
428
- * @since 3.0.0-beta.4
429
- * @method player.createDynamicEvent(eventObj | eventObj[])
430
- * @param { { x: number, y: number, z?: number, event: eventClass } } [eventsList]
431
- * @returns { { [eventId: string]: RpgEvent } }
432
- * @memberof Player
433
- */
434
- createDynamicEvent(eventsList: EventPosOption | EventPosOption[], forceMode: boolean = true): {
435
- [eventId: string]: RpgEvent
436
- } {
437
- if (!eventsList) return {}
438
- const mapInstance = this.getCurrentMap<RpgMap>()
439
- if (!mapInstance) {
440
- throw 'The player is not assigned to any map'
441
- }
442
- if (!Utils.isArray(eventsList)) {
443
- eventsList = [eventsList as EventPosOption]
444
- }
445
- let eventsListMode = eventsList
446
- if (forceMode) {
447
- eventsListMode = (eventsList as EventPosOption[]).map(event => {
448
- event.event.mode = EventMode.Scenario
449
- return event
450
- })
451
- }
452
- const events = mapInstance.createEvents(eventsListMode as EventPosOption[], EventMode.Scenario)
453
- let ret = {}
454
- for (let key in events) {
455
- this.events[key] = events[key]
456
- this.events[key].playerRelated = this
457
- this.events[key].execMethod('onInit', [this])
458
- // force to get Proxy object to sync with client
459
- ret = { ...ret, [key]: this.events[key] }
460
- }
461
- return ret
462
- }
463
-
464
- /**
465
- * Allows to change the positions of the player on the current map.
466
- * You can put the X and Y positions or the name of the created shape on Tiled Map Editor.
467
- * If you have several shapes with the same name, one position will be chosen randomly.
468
- *
469
- * ```ts
470
- * player.teleport({ x: 100, y: 500 })
471
- * ```
472
- *
473
- * or
474
- *
475
- * ```ts
476
- * player.teleport('my-shape-name')
477
- * ```
478
- *
479
- * If no parameter:
480
- *
481
- * ```ts
482
- * player.teleport() // { x: 0, y: 0, z: 0 }
483
- * ```
484
- *
485
- * @title Teleport on the map
486
- * @method player.teleport(positions)
487
- * @param { {x: number, y: number, z?: number} | string } [positions]
488
- * @returns {Promise<{ {x: number, y: number, z: number} }>}
489
- * @memberof Player
490
- */
491
- async teleport(positions?: PositionXY_OptionalZ | string): Promise<Position> {
492
- if (isString(positions)) positions = <Position>this.getCurrentMap()?.getPositionByShape(shape => shape.name == positions || shape.getType() == positions)
493
- if (!positions) positions = { x: 0, y: 0, z: 0 }
494
- if (!(positions as Position).z) (positions as Position).z = 0
495
- this.teleported++
496
- this.position = positions as Position
497
- // delete last frame positions because when the character is teleported, no server reconciliation is needed on the client side
498
- this._lastFramePositions = undefined
499
- // force interaction with event or shape
500
- await this.isCollided(this.position)
501
- return (positions as Position)
502
- }
503
-
504
- /**
505
- * Load the saved data with the method save()
506
- * If the player was on a map, it repositions the player on the map.
507
- *
508
- * ```ts
509
- * const json = player.save()
510
- * player.load(json)
511
- * ```
512
- *
513
- * @title Load progress
514
- * @method player.load(json)
515
- * @param {string} json The JSON sent by the method save()
516
- * @returns {Promise<boolean | RpgMap | null>}
517
- * @memberof Player
518
- */
519
- async load(json: any): Promise<boolean | RpgMap | null> {
520
- this._dataLoading = true
521
-
522
- if (isString(json)) json = JSON.parse(json)
523
-
524
- const getData = (id) => new (this.databaseById(id))()
525
-
526
- for (let key in json) {
527
- const val = json[key]
528
- if (Utils.isObject(val) && val.hasOwnProperty('0')) {
529
- json[key] = Object.values(val)
530
- }
531
- }
532
-
533
- const items = {}
534
-
535
- if (json.items) {
536
- for (let it of json.items) {
537
- items[it.item.id] = getData(it.item.id)
538
- }
539
- json.items = json.items.map(it => ({ nb: it.nb, item: items[it.item.id] }))
540
- if (Array.isArray(json.equipments)) {
541
- json.equipments = json.equipments.map(it => {
542
- items[it.id].equipped = true
543
- return items[it.id]
544
- })
545
- }
546
- }
547
- if (json.states) json.states = json.states.map(state => getData(state.id))
548
- if (json.skills) json.skills = json.skills.map(skill => getData(skill.id))
549
- if (json.variables) json.variables = new Map(json.variables)
550
-
551
- merge(this, json)
552
-
553
- this.position = json.position
554
-
555
- if (json.map) {
556
- this.map = ''
557
- const map = await this.changeMap(json.map, json.tmpPositions || json.position)
558
- this._dataLoading = false
559
- return map
560
- }
561
-
562
- this._dataLoading = false
563
-
564
- return null
565
- }
566
-
567
- /**
568
- * Returns a JSON with all the data to keep in memory. Then use the `load()` method to load the data
569
- *
570
- * You can also use the JSON.stringify
571
- *
572
- * ```ts
573
- * const json = player.save() // or JSON.stringify(player)
574
- * player.load(json)
575
- * ```
576
- *
577
- * @title Save progress
578
- * @method player.save()
579
- * @returns {string}
580
- * @memberof Player
581
- */
582
- save() {
583
- return JSON.stringify(this)
584
- }
585
-
586
- // TODO
587
- toObject() {
588
- return {
589
- direction: this.direction,
590
- id: this.id,
591
- canMove: this.canMove,
592
- position: {
593
- x: this.position.x,
594
- y: this.position.y,
595
- z: this.position.z
596
- },
597
- hitbox: {
598
- width: this.wHitbox,
599
- height: this.hHitbox
600
- },
601
- map: this.map,
602
- pendingMove: this.pendingMove,
603
- speed: this.speed
604
- }
605
- }
606
-
607
- toJSON() {
608
- const { permanentObject } = Room.compileSchema(this.schema)
609
- const snapshot = Room.extractObjectOfRoom(this, permanentObject)
610
- snapshot.variables = [...this.variables]
611
- return snapshot
612
- }
613
-
614
- /**
615
- * Run the change detection cycle. Normally, as soon as a hook is called in a class, the cycle is started. But you can start it manually
616
- * The method calls the `onChanges` method on events and synchronizes all map data with the client.
617
-
618
- * @title Run Sync Changes
619
- * @method player.syncChanges()
620
- * @returns {void}
621
- * @memberof Player
622
- */
623
- syncChanges() {
624
- this._eventChanges()
625
- }
626
-
627
- databaseById(id: string) {
628
- const data = this.server.database[id]
629
- if (!data) throw new Error(`The ID=${id} data is not found in the database. Add the data in the property "database" of @RpgServer decorator.`)
630
- return data
631
- }
632
-
633
- /**
634
- * Retrieves data from the current map
635
- *
636
- * returns null if the player is not assigned to a map
637
- *
638
- * @title Get Current Map
639
- * @method player.getCurrentMap()
640
- * @returns {RpgMap | null}
641
- * @memberof Player
642
- */
643
- getCurrentMap<T extends RpgMap = RpgMap>(): T | null {
644
- return this._getMap(this.map)
645
- }
646
-
647
- loadScene(name: string, data: any): void {
648
- this.emit(SocketEvents.LoadScene, {
649
- name,
650
- data
651
- })
652
- }
653
-
654
- emitSceneMap() {
655
- const mapInstance = this.getCurrentMap<RpgMap>()
656
- if (!mapInstance) {
657
- throw '[Emit] The player is not assigned to any map'
658
- }
659
- let { data: serializeMap } = Object.assign({}, mapInstance) as any
660
- delete serializeMap.shapes
661
- delete serializeMap.events
662
- delete serializeMap._events
663
-
664
- for (let layer of serializeMap.layers) {
665
- delete layer.map
666
- }
667
-
668
- this.loadScene('map', {
669
- id: mapInstance.id,
670
- sounds: mapInstance.sounds,
671
- ...serializeMap
672
- })
673
- }
674
-
675
- gameReload() {
676
- this.emit(SocketEvents.GameReload)
677
- }
678
-
679
- changeServer(url: string, port: number) {
680
- this.emit(SocketEvents.ChangeServer, {
681
- url,
682
- port
683
- })
684
- }
685
-
686
- private _getMap(id) {
687
- return RpgCommonMap.buffer.get(id)
688
- }
689
-
690
- /**
691
- * Calls the showAnimation() method on the client side to display an animation on the player
692
- * You must remember to create the spritesheet beforehand
693
- *
694
- * For this type of spritesheet:
695
- *
696
- * ```ts
697
- * @Spritesheet({
698
- * id: 'fire',
699
- * image: require('')
700
- * textures: {
701
- * default: {
702
- * animations: [
703
- *
704
- * ]
705
- * }
706
- * }
707
- * })
708
- * export class FireAnimation {}
709
- * ```
710
- *
711
- * Here is the call of the method:
712
- *
713
- * ```ts
714
- * player.showAnimation('fire', 'default')
715
- * ```
716
- *
717
- * If you don't want to put an animation on top of the event but replace the event graphic with another one, set true as last parameter.
718
- * This is useful, if for example, you want to make an animated character (sword stroke when pressing a key)
719
- * When the animation is finished, the original graphic is displayed again
720
- *
721
- *
722
- * ```ts
723
- * player.showAnimation('sword_stroke', 'default', true)
724
- * ```
725
- *
726
- * Since version 3.0.0-rc, you can define several graphic elements. This allows you to animate them all at once
727
- *
728
- * ```ts
729
- * player.showAnimation(['body', 'sword_stroke'], 'default', true)
730
- * ```
731
- *
732
- * ::: tip
733
- * For this to work, the animations must have been previously defined in `setGraphic`.
734
- * :::
735
- *
736
- * @title Show Animation
737
- * @method player.showAnimation(graphic,animationName,replaceGraphic=false)
738
- * @param {string | string[]} graphic spritesheet identifier
739
- * @param {string} animationName Name of the animation in the spritesheet
740
- * @param {boolean} [replaceGraphic] Replace the event graphic with the animation. After the end of the animation, the original graphic is reapplied
741
- * @returns {void}
742
- * @memberof Player
743
- */
744
- showAnimation(graphic: string | string[], animationName: string, replaceGraphic: boolean = false) {
745
- this.emitToMap('callMethod', {
746
- name: SocketMethods.ShowAnimation,
747
- params: [graphic, animationName, replaceGraphic]
748
- })
749
- }
750
-
751
- /**
752
- * TODO:
753
- * 1. It is necessary, on the client side, to make the character move even if controlled by someone else (problem: same playerId so, one will not move because of the client side prediction. Solution: create a new Id ? like session Id ?
754
- * 2. You would need several sockets per character. If the character changes map or changes server, all players controlling the character must be able to see it
755
- * 3. If the player regains control, what happens, do we return to the previous map?
756
- * 4. If it's an event, you must be able to get the event by id in GameEngine
757
- */
758
- takePossessionOf(otherPlayer: RpgPlayer | RpgEvent) {
759
- this.otherPossessedPlayer = otherPlayer
760
- this._socket.emit('playerJoined', { playerId: otherPlayer.id, session: this.session })
761
- this.cameraFollow(otherPlayer)
762
- }
763
-
764
- /**
765
- * Sends the client which event or player the camera should follow. You can set options to perform a motion animation
766
- *
767
- * @title Camera Follow
768
- * @method player.cameraFollow(otherPlayer,options)
769
- * @param {RpgPlayer | RpgEvent} otherPlayer
770
- * @param {options} options
771
- * @param {object | boolean} [options.smoothMove] - animate. Set a boolean to use default parameters
772
- * @param {number} [options.smoothMove.time=1000] - time to animate
773
- * @param {string} [options.smoothMove.ease=linear] - easing to use. Go to https://easings.net to get function name
774
- * @returns {void}
775
- * @since 3.1.0
776
- * @memberof Player
777
- */
778
- cameraFollow(otherPlayer: RpgPlayer | RpgEvent, options: CameraOptions = {}) {
779
- if (otherPlayer.id == this.id) {
780
- this.followingId = null
781
- }
782
- else {
783
- this.followingId = otherPlayer.id
784
- }
785
- this.emit(SocketEvents.CallMethod, {
786
- objectId: this.playerId,
787
- name: SocketMethods.CameraFollow,
788
- params: [otherPlayer.id, options]
789
- })
790
- }
791
-
792
- /**
793
- * Emit data to clients with socket
794
- *
795
- * @title Emit to client
796
- * @method player.emit(key,value)
797
- * @param {string} key
798
- * @param {any} value
799
- * @returns {void}
800
- * @memberof Player
801
- */
802
- public emit(key: string, value?: any): void {
803
- if (this._socket) this._socket.emit(key, value)
804
- }
805
-
806
-
807
- /**
808
- * Listen to the data (socket) sent by the client
809
- *
810
- * @title Listen to data from the client
811
- * @method player.on(key,cb)
812
- * @param {string} key
813
- * @param {function} cb
814
- * @returns {void}
815
- * @memberof Player
816
- */
817
- public on(key: string, cb: Function) {
818
- if (this._socket) this._socket.on(key, cb)
819
- }
820
-
821
- /**
822
- * Adds a one-time listener function for the event named eventName
823
- *
824
- * @title Listen one-time to data from the client
825
- * @method player.once(key,cb)
826
- * @since 3.0.0-beta.5
827
- * @param {string} key
828
- * @param {function} cb
829
- * @returns {void}
830
- * @memberof Player
831
- */
832
- public once(key: string, cb: Function) {
833
- if (this._socket) this._socket.once(key, cb)
834
- }
835
-
836
- /**
837
- * Removes all listeners of the specified eventName.
838
- *
839
- * @title Removes all listeners of the client
840
- * @method player.off(key)
841
- * @since 3.0.0-beta.5
842
- * @param {string} key
843
- * @returns {void}
844
- * @memberof Player
845
- */
846
- public off(key: string) {
847
- if (this._socket) this._socket.removeAllListeners(key)
848
- }
849
-
850
- disconnect() {
851
- if (this._socket) this._socket.disconnect()
852
- }
853
-
854
- emitToMap(key: string, value: any) {
855
- const map = this.getCurrentMap()
856
- if (map) {
857
- map.$setCurrentState(`users.${this.id}.${key}`, value)
858
- }
859
- }
860
-
861
- private execMethodSync<T>(methodName: string, methodData = [], target?: any): T {
862
- let ret
863
- if (target && target[methodName]) {
864
- ret = target[methodName](...methodData)
865
- }
866
- return ret
867
- }
868
-
869
- async execMethod(methodName: string, methodData = [], target?: any) {
870
- const ignoreIfDataLoading = ['onLevelUp', 'onDead']
871
- if (ignoreIfDataLoading.includes(methodName) && this._dataLoading) {
872
- return
873
- }
874
- let ret
875
- if (target && target[methodName]) {
876
- ret = target[methodName](...methodData)
877
- if (isPromise(ret)) await ret
878
- }
879
- else {
880
- ret = await RpgPlugin.emit(`Server.${methodName}`, [this, ...methodData], true)
881
- }
882
- this.syncChanges()
883
- return ret
884
- }
885
-
886
- _triggerHook(name, val?) {
887
- if (this[name]) this[name](val)
888
- this.emit('Player.' + name, val)
889
- }
890
-
891
- // @internal
892
- /**
893
- * Allows you to manually update a status in the rooms that will then be sent to the customer.
894
- * @param path
895
- * @example
896
- * ```ts
897
- * player.changeRoomState('hp')
898
- * ```
899
- */
900
- changeRoomState(path: string) {
901
- this.world?.forEachUserRooms(this.id, (room) => {
902
- (room as any).$setCurrentState(`users.${this.id}.${path}`)
903
- })
904
- }
905
-
906
- private _eventChanges() {
907
- if (!this._getMap(this.map)) return
908
- const {
909
- events
910
- } = this._getMap(this.map)
911
- const arrayEvents: any[] = [...Object.values(this.events), ...Object.values(events)]
912
- for (let event of arrayEvents) {
913
- if (event.onChanges) event.onChanges(this)
914
- }
915
- }
916
-
917
- /**
918
- * Allows to play a sound, heard only by the player or by the players of the map
919
- *
920
- * Here is a sound, client side:
921
- *
922
- * ```ts
923
- * import { Sound } from '@rpgjs/client'
924
- * @Sound({
925
- * id: 'town-music',
926
- * sound: require('./sound/town.ogg')
927
- * })
928
- * export class TownMusic {}
929
- * ```
930
- *
931
- * Here is the call of the method, server side:
932
- *
933
- * ```ts
934
- * player.playSound('town-music')
935
- * ```
936
- *
937
- * If you want everyone to listen to the sound on the map:
938
- *
939
- * ```ts
940
- * player.playSound('town-music', true)
941
- * ```
942
- *
943
- * @title Play Sound
944
- * @method player.playSound(soundId,allMap=false)
945
- * @param {string} soundId Sound identifier, defined on the client side
946
- * @param {boolean} [forEveryone=false] Indicate if the sound is heard by the players on the map
947
- * @since 3.0.0-alpha.9
948
- * @returns {void}
949
- * @memberof Player
950
- */
951
- playSound(soundId: string, forEveryone: boolean = false) {
952
- const obj = {
953
- objectId: this.playerId,
954
- name: SocketMethods.PlaySound,
955
- params: [soundId]
956
- }
957
- if (!forEveryone) {
958
- this.emit(SocketEvents.CallMethod, obj)
959
- return
960
- }
961
- this.emitToMap(SocketEvents.CallMethod, obj)
962
- }
963
-
2
+ combineMixins,
3
+ Hooks,
4
+ ModulesToken,
5
+ RpgCommonPlayer,
6
+ ShowAnimationParams,
7
+ Constructor,
8
+ ZoneOptions,
9
+ } from "@rpgjs/common";
10
+ import { WithComponentManager, IComponentManager } from "./ComponentManager";
11
+ import { RpgMap } from "../rooms/map";
12
+ import { Context, inject } from "@signe/di";
13
+ import { IGuiManager, WithGuiManager } from "./GuiManager";
14
+ import { MockConnection } from "@signe/room";
15
+ import { IMoveManager, WithMoveManager } from "./MoveManager";
16
+ import { IGoldManager, WithGoldManager } from "./GoldManager";
17
+ import { IWithVariableManager, WithVariableManager } from "./VariableManager";
18
+ import { sync } from "@signe/sync";
19
+ import { signal } from "@signe/reactive";
20
+ import {
21
+ IWithParameterManager,
22
+ WithParameterManager,
23
+ } from "./ParameterManager";
24
+ import { WithItemFixture } from "./ItemFixture";
25
+ import { WithStateManager } from "./StateManager";
26
+ import { WithItemManager } from "./ItemManager";
27
+ import { lastValueFrom } from "rxjs";
28
+ import { WithBattleManager } from "./BattleManager";
29
+ import { WithEffectManager } from "./EffectManager";
30
+ import { WithSkillManager, IWithSkillManager } from "./SkillManager";
31
+ import { AGI, AGI_CURVE, DEX, DEX_CURVE, INT, INT_CURVE, MAXHP, MAXHP_CURVE, MAXSP, MAXSP_CURVE, STR, STR_CURVE } from "../presets";
32
+ import { WithClassManager } from "./ClassManager";
33
+ import { WithElementManager } from "./ElementManager";
34
+
35
+
36
+ /**
37
+ * Combines multiple RpgCommonPlayer mixins into one
38
+ *
39
+ * @param mixins - Array of mixin functions that extend RpgCommonPlayer
40
+ * @returns A single mixin function that applies all mixins
41
+ */
42
+ function combinePlayerMixins<T extends Constructor<RpgCommonPlayer>>(
43
+ mixins: Array<(Base: T) => any>
44
+ ) {
45
+ return (Base: T) =>
46
+ mixins.reduce((ExtendedClass, mixin) => mixin(ExtendedClass), Base);
964
47
  }
965
48
 
966
- export interface RpgPlayer extends
967
- EventManager,
968
- ItemManager,
969
- GoldManager,
970
- StateManager,
971
- SkillManager,
972
- ParameterManager,
973
- EffectManager,
974
- ClassManager,
975
- ElementManager,
976
- GuiManager,
977
- VariableManager,
978
- MoveManager,
979
- BattleManager,
980
- ComponentManager {
981
- _socket: any
982
- vision,
983
- attachShape: any
984
- }
985
-
986
- applyMixins(RpgPlayer, [
987
- EventManager,
988
- ItemManager,
989
- GoldManager,
990
- StateManager,
991
- SkillManager,
992
- ParameterManager,
993
- EffectManager,
994
- ClassManager,
995
- ElementManager,
996
- GuiManager,
997
- VariableManager,
998
- MoveManager,
999
- BattleManager,
1000
- ComponentManager
1001
- ])
1002
-
1003
- export interface RpgClassEvent<T> {
1004
- _name: string
1005
- new(): T,
49
+ const PlayerMixins = combinePlayerMixins([
50
+ WithComponentManager,
51
+ WithEffectManager,
52
+ WithGuiManager,
53
+ WithMoveManager,
54
+ WithGoldManager,
55
+ WithVariableManager,
56
+ WithParameterManager,
57
+ WithItemFixture,
58
+ WithStateManager,
59
+ WithItemManager,
60
+ WithSkillManager,
61
+ WithClassManager,
62
+ WithBattleManager,
63
+ WithElementManager,
64
+ ]);
65
+
66
+ /**
67
+ * RPG Player class with component management capabilities
68
+ */
69
+ export class RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
70
+ map: RpgMap | null = null;
71
+ context?: Context;
72
+ conn: MockConnection | null = null;
73
+
74
+ @sync(RpgPlayer) events = signal<RpgEvent[]>([]);
75
+
76
+ constructor() {
77
+ super();
78
+ this.expCurve = {
79
+ basis: 30,
80
+ extra: 20,
81
+ accelerationA: 30,
82
+ accelerationB: 30
83
+ }
84
+
85
+ this.addParameter(MAXHP, MAXHP_CURVE)
86
+ this.addParameter(MAXSP, MAXSP_CURVE)
87
+ this.addParameter(STR, STR_CURVE)
88
+ this.addParameter(INT, INT_CURVE)
89
+ this.addParameter(DEX, DEX_CURVE)
90
+ this.addParameter(AGI, AGI_CURVE)
91
+ this.allRecovery()
92
+ }
93
+
94
+ async execMethod(method: string, methodData: any[] = [], target?: any) {
95
+ let ret: any;
96
+ if (target) {
97
+ ret = await target[method](...methodData);
98
+ }
99
+ else {
100
+ const hooks = inject<Hooks>(this.context as any, ModulesToken);
101
+ ret = await lastValueFrom(hooks
102
+ .callHooks(`server-player-${method}`, target ?? this, ...methodData));
103
+ }
104
+ this.syncChanges()
105
+ return ret;
106
+ }
107
+
108
+ /**
109
+ * Change the map for this player
110
+ *
111
+ * @param mapId - The ID of the map to change to
112
+ * @param positions - Optional positions to place the player at
113
+ * @returns A promise that resolves when the map change is complete
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * // Change player to map "town" at position {x: 10, y: 20}
118
+ * await player.changeMap("town", {x: 10, y: 20});
119
+ *
120
+ * // Change player to map "dungeon" at a named position
121
+ * await player.changeMap("dungeon", "entrance");
122
+ * ```
123
+ */
124
+ async changeMap(
125
+ mapId: string,
126
+ positions?: { x: number; y: number; z?: number } | string
127
+ ): Promise<any | null | boolean> {
128
+ this.emit("changeMap", {
129
+ mapId: 'map-' + mapId,
130
+ positions,
131
+ });
132
+ return true;
133
+ }
134
+
135
+ async teleport(positions: { x: number; y: number }) {
136
+ if (!this.map) return false;
137
+ // For movable objects like players, the position represents the center
138
+ this.map.physic.updateHitbox(this.id, positions.x, positions.y);
139
+ }
140
+
141
+ getCurrentMap<T extends RpgMap = RpgMap>(): T | null {
142
+ return this.map as T | null;
143
+ }
144
+
145
+ emit(type: string, value?: any) {
146
+ const map = this.getCurrentMap();
147
+ if (!map || !this.conn) return;
148
+ map.$send(this.conn, {
149
+ type,
150
+ value,
151
+ });
152
+ }
153
+
154
+ showAnimation(params: ShowAnimationParams) {}
155
+
156
+ /**
157
+ * Run the change detection cycle. Normally, as soon as a hook is called in a class, the cycle is started. But you can start it manually
158
+ * The method calls the `onChanges` method on events and synchronizes all map data with the client.
159
+
160
+ * @title Run Sync Changes
161
+ * @method player.syncChanges()
162
+ * @returns {void}
163
+ * @memberof Player
164
+ */
165
+ syncChanges() {
166
+ this._eventChanges();
167
+ }
168
+
169
+ databaseById(id: string) {
170
+ const map = this.getCurrentMap();
171
+ if (!map) return;
172
+ const data = map.database()[id];
173
+ if (!data)
174
+ throw new Error(
175
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
176
+ );
177
+ return data;
178
+ }
179
+
180
+ private _eventChanges() {
181
+ const map = this.getCurrentMap();
182
+ if (!map) return;
183
+ const { events } = map;
184
+ const arrayEvents: any[] = [
185
+ ...Object.values(this.events()),
186
+ ...Object.values(events()),
187
+ ];
188
+ for (let event of arrayEvents) {
189
+ if (event.onChanges) event.onChanges(this);
190
+ }
191
+ }
192
+
193
+ attachShape(id: string, options: ZoneOptions) {
194
+ const map = this.getCurrentMap();
195
+ if (!map) return;
196
+
197
+ const physic = map.physic;
198
+
199
+ const zoneId = physic.addZone(id, {
200
+ linkedTo: this.id,
201
+ ...options,
202
+ });
203
+
204
+ physic.registerZoneEvents(
205
+ id,
206
+ (hitIds) => {
207
+ hitIds.forEach((id) => {
208
+ const event = map.getEvent<RpgEvent>(id);
209
+ const player = map.getPlayer(id);
210
+ const zone = physic.getZone(zoneId);
211
+ if (event) {
212
+ event.execMethod("onInShape", [zone, this]);
213
+ }
214
+ if (player) this.execMethod("onDetectInShape", [player, zone]);
215
+ });
216
+ },
217
+ (hitIds) => {
218
+ hitIds.forEach((id) => {
219
+ const event = map.getEvent<RpgEvent>(id);
220
+ const zone = physic.getZone(zoneId);
221
+ const player = map.getPlayer(id);
222
+ if (event) {
223
+ event.execMethod("onOutShape", [zone, this]);
224
+ }
225
+ if (player) this.execMethod("onDetectOutShape", [player, zone]);
226
+ });
227
+ }
228
+ );
229
+ }
230
+
231
+ broadcastEffect(id: string, params: any) {
232
+ const map = this.getCurrentMap();
233
+ if (!map) return;
234
+ map.$broadcast({
235
+ type: "showEffect",
236
+ value: {
237
+ id,
238
+ params,
239
+ object: this.id,
240
+ },
241
+ });
242
+ }
243
+
244
+ showHit(text: string) {
245
+ this.broadcastEffect("hit", {
246
+ text,
247
+ direction: this.direction(),
248
+ });
249
+ }
1006
250
  }
1007
251
 
1008
252
  export class RpgEvent extends RpgPlayer {
1009
-
1010
- public readonly type: string = 'event'
1011
- properties: any = {}
1012
- mode: EventMode
1013
- playerRelated: RpgPlayer | null = null
1014
-
1015
- constructor(playerId: string) {
1016
- super(playerId)
1017
- }
1018
-
1019
- async execMethod(methodName: string, methodData = []) {
1020
- if (!this[methodName]) {
1021
- return
1022
- }
1023
- const ret = this[methodName](...methodData)
1024
- return ret
1025
- }
1026
-
1027
- changeRoomState(path: string) {
1028
- const room = this.getCurrentMap()
1029
- if (room) {
1030
- (room as any).$setCurrentState(`events.${this.id}.${path}`)
1031
- }
1032
- }
1033
-
1034
- /**
1035
- * Deletes the event from the map (in shared or scenario mode)
1036
- *
1037
- * @title Remove
1038
- * @since 4.0.0
1039
- * @method event.remove()
1040
- * @returns {boolean} true if the event has been removed. If false, the event is not on the map
1041
- * @memberof RpgEvent
1042
- */
1043
- remove(): boolean {
1044
- let bool = false
1045
- if (this.playerRelated) bool = this.playerRelated.removeEvent(this.id)
1046
- const map = this.getCurrentMap()
1047
- if (map) {
1048
- bool = map.removeEvent(this.id)
1049
- }
1050
- return bool
1051
- }
1052
-
1053
- override emitToMap(key: string, value: any) {
1054
- const map = this.getCurrentMap()
1055
- if (map) {
1056
- const eventPath = `events.${this.id}.${key}`
1057
- if (this.playerRelated) {
1058
- map.$setCurrentState(`users.${this.playerRelated.id}.${eventPath}`, value)
1059
- }
1060
- else {
1061
- map.$setCurrentState(eventPath, value)
1062
- }
1063
- }
1064
- }
253
+ override async execMethod(methodName: string, methodData: any[] = [], instance = this) {
254
+ const hooks = inject<Hooks>(this.context as any, ModulesToken);
255
+ await lastValueFrom(hooks
256
+ .callHooks(`server-event-${methodName}`, instance, ...methodData));
257
+ if (!instance[methodName]) {
258
+ return;
259
+ }
260
+ const ret = instance[methodName](...methodData);
261
+ return ret;
262
+ }
263
+
264
+ remove() {
265
+ const map = this.getCurrentMap();
266
+ if (!map) return;
267
+ map.removeEvent(this.id);
268
+ }
1065
269
  }
270
+
271
+ export interface RpgPlayer
272
+ extends RpgCommonPlayer,
273
+ IComponentManager,
274
+ IGuiManager,
275
+ IMoveManager,
276
+ IGoldManager,
277
+ IWithVariableManager,
278
+ IWithParameterManager,
279
+ IWithSkillManager {}