@rpgjs/server 4.2.2 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{lib → dist}/Gui/DialogGui.d.ts +1 -2
- package/{lib → dist}/Gui/Gui.d.ts +1 -2
- package/dist/Player/BattleManager.d.ts +22 -0
- package/dist/Player/ClassManager.d.ts +18 -0
- package/dist/Player/Event.d.ts +0 -0
- package/dist/Player/ItemManager.d.ts +17 -0
- package/dist/Player/MoveManager.d.ts +177 -0
- package/dist/Player/ParameterManager.d.ts +42 -0
- package/dist/Player/Player.d.ts +73 -0
- package/dist/Player/SkillManager.d.ts +23 -0
- package/dist/Player/StateManager.d.ts +39 -0
- package/{lib → dist}/RpgServer.d.ts +34 -17
- package/dist/RpgServerEngine.d.ts +5 -0
- package/dist/core/context.d.ts +2 -0
- package/dist/core/inject.d.ts +5 -0
- package/dist/core/setup.d.ts +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +29673 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +2 -0
- package/dist/rooms/lobby.d.ts +6 -0
- package/dist/rooms/map.d.ts +110 -0
- package/package.json +16 -38
- package/src/Gui/DialogGui.ts +3 -4
- package/src/Gui/Gui.ts +4 -6
- package/src/Player/BattleManager.ts +108 -99
- package/src/Player/ClassManager.ts +47 -46
- package/src/Player/ComponentManager.ts +26 -535
- package/src/Player/EffectManager.ts +44 -59
- package/src/Player/ElementManager.ts +70 -72
- package/src/Player/Event.ts +0 -0
- package/src/Player/GoldManager.ts +24 -6
- package/src/Player/GuiManager.ts +106 -97
- package/src/Player/ItemFixture.ts +22 -17
- package/src/Player/ItemManager.ts +234 -225
- package/src/Player/MoveManager.ts +1047 -457
- package/src/Player/ParameterManager.ts +59 -37
- package/src/Player/Player.ts +272 -1058
- package/src/Player/SkillManager.ts +146 -137
- package/src/Player/StateManager.ts +124 -98
- package/src/Player/VariableManager.ts +23 -3
- package/src/RpgServer.ts +38 -14
- package/src/RpgServerEngine.ts +7 -0
- package/src/core/context.ts +3 -0
- package/src/core/inject.ts +17 -0
- package/src/core/setup.ts +20 -0
- package/src/index.ts +9 -31
- package/src/module.ts +32 -0
- package/src/presets/index.ts +1 -3
- package/src/rooms/lobby.ts +24 -0
- package/src/rooms/map.ts +311 -0
- package/tsconfig.json +26 -23
- package/vite.config.ts +21 -0
- package/CHANGELOG.md +0 -163
- package/LICENSE +0 -19
- package/browser/manifest.json +0 -7
- package/browser/rpg.server.js +0 -22372
- package/browser/rpg.server.umd.cjs +0 -22403
- package/lib/Game/EventManager.d.ts +0 -54
- package/lib/Game/EventManager.js +0 -107
- package/lib/Game/EventManager.js.map +0 -1
- package/lib/Game/Map.d.ts +0 -191
- package/lib/Game/Map.js +0 -414
- package/lib/Game/Map.js.map +0 -1
- package/lib/Game/WorldMaps.d.ts +0 -17
- package/lib/Game/WorldMaps.js +0 -28
- package/lib/Game/WorldMaps.js.map +0 -1
- package/lib/Gui/DialogGui.js +0 -57
- package/lib/Gui/DialogGui.js.map +0 -1
- package/lib/Gui/Gui.js +0 -37
- package/lib/Gui/Gui.js.map +0 -1
- package/lib/Gui/MenuGui.js +0 -23
- package/lib/Gui/MenuGui.js.map +0 -1
- package/lib/Gui/NotificationGui.js +0 -8
- package/lib/Gui/NotificationGui.js.map +0 -1
- package/lib/Gui/ShopGui.js +0 -40
- package/lib/Gui/ShopGui.js.map +0 -1
- package/lib/Gui/index.js +0 -7
- package/lib/Gui/index.js.map +0 -1
- package/lib/Interfaces/Gui.d.ts +0 -4
- package/lib/Interfaces/Gui.js +0 -2
- package/lib/Interfaces/Gui.js.map +0 -1
- package/lib/Interfaces/StateStore.d.ts +0 -5
- package/lib/Interfaces/StateStore.js +0 -2
- package/lib/Interfaces/StateStore.js.map +0 -1
- package/lib/MatchMaker.d.ts +0 -24
- package/lib/MatchMaker.js +0 -42
- package/lib/MatchMaker.js.map +0 -1
- package/lib/Monitor/index.d.ts +0 -17
- package/lib/Monitor/index.js +0 -72
- package/lib/Monitor/index.js.map +0 -1
- package/lib/Player/BattleManager.d.ts +0 -36
- package/lib/Player/BattleManager.js +0 -101
- package/lib/Player/BattleManager.js.map +0 -1
- package/lib/Player/ClassManager.d.ts +0 -47
- package/lib/Player/ClassManager.js +0 -65
- package/lib/Player/ClassManager.js.map +0 -1
- package/lib/Player/ComponentManager.d.ts +0 -397
- package/lib/Player/ComponentManager.js +0 -505
- package/lib/Player/ComponentManager.js.map +0 -1
- package/lib/Player/EffectManager.d.ts +0 -46
- package/lib/Player/EffectManager.js +0 -75
- package/lib/Player/EffectManager.js.map +0 -1
- package/lib/Player/ElementManager.d.ts +0 -108
- package/lib/Player/ElementManager.js +0 -120
- package/lib/Player/ElementManager.js.map +0 -1
- package/lib/Player/GoldManager.d.ts +0 -17
- package/lib/Player/GoldManager.js +0 -27
- package/lib/Player/GoldManager.js.map +0 -1
- package/lib/Player/GuiManager.d.ts +0 -247
- package/lib/Player/GuiManager.js +0 -291
- package/lib/Player/GuiManager.js.map +0 -1
- package/lib/Player/ItemFixture.d.ts +0 -7
- package/lib/Player/ItemFixture.js +0 -19
- package/lib/Player/ItemFixture.js.map +0 -1
- package/lib/Player/ItemManager.d.ts +0 -331
- package/lib/Player/ItemManager.js +0 -475
- package/lib/Player/ItemManager.js.map +0 -1
- package/lib/Player/MoveManager.d.ts +0 -333
- package/lib/Player/MoveManager.js +0 -450
- package/lib/Player/MoveManager.js.map +0 -1
- package/lib/Player/ParameterManager.d.ts +0 -316
- package/lib/Player/ParameterManager.js +0 -408
- package/lib/Player/ParameterManager.js.map +0 -1
- package/lib/Player/Player.d.ts +0 -828
- package/lib/Player/Player.js +0 -927
- package/lib/Player/Player.js.map +0 -1
- package/lib/Player/SkillManager.d.ts +0 -163
- package/lib/Player/SkillManager.js +0 -223
- package/lib/Player/SkillManager.js.map +0 -1
- package/lib/Player/StateManager.d.ts +0 -185
- package/lib/Player/StateManager.js +0 -228
- package/lib/Player/StateManager.js.map +0 -1
- package/lib/Player/VariableManager.d.ts +0 -46
- package/lib/Player/VariableManager.js +0 -52
- package/lib/Player/VariableManager.js.map +0 -1
- package/lib/Query.d.ts +0 -127
- package/lib/Query.js +0 -159
- package/lib/Query.js.map +0 -1
- package/lib/RpgServer.js +0 -2
- package/lib/RpgServer.js.map +0 -1
- package/lib/Scenes/Map.d.ts +0 -136
- package/lib/Scenes/Map.js +0 -273
- package/lib/Scenes/Map.js.map +0 -1
- package/lib/decorators/event.d.ts +0 -43
- package/lib/decorators/event.js +0 -13
- package/lib/decorators/event.js.map +0 -1
- package/lib/decorators/map.d.ts +0 -178
- package/lib/decorators/map.js +0 -43
- package/lib/decorators/map.js.map +0 -1
- package/lib/entry-point.d.ts +0 -34
- package/lib/entry-point.js +0 -70
- package/lib/entry-point.js.map +0 -1
- package/lib/express/api.d.ts +0 -3
- package/lib/express/api.js +0 -105
- package/lib/express/api.js.map +0 -1
- package/lib/express/errors/NotAuthorized.d.ts +0 -4
- package/lib/express/errors/NotAuthorized.js +0 -7
- package/lib/express/errors/NotAuthorized.js.map +0 -1
- package/lib/express/errors/NotFound.d.ts +0 -4
- package/lib/express/errors/NotFound.js +0 -7
- package/lib/express/errors/NotFound.js.map +0 -1
- package/lib/express/server.d.ts +0 -16
- package/lib/express/server.js +0 -65
- package/lib/express/server.js.map +0 -1
- package/lib/index.d.ts +0 -20
- package/lib/index.js +0 -19
- package/lib/index.js.map +0 -1
- package/lib/inject.d.ts +0 -22
- package/lib/inject.js +0 -29
- package/lib/inject.js.map +0 -1
- package/lib/logs/index.js +0 -6
- package/lib/logs/index.js.map +0 -1
- package/lib/logs/item.js +0 -34
- package/lib/logs/item.js.map +0 -1
- package/lib/logs/log.js +0 -7
- package/lib/logs/log.js.map +0 -1
- package/lib/logs/skill.js +0 -19
- package/lib/logs/skill.js.map +0 -1
- package/lib/logs/state.js +0 -13
- package/lib/logs/state.js.map +0 -1
- package/lib/models/Item.d.ts +0 -10
- package/lib/models/Item.js +0 -2
- package/lib/models/Item.js.map +0 -1
- package/lib/presets/index.js +0 -65
- package/lib/presets/index.js.map +0 -1
- package/lib/server.d.ts +0 -137
- package/lib/server.js +0 -443
- package/lib/server.js.map +0 -1
- package/rpg.toml +0 -14
- package/src/Game/EventManager.ts +0 -125
- package/src/Game/Map.ts +0 -495
- package/src/Game/WorldMaps.ts +0 -45
- package/src/Interfaces/Gui.ts +0 -4
- package/src/Interfaces/StateStore.ts +0 -5
- package/src/MatchMaker.ts +0 -65
- package/src/Monitor/index.ts +0 -78
- package/src/Query.ts +0 -172
- package/src/Scenes/Map.ts +0 -310
- package/src/decorators/event.ts +0 -58
- package/src/decorators/map.ts +0 -226
- package/src/entry-point.ts +0 -111
- package/src/express/api.ts +0 -118
- package/src/express/errors/NotAuthorized.ts +0 -6
- package/src/express/errors/NotFound.ts +0 -6
- package/src/express/server.ts +0 -87
- package/src/inject.ts +0 -33
- package/src/models/Item.ts +0 -11
- package/src/server.ts +0 -459
- /package/{lib → dist}/Gui/MenuGui.d.ts +0 -0
- /package/{lib → dist}/Gui/NotificationGui.d.ts +0 -0
- /package/{lib → dist}/Gui/ShopGui.d.ts +0 -0
- /package/{lib → dist}/Gui/index.d.ts +0 -0
- /package/{lib → dist}/logs/index.d.ts +0 -0
- /package/{lib → dist}/logs/item.d.ts +0 -0
- /package/{lib → dist}/logs/log.d.ts +0 -0
- /package/{lib → dist}/logs/skill.d.ts +0 -0
- /package/{lib → dist}/logs/state.d.ts +0 -0
- /package/{lib → dist}/presets/index.d.ts +0 -0
|
@@ -1,21 +1,65 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { type Constructor } from "@rpgjs/common";
|
|
2
|
+
import { RpgCommonPlayer, Matter, Direction } from "@rpgjs/common";
|
|
3
|
+
import {
|
|
4
|
+
MovementManager,
|
|
5
|
+
MovementStrategy,
|
|
6
|
+
LinearMove,
|
|
7
|
+
Dash,
|
|
8
|
+
Knockback,
|
|
9
|
+
PathFollow,
|
|
10
|
+
Oscillate,
|
|
11
|
+
CompositeMovement,
|
|
12
|
+
SeekAvoid,
|
|
13
|
+
LinearRepulsion,
|
|
14
|
+
IceMovement,
|
|
15
|
+
ProjectileMovement,
|
|
16
|
+
ProjectileType,
|
|
17
|
+
random,
|
|
18
|
+
isFunction,
|
|
19
|
+
capitalize
|
|
20
|
+
} from "@rpgjs/common";
|
|
21
|
+
import { RpgMap } from "../rooms/map";
|
|
22
|
+
import { Observable, Subscription, takeUntil, Subject, tap, switchMap, of, from } from 'rxjs';
|
|
23
|
+
import { RpgPlayer } from "./Player";
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
interface PlayerWithMixins extends RpgCommonPlayer {
|
|
27
|
+
getCurrentMap(): RpgMap;
|
|
28
|
+
id: string;
|
|
29
|
+
server: any;
|
|
30
|
+
_destroy$: Subject<void>;
|
|
31
|
+
frequency: number;
|
|
32
|
+
nbPixelInTile: number;
|
|
33
|
+
moveByDirection: (direction: Direction, deltaTimeInt: number) => Promise<boolean>;
|
|
34
|
+
changeDirection: (direction: Direction) => boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IMoveManager {
|
|
38
|
+
addMovement(strategy: MovementStrategy): void;
|
|
39
|
+
removeMovement(strategy: MovementStrategy): boolean;
|
|
40
|
+
clearMovements(): void;
|
|
41
|
+
hasActiveMovements(): boolean;
|
|
42
|
+
getActiveMovements(): MovementStrategy[];
|
|
43
|
+
|
|
44
|
+
moveTo(target: RpgCommonPlayer | { x: number, y: number }): void;
|
|
45
|
+
stopMoveTo(): void;
|
|
46
|
+
dash(direction: { x: number, y: number }, speed?: number, duration?: number): void;
|
|
47
|
+
knockback(direction: { x: number, y: number }, force?: number, duration?: number): void;
|
|
48
|
+
followPath(waypoints: Array<{ x: number, y: number }>, speed?: number, loop?: boolean): void;
|
|
49
|
+
oscillate(direction: { x: number, y: number }, amplitude?: number, period?: number): void;
|
|
50
|
+
applyIceMovement(direction: { x: number, y: number }, maxSpeed?: number): void;
|
|
51
|
+
shootProjectile(type: ProjectileType, direction: { x: number, y: number }, speed?: number): void;
|
|
52
|
+
moveRoutes(routes: Routes): Promise<boolean>;
|
|
53
|
+
infiniteMoveRoute(routes: Routes): void;
|
|
54
|
+
breakRoutes(force?: boolean): void;
|
|
55
|
+
replayRoutes(): void;
|
|
56
|
+
}
|
|
57
|
+
|
|
14
58
|
|
|
15
59
|
function wait(sec: number) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
setTimeout(resolve, sec * 1000)
|
|
62
|
+
})
|
|
19
63
|
}
|
|
20
64
|
|
|
21
65
|
type CallbackTileMove = (player: RpgPlayer, map) => Direction[]
|
|
@@ -23,23 +67,23 @@ type CallbackTurnMove = (player: RpgPlayer, map) => string
|
|
|
23
67
|
type Routes = (string | Promise<any> | Direction | Direction[] | Function)[]
|
|
24
68
|
|
|
25
69
|
export enum Frequency {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
70
|
+
Lowest = 600,
|
|
71
|
+
Lower = 400,
|
|
72
|
+
Low = 200,
|
|
73
|
+
High = 100,
|
|
74
|
+
Higher = 50,
|
|
75
|
+
Highest = 25,
|
|
76
|
+
None = 0
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
export enum Speed {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
80
|
+
Slowest = 0.2,
|
|
81
|
+
Slower = 0.5,
|
|
82
|
+
Slow = 1,
|
|
83
|
+
Normal = 3,
|
|
84
|
+
Fast = 5,
|
|
85
|
+
Faster = 7,
|
|
86
|
+
Fastest = 10
|
|
43
87
|
}
|
|
44
88
|
|
|
45
89
|
/**
|
|
@@ -71,262 +115,375 @@ export enum Speed {
|
|
|
71
115
|
* */
|
|
72
116
|
class MoveList {
|
|
73
117
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
repeatMove(direction: Direction, repeat: number): Direction[] {
|
|
119
|
+
// Safety check for valid repeat value
|
|
120
|
+
if (!Number.isFinite(repeat) || repeat < 0 || repeat > 10000) {
|
|
121
|
+
console.warn('Invalid repeat value:', repeat, 'using default value 1');
|
|
122
|
+
repeat = 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Ensure repeat is an integer
|
|
126
|
+
repeat = Math.floor(repeat);
|
|
127
|
+
|
|
128
|
+
// Additional safety check - ensure repeat is a safe integer
|
|
129
|
+
if (repeat < 0 || repeat > Number.MAX_SAFE_INTEGER || !Number.isSafeInteger(repeat)) {
|
|
130
|
+
console.warn('Unsafe repeat value:', repeat, 'using default value 1');
|
|
131
|
+
repeat = 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
return new Array(repeat).fill(direction);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Error creating array with repeat:', repeat, error);
|
|
138
|
+
return [direction]; // Return single direction as fallback
|
|
139
|
+
}
|
|
140
|
+
}
|
|
77
141
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
142
|
+
private repeatTileMove(direction: string, repeat: number, propMap: string): CallbackTileMove {
|
|
143
|
+
return (player: RpgPlayer, map): Direction[] => {
|
|
144
|
+
const playerSpeed = typeof player.speed === 'function' ? player.speed() : player.speed;
|
|
145
|
+
|
|
146
|
+
// Safety checks
|
|
147
|
+
if (!playerSpeed || playerSpeed <= 0) {
|
|
148
|
+
console.warn('Invalid player speed:', playerSpeed, 'using default speed 3');
|
|
149
|
+
return this[direction](repeat);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const repeatTile = Math.floor((map[propMap] || 32) / playerSpeed) * repeat;
|
|
153
|
+
|
|
154
|
+
// Additional safety check for the calculated repeat value
|
|
155
|
+
if (!Number.isFinite(repeatTile) || repeatTile < 0 || repeatTile > 10000) {
|
|
156
|
+
console.warn('Calculated repeatTile is invalid:', repeatTile, 'using original repeat:', repeat);
|
|
157
|
+
return this[direction](repeat);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Final safety check before calling the method
|
|
161
|
+
if (!Number.isSafeInteger(repeatTile)) {
|
|
162
|
+
console.warn('repeatTile is not a safe integer:', repeatTile, 'using original repeat:', repeat);
|
|
163
|
+
return this[direction](repeat);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
return this[direction](repeatTile);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error calling direction method with repeatTile:', repeatTile, error);
|
|
170
|
+
return this[direction](repeat); // Fallback to original repeat
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
84
174
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
175
|
+
right(repeat: number = 1): Direction[] {
|
|
176
|
+
return this.repeatMove(Direction.Right, repeat)
|
|
177
|
+
}
|
|
88
178
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
179
|
+
left(repeat: number = 1): Direction[] {
|
|
180
|
+
return this.repeatMove(Direction.Left, repeat)
|
|
181
|
+
}
|
|
92
182
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
183
|
+
up(repeat: number = 1): Direction[] {
|
|
184
|
+
return this.repeatMove(Direction.Up, repeat)
|
|
185
|
+
}
|
|
96
186
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
187
|
+
down(repeat: number = 1): Direction[] {
|
|
188
|
+
return this.repeatMove(Direction.Down, repeat)
|
|
189
|
+
}
|
|
100
190
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
191
|
+
wait(sec: number): Promise<unknown> {
|
|
192
|
+
return wait(sec)
|
|
193
|
+
}
|
|
104
194
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
195
|
+
random(repeat: number = 1): Direction[] {
|
|
196
|
+
// Safety check for valid repeat value
|
|
197
|
+
if (!Number.isFinite(repeat) || repeat < 0 || repeat > 10000) {
|
|
198
|
+
console.warn('Invalid repeat value in random:', repeat, 'using default value 1');
|
|
199
|
+
repeat = 1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Ensure repeat is an integer
|
|
203
|
+
repeat = Math.floor(repeat);
|
|
204
|
+
|
|
205
|
+
// Additional safety check - ensure repeat is a safe integer
|
|
206
|
+
if (repeat < 0 || repeat > Number.MAX_SAFE_INTEGER || !Number.isSafeInteger(repeat)) {
|
|
207
|
+
console.warn('Unsafe repeat value in random:', repeat, 'using default value 1');
|
|
208
|
+
repeat = 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
return new Array(repeat).fill(null).map(() => [
|
|
213
|
+
Direction.Right,
|
|
214
|
+
Direction.Left,
|
|
215
|
+
Direction.Up,
|
|
216
|
+
Direction.Down
|
|
217
|
+
][random(0, 3)]);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('Error creating random array with repeat:', repeat, error);
|
|
220
|
+
return [Direction.Down]; // Return single direction as fallback
|
|
221
|
+
}
|
|
222
|
+
}
|
|
113
223
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
224
|
+
tileRight(repeat: number = 1): CallbackTileMove {
|
|
225
|
+
return this.repeatTileMove('right', repeat, 'tileWidth')
|
|
226
|
+
}
|
|
117
227
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
228
|
+
tileLeft(repeat: number = 1): CallbackTileMove {
|
|
229
|
+
return this.repeatTileMove('left', repeat, 'tileWidth')
|
|
230
|
+
}
|
|
121
231
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
232
|
+
tileUp(repeat: number = 1): CallbackTileMove {
|
|
233
|
+
return this.repeatTileMove('up', repeat, 'tileHeight')
|
|
234
|
+
}
|
|
125
235
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
236
|
+
tileDown(repeat: number = 1): CallbackTileMove {
|
|
237
|
+
return this.repeatTileMove('down', repeat, 'tileHeight')
|
|
238
|
+
}
|
|
129
239
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
240
|
+
tileRandom(repeat: number = 1): CallbackTileMove {
|
|
241
|
+
return (player: RpgPlayer, map): Direction[] => {
|
|
242
|
+
// Safety check for valid repeat value
|
|
243
|
+
if (!Number.isFinite(repeat) || repeat < 0 || repeat > 1000) {
|
|
244
|
+
console.warn('Invalid repeat value in tileRandom:', repeat, 'using default value 1');
|
|
245
|
+
repeat = 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Ensure repeat is an integer
|
|
249
|
+
repeat = Math.floor(repeat);
|
|
250
|
+
|
|
251
|
+
let directions: Direction[] = []
|
|
252
|
+
for (let i = 0; i < repeat; i++) {
|
|
253
|
+
const randFn: CallbackTileMove = [
|
|
254
|
+
this.tileRight(),
|
|
255
|
+
this.tileLeft(),
|
|
256
|
+
this.tileUp(),
|
|
257
|
+
this.tileDown()
|
|
258
|
+
][random(0, 3)]
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const newDirections = randFn(player, map);
|
|
262
|
+
if (Array.isArray(newDirections)) {
|
|
263
|
+
directions = [...directions, ...newDirections];
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.warn('Error in tileRandom iteration:', error);
|
|
267
|
+
// Continue with next iteration instead of breaking
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Safety check to prevent excessive array growth
|
|
271
|
+
if (directions.length > 10000) {
|
|
272
|
+
console.warn('tileRandom generated too many directions, truncating');
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return directions
|
|
277
|
+
}
|
|
278
|
+
}
|
|
148
279
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
switch (directionOtherPlayer) {
|
|
153
|
-
case Direction.Left:
|
|
154
|
-
case Direction.Right:
|
|
155
|
-
if (otherPlayer.position.x > player.position.x) {
|
|
156
|
-
newDirection = Direction.Left
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
newDirection = Direction.Right
|
|
160
|
-
}
|
|
161
|
-
break
|
|
162
|
-
case Direction.Up:
|
|
163
|
-
case Direction.Down:
|
|
164
|
-
if (otherPlayer.position.y > player.position.y) {
|
|
165
|
-
newDirection = Direction.Up
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
newDirection = Direction.Down
|
|
169
|
-
}
|
|
170
|
-
break
|
|
171
|
-
}
|
|
172
|
-
return newDirection
|
|
173
|
-
}
|
|
280
|
+
private _awayFromPlayerDirection(player: RpgPlayer, otherPlayer: RpgPlayer): Direction {
|
|
281
|
+
const directionOtherPlayer = otherPlayer.getDirection()
|
|
282
|
+
let newDirection: Direction = Direction.Down
|
|
174
283
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
return newDirection
|
|
199
|
-
}
|
|
284
|
+
switch (directionOtherPlayer) {
|
|
285
|
+
case Direction.Left:
|
|
286
|
+
case Direction.Right:
|
|
287
|
+
if (otherPlayer.x() > player.x()) {
|
|
288
|
+
newDirection = Direction.Left
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
newDirection = Direction.Right
|
|
292
|
+
}
|
|
293
|
+
break
|
|
294
|
+
case Direction.Up:
|
|
295
|
+
case Direction.Down:
|
|
296
|
+
if (otherPlayer.y() > player.y()) {
|
|
297
|
+
newDirection = Direction.Up
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
newDirection = Direction.Down
|
|
301
|
+
}
|
|
302
|
+
break
|
|
303
|
+
}
|
|
304
|
+
return newDirection
|
|
305
|
+
}
|
|
200
306
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
307
|
+
private _towardPlayerDirection(player: RpgPlayer, otherPlayer: RpgPlayer): Direction {
|
|
308
|
+
const directionOtherPlayer = otherPlayer.getDirection()
|
|
309
|
+
let newDirection: Direction = Direction.Down
|
|
310
|
+
|
|
311
|
+
switch (directionOtherPlayer) {
|
|
312
|
+
case Direction.Left:
|
|
313
|
+
case Direction.Right:
|
|
314
|
+
if (otherPlayer.x() > player.x()) {
|
|
315
|
+
newDirection = Direction.Right
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
newDirection = Direction.Left
|
|
319
|
+
}
|
|
320
|
+
break
|
|
321
|
+
case Direction.Up:
|
|
322
|
+
case Direction.Down:
|
|
323
|
+
if (otherPlayer.y() > player.y()) {
|
|
324
|
+
newDirection = Direction.Down
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
newDirection = Direction.Up
|
|
328
|
+
}
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
return newDirection
|
|
332
|
+
}
|
|
223
333
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
334
|
+
private _awayFromPlayer({ isTile, typeMov }: { isTile: boolean, typeMov: string }, otherPlayer: RpgPlayer, repeat: number = 1) {
|
|
335
|
+
const method = (dir: Direction) => {
|
|
336
|
+
const direction: string = DirectionNames[dir as any] || 'down'
|
|
337
|
+
return this[isTile ? 'tile' + capitalize(direction) : direction](repeat)
|
|
338
|
+
}
|
|
339
|
+
return (player: RpgPlayer, map) => {
|
|
340
|
+
let newDirection: Direction = Direction.Down
|
|
341
|
+
switch (typeMov) {
|
|
342
|
+
case 'away':
|
|
343
|
+
newDirection = this._awayFromPlayerDirection(player, otherPlayer)
|
|
344
|
+
break;
|
|
345
|
+
case 'toward':
|
|
346
|
+
newDirection = this._towardPlayerDirection(player, otherPlayer)
|
|
347
|
+
break
|
|
348
|
+
}
|
|
349
|
+
let direction: any = method(newDirection)
|
|
350
|
+
if (isFunction(direction)) {
|
|
351
|
+
direction = direction(player, map)
|
|
352
|
+
}
|
|
353
|
+
return direction
|
|
354
|
+
}
|
|
355
|
+
}
|
|
227
356
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
357
|
+
towardPlayer(player: RpgPlayer, repeat: number = 1) {
|
|
358
|
+
return this._awayFromPlayer({ isTile: false, typeMov: 'toward' }, player, repeat)
|
|
359
|
+
}
|
|
231
360
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
361
|
+
tileTowardPlayer(player: RpgPlayer, repeat: number = 1) {
|
|
362
|
+
return this._awayFromPlayer({ isTile: true, typeMov: 'toward' }, player, repeat)
|
|
363
|
+
}
|
|
235
364
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
365
|
+
awayFromPlayer(player: RpgPlayer, repeat: number = 1): CallbackTileMove {
|
|
366
|
+
return this._awayFromPlayer({ isTile: false, typeMov: 'away' }, player, repeat)
|
|
367
|
+
}
|
|
239
368
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
369
|
+
tileAwayFromPlayer(player: RpgPlayer, repeat: number = 1): CallbackTileMove {
|
|
370
|
+
return this._awayFromPlayer({ isTile: true, typeMov: 'away' }, player, repeat)
|
|
371
|
+
}
|
|
243
372
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
373
|
+
turnLeft(): string {
|
|
374
|
+
return 'turn-' + Direction.Left
|
|
375
|
+
}
|
|
247
376
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
377
|
+
turnRight(): string {
|
|
378
|
+
return 'turn-' + Direction.Right
|
|
379
|
+
}
|
|
251
380
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
381
|
+
turnUp(): string {
|
|
382
|
+
return 'turn-' + Direction.Up
|
|
383
|
+
}
|
|
255
384
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.turnLeft(),
|
|
260
|
-
this.turnUp(),
|
|
261
|
-
this.turnDown()
|
|
262
|
-
][random(0, 3)]
|
|
263
|
-
}
|
|
385
|
+
turnDown(): string {
|
|
386
|
+
return 'turn-' + Direction.Down
|
|
387
|
+
}
|
|
264
388
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
389
|
+
turnRandom(): string {
|
|
390
|
+
return [
|
|
391
|
+
this.turnRight(),
|
|
392
|
+
this.turnLeft(),
|
|
393
|
+
this.turnUp(),
|
|
394
|
+
this.turnDown()
|
|
395
|
+
][random(0, 3)]
|
|
396
|
+
}
|
|
271
397
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
398
|
+
turnAwayFromPlayer(otherPlayer: RpgPlayer): CallbackTurnMove {
|
|
399
|
+
return (player: RpgPlayer) => {
|
|
400
|
+
const direction = this._awayFromPlayerDirection(player, otherPlayer)
|
|
401
|
+
return 'turn-' + direction
|
|
402
|
+
}
|
|
403
|
+
}
|
|
279
404
|
|
|
280
|
-
|
|
405
|
+
turnTowardPlayer(otherPlayer: RpgPlayer): CallbackTurnMove {
|
|
406
|
+
return (player: RpgPlayer) => {
|
|
407
|
+
const direction = this._towardPlayerDirection(player, otherPlayer)
|
|
408
|
+
return 'turn-' + direction
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
281
412
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
413
|
+
// Direction mapping for string conversion
|
|
414
|
+
const DirectionNames: { [key: string]: string } = {
|
|
415
|
+
[Direction.Up]: 'up',
|
|
416
|
+
[Direction.Down]: 'down',
|
|
417
|
+
[Direction.Left]: 'left',
|
|
418
|
+
[Direction.Right]: 'right'
|
|
419
|
+
};
|
|
286
420
|
|
|
287
|
-
|
|
288
|
-
* Changes the player's speed
|
|
289
|
-
*
|
|
290
|
-
* ```ts
|
|
291
|
-
* player.speed = 1
|
|
292
|
-
* ```
|
|
293
|
-
*
|
|
294
|
-
* You can use Speed enum
|
|
295
|
-
*
|
|
296
|
-
* ```ts
|
|
297
|
-
* import { Speed } from '@rpgjs/server'
|
|
298
|
-
* player.speed = Speed.Slow
|
|
299
|
-
* ```
|
|
300
|
-
*
|
|
301
|
-
* @title Change Speed
|
|
302
|
-
* @prop {number} player.speed
|
|
303
|
-
* @enum {number}
|
|
304
|
-
*
|
|
305
|
-
* Speed.Slowest | 0.2
|
|
306
|
-
* Speed.Slower | 0.5
|
|
307
|
-
* Speed.Slow | 1
|
|
308
|
-
* Speed.Normal | 3
|
|
309
|
-
* Speed.Fast | 5
|
|
310
|
-
* Speed.Faster | 7
|
|
311
|
-
* Speed.Fastest | 10
|
|
312
|
-
* @default 3
|
|
313
|
-
* @memberof MoveManager
|
|
314
|
-
* */
|
|
315
|
-
speed: number
|
|
421
|
+
export const Move = new MoveList();
|
|
316
422
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Move Manager mixin
|
|
425
|
+
*
|
|
426
|
+
* Adds comprehensive movement management capabilities to a player class.
|
|
427
|
+
* Provides access to all available movement strategies and utility methods
|
|
428
|
+
* for common movement patterns.
|
|
429
|
+
*
|
|
430
|
+
* ## Features
|
|
431
|
+
* - **Strategy Management**: Add, remove, and query movement strategies
|
|
432
|
+
* - **Predefined Movements**: Quick access to common movement patterns
|
|
433
|
+
* - **Composite Movements**: Combine multiple strategies
|
|
434
|
+
* - **Physics Integration**: Seamless integration with Matter.js physics
|
|
435
|
+
*
|
|
436
|
+
* ## Available Movement Strategies
|
|
437
|
+
* - `LinearMove`: Constant velocity movement
|
|
438
|
+
* - `Dash`: Quick burst movement
|
|
439
|
+
* - `Knockback`: Push effect with decay
|
|
440
|
+
* - `PathFollow`: Follow waypoint sequences
|
|
441
|
+
* - `Oscillate`: Back-and-forth patterns
|
|
442
|
+
* - `SeekAvoid`: AI pathfinding with obstacle avoidance
|
|
443
|
+
* - `LinearRepulsion`: Smoother obstacle avoidance
|
|
444
|
+
* - `IceMovement`: Slippery surface physics
|
|
445
|
+
* - `ProjectileMovement`: Ballistic trajectories
|
|
446
|
+
* - `CompositeMovement`: Combine multiple strategies
|
|
447
|
+
*
|
|
448
|
+
* @param Base - The base class to extend
|
|
449
|
+
* @returns A new class with comprehensive movement management capabilities
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```ts
|
|
453
|
+
* // Basic usage
|
|
454
|
+
* class MyPlayer extends WithMoveManager(RpgCommonPlayer) {
|
|
455
|
+
* onInput(direction: { x: number, y: number }) {
|
|
456
|
+
* // Apply dash movement on input
|
|
457
|
+
* this.dash(direction, 8, 200);
|
|
458
|
+
* }
|
|
459
|
+
*
|
|
460
|
+
* onIceTerrain() {
|
|
461
|
+
* // Switch to ice physics
|
|
462
|
+
* this.clearMovements();
|
|
463
|
+
* this.applyIceMovement({ x: 1, y: 0 }, 4);
|
|
464
|
+
* }
|
|
465
|
+
*
|
|
466
|
+
* createPatrol() {
|
|
467
|
+
* // Create patrol path
|
|
468
|
+
* const waypoints = [
|
|
469
|
+
* { x: 100, y: 100 },
|
|
470
|
+
* { x: 300, y: 100 },
|
|
471
|
+
* { x: 300, y: 300 }
|
|
472
|
+
* ];
|
|
473
|
+
* this.followPath(waypoints, 2, true);
|
|
474
|
+
* }
|
|
475
|
+
* }
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
export function WithMoveManager<TBase extends Constructor<RpgCommonPlayer>>(
|
|
479
|
+
Base: TBase
|
|
480
|
+
): Constructor<IMoveManager> & TBase {
|
|
481
|
+
return class extends Base implements IMoveManager {
|
|
482
|
+
|
|
483
|
+
// Private properties for infinite route management
|
|
484
|
+
private _infiniteRoutes: Routes | null = null;
|
|
485
|
+
private _finishRoute: ((value: boolean) => void) | null = null;
|
|
486
|
+
private _isInfiniteRouteActive: boolean = false;
|
|
330
487
|
|
|
331
488
|
/**
|
|
332
489
|
* The player passes through the other players (or vice versa). But the player does not go through the events.
|
|
@@ -340,7 +497,13 @@ export class MoveManager {
|
|
|
340
497
|
* @default true
|
|
341
498
|
* @memberof MoveManager
|
|
342
499
|
* */
|
|
343
|
-
throughOtherPlayer: boolean
|
|
500
|
+
set throughOtherPlayer(value: boolean) {
|
|
501
|
+
this._throughOtherPlayer.set(value);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
get throughOtherPlayer(): boolean {
|
|
505
|
+
return this._throughOtherPlayer();
|
|
506
|
+
}
|
|
344
507
|
|
|
345
508
|
/**
|
|
346
509
|
* The player goes through the event or the other players (or vice versa)
|
|
@@ -354,7 +517,13 @@ export class MoveManager {
|
|
|
354
517
|
* @default false
|
|
355
518
|
* @memberof MoveManager
|
|
356
519
|
* */
|
|
357
|
-
through: boolean
|
|
520
|
+
set through(value: boolean) {
|
|
521
|
+
this._through.set(value);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
get through(): boolean {
|
|
525
|
+
return this._through();
|
|
526
|
+
}
|
|
358
527
|
|
|
359
528
|
/**
|
|
360
529
|
* The frequency allows to put a stop time between each movement in the array of the moveRoutes() method.
|
|
@@ -385,271 +554,692 @@ export class MoveManager {
|
|
|
385
554
|
* @default 0
|
|
386
555
|
* @memberof MoveManager
|
|
387
556
|
* */
|
|
388
|
-
frequency: number
|
|
557
|
+
set frequency(value: number) {
|
|
558
|
+
this._frequency.set(value);
|
|
559
|
+
}
|
|
389
560
|
|
|
561
|
+
get frequency(): number {
|
|
562
|
+
return this._frequency();
|
|
563
|
+
}
|
|
564
|
+
|
|
390
565
|
/**
|
|
391
|
-
*
|
|
566
|
+
* Add a custom movement strategy to this entity
|
|
392
567
|
*
|
|
393
|
-
*
|
|
568
|
+
* Allows adding any custom MovementStrategy implementation.
|
|
569
|
+
* Multiple strategies can be active simultaneously.
|
|
394
570
|
*
|
|
571
|
+
* @param strategy - The movement strategy to add
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
395
574
|
* ```ts
|
|
396
|
-
*
|
|
575
|
+
* // Add custom movement
|
|
576
|
+
* const customMove = new LinearMove(5, 0, 1000);
|
|
577
|
+
* player.addMovement(customMove);
|
|
397
578
|
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
* }
|
|
579
|
+
* // Add multiple movements
|
|
580
|
+
* player.addMovement(new Dash(8, { x: 1, y: 0 }, 200));
|
|
581
|
+
* player.addMovement(new Oscillate({ x: 0, y: 1 }, 10, 1000));
|
|
582
|
+
* ```
|
|
583
|
+
*/
|
|
584
|
+
addMovement(strategy: MovementStrategy): void {
|
|
585
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
586
|
+
if (!map) return;
|
|
587
|
+
|
|
588
|
+
map.moveManager.add((this as unknown as PlayerWithMixins).id, strategy);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Remove a specific movement strategy from this entity
|
|
593
|
+
*
|
|
594
|
+
* @param strategy - The strategy instance to remove
|
|
595
|
+
* @returns True if the strategy was found and removed
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```ts
|
|
599
|
+
* const dashMove = new Dash(8, { x: 1, y: 0 }, 200);
|
|
600
|
+
* player.addMovement(dashMove);
|
|
401
601
|
*
|
|
402
|
-
*
|
|
602
|
+
* // Later, remove the specific movement
|
|
603
|
+
* const removed = player.removeMovement(dashMove);
|
|
604
|
+
* console.log('Movement removed:', removed);
|
|
403
605
|
* ```
|
|
606
|
+
*/
|
|
607
|
+
removeMovement(strategy: MovementStrategy): boolean {
|
|
608
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
609
|
+
if (!map) return false;
|
|
610
|
+
|
|
611
|
+
return map.moveManager.remove((this as unknown as PlayerWithMixins).id, strategy);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Remove all active movement strategies from this entity
|
|
404
616
|
*
|
|
405
|
-
*
|
|
617
|
+
* Stops all current movements immediately.
|
|
406
618
|
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```ts
|
|
621
|
+
* // Stop all movements when player dies
|
|
622
|
+
* player.clearMovements();
|
|
409
623
|
*
|
|
410
|
-
* //
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
|
|
624
|
+
* // Clear movements before applying new ones
|
|
625
|
+
* player.clearMovements();
|
|
626
|
+
* player.dash({ x: 1, y: 0 });
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
629
|
+
clearMovements(): void {
|
|
630
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
631
|
+
if (!map) return;
|
|
632
|
+
|
|
633
|
+
map.moveManager.clear((this as unknown as PlayerWithMixins).id);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Check if this entity has any active movement strategies
|
|
638
|
+
*
|
|
639
|
+
* @returns True if entity has active movements
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```ts
|
|
643
|
+
* // Don't accept input while movements are active
|
|
644
|
+
* if (!player.hasActiveMovements()) {
|
|
645
|
+
* player.dash(inputDirection);
|
|
415
646
|
* }
|
|
416
647
|
*
|
|
417
|
-
*
|
|
648
|
+
* // Check before adding new movement
|
|
649
|
+
* if (player.hasActiveMovements()) {
|
|
650
|
+
* player.clearMovements();
|
|
651
|
+
* }
|
|
418
652
|
* ```
|
|
653
|
+
*/
|
|
654
|
+
hasActiveMovements(): boolean {
|
|
655
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
656
|
+
if (!map) return false;
|
|
657
|
+
|
|
658
|
+
return map.moveManager.hasActiveStrategies((this as unknown as PlayerWithMixins).id);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Get all active movement strategies for this entity
|
|
419
663
|
*
|
|
420
|
-
*
|
|
664
|
+
* @returns Array of active movement strategies
|
|
421
665
|
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```ts
|
|
668
|
+
* // Check what movements are currently active
|
|
669
|
+
* const movements = player.getActiveMovements();
|
|
670
|
+
* console.log(`Player has ${movements.length} active movements`);
|
|
424
671
|
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
672
|
+
* // Find specific movement type
|
|
673
|
+
* const hasDash = movements.some(m => m instanceof Dash);
|
|
674
|
+
* ```
|
|
675
|
+
*/
|
|
676
|
+
getActiveMovements(): MovementStrategy[] {
|
|
677
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
678
|
+
if (!map) return [];
|
|
679
|
+
|
|
680
|
+
return map.moveManager.getStrategies((this as unknown as PlayerWithMixins).id);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Move toward a target player or position using AI pathfinding
|
|
685
|
+
*
|
|
686
|
+
* Uses SeekAvoid strategy for intelligent pathfinding with obstacle avoidance.
|
|
687
|
+
* The entity will seek toward the target while avoiding obstacles.
|
|
431
688
|
*
|
|
689
|
+
* @param target - Target player or position to move toward
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
432
692
|
* ```ts
|
|
433
|
-
*
|
|
693
|
+
* // Move toward another player
|
|
694
|
+
* const targetPlayer = game.getPlayer('player2');
|
|
695
|
+
* player.moveTo(targetPlayer);
|
|
696
|
+
*
|
|
697
|
+
* // Move toward a specific position
|
|
698
|
+
* player.moveTo({ x: 300, y: 200 });
|
|
434
699
|
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
700
|
+
* // Stop the movement later
|
|
701
|
+
* player.stopMoveTo();
|
|
437
702
|
* ```
|
|
438
703
|
*/
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (count >= this['nbPixelInTile']) {
|
|
466
|
-
if (frequence < this.frequency) {
|
|
467
|
-
frequence++
|
|
468
|
-
return of(null)
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
frequence = 0
|
|
473
|
-
count++
|
|
704
|
+
moveTo(target: RpgCommonPlayer | { x: number, y: number }): void {
|
|
705
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
706
|
+
if (!map) return;
|
|
707
|
+
|
|
708
|
+
let targetBody: Matter.Body | null = null;
|
|
709
|
+
|
|
710
|
+
if ('id' in target) {
|
|
711
|
+
// Target is a player
|
|
712
|
+
targetBody = map.physic.getBody(target.id);
|
|
713
|
+
} else {
|
|
714
|
+
// Target is a position - create a temporary target function
|
|
715
|
+
const getTargetPos = () => Matter.Vector.create(target.x, target.y);
|
|
716
|
+
map.moveManager.add(
|
|
717
|
+
(this as unknown as PlayerWithMixins).id,
|
|
718
|
+
new SeekAvoid(map.physic, getTargetPos, 3, 50, 5)
|
|
719
|
+
);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (targetBody) {
|
|
724
|
+
map.moveManager.add(
|
|
725
|
+
(this as unknown as PlayerWithMixins).id,
|
|
726
|
+
new SeekAvoid(map.physic, targetBody, 3, 50, 5)
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
474
730
|
|
|
475
|
-
|
|
731
|
+
/**
|
|
732
|
+
* Stop the current moveTo behavior
|
|
733
|
+
*
|
|
734
|
+
* Removes any active SeekAvoid strategies.
|
|
735
|
+
*
|
|
736
|
+
* @example
|
|
737
|
+
* ```ts
|
|
738
|
+
* // Start following a target
|
|
739
|
+
* player.moveTo(targetPlayer);
|
|
740
|
+
*
|
|
741
|
+
* // Stop following when target is reached
|
|
742
|
+
* if (distanceToTarget < 10) {
|
|
743
|
+
* player.stopMoveTo();
|
|
744
|
+
* }
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
stopMoveTo(): void {
|
|
748
|
+
const map = (this as unknown as PlayerWithMixins).getCurrentMap();
|
|
749
|
+
if (!map) return;
|
|
750
|
+
|
|
751
|
+
const strategies = this.getActiveMovements();
|
|
752
|
+
strategies.forEach(strategy => {
|
|
753
|
+
if (strategy instanceof SeekAvoid || strategy instanceof LinearRepulsion) {
|
|
754
|
+
this.removeMovement(strategy);
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
}
|
|
476
758
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
759
|
+
/**
|
|
760
|
+
* Perform a dash movement in the specified direction
|
|
761
|
+
*
|
|
762
|
+
* Applies high-speed movement for a short duration.
|
|
763
|
+
*
|
|
764
|
+
* @param direction - Normalized direction vector
|
|
765
|
+
* @param speed - Movement speed (default: 8)
|
|
766
|
+
* @param duration - Duration in milliseconds (default: 200)
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* // Dash right
|
|
771
|
+
* player.dash({ x: 1, y: 0 });
|
|
772
|
+
*
|
|
773
|
+
* // Dash diagonally with custom speed and duration
|
|
774
|
+
* player.dash({ x: 0.7, y: 0.7 }, 12, 300);
|
|
775
|
+
*
|
|
776
|
+
* // Dash in input direction
|
|
777
|
+
* player.dash(inputDirection, 10, 150);
|
|
778
|
+
* ```
|
|
779
|
+
*/
|
|
780
|
+
dash(direction: { x: number, y: number }, speed: number = 8, duration: number = 200): void {
|
|
781
|
+
this.addMovement(new Dash(speed, direction, duration));
|
|
782
|
+
}
|
|
481
783
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
784
|
+
/**
|
|
785
|
+
* Apply knockback effect in the specified direction
|
|
786
|
+
*
|
|
787
|
+
* Creates a push effect that gradually decreases over time.
|
|
788
|
+
*
|
|
789
|
+
* @param direction - Normalized direction vector
|
|
790
|
+
* @param force - Initial knockback force (default: 5)
|
|
791
|
+
* @param duration - Duration in milliseconds (default: 300)
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```ts
|
|
795
|
+
* // Knockback from explosion
|
|
796
|
+
* const explosionDir = { x: -1, y: 0 };
|
|
797
|
+
* player.knockback(explosionDir, 8, 400);
|
|
798
|
+
*
|
|
799
|
+
* // Light knockback from attack
|
|
800
|
+
* player.knockback(attackDirection, 3, 200);
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
knockback(direction: { x: number, y: number }, force: number = 5, duration: number = 300): void {
|
|
804
|
+
this.addMovement(new Knockback(direction, force, duration));
|
|
805
|
+
}
|
|
504
806
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
807
|
+
/**
|
|
808
|
+
* Follow a sequence of waypoints
|
|
809
|
+
*
|
|
810
|
+
* Entity will move through each waypoint in order.
|
|
811
|
+
*
|
|
812
|
+
* @param waypoints - Array of x,y positions to follow
|
|
813
|
+
* @param speed - Movement speed (default: 2)
|
|
814
|
+
* @param loop - Whether to loop back to start (default: false)
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```ts
|
|
818
|
+
* // Create a patrol route
|
|
819
|
+
* const patrolPoints = [
|
|
820
|
+
* { x: 100, y: 100 },
|
|
821
|
+
* { x: 300, y: 100 },
|
|
822
|
+
* { x: 300, y: 300 },
|
|
823
|
+
* { x: 100, y: 300 }
|
|
824
|
+
* ];
|
|
825
|
+
* player.followPath(patrolPoints, 3, true);
|
|
826
|
+
*
|
|
827
|
+
* // One-time path to destination
|
|
828
|
+
* player.followPath([{ x: 500, y: 200 }], 4);
|
|
829
|
+
* ```
|
|
830
|
+
*/
|
|
831
|
+
followPath(waypoints: Array<{ x: number, y: number }>, speed: number = 2, loop: boolean = false): void {
|
|
832
|
+
this.addMovement(new PathFollow(waypoints, speed, loop));
|
|
523
833
|
}
|
|
524
834
|
|
|
525
835
|
/**
|
|
526
|
-
*
|
|
836
|
+
* Apply oscillating movement pattern
|
|
527
837
|
*
|
|
528
|
-
*
|
|
838
|
+
* Entity moves back and forth along the specified axis.
|
|
529
839
|
*
|
|
530
|
-
* @
|
|
531
|
-
* @
|
|
532
|
-
* @param
|
|
533
|
-
* @returns {void}
|
|
534
|
-
* @memberof MoveManager
|
|
535
|
-
* @example
|
|
840
|
+
* @param direction - Primary oscillation axis (normalized)
|
|
841
|
+
* @param amplitude - Maximum distance from center (default: 50)
|
|
842
|
+
* @param period - Time for complete cycle in ms (default: 2000)
|
|
536
843
|
*
|
|
844
|
+
* @example
|
|
537
845
|
* ```ts
|
|
538
|
-
*
|
|
846
|
+
* // Horizontal oscillation
|
|
847
|
+
* player.oscillate({ x: 1, y: 0 }, 100, 3000);
|
|
848
|
+
*
|
|
849
|
+
* // Vertical oscillation
|
|
850
|
+
* player.oscillate({ x: 0, y: 1 }, 30, 1500);
|
|
539
851
|
*
|
|
540
|
-
*
|
|
852
|
+
* // Diagonal oscillation
|
|
853
|
+
* player.oscillate({ x: 0.7, y: 0.7 }, 75, 2500);
|
|
541
854
|
* ```
|
|
542
855
|
*/
|
|
543
|
-
|
|
544
|
-
|
|
856
|
+
oscillate(direction: { x: number, y: number }, amplitude: number = 50, period: number = 2000): void {
|
|
857
|
+
this.addMovement(new Oscillate(direction, amplitude, period));
|
|
858
|
+
}
|
|
545
859
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
860
|
+
/**
|
|
861
|
+
* Apply ice movement physics
|
|
862
|
+
*
|
|
863
|
+
* Creates slippery movement with gradual acceleration and inertia.
|
|
864
|
+
* Perfect for ice terrains or slippery surfaces.
|
|
865
|
+
*
|
|
866
|
+
* @param direction - Target movement direction
|
|
867
|
+
* @param maxSpeed - Maximum speed when fully accelerated (default: 4)
|
|
868
|
+
*
|
|
869
|
+
* @example
|
|
870
|
+
* ```ts
|
|
871
|
+
* // Apply ice physics when on ice terrain
|
|
872
|
+
* if (onIceTerrain) {
|
|
873
|
+
* player.applyIceMovement(inputDirection, 5);
|
|
874
|
+
* }
|
|
875
|
+
*
|
|
876
|
+
* // Update direction when input changes
|
|
877
|
+
* iceMovement.setTargetDirection(newDirection);
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
applyIceMovement(direction: { x: number, y: number }, maxSpeed: number = 4): void {
|
|
881
|
+
this.addMovement(new IceMovement(direction, maxSpeed));
|
|
882
|
+
}
|
|
550
883
|
|
|
551
|
-
|
|
884
|
+
/**
|
|
885
|
+
* Shoot a projectile in the specified direction
|
|
886
|
+
*
|
|
887
|
+
* Creates projectile movement with various trajectory types.
|
|
888
|
+
*
|
|
889
|
+
* @param type - Type of projectile trajectory
|
|
890
|
+
* @param direction - Normalized direction vector
|
|
891
|
+
* @param speed - Projectile speed (default: 200)
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* // Shoot arrow
|
|
896
|
+
* player.shootProjectile(ProjectileType.Straight, { x: 1, y: 0 }, 300);
|
|
897
|
+
*
|
|
898
|
+
* // Throw grenade with arc
|
|
899
|
+
* player.shootProjectile(ProjectileType.Arc, { x: 0.7, y: 0.7 }, 150);
|
|
900
|
+
*
|
|
901
|
+
* // Bouncing projectile
|
|
902
|
+
* player.shootProjectile(ProjectileType.Bounce, { x: 1, y: 0 }, 100);
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
shootProjectile(type: ProjectileType, direction: { x: number, y: number }, speed: number = 200): void {
|
|
906
|
+
const config = {
|
|
907
|
+
speed,
|
|
908
|
+
direction,
|
|
909
|
+
maxRange: type === ProjectileType.Straight ? 500 : undefined,
|
|
910
|
+
maxHeight: type === ProjectileType.Arc ? 100 : undefined,
|
|
911
|
+
gravity: type !== ProjectileType.Straight ? 400 : undefined,
|
|
912
|
+
maxBounces: type === ProjectileType.Bounce ? 3 : undefined,
|
|
913
|
+
bounciness: type === ProjectileType.Bounce ? 0.6 : undefined
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
this.addMovement(new ProjectileMovement(type, config));
|
|
552
917
|
}
|
|
553
918
|
|
|
554
919
|
/**
|
|
555
|
-
*
|
|
920
|
+
* Give an itinerary to follow using movement strategies
|
|
556
921
|
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
922
|
+
* Executes a sequence of movements and actions in order. Each route can be:
|
|
923
|
+
* - A Direction enum value for basic movement
|
|
924
|
+
* - A string starting with "turn-" for direction changes
|
|
925
|
+
* - A function that returns directions or actions
|
|
926
|
+
* - A Promise for async operations
|
|
927
|
+
*
|
|
928
|
+
* The method processes routes sequentially, respecting the entity's frequency
|
|
929
|
+
* setting for timing between movements.
|
|
563
930
|
*
|
|
931
|
+
* @param routes - Array of movement instructions to execute
|
|
932
|
+
* @returns Promise that resolves when all routes are completed
|
|
933
|
+
*
|
|
934
|
+
* @example
|
|
564
935
|
* ```ts
|
|
565
|
-
*
|
|
936
|
+
* // Basic directional movements
|
|
937
|
+
* await player.moveRoutes([
|
|
938
|
+
* Direction.Right,
|
|
939
|
+
* Direction.Up,
|
|
940
|
+
* Direction.Left
|
|
941
|
+
* ]);
|
|
942
|
+
*
|
|
943
|
+
* // Mix of movements and turns
|
|
944
|
+
* await player.moveRoutes([
|
|
945
|
+
* Direction.Right,
|
|
946
|
+
* 'turn-' + Direction.Up,
|
|
947
|
+
* Direction.Up
|
|
948
|
+
* ]);
|
|
949
|
+
*
|
|
950
|
+
* // Using functions for dynamic behavior
|
|
951
|
+
* const customMove = (player, map) => [Direction.Right, Direction.Down];
|
|
952
|
+
* await player.moveRoutes([customMove]);
|
|
566
953
|
*
|
|
567
|
-
*
|
|
568
|
-
* player.
|
|
954
|
+
* // With async operations
|
|
955
|
+
* await player.moveRoutes([
|
|
956
|
+
* Direction.Right,
|
|
957
|
+
* new Promise(resolve => setTimeout(resolve, 1000)), // Wait 1 second
|
|
958
|
+
* Direction.Left
|
|
959
|
+
* ]);
|
|
569
960
|
* ```
|
|
570
961
|
*/
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
962
|
+
moveRoutes(routes: Routes): Promise<boolean> {
|
|
963
|
+
let count = 0;
|
|
964
|
+
let frequence = 0;
|
|
965
|
+
const player = this as unknown as PlayerWithMixins;
|
|
966
|
+
|
|
967
|
+
// Break any existing route movement
|
|
968
|
+
this.clearMovements();
|
|
969
|
+
|
|
970
|
+
return new Promise(async (resolve) => {
|
|
971
|
+
// Store the resolve function for potential breaking
|
|
972
|
+
this._finishRoute = resolve;
|
|
973
|
+
|
|
974
|
+
// Process function routes first
|
|
975
|
+
const processedRoutes = routes.map((route: any) => {
|
|
976
|
+
if (typeof route === 'function') {
|
|
977
|
+
const map = player.getCurrentMap();
|
|
978
|
+
if (!map) {
|
|
979
|
+
return undefined;
|
|
980
|
+
}
|
|
981
|
+
return route.apply(route, [player, map]);
|
|
982
|
+
}
|
|
983
|
+
return route;
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// Flatten nested arrays
|
|
987
|
+
const flatRoutes = this.flattenRoutes(processedRoutes);
|
|
988
|
+
let routeIndex = 0;
|
|
989
|
+
|
|
990
|
+
const executeNextRoute = async (): Promise<void> => {
|
|
991
|
+
// Check if player still exists and is on a map
|
|
992
|
+
if (!player || !player.getCurrentMap()) {
|
|
993
|
+
this._finishRoute = null;
|
|
994
|
+
resolve(false);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Handle frequency timing
|
|
999
|
+
if (count >= (player.nbPixelInTile || 32)) {
|
|
1000
|
+
if (frequence < (player.frequency || 0)) {
|
|
1001
|
+
frequence++;
|
|
1002
|
+
setTimeout(executeNextRoute, 16); // ~60fps timing
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
frequence = 0;
|
|
1008
|
+
count++;
|
|
1009
|
+
|
|
1010
|
+
// Check if we've completed all routes
|
|
1011
|
+
if (routeIndex >= flatRoutes.length) {
|
|
1012
|
+
this._finishRoute = null;
|
|
1013
|
+
resolve(true);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const currentRoute = flatRoutes[routeIndex];
|
|
1018
|
+
routeIndex++;
|
|
1019
|
+
|
|
1020
|
+
if (currentRoute === undefined) {
|
|
1021
|
+
executeNextRoute();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
try {
|
|
1026
|
+
// Handle different route types
|
|
1027
|
+
if (typeof currentRoute === 'object' && 'then' in currentRoute) {
|
|
1028
|
+
// Handle Promise
|
|
1029
|
+
await currentRoute;
|
|
1030
|
+
executeNextRoute();
|
|
1031
|
+
} else if (typeof currentRoute === 'string' && currentRoute.startsWith('turn-')) {
|
|
1032
|
+
// Handle turn commands
|
|
1033
|
+
const directionStr = currentRoute.replace('turn-', '');
|
|
1034
|
+
let direction: Direction = Direction.Down;
|
|
1035
|
+
|
|
1036
|
+
// Convert string direction to Direction enum
|
|
1037
|
+
switch (directionStr) {
|
|
1038
|
+
case 'up':
|
|
1039
|
+
case Direction.Up:
|
|
1040
|
+
direction = Direction.Up;
|
|
1041
|
+
break;
|
|
1042
|
+
case 'down':
|
|
1043
|
+
case Direction.Down:
|
|
1044
|
+
direction = Direction.Down;
|
|
1045
|
+
break;
|
|
1046
|
+
case 'left':
|
|
1047
|
+
case Direction.Left:
|
|
1048
|
+
direction = Direction.Left;
|
|
1049
|
+
break;
|
|
1050
|
+
case 'right':
|
|
1051
|
+
case Direction.Right:
|
|
1052
|
+
direction = Direction.Right;
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
if (player.changeDirection) {
|
|
1057
|
+
player.changeDirection(direction);
|
|
1058
|
+
}
|
|
1059
|
+
executeNextRoute();
|
|
1060
|
+
} else if (typeof currentRoute === 'number') {
|
|
1061
|
+
// Handle Direction enum values
|
|
1062
|
+
if (player.moveByDirection) {
|
|
1063
|
+
await player.moveByDirection(currentRoute as unknown as Direction, 1);
|
|
1064
|
+
} else {
|
|
1065
|
+
// Fallback to movement strategy - use direction as velocity components
|
|
1066
|
+
let vx = 0, vy = 0;
|
|
1067
|
+
const direction = currentRoute as unknown as Direction;
|
|
1068
|
+
switch (direction) {
|
|
1069
|
+
case Direction.Right: vx = 1; break;
|
|
1070
|
+
case Direction.Left: vx = -1; break;
|
|
1071
|
+
case Direction.Down: vy = 1; break;
|
|
1072
|
+
case Direction.Up: vy = -1; break;
|
|
1073
|
+
}
|
|
1074
|
+
this.addMovement(new LinearMove(vx * (player.speed?.() || 3), vy * (player.speed?.() || 3), 100));
|
|
1075
|
+
setTimeout(executeNextRoute, 100);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
executeNextRoute();
|
|
1079
|
+
} else {
|
|
1080
|
+
// Unknown route type, skip
|
|
1081
|
+
executeNextRoute();
|
|
1082
|
+
}
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
console.warn('Error executing route:', error);
|
|
1085
|
+
executeNextRoute();
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
executeNextRoute();
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Utility method to flatten nested route arrays
|
|
1095
|
+
*
|
|
1096
|
+
* @private
|
|
1097
|
+
* @param routes - Routes array that may contain nested arrays
|
|
1098
|
+
* @returns Flattened array of routes
|
|
1099
|
+
*/
|
|
1100
|
+
private flattenRoutes(routes: any[]): any[] {
|
|
1101
|
+
const result: any[] = [];
|
|
1102
|
+
|
|
1103
|
+
for (const route of routes) {
|
|
1104
|
+
if (Array.isArray(route)) {
|
|
1105
|
+
result.push(...this.flattenRoutes(route));
|
|
1106
|
+
} else {
|
|
1107
|
+
result.push(route);
|
|
575
1108
|
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return result;
|
|
576
1112
|
}
|
|
577
1113
|
|
|
578
1114
|
/**
|
|
579
|
-
*
|
|
580
|
-
* If the road was stopped with `breakRoutes()`, you can restart it with this method
|
|
1115
|
+
* Give a path that repeats itself in a loop to a character
|
|
581
1116
|
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
* @memberof MoveManager
|
|
586
|
-
* @example
|
|
1117
|
+
* Creates an infinite movement pattern that continues until manually stopped.
|
|
1118
|
+
* The routes will repeat in a continuous loop, making it perfect for patrol
|
|
1119
|
+
* patterns, ambient movements, or any repetitive behavior.
|
|
587
1120
|
*
|
|
1121
|
+
* You can stop the movement at any time with `breakRoutes()` and replay it
|
|
1122
|
+
* with `replayRoutes()`.
|
|
1123
|
+
*
|
|
1124
|
+
* @param routes - Array of movement instructions to repeat infinitely
|
|
1125
|
+
*
|
|
1126
|
+
* @example
|
|
588
1127
|
* ```ts
|
|
589
|
-
*
|
|
1128
|
+
* // Create an infinite random movement pattern
|
|
1129
|
+
* player.infiniteMoveRoute([Move.random()]);
|
|
590
1130
|
*
|
|
591
|
-
*
|
|
592
|
-
* player.
|
|
593
|
-
*
|
|
1131
|
+
* // Create a patrol route
|
|
1132
|
+
* player.infiniteMoveRoute([
|
|
1133
|
+
* Direction.Right,
|
|
1134
|
+
* Direction.Right,
|
|
1135
|
+
* Direction.Down,
|
|
1136
|
+
* Direction.Left,
|
|
1137
|
+
* Direction.Left,
|
|
1138
|
+
* Direction.Up
|
|
1139
|
+
* ]);
|
|
1140
|
+
*
|
|
1141
|
+
* // Mix movements and rotations
|
|
1142
|
+
* player.infiniteMoveRoute([
|
|
1143
|
+
* Move.turnRight(),
|
|
1144
|
+
* Direction.Right,
|
|
1145
|
+
* Move.wait(1),
|
|
1146
|
+
* Move.turnLeft(),
|
|
1147
|
+
* Direction.Left
|
|
1148
|
+
* ]);
|
|
594
1149
|
* ```
|
|
595
1150
|
*/
|
|
596
|
-
|
|
597
|
-
|
|
1151
|
+
infiniteMoveRoute(routes: Routes): void {
|
|
1152
|
+
this._infiniteRoutes = routes;
|
|
1153
|
+
this._isInfiniteRouteActive = true;
|
|
1154
|
+
|
|
1155
|
+
const executeInfiniteRoute = (isBreaking: boolean = false) => {
|
|
1156
|
+
if (isBreaking || !this._isInfiniteRouteActive) return;
|
|
1157
|
+
|
|
1158
|
+
this.moveRoutes(routes).then((completed) => {
|
|
1159
|
+
// Only continue if the route completed successfully and we're still active
|
|
1160
|
+
if (completed && this._isInfiniteRouteActive) {
|
|
1161
|
+
executeInfiniteRoute();
|
|
1162
|
+
}
|
|
1163
|
+
}).catch((error) => {
|
|
1164
|
+
console.warn('Error in infinite route execution:', error);
|
|
1165
|
+
// Try to continue even if there was an error
|
|
1166
|
+
if (this._isInfiniteRouteActive) {
|
|
1167
|
+
setTimeout(() => executeInfiniteRoute(), 100);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
executeInfiniteRoute();
|
|
598
1173
|
}
|
|
599
1174
|
|
|
600
1175
|
/**
|
|
601
|
-
*
|
|
602
|
-
* The event will avoid obstacles, but you can tell if it is stuck or has completed its path
|
|
603
|
-
*
|
|
604
|
-
* @title Move To
|
|
605
|
-
* @method player.moveTo()
|
|
606
|
-
* @param {RpgPlayer|RpgEvent|RpgShape|Position} target the target
|
|
607
|
-
* @param {object} [options] - animate. Set a boolean to use default parameters
|
|
608
|
-
* @param {boolean} [options.infinite=false] - moves infinitely towards the target, you have to stop its movement manually with the method `stopMoveTo()`
|
|
609
|
-
* @param {() => void} [options.onComplete] - Callback when the event arrives at the destination
|
|
610
|
-
* @param {(duration:number) => void} [options.onStuck] - callback when the event is blocked against a wall. Duration gives you the duration (in frames) of the blocking time
|
|
611
|
-
* @returns {Observable<void>}
|
|
612
|
-
* @since 3.2.0
|
|
613
|
-
* @memberof MoveManager
|
|
614
|
-
* @example
|
|
1176
|
+
* Stop an infinite movement
|
|
615
1177
|
*
|
|
1178
|
+
* Works only for infinite movements created with `infiniteMoveRoute()`.
|
|
1179
|
+
* This method stops the current route execution and prevents the next
|
|
1180
|
+
* iteration from starting.
|
|
1181
|
+
*
|
|
1182
|
+
* @param force - Forces the stop of the infinite movement immediately
|
|
1183
|
+
*
|
|
1184
|
+
* @example
|
|
616
1185
|
* ```ts
|
|
617
|
-
*
|
|
1186
|
+
* // Start infinite movement
|
|
1187
|
+
* player.infiniteMoveRoute([Move.random()]);
|
|
618
1188
|
*
|
|
619
|
-
* player
|
|
1189
|
+
* // Stop it when player enters combat
|
|
1190
|
+
* if (inCombat) {
|
|
1191
|
+
* player.breakRoutes(true);
|
|
1192
|
+
* }
|
|
1193
|
+
*
|
|
1194
|
+
* // Gentle stop (completes current route first)
|
|
1195
|
+
* player.breakRoutes();
|
|
620
1196
|
* ```
|
|
621
1197
|
*/
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1198
|
+
breakRoutes(force: boolean = false): void {
|
|
1199
|
+
this._isInfiniteRouteActive = false;
|
|
1200
|
+
|
|
1201
|
+
if (force) {
|
|
1202
|
+
// Force stop by clearing all movements immediately
|
|
1203
|
+
this.clearMovements();
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// If there's an active route promise, resolve it
|
|
1207
|
+
if (this._finishRoute) {
|
|
1208
|
+
this._finishRoute(force);
|
|
1209
|
+
this._finishRoute = null;
|
|
1210
|
+
}
|
|
628
1211
|
}
|
|
629
1212
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1213
|
+
/**
|
|
1214
|
+
* Replay an infinite movement
|
|
1215
|
+
*
|
|
1216
|
+
* Works only for infinite movements that were previously created with
|
|
1217
|
+
* `infiniteMoveRoute()`. If the route was stopped with `breakRoutes()`,
|
|
1218
|
+
* you can restart it with this method using the same route configuration.
|
|
1219
|
+
*
|
|
1220
|
+
* @example
|
|
1221
|
+
* ```ts
|
|
1222
|
+
* // Create infinite movement
|
|
1223
|
+
* player.infiniteMoveRoute([Move.random()]);
|
|
1224
|
+
*
|
|
1225
|
+
* // Stop it temporarily
|
|
1226
|
+
* player.breakRoutes(true);
|
|
1227
|
+
*
|
|
1228
|
+
* // Resume the same movement pattern
|
|
1229
|
+
* player.replayRoutes();
|
|
1230
|
+
*
|
|
1231
|
+
* // Stop and start with different conditions
|
|
1232
|
+
* if (playerNearby) {
|
|
1233
|
+
* player.breakRoutes();
|
|
1234
|
+
* } else {
|
|
1235
|
+
* player.replayRoutes();
|
|
1236
|
+
* }
|
|
1237
|
+
* ```
|
|
1238
|
+
*/
|
|
1239
|
+
replayRoutes(): void {
|
|
1240
|
+
if (this._infiniteRoutes && !this._isInfiniteRouteActive) {
|
|
1241
|
+
this.infiniteMoveRoute(this._infiniteRoutes);
|
|
1242
|
+
}
|
|
640
1243
|
}
|
|
1244
|
+
};
|
|
641
1245
|
}
|
|
642
|
-
|
|
643
|
-
export interface MoveManager {
|
|
644
|
-
moveByDirection: (direction: Direction, deltaTimeInt: number) => Promise<boolean>
|
|
645
|
-
changeDirection: (direction: Direction) => boolean
|
|
646
|
-
getCurrentMap: any
|
|
647
|
-
checkCollision: boolean
|
|
648
|
-
clientModeMove: ClientMode
|
|
649
|
-
behavior: Behavior
|
|
650
|
-
emit(name: SocketEvents, params: any)
|
|
651
|
-
id: string
|
|
652
|
-
server: RpgServerEngine
|
|
653
|
-
position: Vector2d
|
|
654
|
-
_destroy$: Subject<void>
|
|
655
|
-
}
|