@rpgjs/action-battle 5.0.0-beta.11 → 5.0.0-beta.12
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/CHANGELOG.md +11 -0
- package/dist/client/ai.server.d.ts +45 -8
- package/dist/client/attack-input.d.ts +3 -0
- package/dist/client/core/action-use.d.ts +18 -0
- package/dist/client/core/ai-behavior-tree.d.ts +99 -0
- package/dist/client/core/attack-runtime.d.ts +2 -0
- package/dist/client/core/defaults.d.ts +2 -1
- package/dist/client/core/equipment.d.ts +1 -0
- package/dist/client/core/targets.d.ts +15 -0
- package/dist/client/enemies/factory.d.ts +2 -0
- package/dist/client/index.d.ts +12 -7
- package/dist/client/index.js +16 -11
- package/dist/client/index10.js +32 -56
- package/dist/client/index11.js +99 -52
- package/dist/client/index12.js +76 -103
- package/dist/client/index13.js +72 -135
- package/dist/client/index14.js +67 -23
- package/dist/client/index15.js +197 -63
- package/dist/client/index16.js +112 -1337
- package/dist/client/index17.js +193 -7
- package/dist/client/index18.js +32 -58
- package/dist/client/index19.js +70 -8
- package/dist/client/index20.js +57 -501
- package/dist/client/index21.js +69 -0
- package/dist/client/index22.js +225 -0
- package/dist/client/index23.js +16 -0
- package/dist/client/index24.js +25 -0
- package/dist/client/index25.js +107 -0
- package/dist/client/index26.js +1707 -0
- package/dist/client/index27.js +12 -0
- package/dist/client/index28.js +589 -0
- package/dist/client/index4.js +79 -38
- package/dist/client/index6.js +65 -306
- package/dist/client/index7.js +33 -33
- package/dist/client/index8.js +24 -100
- package/dist/client/index9.js +293 -61
- package/dist/client/locomotion.d.ts +16 -0
- package/dist/client/movement.d.ts +14 -0
- package/dist/client/server.d.ts +7 -3
- package/dist/client/ui.d.ts +22 -0
- package/dist/client/visual.d.ts +15 -0
- package/dist/server/ai.server.d.ts +45 -8
- package/dist/server/attack-input.d.ts +3 -0
- package/dist/server/core/action-use.d.ts +18 -0
- package/dist/server/core/ai-behavior-tree.d.ts +99 -0
- package/dist/server/core/attack-runtime.d.ts +2 -0
- package/dist/server/core/defaults.d.ts +2 -1
- package/dist/server/core/equipment.d.ts +1 -0
- package/dist/server/core/targets.d.ts +15 -0
- package/dist/server/enemies/factory.d.ts +2 -0
- package/dist/server/index.d.ts +12 -7
- package/dist/server/index.js +14 -9
- package/dist/server/index10.js +64 -1336
- package/dist/server/index11.js +33 -33
- package/dist/server/index13.js +66 -11
- package/dist/server/index14.js +206 -484
- package/dist/server/index15.js +15 -9
- package/dist/server/index16.js +26 -0
- package/dist/server/index17.js +25 -0
- package/dist/server/index18.js +107 -0
- package/dist/server/index19.js +1707 -0
- package/dist/server/index2.js +10 -2
- package/dist/server/index20.js +37 -0
- package/dist/server/index21.js +588 -0
- package/dist/server/index22.js +78 -0
- package/dist/server/index23.js +12 -0
- package/dist/server/index5.js +79 -38
- package/dist/server/index6.js +192 -129
- package/dist/server/index7.js +198 -24
- package/dist/server/index8.js +28 -66
- package/dist/server/index9.js +68 -51
- package/dist/server/locomotion.d.ts +16 -0
- package/dist/server/movement.d.ts +14 -0
- package/dist/server/server.d.ts +7 -3
- package/dist/server/ui.d.ts +22 -0
- package/dist/server/visual.d.ts +15 -0
- package/package.json +5 -5
- package/src/ai.server.spec.ts +233 -0
- package/src/ai.server.ts +627 -108
- package/src/animations.spec.ts +40 -0
- package/src/animations.ts +31 -9
- package/src/attack-input.spec.ts +51 -0
- package/src/attack-input.ts +59 -0
- package/src/client.ts +75 -62
- package/src/config.ts +84 -37
- package/src/core/action-use.spec.ts +317 -0
- package/src/core/action-use.ts +386 -0
- package/src/core/ai-behavior-tree.spec.ts +116 -0
- package/src/core/ai-behavior-tree.ts +272 -0
- package/src/core/attack-profile.spec.ts +46 -0
- package/src/core/attack-runtime.spec.ts +35 -0
- package/src/core/attack-runtime.ts +32 -0
- package/src/core/context.ts +9 -0
- package/src/core/contracts.ts +146 -1
- package/src/core/defaults.ts +56 -0
- package/src/core/equipment.ts +9 -5
- package/src/core/targets.spec.ts +112 -0
- package/src/core/targets.ts +147 -0
- package/src/enemies/factory.ts +8 -0
- package/src/index.ts +111 -2
- package/src/locomotion.spec.ts +51 -0
- package/src/locomotion.ts +48 -0
- package/src/movement.spec.ts +78 -0
- package/src/movement.ts +46 -0
- package/src/server.ts +242 -66
- package/src/types.ts +105 -35
- package/src/ui.ts +113 -0
- package/src/visual.spec.ts +166 -0
- package/src/visual.ts +285 -0
- package/README.md +0 -1242
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type {
|
|
|
26
26
|
ActionBattleAnimationOptions,
|
|
27
27
|
ActionBattleAnimationResolver,
|
|
28
28
|
ActionBattleAnimationResult,
|
|
29
|
+
ActionBattleAiOptions,
|
|
29
30
|
ActionBattleOptions,
|
|
30
31
|
ActionBattleActionBarData,
|
|
31
32
|
ActionBattleActionBarItem,
|
|
@@ -35,30 +36,49 @@ export type {
|
|
|
35
36
|
ActionBattleAttackOptions,
|
|
36
37
|
ActionBattleUiOptions,
|
|
37
38
|
ActionBattleUiActionBarOptions,
|
|
39
|
+
ActionBattleUiAttackPreviewOptions,
|
|
40
|
+
ActionBattleUiGuiEntry,
|
|
38
41
|
ActionBattleUiTargetingOptions,
|
|
39
42
|
ActionBattleAttackDirection,
|
|
40
43
|
ActionBattleAttackHitboxConfig,
|
|
41
44
|
ActionBattleAttackHitboxMap,
|
|
42
45
|
ActionBattleAttackHitPolicy,
|
|
43
46
|
ActionBattleAttackProfile,
|
|
44
|
-
ActionBattleDebugOptions,
|
|
45
47
|
ActionBattleHitReactionProfile,
|
|
46
48
|
NormalizedActionBattleHitReactionProfile,
|
|
47
49
|
NormalizedActionBattleAttackProfile,
|
|
48
50
|
ActionBattleCombatOptions,
|
|
49
51
|
ActionBattleSystemOptions,
|
|
50
52
|
ActionBattleAiSystemOptions,
|
|
53
|
+
ActionBattleVisualComposer,
|
|
54
|
+
ActionBattleVisualContext,
|
|
55
|
+
ActionBattleVisualHelpers,
|
|
56
|
+
ActionBattleVisualInput,
|
|
57
|
+
ActionBattleVisualMoment,
|
|
58
|
+
ActionBattleVisualPart,
|
|
59
|
+
ActionBattleVisualPreset,
|
|
51
60
|
} from "./types";
|
|
52
61
|
export type {
|
|
53
62
|
ActionBattleAiBehavior,
|
|
54
63
|
ActionBattleAiContext,
|
|
55
64
|
ActionBattleAiDecision,
|
|
65
|
+
ActionBattleAiPreset,
|
|
56
66
|
ActionBattleAttackContext,
|
|
57
67
|
ActionBattleCombatSystem,
|
|
58
68
|
ActionBattleDamageContext,
|
|
59
69
|
ActionBattleDamageResult,
|
|
60
70
|
ActionBattleDirection,
|
|
61
71
|
ActionBattleEntity,
|
|
72
|
+
ActionBattleActionConfig,
|
|
73
|
+
ActionBattleActionMode,
|
|
74
|
+
ActionBattleActionTarget,
|
|
75
|
+
ActionBattleTargetContext,
|
|
76
|
+
ActionBattleTargetOptions,
|
|
77
|
+
ActionBattleTargetSelector,
|
|
78
|
+
ActionBattleProjectileImpactContext,
|
|
79
|
+
ActionBattleProjectileOptions,
|
|
80
|
+
ActionBattleUsable,
|
|
81
|
+
ActionBattleUseContext,
|
|
62
82
|
ActionBattleHitContext,
|
|
63
83
|
ActionBattleHitHooks,
|
|
64
84
|
ActionBattleHitResult,
|
|
@@ -67,6 +87,50 @@ export type {
|
|
|
67
87
|
ActionBattleKnockbackResult,
|
|
68
88
|
ActionBattleSystems,
|
|
69
89
|
} from "./core/contracts";
|
|
90
|
+
export {
|
|
91
|
+
action,
|
|
92
|
+
chase,
|
|
93
|
+
condition,
|
|
94
|
+
decision,
|
|
95
|
+
defineAiBehavior,
|
|
96
|
+
defineAiTree,
|
|
97
|
+
distanceLessThan,
|
|
98
|
+
faceTarget,
|
|
99
|
+
flee,
|
|
100
|
+
fleeFromTarget,
|
|
101
|
+
hpBelow,
|
|
102
|
+
idle,
|
|
103
|
+
ifDistanceLessThan,
|
|
104
|
+
ifHpBelow,
|
|
105
|
+
ifTargetInRange,
|
|
106
|
+
ifTargetVisible,
|
|
107
|
+
inState,
|
|
108
|
+
isEnemyType,
|
|
109
|
+
keepDistance,
|
|
110
|
+
moveToTarget,
|
|
111
|
+
patrol,
|
|
112
|
+
rule,
|
|
113
|
+
selector,
|
|
114
|
+
sequence,
|
|
115
|
+
setMode,
|
|
116
|
+
targetInRange,
|
|
117
|
+
targetVisible,
|
|
118
|
+
useAttack,
|
|
119
|
+
useSkill,
|
|
120
|
+
type ActionBattleAiCondition,
|
|
121
|
+
type ActionBattleAiIntent,
|
|
122
|
+
type ActionBattleAiIntentInput,
|
|
123
|
+
type ActionBattleAiMemory,
|
|
124
|
+
type ActionBattleAiRule,
|
|
125
|
+
type ActionBattleAiSimpleBehavior,
|
|
126
|
+
type ActionBattleAiSnapshotSelf,
|
|
127
|
+
type ActionBattleAiSnapshotTarget,
|
|
128
|
+
type ActionBattleAiTreeContext,
|
|
129
|
+
type ActionBattleAiTreeInput,
|
|
130
|
+
type ActionBattleAiTreeNode,
|
|
131
|
+
type ActionBattleAiTreeResult,
|
|
132
|
+
type ActionBattleAiTreeStatus,
|
|
133
|
+
} from "./core/ai-behavior-tree";
|
|
70
134
|
export {
|
|
71
135
|
DEFAULT_ACTION_BATTLE_ATTACK_PROFILE,
|
|
72
136
|
normalizeActionBattleAttackProfile,
|
|
@@ -78,8 +142,18 @@ export {
|
|
|
78
142
|
createActionBattleAttackId,
|
|
79
143
|
getNormalizedActionBattleAttackProfile,
|
|
80
144
|
resolveActionBattleHitboxSpeed,
|
|
145
|
+
runActionBattleActiveHitbox,
|
|
81
146
|
scheduleActionBattleStartup,
|
|
82
147
|
} from "./core/attack-runtime";
|
|
148
|
+
export {
|
|
149
|
+
canActionBattleUseTarget,
|
|
150
|
+
executeActionBattleUse,
|
|
151
|
+
getActionBattleActionConfig,
|
|
152
|
+
getActionBattleActionRange,
|
|
153
|
+
handleActionBattleProjectileDestroy,
|
|
154
|
+
handleActionBattleProjectileImpact,
|
|
155
|
+
shouldUseActionBattleUsable,
|
|
156
|
+
} from "./core/action-use";
|
|
83
157
|
export {
|
|
84
158
|
DEFAULT_ACTION_BATTLE_HIT_REACTION,
|
|
85
159
|
isActionBattleEntityInvincible,
|
|
@@ -93,22 +167,56 @@ export {
|
|
|
93
167
|
type ActionBattleEnemyAttackProfileMap,
|
|
94
168
|
type NormalizedActionBattleEnemyAttackProfileMap,
|
|
95
169
|
} from "./core/enemy-attack-profiles";
|
|
96
|
-
export {
|
|
170
|
+
export {
|
|
171
|
+
resolveActionBattleWeapon,
|
|
172
|
+
resolveActionBattleWeaponAttackProfile,
|
|
173
|
+
} from "./core/equipment";
|
|
97
174
|
export {
|
|
98
175
|
DEFAULT_ZELDA_PLAYER_HITBOXES,
|
|
99
176
|
createDefaultPlayerHitboxResolver,
|
|
100
177
|
defaultCombatSystem,
|
|
101
178
|
defaultEnemyBehaviors,
|
|
179
|
+
defaultEnemyPresets,
|
|
102
180
|
defaultKnockbackResolver,
|
|
103
181
|
defaultRpgjsDamageResolver,
|
|
104
182
|
} from "./core/defaults";
|
|
183
|
+
export {
|
|
184
|
+
ACTION_BATTLE_ENEMY_FACTION,
|
|
185
|
+
ACTION_BATTLE_PLAYER_FACTION,
|
|
186
|
+
canActionBattleTarget,
|
|
187
|
+
getActionBattleFaction,
|
|
188
|
+
getActionBattleTargets,
|
|
189
|
+
isActionBattleCombatEntity,
|
|
190
|
+
isActionBattleEvent,
|
|
191
|
+
isActionBattlePlayer,
|
|
192
|
+
matchesActionBattleTargetSelector,
|
|
193
|
+
} from "./core/targets";
|
|
105
194
|
export {
|
|
106
195
|
createActionBattleSystems,
|
|
107
196
|
getActionBattleSystems,
|
|
108
197
|
} from "./core/context";
|
|
109
198
|
export { applyActionBattleHit } from "./core/hit";
|
|
199
|
+
export {
|
|
200
|
+
ACTION_BATTLE_CLIENT_VISUAL_ID,
|
|
201
|
+
ACTION_BATTLE_HIT_FX_COMPONENT_ID,
|
|
202
|
+
createActionBattleClientVisuals,
|
|
203
|
+
createActionBattleVisual,
|
|
204
|
+
createClassicActionBattleVisual,
|
|
205
|
+
createFxActionBattleVisual,
|
|
206
|
+
emitActionBattleClientVisual,
|
|
207
|
+
playActionBattleVisual,
|
|
208
|
+
setActionBattlePreviewStarter,
|
|
209
|
+
} from "./visual";
|
|
210
|
+
export {
|
|
211
|
+
ActionBattleUi,
|
|
212
|
+
createActionBattleUi,
|
|
213
|
+
resolveActionBattleUi,
|
|
214
|
+
type ResolvedActionBattleUi,
|
|
215
|
+
} from "./ui";
|
|
110
216
|
export {
|
|
111
217
|
createActionEnemy,
|
|
218
|
+
defineActionBattleAiPreset,
|
|
219
|
+
defineActionBattleEnemy,
|
|
112
220
|
type ActionBattleEnemyPreset,
|
|
113
221
|
type ActionBattleEnemyPresetMap,
|
|
114
222
|
} from "./enemies/factory";
|
|
@@ -117,6 +225,7 @@ export {
|
|
|
117
225
|
export {
|
|
118
226
|
DEFAULT_PLAYER_ATTACK_HITBOXES,
|
|
119
227
|
getPlayerWeaponKnockbackForce,
|
|
228
|
+
applyActionBattleEntityHit,
|
|
120
229
|
applyPlayerHitToEvent,
|
|
121
230
|
ACTION_BATTLE_ACTION_BAR_GUI_ID,
|
|
122
231
|
openActionBattleActionBar,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
forceActionBattleLocomotionAnimation,
|
|
4
|
+
withActionBattleAnimationUnlocked,
|
|
5
|
+
} from "./locomotion";
|
|
6
|
+
|
|
7
|
+
describe("action battle locomotion helpers", () => {
|
|
8
|
+
test("temporarily unlocks animation to restore locomotion state", () => {
|
|
9
|
+
const entity = {
|
|
10
|
+
animationFixed: true,
|
|
11
|
+
setGraphicAnimation: vi.fn(function (this: { animationFixed: boolean }) {
|
|
12
|
+
expect(this.animationFixed).toBe(false);
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
forceActionBattleLocomotionAnimation(entity, "stand");
|
|
17
|
+
|
|
18
|
+
expect(entity.setGraphicAnimation).toHaveBeenCalledWith("stand");
|
|
19
|
+
expect(entity.animationFixed).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("updates client-side animation signals when no server animation API exists", () => {
|
|
23
|
+
const setAnimationName = vi.fn();
|
|
24
|
+
const entity = {
|
|
25
|
+
animationFixed: false,
|
|
26
|
+
animationName: {
|
|
27
|
+
set: setAnimationName,
|
|
28
|
+
},
|
|
29
|
+
resetAnimationState: vi.fn(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
forceActionBattleLocomotionAnimation(entity, "stand");
|
|
33
|
+
|
|
34
|
+
expect(setAnimationName).toHaveBeenCalledWith("stand");
|
|
35
|
+
expect(entity.resetAnimationState).toHaveBeenCalled();
|
|
36
|
+
expect(entity.resetAnimationState.mock.invocationCallOrder[0]).toBeLessThan(
|
|
37
|
+
setAnimationName.mock.invocationCallOrder[0]
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("temporarily unlocks action animations while preserving the current lock", () => {
|
|
42
|
+
const entity = { animationFixed: true };
|
|
43
|
+
const callback = vi.fn(() => {
|
|
44
|
+
expect(entity.animationFixed).toBe(false);
|
|
45
|
+
return "played";
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(withActionBattleAnimationUnlocked(entity, callback)).toBe("played");
|
|
49
|
+
expect(entity.animationFixed).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
type LocomotionEntity = {
|
|
2
|
+
animationFixed?: boolean;
|
|
3
|
+
setGraphicAnimation?: (...args: any[]) => unknown;
|
|
4
|
+
animationName?: {
|
|
5
|
+
set?: (animationName: string) => unknown;
|
|
6
|
+
};
|
|
7
|
+
resetAnimationState?: () => unknown;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const withActionBattleAnimationUnlocked = <T>(
|
|
11
|
+
entity: LocomotionEntity | undefined,
|
|
12
|
+
callback: () => T
|
|
13
|
+
): T => {
|
|
14
|
+
if (!entity) return callback();
|
|
15
|
+
|
|
16
|
+
const previousAnimationFixed = entity.animationFixed;
|
|
17
|
+
entity.animationFixed = false;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
return callback();
|
|
21
|
+
} finally {
|
|
22
|
+
entity.animationFixed = previousAnimationFixed;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Force a locomotion animation even when an action lock temporarily froze
|
|
28
|
+
* animation changes. This keeps server state and local rendering coherent
|
|
29
|
+
* after attack recovery interrupts movement.
|
|
30
|
+
*/
|
|
31
|
+
export const forceActionBattleLocomotionAnimation = (
|
|
32
|
+
entity: LocomotionEntity | undefined,
|
|
33
|
+
animationName: "stand" | "walk"
|
|
34
|
+
) => {
|
|
35
|
+
if (!entity) return;
|
|
36
|
+
|
|
37
|
+
withActionBattleAnimationUnlocked(entity, () => {
|
|
38
|
+
if (typeof entity.resetAnimationState === "function") {
|
|
39
|
+
entity.resetAnimationState();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (typeof entity.setGraphicAnimation === "function") {
|
|
43
|
+
entity.setGraphicAnimation(animationName);
|
|
44
|
+
} else if (typeof entity.animationName?.set === "function") {
|
|
45
|
+
entity.animationName.set(animationName);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
hasActionBattlePhysicsBody,
|
|
4
|
+
isActionBattleMovementResolutionError,
|
|
5
|
+
safeActionBattleDash,
|
|
6
|
+
} from "./movement";
|
|
7
|
+
|
|
8
|
+
describe("action battle movement helpers", () => {
|
|
9
|
+
test("does not dash when the entity physics body has already been removed", () => {
|
|
10
|
+
const entity = {
|
|
11
|
+
id: "enemy-1",
|
|
12
|
+
getCurrentMap: vi.fn(() => ({
|
|
13
|
+
getBody: vi.fn(() => undefined),
|
|
14
|
+
})),
|
|
15
|
+
dash: vi.fn(),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
expect(safeActionBattleDash(entity, { x: 1, y: 0 }, 10, 200)).toBe(false);
|
|
19
|
+
expect(entity.dash).not.toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("dashes when the entity still has a physics body", () => {
|
|
23
|
+
const entity = {
|
|
24
|
+
id: "enemy-1",
|
|
25
|
+
getCurrentMap: vi.fn(() => ({
|
|
26
|
+
getBody: vi.fn(() => ({ id: "enemy-1" })),
|
|
27
|
+
})),
|
|
28
|
+
dash: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
expect(safeActionBattleDash(entity, { x: 1, y: 0 }, 10, 200)).toBe(true);
|
|
32
|
+
expect(entity.dash).toHaveBeenCalledWith({ x: 1, y: 0 }, 10, 200);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("absorbs teardown races between body check and dash registration", () => {
|
|
36
|
+
const entity = {
|
|
37
|
+
id: "enemy-1",
|
|
38
|
+
getCurrentMap: vi.fn(() => ({
|
|
39
|
+
getBody: vi.fn(() => ({ id: "enemy-1" })),
|
|
40
|
+
})),
|
|
41
|
+
dash: vi.fn(() => {
|
|
42
|
+
throw new Error("MovementManager: unable to resolve entity for identifier enemy-1.");
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
expect(safeActionBattleDash(entity, { x: 1, y: 0 }, 10, 200)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("keeps unexpected movement errors visible", () => {
|
|
50
|
+
const entity = {
|
|
51
|
+
id: "enemy-1",
|
|
52
|
+
getCurrentMap: vi.fn(() => ({
|
|
53
|
+
getBody: vi.fn(() => ({ id: "enemy-1" })),
|
|
54
|
+
})),
|
|
55
|
+
dash: vi.fn(() => {
|
|
56
|
+
throw new Error("boom");
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
expect(() => safeActionBattleDash(entity, { x: 1, y: 0 }, 10, 200)).toThrow(
|
|
61
|
+
"boom"
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("detects unavailable bodies only when the map exposes body lookup", () => {
|
|
66
|
+
expect(
|
|
67
|
+
hasActionBattlePhysicsBody({
|
|
68
|
+
id: "enemy-1",
|
|
69
|
+
getCurrentMap: () => ({}),
|
|
70
|
+
})
|
|
71
|
+
).toBe(true);
|
|
72
|
+
expect(
|
|
73
|
+
isActionBattleMovementResolutionError(
|
|
74
|
+
new Error("MovementManager: unable to resolve entity for identifier enemy-1.")
|
|
75
|
+
)
|
|
76
|
+
).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
});
|
package/src/movement.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const UNRESOLVED_ENTITY_MESSAGE = "unable to resolve entity";
|
|
2
|
+
|
|
3
|
+
export type ActionBattleDashEntity = {
|
|
4
|
+
id?: string;
|
|
5
|
+
getCurrentMap?: () => any;
|
|
6
|
+
dash?: (
|
|
7
|
+
direction: { x: number; y: number },
|
|
8
|
+
additionalSpeed?: number,
|
|
9
|
+
duration?: number
|
|
10
|
+
) => unknown;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const isActionBattleMovementResolutionError = (error: unknown): boolean => {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
15
|
+
return message.includes(UNRESOLVED_ENTITY_MESSAGE);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const hasActionBattlePhysicsBody = (
|
|
19
|
+
entity: ActionBattleDashEntity | null | undefined
|
|
20
|
+
): boolean => {
|
|
21
|
+
if (!entity) return false;
|
|
22
|
+
const map = entity.getCurrentMap?.();
|
|
23
|
+
if (!map) return false;
|
|
24
|
+
if (typeof map.getBody !== "function" || !entity.id) return true;
|
|
25
|
+
return Boolean(map.getBody(entity.id));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const safeActionBattleDash = (
|
|
29
|
+
entity: ActionBattleDashEntity | null | undefined,
|
|
30
|
+
direction: { x: number; y: number },
|
|
31
|
+
additionalSpeed?: number,
|
|
32
|
+
duration?: number
|
|
33
|
+
): boolean => {
|
|
34
|
+
if (!entity || typeof entity.dash !== "function") return false;
|
|
35
|
+
if (!hasActionBattlePhysicsBody(entity)) return false;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
entity.dash(direction, additionalSpeed, duration);
|
|
39
|
+
return true;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (isActionBattleMovementResolutionError(error)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
};
|