@rpgjs/common 4.2.1 → 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/dist/Physic.d.ts +619 -0
- package/dist/Player.d.ts +198 -0
- package/{lib → dist}/Utils.d.ts +19 -2
- package/dist/database/Item.d.ts +10 -0
- package/dist/database/index.d.ts +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +16741 -0
- package/dist/index.js.map +1 -0
- package/dist/modules.d.ts +92 -0
- package/dist/movement/MovementManager.d.ts +84 -0
- package/dist/movement/MovementStrategy.d.ts +39 -0
- package/dist/movement/index.d.ts +12 -0
- package/dist/movement/strategies/CompositeMovement.d.ts +76 -0
- package/dist/movement/strategies/Dash.d.ts +52 -0
- package/dist/movement/strategies/IceMovement.d.ts +87 -0
- package/dist/movement/strategies/Knockback.d.ts +50 -0
- package/dist/movement/strategies/LinearMove.d.ts +43 -0
- package/dist/movement/strategies/LinearRepulsion.d.ts +55 -0
- package/dist/movement/strategies/Oscillate.d.ts +60 -0
- package/dist/movement/strategies/PathFollow.d.ts +78 -0
- package/dist/movement/strategies/ProjectileMovement.d.ts +138 -0
- package/dist/movement/strategies/SeekAvoid.d.ts +27 -0
- package/dist/rooms/Map.d.ts +109 -0
- package/dist/services/updateMap.d.ts +7 -0
- package/package.json +17 -17
- package/src/Physic.ts +1644 -0
- package/src/Player.ts +262 -26
- package/src/{gui/PrebuiltGui.ts → PrebuiltGui.ts} +1 -1
- package/src/Utils.ts +184 -123
- package/src/database/Item.ts +19 -0
- package/src/database/index.ts +1 -0
- package/src/index.ts +9 -25
- package/src/modules.ts +230 -0
- package/src/movement/MovementManager.ts +142 -0
- package/src/movement/MovementStrategy.ts +42 -0
- package/src/movement/index.ts +15 -0
- package/src/movement/strategies/CompositeMovement.ts +173 -0
- package/src/movement/strategies/Dash.ts +82 -0
- package/src/movement/strategies/IceMovement.ts +158 -0
- package/src/movement/strategies/Knockback.ts +81 -0
- package/src/movement/strategies/LinearMove.ts +58 -0
- package/src/movement/strategies/LinearRepulsion.ts +128 -0
- package/src/movement/strategies/Oscillate.ts +144 -0
- package/src/movement/strategies/PathFollow.ts +156 -0
- package/src/movement/strategies/ProjectileMovement.ts +322 -0
- package/src/movement/strategies/SeekAvoid.ts +123 -0
- package/src/rooms/Map.ts +272 -0
- package/src/services/updateMap.ts +9 -0
- package/tests/physic.spec.ts +454 -0
- package/tsconfig.json +8 -3
- package/vite.config.ts +21 -0
- package/CHANGELOG.md +0 -152
- package/LICENSE +0 -19
- package/browser/manifest.json +0 -7
- package/browser/rpg.common.js +0 -11357
- package/browser/rpg.common.umd.cjs +0 -11358
- package/lib/AbstractObject.d.ts +0 -322
- package/lib/AbstractObject.js +0 -872
- package/lib/AbstractObject.js.map +0 -1
- package/lib/Color.d.ts +0 -1
- package/lib/Color.js +0 -25
- package/lib/Color.js.map +0 -1
- package/lib/DefaultInput.d.ts +0 -2
- package/lib/DefaultInput.js +0 -26
- package/lib/DefaultInput.js.map +0 -1
- package/lib/Event.d.ts +0 -3
- package/lib/Event.js +0 -4
- package/lib/Event.js.map +0 -1
- package/lib/EventEmitter.d.ts +0 -10
- package/lib/EventEmitter.js +0 -61
- package/lib/EventEmitter.js.map +0 -1
- package/lib/Game.d.ts +0 -28
- package/lib/Game.js +0 -127
- package/lib/Game.js.map +0 -1
- package/lib/Hit.d.ts +0 -16
- package/lib/Hit.js +0 -65
- package/lib/Hit.js.map +0 -1
- package/lib/Inject.d.ts +0 -9
- package/lib/Inject.js +0 -17
- package/lib/Inject.js.map +0 -1
- package/lib/Logger.d.ts +0 -2
- package/lib/Logger.js +0 -7
- package/lib/Logger.js.map +0 -1
- package/lib/Map.d.ts +0 -174
- package/lib/Map.js +0 -263
- package/lib/Map.js.map +0 -1
- package/lib/Module.d.ts +0 -16
- package/lib/Module.js +0 -139
- package/lib/Module.js.map +0 -1
- package/lib/Player.d.ts +0 -26
- package/lib/Player.js +0 -19
- package/lib/Player.js.map +0 -1
- package/lib/Plugin.d.ts +0 -67
- package/lib/Plugin.js +0 -92
- package/lib/Plugin.js.map +0 -1
- package/lib/Scheduler.d.ts +0 -26
- package/lib/Scheduler.js +0 -90
- package/lib/Scheduler.js.map +0 -1
- package/lib/Shape.d.ts +0 -127
- package/lib/Shape.js +0 -261
- package/lib/Shape.js.map +0 -1
- package/lib/Utils.js +0 -181
- package/lib/Utils.js.map +0 -1
- package/lib/Vector2d.d.ts +0 -20
- package/lib/Vector2d.js +0 -63
- package/lib/Vector2d.js.map +0 -1
- package/lib/VirtualGrid.d.ts +0 -26
- package/lib/VirtualGrid.js +0 -68
- package/lib/VirtualGrid.js.map +0 -1
- package/lib/Worker.d.ts +0 -7
- package/lib/Worker.js +0 -13
- package/lib/Worker.js.map +0 -1
- package/lib/WorldMaps.d.ts +0 -105
- package/lib/WorldMaps.js +0 -184
- package/lib/WorldMaps.js.map +0 -1
- package/lib/gui/PrebuiltGui.js +0 -29
- package/lib/gui/PrebuiltGui.js.map +0 -1
- package/lib/index.d.ts +0 -25
- package/lib/index.js +0 -26
- package/lib/index.js.map +0 -1
- package/lib/transports/io.d.ts +0 -22
- package/lib/transports/io.js +0 -82
- package/lib/transports/io.js.map +0 -1
- package/lib/workers/move.d.ts +0 -1
- package/lib/workers/move.js +0 -57
- package/lib/workers/move.js.map +0 -1
- package/rpg.toml +0 -11
- package/src/AbstractObject.ts +0 -973
- package/src/Color.ts +0 -29
- package/src/DefaultInput.ts +0 -26
- package/src/Event.ts +0 -3
- package/src/EventEmitter.ts +0 -65
- package/src/Game.ts +0 -159
- package/src/Hit.ts +0 -70
- package/src/Inject.ts +0 -22
- package/src/Logger.ts +0 -7
- package/src/Map.ts +0 -335
- package/src/Module.ts +0 -144
- package/src/Plugin.ts +0 -100
- package/src/Scheduler.ts +0 -95
- package/src/Shape.ts +0 -302
- package/src/Vector2d.ts +0 -70
- package/src/VirtualGrid.ts +0 -78
- package/src/Worker.ts +0 -17
- package/src/WorldMaps.ts +0 -204
- package/src/transports/io.ts +0 -91
- package/src/workers/move.ts +0 -61
- /package/{lib/gui → dist}/PrebuiltGui.d.ts +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
import { MovementStrategy } from './MovementStrategy';
|
|
3
|
+
import { RpgCommonPhysic } from '../Physic';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manager for entity movement strategies
|
|
7
|
+
*
|
|
8
|
+
* Handles the execution and lifecycle of all movement strategies
|
|
9
|
+
* for entities in the game world.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // In game loop
|
|
14
|
+
* const movementManager = new MovementManager();
|
|
15
|
+
*
|
|
16
|
+
* // Add dash movement to player
|
|
17
|
+
* movementManager.add('player1', new Dash(200, { x: 1, y: 0 }, 300));
|
|
18
|
+
*
|
|
19
|
+
* // Update in game loop
|
|
20
|
+
* function gameLoop(dt) {
|
|
21
|
+
* movementManager.update(dt, physic);
|
|
22
|
+
* physic.update(dt);
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class MovementManager {
|
|
27
|
+
/** Map of entity IDs to their active movement strategies */
|
|
28
|
+
private strategies: Map<string, MovementStrategy[]> = new Map();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Add a movement strategy to an entity
|
|
32
|
+
*
|
|
33
|
+
* @param id - Entity ID
|
|
34
|
+
* @param strategy - Movement strategy to apply
|
|
35
|
+
*/
|
|
36
|
+
add(id: string, strategy: MovementStrategy): void {
|
|
37
|
+
if (!this.strategies.has(id)) {
|
|
38
|
+
this.strategies.set(id, []);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.strategies.get(id)!.push(strategy);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Remove a specific movement strategy from an entity
|
|
46
|
+
*
|
|
47
|
+
* @param id - Entity ID
|
|
48
|
+
* @param strategy - The strategy instance to remove
|
|
49
|
+
* @returns True if the strategy was found and removed
|
|
50
|
+
*/
|
|
51
|
+
remove(id: string, strategy: MovementStrategy): boolean {
|
|
52
|
+
const entityStrategies = this.strategies.get(id);
|
|
53
|
+
if (!entityStrategies) return false;
|
|
54
|
+
|
|
55
|
+
const index = entityStrategies.indexOf(strategy);
|
|
56
|
+
if (index === -1) return false;
|
|
57
|
+
|
|
58
|
+
entityStrategies.splice(index, 1);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Remove all movement strategies from an entity
|
|
64
|
+
*
|
|
65
|
+
* @param id - Entity ID
|
|
66
|
+
*/
|
|
67
|
+
clear(id: string): void {
|
|
68
|
+
this.strategies.delete(id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if an entity has any active movement strategies
|
|
73
|
+
*
|
|
74
|
+
* @param id - Entity ID
|
|
75
|
+
* @returns True if entity has active strategies
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* // Check if player has active movements
|
|
80
|
+
* if (movementManager.hasActiveStrategies('player1')) {
|
|
81
|
+
* // Don't accept input while movements are active
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
hasActiveStrategies(id: string): boolean {
|
|
86
|
+
const entityStrategies = this.strategies.get(id);
|
|
87
|
+
return Boolean(entityStrategies && entityStrategies.length > 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get all active movement strategies for an entity
|
|
92
|
+
*
|
|
93
|
+
* @param id - Entity ID
|
|
94
|
+
* @returns Array of active strategies or empty array if none
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* // Get all player movements
|
|
99
|
+
* const movements = movementManager.getStrategies('player1');
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
getStrategies(id: string): MovementStrategy[] {
|
|
103
|
+
return this.strategies.get(id) || [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Update all movement strategies
|
|
108
|
+
* Must be called BEFORE physics.update()
|
|
109
|
+
*
|
|
110
|
+
* @param dt - Time delta in milliseconds
|
|
111
|
+
* @param physic - Physics system to get bodies from
|
|
112
|
+
*/
|
|
113
|
+
update(dt: number, physic: RpgCommonPhysic): void {
|
|
114
|
+
for (const [id, strategies] of this.strategies.entries()) {
|
|
115
|
+
// Skip if no strategies
|
|
116
|
+
if (strategies.length === 0) {
|
|
117
|
+
this.strategies.delete(id);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Get the body for this entity
|
|
122
|
+
const body = physic.getBody(id);
|
|
123
|
+
if (!body) continue;
|
|
124
|
+
|
|
125
|
+
// Update each strategy and remove finished ones
|
|
126
|
+
for (let i = strategies.length - 1; i >= 0; i--) {
|
|
127
|
+
const strategy = strategies[i];
|
|
128
|
+
|
|
129
|
+
// Apply the movement logic
|
|
130
|
+
strategy.update(body, dt);
|
|
131
|
+
|
|
132
|
+
// Check if movement is finished
|
|
133
|
+
if (strategy.isFinished?.()) {
|
|
134
|
+
strategies.splice(i, 1);
|
|
135
|
+
|
|
136
|
+
// Trigger onFinished callback if present
|
|
137
|
+
strategy.onFinished?.();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface defining the contract for movement strategies
|
|
5
|
+
*
|
|
6
|
+
* Movement strategies are responsible for calculating how a body should move
|
|
7
|
+
* each frame without directly modifying the physics engine.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* class SimpleMove implements MovementStrategy {
|
|
12
|
+
* constructor(private vx: number, private vy: number) {}
|
|
13
|
+
*
|
|
14
|
+
* update(body: Matter.Body, dt: number): void {
|
|
15
|
+
* Matter.Body.setVelocity(body, { x: this.vx, y: this.vy });
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface MovementStrategy {
|
|
21
|
+
/**
|
|
22
|
+
* Called each frame BEFORE the physics engine update
|
|
23
|
+
* Calculates and applies movement to the provided body
|
|
24
|
+
*
|
|
25
|
+
* @param body - The Matter.js body to move
|
|
26
|
+
* @param dt - Time delta in milliseconds
|
|
27
|
+
*/
|
|
28
|
+
update(body: Matter.Body, dt: number): void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns true when the movement is finished (optional)
|
|
32
|
+
* If implemented, the strategy will be automatically removed
|
|
33
|
+
* from the movement manager when it returns true
|
|
34
|
+
*/
|
|
35
|
+
isFinished?(): boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Event triggered when the movement completes (optional)
|
|
39
|
+
* Useful for chaining movements
|
|
40
|
+
*/
|
|
41
|
+
onFinished?(): void;
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Movement system core
|
|
2
|
+
export * from './MovementStrategy';
|
|
3
|
+
export * from './MovementManager';
|
|
4
|
+
|
|
5
|
+
// Movement strategies
|
|
6
|
+
export * from './strategies/LinearMove';
|
|
7
|
+
export * from './strategies/Dash';
|
|
8
|
+
export * from './strategies/Knockback';
|
|
9
|
+
export * from './strategies/PathFollow';
|
|
10
|
+
export * from './strategies/Oscillate';
|
|
11
|
+
export * from './strategies/CompositeMovement';
|
|
12
|
+
export * from './strategies/SeekAvoid';
|
|
13
|
+
export * from './strategies/LinearRepulsion';
|
|
14
|
+
export * from './strategies/IceMovement';
|
|
15
|
+
export * from './strategies/ProjectileMovement';
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
import { MovementStrategy } from '../MovementStrategy';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Combines multiple movement strategies into a single movement
|
|
6
|
+
*
|
|
7
|
+
* Allows composition of movement patterns by either:
|
|
8
|
+
* - Running multiple strategies in parallel (adding their effects)
|
|
9
|
+
* - Running strategies in sequence (one after another)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Parallel movement (oscillate while following a path)
|
|
14
|
+
* const composite = new CompositeMovement('parallel', [
|
|
15
|
+
* new PathFollow(waypoints, 2),
|
|
16
|
+
* new Oscillate({ x: 0, y: 1 }, 10, 1000)
|
|
17
|
+
* ]);
|
|
18
|
+
*
|
|
19
|
+
* // Sequential movement (dash, then knockback)
|
|
20
|
+
* const sequence = new CompositeMovement('sequence', [
|
|
21
|
+
* new Dash(8, { x: 1, y: 0 }, 200),
|
|
22
|
+
* new Knockback({ x: -0.5, y: 0 }, 3, 300)
|
|
23
|
+
* ]);
|
|
24
|
+
*
|
|
25
|
+
* movementManager.add('entity1', composite);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class CompositeMovement implements MovementStrategy {
|
|
29
|
+
private currentStrategyIndex: number = 0;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a composite movement
|
|
33
|
+
*
|
|
34
|
+
* @param mode - How to combine strategies ('parallel' or 'sequence')
|
|
35
|
+
* @param strategies - Array of movement strategies to combine
|
|
36
|
+
*/
|
|
37
|
+
constructor(
|
|
38
|
+
private mode: 'parallel' | 'sequence',
|
|
39
|
+
private strategies: MovementStrategy[]
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Apply the composite movement
|
|
44
|
+
*
|
|
45
|
+
* @param body - Matter.js body to move
|
|
46
|
+
* @param dt - Time delta in milliseconds
|
|
47
|
+
*/
|
|
48
|
+
update(body: Matter.Body, dt: number): void {
|
|
49
|
+
if (this.strategies.length === 0) return;
|
|
50
|
+
|
|
51
|
+
if (this.mode === 'parallel') {
|
|
52
|
+
// Update all strategies simultaneously
|
|
53
|
+
// Their effects will be combined by the physics engine
|
|
54
|
+
this.updateParallel(body, dt);
|
|
55
|
+
} else {
|
|
56
|
+
// Update strategies in sequence
|
|
57
|
+
this.updateSequence(body, dt);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Update all strategies in parallel
|
|
63
|
+
*/
|
|
64
|
+
private updateParallel(body: Matter.Body, dt: number): void {
|
|
65
|
+
// Make a copy of the array since we might remove elements during iteration
|
|
66
|
+
const currentStrategies = [...this.strategies];
|
|
67
|
+
|
|
68
|
+
// Track original velocity to combine movement effects
|
|
69
|
+
const originalVelocity = { x: body.velocity.x, y: body.velocity.y };
|
|
70
|
+
let velocitiesApplied = 0;
|
|
71
|
+
|
|
72
|
+
// Apply each strategy
|
|
73
|
+
for (let i = currentStrategies.length - 1; i >= 0; i--) {
|
|
74
|
+
const strategy = currentStrategies[i];
|
|
75
|
+
|
|
76
|
+
// Store velocity before strategy update
|
|
77
|
+
const beforeVx = body.velocity.x;
|
|
78
|
+
const beforeVy = body.velocity.y;
|
|
79
|
+
|
|
80
|
+
// Apply the strategy
|
|
81
|
+
strategy.update(body, dt);
|
|
82
|
+
|
|
83
|
+
// Track if velocity was changed
|
|
84
|
+
if (beforeVx !== body.velocity.x || beforeVy !== body.velocity.y) {
|
|
85
|
+
velocitiesApplied++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Remove finished strategies
|
|
89
|
+
if (strategy.isFinished?.()) {
|
|
90
|
+
this.strategies.splice(this.strategies.indexOf(strategy), 1);
|
|
91
|
+
strategy.onFinished?.();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If multiple strategies applied velocities, calculate the average
|
|
96
|
+
if (velocitiesApplied > 1) {
|
|
97
|
+
Matter.Body.setVelocity(body, {
|
|
98
|
+
x: (body.velocity.x + originalVelocity.x) / 2,
|
|
99
|
+
y: (body.velocity.y + originalVelocity.y) / 2
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Update strategies in sequence
|
|
106
|
+
*/
|
|
107
|
+
private updateSequence(body: Matter.Body, dt: number): void {
|
|
108
|
+
if (this.currentStrategyIndex >= this.strategies.length) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const currentStrategy = this.strategies[this.currentStrategyIndex];
|
|
113
|
+
|
|
114
|
+
// Apply the current strategy
|
|
115
|
+
currentStrategy.update(body, dt);
|
|
116
|
+
|
|
117
|
+
// Check if current strategy is finished
|
|
118
|
+
if (currentStrategy.isFinished?.()) {
|
|
119
|
+
// Trigger completion callback
|
|
120
|
+
currentStrategy.onFinished?.();
|
|
121
|
+
|
|
122
|
+
// Move to next strategy
|
|
123
|
+
this.currentStrategyIndex++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if all strategies have finished
|
|
129
|
+
*
|
|
130
|
+
* @returns True if all strategies are completed
|
|
131
|
+
*/
|
|
132
|
+
isFinished(): boolean {
|
|
133
|
+
if (this.strategies.length === 0) return true;
|
|
134
|
+
|
|
135
|
+
if (this.mode === 'parallel') {
|
|
136
|
+
// In parallel mode, we're finished when all strategies are finished
|
|
137
|
+
return this.strategies.length === 0;
|
|
138
|
+
} else {
|
|
139
|
+
// In sequence mode, we're finished when we've gone through all strategies
|
|
140
|
+
return this.currentStrategyIndex >= this.strategies.length;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Add a new strategy to the composite
|
|
146
|
+
*
|
|
147
|
+
* @param strategy - Movement strategy to add
|
|
148
|
+
*/
|
|
149
|
+
add(strategy: MovementStrategy): void {
|
|
150
|
+
this.strategies.push(strategy);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Remove a strategy from the composite
|
|
155
|
+
*
|
|
156
|
+
* @param strategy - Movement strategy to remove
|
|
157
|
+
* @returns True if the strategy was found and removed
|
|
158
|
+
*/
|
|
159
|
+
remove(strategy: MovementStrategy): boolean {
|
|
160
|
+
const index = this.strategies.indexOf(strategy);
|
|
161
|
+
if (index === -1) return false;
|
|
162
|
+
|
|
163
|
+
this.strategies.splice(index, 1);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Reset the composite to start from the beginning
|
|
169
|
+
*/
|
|
170
|
+
reset(): void {
|
|
171
|
+
this.currentStrategyIndex = 0;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
import { MovementStrategy } from '../MovementStrategy';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements a dash movement in a specified direction for a limited time
|
|
6
|
+
*
|
|
7
|
+
* Dash applies high velocity movement in a direction for a short duration
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Dash right for 200ms at speed 10
|
|
12
|
+
* movementManager.add('player1', new Dash(10, { x: 1, y: 0 }, 200));
|
|
13
|
+
*
|
|
14
|
+
* // Dash diagonally (normalized)
|
|
15
|
+
* movementManager.add('player1', new Dash(8, { x: 0.7071, y: 0.7071 }, 150));
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export class Dash implements MovementStrategy {
|
|
19
|
+
private elapsed: number = 0;
|
|
20
|
+
private finished: boolean = false;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a dash movement
|
|
24
|
+
*
|
|
25
|
+
* @param speed - Movement speed (pixels per frame)
|
|
26
|
+
* @param direction - Normalized direction vector { x, y }
|
|
27
|
+
* @param duration - Duration in milliseconds
|
|
28
|
+
*/
|
|
29
|
+
constructor(
|
|
30
|
+
private speed: number,
|
|
31
|
+
private direction: { x: number, y: number },
|
|
32
|
+
private duration: number
|
|
33
|
+
) {
|
|
34
|
+
// Normalize direction vector if not already normalized
|
|
35
|
+
const magnitude = Math.sqrt(direction.x * direction.x + direction.y * direction.y);
|
|
36
|
+
if (magnitude !== 0 && magnitude !== 1) {
|
|
37
|
+
this.direction = {
|
|
38
|
+
x: direction.x / magnitude,
|
|
39
|
+
y: direction.y / magnitude
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Apply dash movement to the body
|
|
46
|
+
*
|
|
47
|
+
* @param body - Matter.js body to move
|
|
48
|
+
* @param dt - Time delta in milliseconds
|
|
49
|
+
*/
|
|
50
|
+
update(body: Matter.Body, dt: number): void {
|
|
51
|
+
this.elapsed += dt;
|
|
52
|
+
|
|
53
|
+
if (this.elapsed <= this.duration) {
|
|
54
|
+
// Apply dash velocity
|
|
55
|
+
Matter.Body.setVelocity(body, {
|
|
56
|
+
x: this.direction.x * this.speed,
|
|
57
|
+
y: this.direction.y * this.speed
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
// Stop the body when dash is complete
|
|
61
|
+
Matter.Body.setVelocity(body, { x: 0, y: 0 });
|
|
62
|
+
this.finished = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if dash duration has elapsed
|
|
68
|
+
*
|
|
69
|
+
* @returns True if dash is complete
|
|
70
|
+
*/
|
|
71
|
+
isFinished(): boolean {
|
|
72
|
+
return this.finished;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Optional callback for when dash completes
|
|
77
|
+
* Can be used to chain into another movement
|
|
78
|
+
*/
|
|
79
|
+
onFinished(): void {
|
|
80
|
+
// Optional event hook for dash completion
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
import { MovementStrategy } from '../MovementStrategy';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements a slippery movement for icy surfaces
|
|
6
|
+
*
|
|
7
|
+
* Creates a realistic ice-physics effect with:
|
|
8
|
+
* - Slow acceleration when starting to move
|
|
9
|
+
* - Slippery inertia when stopping
|
|
10
|
+
* - Gradual turning with momentum
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Apply ice movement to player on ice terrain
|
|
15
|
+
* movementManager.add('player1', new IceMovement({ x: 1, y: 0 }, 5));
|
|
16
|
+
*
|
|
17
|
+
* // Update direction when player changes direction
|
|
18
|
+
* iceMovement.setTargetDirection({ x: 0, y: 1 });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export class IceMovement implements MovementStrategy {
|
|
22
|
+
// Current velocity components
|
|
23
|
+
private currentVx: number = 0;
|
|
24
|
+
private currentVy: number = 0;
|
|
25
|
+
|
|
26
|
+
// Flags
|
|
27
|
+
private stopped: boolean = false;
|
|
28
|
+
private elapsed: number = 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create an ice movement behavior
|
|
32
|
+
*
|
|
33
|
+
* @param targetDirection - Desired movement direction (normalized vector)
|
|
34
|
+
* @param maxSpeed - Maximum speed when fully accelerated
|
|
35
|
+
* @param acceleration - Acceleration rate (0-1, lower = more slippery start)
|
|
36
|
+
* @param friction - Friction rate (0-1, lower = more slippery stop)
|
|
37
|
+
* @param duration - Optional duration limit for the movement
|
|
38
|
+
*/
|
|
39
|
+
constructor(
|
|
40
|
+
private targetDirection: { x: number, y: number },
|
|
41
|
+
private maxSpeed: number = 4,
|
|
42
|
+
private acceleration: number = 0.15,
|
|
43
|
+
private friction: number = 0.05,
|
|
44
|
+
private duration?: number
|
|
45
|
+
) {
|
|
46
|
+
// Normalize direction
|
|
47
|
+
this.normalizeDirection();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Update the ice movement physics
|
|
52
|
+
*
|
|
53
|
+
* @param body - Matter.js body to move
|
|
54
|
+
* @param dt - Time delta in milliseconds
|
|
55
|
+
*/
|
|
56
|
+
update(body: Matter.Body, dt: number): void {
|
|
57
|
+
// Update elapsed time if duration is set
|
|
58
|
+
if (this.duration !== undefined) {
|
|
59
|
+
this.elapsed += dt;
|
|
60
|
+
if (this.elapsed >= this.duration) {
|
|
61
|
+
this.stopped = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle acceleration and friction physics
|
|
66
|
+
if (!this.stopped) {
|
|
67
|
+
// Accelerate gradually toward target speed in target direction
|
|
68
|
+
this.currentVx += (this.targetDirection.x * this.maxSpeed - this.currentVx) * this.acceleration;
|
|
69
|
+
this.currentVy += (this.targetDirection.y * this.maxSpeed - this.currentVy) * this.acceleration;
|
|
70
|
+
} else {
|
|
71
|
+
// Apply friction to gradually slow down
|
|
72
|
+
this.currentVx *= (1 - this.friction);
|
|
73
|
+
this.currentVy *= (1 - this.friction);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Apply the calculated velocity
|
|
77
|
+
Matter.Body.setVelocity(body, {
|
|
78
|
+
x: this.currentVx,
|
|
79
|
+
y: this.currentVy
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if movement is finished
|
|
85
|
+
* (movement is considered finished when almost stopped)
|
|
86
|
+
*
|
|
87
|
+
* @returns True if movement is effectively stopped
|
|
88
|
+
*/
|
|
89
|
+
isFinished(): boolean {
|
|
90
|
+
const speed = Math.hypot(this.currentVx, this.currentVy);
|
|
91
|
+
|
|
92
|
+
// Movement is finished when:
|
|
93
|
+
// 1. We're in stopped state AND
|
|
94
|
+
// 2. Speed is negligible (< 0.05)
|
|
95
|
+
return this.stopped && speed < 0.05;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Stop the movement (will start applying friction)
|
|
100
|
+
*/
|
|
101
|
+
stop(): void {
|
|
102
|
+
this.stopped = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resume movement in the current direction
|
|
107
|
+
*/
|
|
108
|
+
resume(): void {
|
|
109
|
+
this.stopped = false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Set a new target direction
|
|
114
|
+
* The actual movement will gradually change toward this direction
|
|
115
|
+
*
|
|
116
|
+
* @param direction - New target direction
|
|
117
|
+
*/
|
|
118
|
+
setTargetDirection(direction: { x: number, y: number }): void {
|
|
119
|
+
this.targetDirection = direction;
|
|
120
|
+
this.normalizeDirection();
|
|
121
|
+
|
|
122
|
+
// If we were stopped, resume movement
|
|
123
|
+
this.stopped = false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Set movement parameters
|
|
128
|
+
*
|
|
129
|
+
* @param maxSpeed - New maximum speed
|
|
130
|
+
* @param acceleration - New acceleration rate
|
|
131
|
+
* @param friction - New friction rate
|
|
132
|
+
*/
|
|
133
|
+
setParameters(maxSpeed?: number, acceleration?: number, friction?: number): void {
|
|
134
|
+
if (maxSpeed !== undefined) this.maxSpeed = maxSpeed;
|
|
135
|
+
if (acceleration !== undefined) this.acceleration = acceleration;
|
|
136
|
+
if (friction !== undefined) this.friction = friction;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Normalize the direction vector
|
|
141
|
+
*/
|
|
142
|
+
private normalizeDirection(): void {
|
|
143
|
+
const magnitude = Math.sqrt(
|
|
144
|
+
this.targetDirection.x * this.targetDirection.x +
|
|
145
|
+
this.targetDirection.y * this.targetDirection.y
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (magnitude > 0) {
|
|
149
|
+
this.targetDirection = {
|
|
150
|
+
x: this.targetDirection.x / magnitude,
|
|
151
|
+
y: this.targetDirection.y / magnitude
|
|
152
|
+
};
|
|
153
|
+
} else {
|
|
154
|
+
// Default to no direction
|
|
155
|
+
this.targetDirection = { x: 0, y: 0 };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as Matter from 'matter-js';
|
|
2
|
+
import { MovementStrategy } from '../MovementStrategy';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements a knockback effect with decreasing force over time
|
|
6
|
+
*
|
|
7
|
+
* Applies an initial velocity that gradually decreases to simulate
|
|
8
|
+
* being pushed back by an impact
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // Knockback player to the left with force 8 for 300ms
|
|
13
|
+
* movementManager.add('player1', new Knockback({ x: -1, y: 0 }, 8, 300));
|
|
14
|
+
*
|
|
15
|
+
* // Knockback with decay (resistance)
|
|
16
|
+
* movementManager.add('enemy1', new Knockback({ x: 0, y: 1 }, 5, 200, 0.9));
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export class Knockback implements MovementStrategy {
|
|
20
|
+
private elapsed: number = 0;
|
|
21
|
+
private currentSpeed: number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a knockback movement
|
|
25
|
+
*
|
|
26
|
+
* @param direction - Normalized direction vector { x, y }
|
|
27
|
+
* @param force - Initial force of the knockback
|
|
28
|
+
* @param duration - Duration in milliseconds
|
|
29
|
+
* @param decayFactor - Speed decay multiplier per frame (0.8-0.95 typical)
|
|
30
|
+
*/
|
|
31
|
+
constructor(
|
|
32
|
+
private direction: { x: number, y: number },
|
|
33
|
+
private force: number,
|
|
34
|
+
private duration: number,
|
|
35
|
+
private decayFactor: number = 0.9
|
|
36
|
+
) {
|
|
37
|
+
this.currentSpeed = force;
|
|
38
|
+
|
|
39
|
+
// Normalize direction vector
|
|
40
|
+
const magnitude = Math.sqrt(direction.x * direction.x + direction.y * direction.y);
|
|
41
|
+
if (magnitude !== 0 && magnitude !== 1) {
|
|
42
|
+
this.direction = {
|
|
43
|
+
x: direction.x / magnitude,
|
|
44
|
+
y: direction.y / magnitude
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Apply knockback movement with decreasing force
|
|
51
|
+
*
|
|
52
|
+
* @param body - Matter.js body to move
|
|
53
|
+
* @param dt - Time delta in milliseconds
|
|
54
|
+
*/
|
|
55
|
+
update(body: Matter.Body, dt: number): void {
|
|
56
|
+
this.elapsed += dt;
|
|
57
|
+
|
|
58
|
+
if (this.elapsed <= this.duration) {
|
|
59
|
+
// Apply decreasing velocity
|
|
60
|
+
Matter.Body.setVelocity(body, {
|
|
61
|
+
x: this.direction.x * this.currentSpeed,
|
|
62
|
+
y: this.direction.y * this.currentSpeed
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Decay the speed
|
|
66
|
+
this.currentSpeed *= this.decayFactor;
|
|
67
|
+
} else {
|
|
68
|
+
// Stop movement when knockback is complete
|
|
69
|
+
Matter.Body.setVelocity(body, { x: 0, y: 0 });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if knockback duration has elapsed
|
|
75
|
+
*
|
|
76
|
+
* @returns True if knockback is complete
|
|
77
|
+
*/
|
|
78
|
+
isFinished(): boolean {
|
|
79
|
+
return this.elapsed >= this.duration;
|
|
80
|
+
}
|
|
81
|
+
}
|