@rpgjs/action-battle 5.0.0-beta.11 → 5.0.0-beta.13
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 +22 -0
- package/dist/client/ai.server.d.ts +57 -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 +3 -2
- 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 +203 -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 +70 -0
- package/dist/client/index22.js +226 -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 +1949 -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 +57 -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 +3 -2
- 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 +67 -11
- package/dist/server/index14.js +207 -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 +1949 -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 +208 -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 +380 -1
- package/src/ai.server.ts +963 -137
- 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 +387 -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 +72 -1
- package/src/core/equipment.ts +9 -5
- package/src/core/hit.spec.ts +21 -0
- package/src/core/targets.spec.ts +124 -0
- package/src/core/targets.ts +150 -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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { playActionBattleAnimation } from "./animations";
|
|
3
|
+
|
|
4
|
+
describe("action battle animations", () => {
|
|
5
|
+
test("uses setGraphicAnimation when the entity exposes the server animation API", () => {
|
|
6
|
+
const entity = {
|
|
7
|
+
setGraphicAnimation: vi.fn(),
|
|
8
|
+
setAnimation: vi.fn(),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
playActionBattleAnimation("attack", entity);
|
|
12
|
+
|
|
13
|
+
expect(entity.setGraphicAnimation).toHaveBeenCalledWith("attack", 1);
|
|
14
|
+
expect(entity.setAnimation).not.toHaveBeenCalled();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("falls back to setAnimation for client sprite objects", () => {
|
|
18
|
+
const entity = {
|
|
19
|
+
setAnimation: vi.fn(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
playActionBattleAnimation("attack", entity, {
|
|
23
|
+
attack: {
|
|
24
|
+
animationName: "slash",
|
|
25
|
+
graphic: "hero-slash",
|
|
26
|
+
repeat: 2,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(entity.setAnimation).toHaveBeenCalledWith(
|
|
31
|
+
"slash",
|
|
32
|
+
"hero-slash",
|
|
33
|
+
2
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("does not throw when the entity has no animation API", () => {
|
|
38
|
+
expect(() => playActionBattleAnimation("attack", {})).not.toThrow();
|
|
39
|
+
});
|
|
40
|
+
});
|
package/src/animations.ts
CHANGED
|
@@ -20,6 +20,36 @@ export interface ActionBattleAnimationDefaults {
|
|
|
20
20
|
repeat?: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const playResolvedAnimation = (
|
|
24
|
+
entity: ActionBattleAnimationEntity,
|
|
25
|
+
animation: ResolvedActionBattleAnimation
|
|
26
|
+
) => {
|
|
27
|
+
if (typeof entity.setGraphicAnimation === "function") {
|
|
28
|
+
if (animation.graphic !== undefined) {
|
|
29
|
+
entity.setGraphicAnimation(
|
|
30
|
+
animation.animationName,
|
|
31
|
+
animation.graphic,
|
|
32
|
+
animation.repeat
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
entity.setGraphicAnimation(animation.animationName, animation.repeat);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof entity.setAnimation === "function") {
|
|
41
|
+
if (animation.graphic !== undefined) {
|
|
42
|
+
entity.setAnimation(
|
|
43
|
+
animation.animationName,
|
|
44
|
+
animation.graphic,
|
|
45
|
+
animation.repeat
|
|
46
|
+
);
|
|
47
|
+
} else {
|
|
48
|
+
entity.setAnimation(animation.animationName, animation.repeat);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
23
53
|
const DEFAULT_ANIMATION_BY_KEY: Record<ActionBattleAnimationKey, string> = {
|
|
24
54
|
attack: "attack",
|
|
25
55
|
hurt: "hurt",
|
|
@@ -127,15 +157,7 @@ export function playActionBattleAnimation(
|
|
|
127
157
|
);
|
|
128
158
|
if (!animation) return null;
|
|
129
159
|
|
|
130
|
-
|
|
131
|
-
entity.setGraphicAnimation(
|
|
132
|
-
animation.animationName,
|
|
133
|
-
animation.graphic,
|
|
134
|
-
animation.repeat
|
|
135
|
-
);
|
|
136
|
-
} else {
|
|
137
|
-
entity.setGraphicAnimation(animation.animationName, animation.repeat);
|
|
138
|
-
}
|
|
160
|
+
playResolvedAnimation(entity, animation);
|
|
139
161
|
|
|
140
162
|
return animation;
|
|
141
163
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
applyActionBattleAttackDirection,
|
|
4
|
+
resolveActionBattleAttackDirection,
|
|
5
|
+
} from "./attack-input";
|
|
6
|
+
|
|
7
|
+
describe("action battle attack input", () => {
|
|
8
|
+
test("prefers the direction carried by the action payload", () => {
|
|
9
|
+
const entity = {
|
|
10
|
+
getDirection: () => "left",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
resolveActionBattleAttackDirection(entity, {
|
|
15
|
+
data: { direction: "right" },
|
|
16
|
+
})
|
|
17
|
+
).toBe("right");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("falls back to the entity direction when the payload is missing", () => {
|
|
21
|
+
const entity = {
|
|
22
|
+
getDirection: () => "up",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
expect(resolveActionBattleAttackDirection(entity)).toBe("up");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("applies the captured attack direction before direction lock", () => {
|
|
29
|
+
const entity = {
|
|
30
|
+
changeDirection: vi.fn(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
applyActionBattleAttackDirection(entity, "down");
|
|
34
|
+
|
|
35
|
+
expect(entity.changeDirection).toHaveBeenCalledWith("down");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("temporarily unlocks direction so attack intent can face while knocked back", () => {
|
|
39
|
+
const entity = {
|
|
40
|
+
directionFixed: true,
|
|
41
|
+
changeDirection: vi.fn(function (this: { directionFixed: boolean }) {
|
|
42
|
+
expect(this.directionFixed).toBe(false);
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
applyActionBattleAttackDirection(entity, "right");
|
|
47
|
+
|
|
48
|
+
expect(entity.changeDirection).toHaveBeenCalledWith("right");
|
|
49
|
+
expect(entity.directionFixed).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type ActionBattleResolvedDirection = "up" | "down" | "left" | "right";
|
|
2
|
+
|
|
3
|
+
const ACTION_BATTLE_DIRECTIONS = new Set([
|
|
4
|
+
"up",
|
|
5
|
+
"down",
|
|
6
|
+
"left",
|
|
7
|
+
"right",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
const normalizeDirection = (
|
|
11
|
+
value: unknown
|
|
12
|
+
): ActionBattleResolvedDirection | undefined => {
|
|
13
|
+
if (typeof value !== "string") return undefined;
|
|
14
|
+
return ACTION_BATTLE_DIRECTIONS.has(value)
|
|
15
|
+
? (value as ActionBattleResolvedDirection)
|
|
16
|
+
: undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const resolveActionBattleAttackDirection = (
|
|
20
|
+
entity: any,
|
|
21
|
+
input?: any
|
|
22
|
+
): ActionBattleResolvedDirection => {
|
|
23
|
+
const payloadDirection =
|
|
24
|
+
normalizeDirection(input?.data?.direction) ??
|
|
25
|
+
normalizeDirection(input?.data?.attackDirection) ??
|
|
26
|
+
normalizeDirection(input?.direction);
|
|
27
|
+
if (payloadDirection) return payloadDirection;
|
|
28
|
+
|
|
29
|
+
if (typeof entity?.getDirection === "function") {
|
|
30
|
+
const direction = normalizeDirection(entity.getDirection());
|
|
31
|
+
if (direction) return direction;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof entity?.direction === "function") {
|
|
35
|
+
const direction = normalizeDirection(entity.direction());
|
|
36
|
+
if (direction) return direction;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return normalizeDirection(entity?.direction) ?? "down";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const applyActionBattleAttackDirection = (
|
|
43
|
+
entity: any,
|
|
44
|
+
direction: ActionBattleResolvedDirection
|
|
45
|
+
) => {
|
|
46
|
+
if (typeof entity?.changeDirection === "function") {
|
|
47
|
+
const previousDirectionFixed = entity.directionFixed;
|
|
48
|
+
if (previousDirectionFixed === true) {
|
|
49
|
+
entity.directionFixed = false;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
entity.changeDirection(direction);
|
|
53
|
+
} finally {
|
|
54
|
+
if (previousDirectionFixed === true) {
|
|
55
|
+
entity.directionFixed = previousDirectionFixed;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
package/src/client.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PrebuiltComponentAnimations, RpgClient, RpgClientEngine, RpgGui, inject } from "@rpgjs/client";
|
|
2
2
|
import { defineModule } from "@rpgjs/common";
|
|
3
|
-
// @ts-ignore CanvasEngine components are compiled by @canvasengine/compiler.
|
|
4
|
-
import ActionBarComponent from "./components/action-bar.ce";
|
|
5
|
-
// @ts-ignore CanvasEngine components are compiled by @canvasengine/compiler.
|
|
6
|
-
import TargetingOverlayComponent from "./components/targeting-overlay.ce";
|
|
7
|
-
// @ts-ignore CanvasEngine components are compiled by @canvasengine/compiler.
|
|
8
|
-
import AttackPreviewComponent from "./components/attack-preview.ce";
|
|
9
3
|
import {
|
|
10
4
|
setActionBattleOptions,
|
|
11
5
|
startAttackPreview,
|
|
@@ -13,8 +7,22 @@ import {
|
|
|
13
7
|
} from "./ui/state";
|
|
14
8
|
import { ActionBattleOptions } from "./types";
|
|
15
9
|
import { normalizeActionBattleOptions } from "./config";
|
|
16
|
-
import { resolveActionBattleAnimation } from "./animations";
|
|
17
10
|
import { getNormalizedActionBattleAttackProfile } from "./core/attack-runtime";
|
|
11
|
+
import {
|
|
12
|
+
applyActionBattleAttackDirection,
|
|
13
|
+
resolveActionBattleAttackDirection,
|
|
14
|
+
} from "./attack-input";
|
|
15
|
+
import {
|
|
16
|
+
forceActionBattleLocomotionAnimation,
|
|
17
|
+
withActionBattleAnimationUnlocked,
|
|
18
|
+
} from "./locomotion";
|
|
19
|
+
import { resolveActionBattleUi } from "./ui";
|
|
20
|
+
import {
|
|
21
|
+
ACTION_BATTLE_HIT_FX_COMPONENT_ID,
|
|
22
|
+
createActionBattleClientVisuals,
|
|
23
|
+
playActionBattleVisual,
|
|
24
|
+
setActionBattlePreviewStarter,
|
|
25
|
+
} from "./visual";
|
|
18
26
|
|
|
19
27
|
const DEFAULT_ATTACK_LOCK_DURATION_MS = 350;
|
|
20
28
|
|
|
@@ -64,6 +72,9 @@ const beginLocalPlayerAttackLock = (
|
|
|
64
72
|
player.canMove = previousCanMove;
|
|
65
73
|
player.directionFixed = previousDirectionFixed;
|
|
66
74
|
player.animationFixed = previousAnimationFixed;
|
|
75
|
+
if (locks.movement && !previousAnimationFixed) {
|
|
76
|
+
forceActionBattleLocomotionAnimation(player, "stand");
|
|
77
|
+
}
|
|
67
78
|
}, durationMs);
|
|
68
79
|
|
|
69
80
|
return true;
|
|
@@ -79,35 +90,21 @@ const playLocalPlayerAttackAnimation = (
|
|
|
79
90
|
player: any,
|
|
80
91
|
options: ActionBattleOptions
|
|
81
92
|
) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
);
|
|
88
|
-
if (!animation) return;
|
|
89
|
-
|
|
90
|
-
if (animation.graphic !== undefined) {
|
|
91
|
-
player.setAnimation(
|
|
92
|
-
animation.animationName,
|
|
93
|
-
animation.graphic,
|
|
94
|
-
animation.repeat
|
|
95
|
-
);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
player.setAnimation(animation.animationName, animation.repeat);
|
|
93
|
+
withActionBattleAnimationUnlocked(player, () => {
|
|
94
|
+
playActionBattleVisual(options.visual, {
|
|
95
|
+
moment: "attack",
|
|
96
|
+
entity: player,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
const showLocalAttackPreview = (player: any, options: ActionBattleOptions) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
color: options.attack?.previewColor,
|
|
108
|
-
accentColor: options.attack?.previewAccentColor,
|
|
102
|
+
const attackPreview = options.ui?.attackPreview as any;
|
|
103
|
+
if (!player || attackPreview?.enabled === false) return;
|
|
104
|
+
playActionBattleVisual(options.visual, {
|
|
105
|
+
moment: "preview",
|
|
106
|
+
entity: player,
|
|
109
107
|
});
|
|
110
|
-
setTimeout(() => stopAttackPreview(previewId), durationMs);
|
|
111
108
|
};
|
|
112
109
|
|
|
113
110
|
export const createActionBattleClient = (
|
|
@@ -115,60 +112,76 @@ export const createActionBattleClient = (
|
|
|
115
112
|
) => {
|
|
116
113
|
const normalized = normalizeActionBattleOptions(options);
|
|
117
114
|
setActionBattleOptions(normalized);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
setActionBattlePreviewStarter((entity, previewOptions = {}) => {
|
|
116
|
+
const direction = previewOptions.direction ?? resolveLocalPlayerDirection(entity);
|
|
117
|
+
const durationMs = Math.max(
|
|
118
|
+
1,
|
|
119
|
+
previewOptions.durationMs ?? normalized.attack?.previewDurationMs ?? 180
|
|
120
|
+
);
|
|
121
|
+
const previewId = startAttackPreview({
|
|
122
|
+
direction,
|
|
123
|
+
durationMs,
|
|
124
|
+
color: previewOptions.color ?? normalized.attack?.previewColor,
|
|
125
|
+
accentColor:
|
|
126
|
+
previewOptions.accentColor ?? normalized.attack?.previewAccentColor,
|
|
127
|
+
});
|
|
128
|
+
setTimeout(() => stopAttackPreview(previewId), durationMs);
|
|
129
|
+
});
|
|
130
|
+
const resolvedUi = resolveActionBattleUi(normalized.ui);
|
|
131
|
+
const actionBarEnabled = resolvedUi.actionBar.enabled;
|
|
124
132
|
const hitComponent = PrebuiltComponentAnimations?.Hit;
|
|
133
|
+
const fxComponent = PrebuiltComponentAnimations?.Fx;
|
|
125
134
|
return defineModule<RpgClient>({
|
|
126
|
-
componentAnimations:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const engine = inject(RpgClientEngine)
|
|
141
|
-
return [engine.scene.currentPlayer]
|
|
135
|
+
componentAnimations: [
|
|
136
|
+
...(hitComponent
|
|
137
|
+
? [
|
|
138
|
+
{
|
|
139
|
+
id: "hit",
|
|
140
|
+
component: hitComponent,
|
|
141
|
+
},
|
|
142
|
+
]
|
|
143
|
+
: []),
|
|
144
|
+
...(fxComponent
|
|
145
|
+
? [
|
|
146
|
+
{
|
|
147
|
+
id: ACTION_BATTLE_HIT_FX_COMPONENT_ID,
|
|
148
|
+
component: fxComponent,
|
|
142
149
|
},
|
|
143
|
-
|
|
144
|
-
]
|
|
145
|
-
|
|
150
|
+
]
|
|
151
|
+
: []),
|
|
152
|
+
],
|
|
153
|
+
clientVisuals: createActionBattleClientVisuals(normalized),
|
|
154
|
+
gui: resolvedUi.gui,
|
|
146
155
|
sprite: {
|
|
147
|
-
|
|
156
|
+
componentsBehind: resolvedUi.sprite.componentsBehind,
|
|
157
|
+
componentsInFront: resolvedUi.sprite.componentsInFront,
|
|
148
158
|
},
|
|
149
159
|
sceneMap: {
|
|
150
160
|
onAfterLoading() {
|
|
151
|
-
if (actionBarEnabled &&
|
|
161
|
+
if (actionBarEnabled && resolvedUi.actionBar.autoOpen) {
|
|
152
162
|
const gui = inject(RpgGui)
|
|
153
163
|
gui.display('action-battle-action-bar')
|
|
154
164
|
}
|
|
155
165
|
}
|
|
156
166
|
},
|
|
157
167
|
engine: {
|
|
158
|
-
onInput(engine: RpgClientEngine, { input }:
|
|
168
|
+
onInput(engine: RpgClientEngine, { input, data }: any) {
|
|
159
169
|
if (input !== "action") return;
|
|
160
170
|
const player = engine.scene?.getCurrentPlayer?.() as any;
|
|
161
171
|
if (!player) return;
|
|
172
|
+
const direction = resolveActionBattleAttackDirection(player, { data });
|
|
173
|
+
applyActionBattleAttackDirection(player, direction);
|
|
162
174
|
const attackProfile = getNormalizedActionBattleAttackProfile(normalized);
|
|
163
175
|
const lockDurationMs = Math.max(
|
|
164
176
|
0,
|
|
165
177
|
attackProfile.totalDurationMs ?? DEFAULT_ATTACK_LOCK_DURATION_MS
|
|
166
178
|
);
|
|
167
179
|
if (attackProfile.movementLock || attackProfile.directionLock) {
|
|
168
|
-
beginLocalPlayerAttackLock(engine, lockDurationMs, {
|
|
180
|
+
const locked = beginLocalPlayerAttackLock(engine, lockDurationMs, {
|
|
169
181
|
movement: attackProfile.movementLock,
|
|
170
182
|
direction: attackProfile.directionLock,
|
|
171
183
|
});
|
|
184
|
+
if (!locked) return;
|
|
172
185
|
}
|
|
173
186
|
playLocalPlayerAttackAnimation(player, normalized);
|
|
174
187
|
showLocalAttackPreview(player, normalized);
|
package/src/config.ts
CHANGED
|
@@ -42,68 +42,115 @@ let currentActionBattleOptions: ActionBattleOptions =
|
|
|
42
42
|
export function normalizeActionBattleOptions(
|
|
43
43
|
options: ActionBattleOptions = {}
|
|
44
44
|
): ActionBattleOptions {
|
|
45
|
+
const combat = {
|
|
46
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat,
|
|
47
|
+
...options.systems?.combat,
|
|
48
|
+
...options.combat,
|
|
49
|
+
hooks: {
|
|
50
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat?.hooks,
|
|
51
|
+
...options.systems?.combat?.hooks,
|
|
52
|
+
...options.combat?.hooks,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
45
55
|
const attack = {
|
|
46
56
|
...DEFAULT_ACTION_BATTLE_OPTIONS.attack,
|
|
47
57
|
...options.attack,
|
|
58
|
+
...combat.attack,
|
|
48
59
|
};
|
|
49
60
|
const attackProfile = normalizeActionBattleAttackProfile(attack.profile, {
|
|
50
61
|
lockMovement: attack.lockMovement,
|
|
51
62
|
lockDurationMs: attack.lockDurationMs,
|
|
52
63
|
hitboxes: attack.hitboxes,
|
|
53
64
|
});
|
|
65
|
+
const normalizedAttack = {
|
|
66
|
+
...attack,
|
|
67
|
+
profile: attackProfile,
|
|
68
|
+
};
|
|
69
|
+
const skills = {
|
|
70
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.skills,
|
|
71
|
+
...options.skills,
|
|
72
|
+
};
|
|
73
|
+
skills.targeting = skills.targeting ?? skills.getTargeting;
|
|
74
|
+
skills.getTargeting = skills.getTargeting ?? skills.targeting;
|
|
75
|
+
|
|
76
|
+
const defaultActionBar = DEFAULT_ACTION_BATTLE_OPTIONS.ui?.actionBar as any;
|
|
77
|
+
const defaultTargeting = DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting as any;
|
|
78
|
+
const optionActionBar = options.ui?.actionBar as any;
|
|
79
|
+
const optionTargeting = options.ui?.targeting as any;
|
|
80
|
+
const optionAttackPreview = options.ui?.attackPreview as any;
|
|
81
|
+
const actionBar =
|
|
82
|
+
options.ui?.actionBar === false
|
|
83
|
+
? { ...defaultActionBar, enabled: false }
|
|
84
|
+
: {
|
|
85
|
+
...defaultActionBar,
|
|
86
|
+
...(options.ui?.actionBar === true ? { enabled: true } : optionActionBar),
|
|
87
|
+
};
|
|
88
|
+
const legacyPreviewEnabled = normalizedAttack.showPreview !== false;
|
|
89
|
+
const attackPreview =
|
|
90
|
+
options.ui?.attackPreview === false
|
|
91
|
+
? { enabled: false }
|
|
92
|
+
: {
|
|
93
|
+
enabled: options.ui?.attackPreview === true ? true : legacyPreviewEnabled,
|
|
94
|
+
...(options.ui?.attackPreview === true ? {} : optionAttackPreview),
|
|
95
|
+
};
|
|
96
|
+
const targeting =
|
|
97
|
+
options.ui?.targeting === false
|
|
98
|
+
? { ...defaultTargeting, enabled: false }
|
|
99
|
+
: {
|
|
100
|
+
...defaultTargeting,
|
|
101
|
+
...(options.ui?.targeting === true ? { enabled: true } : optionTargeting),
|
|
102
|
+
colors: {
|
|
103
|
+
...defaultTargeting?.colors,
|
|
104
|
+
...(typeof options.ui?.targeting === "object"
|
|
105
|
+
? optionTargeting?.colors
|
|
106
|
+
: undefined),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const ai = {
|
|
110
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai,
|
|
111
|
+
...options.systems?.ai,
|
|
112
|
+
...options.ai,
|
|
113
|
+
behaviors: {
|
|
114
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai?.behaviors,
|
|
115
|
+
...options.systems?.ai?.behaviors,
|
|
116
|
+
...options.ai?.behaviors,
|
|
117
|
+
},
|
|
118
|
+
presets: {
|
|
119
|
+
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai?.presets,
|
|
120
|
+
...options.systems?.ai?.presets,
|
|
121
|
+
...options.ai?.presets,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
54
124
|
|
|
55
125
|
return {
|
|
56
126
|
ui: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
targeting: {
|
|
62
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting,
|
|
63
|
-
...options.ui?.targeting,
|
|
64
|
-
colors: {
|
|
65
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting?.colors,
|
|
66
|
-
...options.ui?.targeting?.colors,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
skills: {
|
|
71
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.skills,
|
|
72
|
-
...options.skills,
|
|
127
|
+
...options.ui,
|
|
128
|
+
actionBar,
|
|
129
|
+
targeting,
|
|
130
|
+
attackPreview,
|
|
73
131
|
},
|
|
132
|
+
skills,
|
|
74
133
|
targeting: {
|
|
75
134
|
...DEFAULT_ACTION_BATTLE_OPTIONS.targeting,
|
|
76
135
|
...options.targeting,
|
|
77
136
|
},
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
...
|
|
81
|
-
|
|
82
|
-
attack: {
|
|
83
|
-
...attack,
|
|
84
|
-
profile: attackProfile,
|
|
137
|
+
attack: normalizedAttack,
|
|
138
|
+
combat: {
|
|
139
|
+
...combat,
|
|
140
|
+
attack: normalizedAttack,
|
|
85
141
|
},
|
|
142
|
+
ai,
|
|
143
|
+
visual: options.visual,
|
|
86
144
|
animations: {
|
|
87
145
|
...DEFAULT_ACTION_BATTLE_OPTIONS.animations,
|
|
88
146
|
...options.animations,
|
|
89
147
|
},
|
|
90
148
|
systems: {
|
|
91
149
|
combat: {
|
|
92
|
-
...
|
|
93
|
-
|
|
94
|
-
hooks: {
|
|
95
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat?.hooks,
|
|
96
|
-
...options.systems?.combat?.hooks,
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
ai: {
|
|
100
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai,
|
|
101
|
-
...options.systems?.ai,
|
|
102
|
-
behaviors: {
|
|
103
|
-
...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai?.behaviors,
|
|
104
|
-
...options.systems?.ai?.behaviors,
|
|
105
|
-
},
|
|
150
|
+
...combat,
|
|
151
|
+
attack: normalizedAttack,
|
|
106
152
|
},
|
|
153
|
+
ai,
|
|
107
154
|
},
|
|
108
155
|
};
|
|
109
156
|
}
|