@rpgjs/action-battle 5.0.0-beta.5 → 5.0.0-beta.6
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/README.md +115 -0
- package/dist/ai.server.d.ts +17 -2
- package/dist/client/index.js +13 -8
- package/dist/client/index10.js +54 -136
- package/dist/client/index11.js +52 -23
- package/dist/client/index12.js +101 -1217
- package/dist/client/index13.js +139 -42
- package/dist/client/index14.js +23 -8
- package/dist/client/index15.js +68 -444
- package/dist/client/index16.js +1281 -0
- package/dist/client/index17.js +13 -0
- package/dist/client/index18.js +60 -0
- package/dist/client/index19.js +10 -0
- package/dist/client/index2.js +25 -87
- package/dist/client/index20.js +504 -0
- package/dist/client/index3.js +45 -83
- package/dist/client/index4.js +98 -297
- package/dist/client/index5.js +81 -33
- package/dist/client/index6.js +284 -78
- package/dist/client/index7.js +33 -74
- package/dist/client/index8.js +95 -55
- package/dist/client/index9.js +75 -96
- package/dist/core/attack-profile.d.ts +9 -0
- package/dist/core/attack-runtime.d.ts +20 -0
- package/dist/core/enemy-attack-profiles.d.ts +6 -0
- package/dist/core/equipment.d.ts +2 -0
- package/dist/core/hit-reaction.d.ts +5 -0
- package/dist/index.d.ts +6 -1
- package/dist/server/index.js +12 -7
- package/dist/server/index10.js +1278 -8
- package/dist/server/index11.js +37 -0
- package/dist/server/index12.js +60 -0
- package/dist/server/index13.js +13 -0
- package/dist/server/index14.js +503 -0
- package/dist/server/index15.js +10 -0
- package/dist/server/index3.js +25 -87
- package/dist/server/index4.js +45 -141
- package/dist/server/index5.js +104 -21
- package/dist/server/index6.js +137 -1215
- package/dist/server/index7.js +22 -34
- package/dist/server/index8.js +70 -44
- package/dist/server/index9.js +44 -437
- package/dist/server.d.ts +7 -1
- package/package.json +5 -5
- package/src/ai.server.ts +172 -43
- package/src/client.ts +21 -12
- package/src/config.ts +17 -2
- package/src/core/attack-profile.spec.ts +118 -0
- package/src/core/attack-profile.ts +100 -0
- package/src/core/attack-runtime.spec.ts +103 -0
- package/src/core/attack-runtime.ts +83 -0
- package/src/core/contracts.ts +3 -0
- package/src/core/enemy-attack-profiles.spec.ts +35 -0
- package/src/core/enemy-attack-profiles.ts +103 -0
- package/src/core/equipment.spec.ts +37 -0
- package/src/core/equipment.ts +17 -0
- package/src/core/hit-reaction.spec.ts +43 -0
- package/src/core/hit-reaction.ts +70 -0
- package/src/core/hit.spec.ts +54 -1
- package/src/core/hit.ts +26 -0
- package/src/index.ts +36 -0
- package/src/server.ts +180 -33
- package/src/types.ts +62 -6
package/src/core/hit.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ActionBattleCombatSystem, ActionBattleHitContext, ActionBattleHitResult } from "./contracts";
|
|
2
|
+
import {
|
|
3
|
+
isActionBattleEntityInvincible,
|
|
4
|
+
setActionBattleInvincibility,
|
|
5
|
+
} from "./hit-reaction";
|
|
2
6
|
|
|
3
7
|
export const applyActionBattleHit = (
|
|
4
8
|
system: ActionBattleCombatSystem,
|
|
@@ -20,6 +24,20 @@ export const applyActionBattleHit = (
|
|
|
20
24
|
}
|
|
21
25
|
if (before) hitContext = before;
|
|
22
26
|
|
|
27
|
+
if (isActionBattleEntityInvincible(hitContext.target)) {
|
|
28
|
+
return {
|
|
29
|
+
damage: 0,
|
|
30
|
+
knockbackForce: 0,
|
|
31
|
+
knockbackDuration: 0,
|
|
32
|
+
defeated: false,
|
|
33
|
+
attacker: hitContext.attacker,
|
|
34
|
+
target: hitContext.target,
|
|
35
|
+
cancelled: true,
|
|
36
|
+
metadata: hitContext.metadata,
|
|
37
|
+
reaction: hitContext.reaction,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
const damage =
|
|
24
42
|
hitContext.damage ??
|
|
25
43
|
system.resolveDamage({
|
|
@@ -50,6 +68,13 @@ export const applyActionBattleHit = (
|
|
|
50
68
|
);
|
|
51
69
|
}
|
|
52
70
|
|
|
71
|
+
if (!damage.defeated && hitContext.reaction?.invincibilityMs) {
|
|
72
|
+
setActionBattleInvincibility(
|
|
73
|
+
hitContext.target,
|
|
74
|
+
hitContext.reaction.invincibilityMs
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
53
78
|
const result: ActionBattleHitResult = {
|
|
54
79
|
damage: damage.damage,
|
|
55
80
|
knockbackForce: knockback.force,
|
|
@@ -58,6 +83,7 @@ export const applyActionBattleHit = (
|
|
|
58
83
|
attacker: hitContext.attacker,
|
|
59
84
|
target: hitContext.target,
|
|
60
85
|
rawDamage: damage.raw,
|
|
86
|
+
reaction: hitContext.reaction,
|
|
61
87
|
metadata: hitContext.metadata,
|
|
62
88
|
};
|
|
63
89
|
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,15 @@ export type {
|
|
|
25
25
|
ActionBattleUiOptions,
|
|
26
26
|
ActionBattleUiActionBarOptions,
|
|
27
27
|
ActionBattleUiTargetingOptions,
|
|
28
|
+
ActionBattleAttackDirection,
|
|
29
|
+
ActionBattleAttackHitboxConfig,
|
|
30
|
+
ActionBattleAttackHitboxMap,
|
|
31
|
+
ActionBattleAttackHitPolicy,
|
|
32
|
+
ActionBattleAttackProfile,
|
|
33
|
+
ActionBattleDebugOptions,
|
|
34
|
+
ActionBattleHitReactionProfile,
|
|
35
|
+
NormalizedActionBattleHitReactionProfile,
|
|
36
|
+
NormalizedActionBattleAttackProfile,
|
|
28
37
|
ActionBattleCombatOptions,
|
|
29
38
|
ActionBattleSystemOptions,
|
|
30
39
|
ActionBattleAiSystemOptions,
|
|
@@ -47,6 +56,33 @@ export type {
|
|
|
47
56
|
ActionBattleKnockbackResult,
|
|
48
57
|
ActionBattleSystems,
|
|
49
58
|
} from "./core/contracts";
|
|
59
|
+
export {
|
|
60
|
+
DEFAULT_ACTION_BATTLE_ATTACK_PROFILE,
|
|
61
|
+
normalizeActionBattleAttackProfile,
|
|
62
|
+
type ActionBattleAttackProfileFallbacks,
|
|
63
|
+
} from "./core/attack-profile";
|
|
64
|
+
export {
|
|
65
|
+
ACTION_BATTLE_HITBOX_FRAME_MS,
|
|
66
|
+
ActionBattleHitTracker,
|
|
67
|
+
createActionBattleAttackId,
|
|
68
|
+
getNormalizedActionBattleAttackProfile,
|
|
69
|
+
resolveActionBattleHitboxSpeed,
|
|
70
|
+
scheduleActionBattleStartup,
|
|
71
|
+
} from "./core/attack-runtime";
|
|
72
|
+
export {
|
|
73
|
+
DEFAULT_ACTION_BATTLE_HIT_REACTION,
|
|
74
|
+
isActionBattleEntityInvincible,
|
|
75
|
+
normalizeActionBattleHitReaction,
|
|
76
|
+
setActionBattleInvincibility,
|
|
77
|
+
} from "./core/hit-reaction";
|
|
78
|
+
export {
|
|
79
|
+
DEFAULT_ACTION_BATTLE_ENEMY_ATTACK_PROFILES,
|
|
80
|
+
normalizeActionBattleEnemyAttackProfiles,
|
|
81
|
+
type ActionBattleEnemyAttackProfileKey,
|
|
82
|
+
type ActionBattleEnemyAttackProfileMap,
|
|
83
|
+
type NormalizedActionBattleEnemyAttackProfileMap,
|
|
84
|
+
} from "./core/enemy-attack-profiles";
|
|
85
|
+
export { resolveActionBattleWeaponAttackProfile } from "./core/equipment";
|
|
50
86
|
export {
|
|
51
87
|
DEFAULT_ZELDA_PLAYER_HITBOXES,
|
|
52
88
|
createDefaultPlayerHitboxResolver,
|
package/src/server.ts
CHANGED
|
@@ -12,7 +12,20 @@ import { playActionBattleAnimation } from "./animations";
|
|
|
12
12
|
import { getActionBattleSystems, setActionBattleSystems } from "./core/context";
|
|
13
13
|
import { applyActionBattleHit } from "./core/hit";
|
|
14
14
|
import { DEFAULT_ZELDA_PLAYER_HITBOXES } from "./core/defaults";
|
|
15
|
+
import {
|
|
16
|
+
ActionBattleHitTracker,
|
|
17
|
+
createActionBattleAttackId,
|
|
18
|
+
getNormalizedActionBattleAttackProfile,
|
|
19
|
+
resolveActionBattleHitboxSpeed,
|
|
20
|
+
scheduleActionBattleStartup,
|
|
21
|
+
} from "./core/attack-runtime";
|
|
22
|
+
import { normalizeActionBattleAttackProfile } from "./core/attack-profile";
|
|
23
|
+
import { resolveActionBattleWeaponAttackProfile } from "./core/equipment";
|
|
15
24
|
import type { ActionBattleHitbox } from "./core/contracts";
|
|
25
|
+
import type {
|
|
26
|
+
ActionBattleAttackProfile,
|
|
27
|
+
NormalizedActionBattleAttackProfile,
|
|
28
|
+
} from "./types";
|
|
16
29
|
|
|
17
30
|
export const ACTION_BATTLE_ACTION_BAR_GUI_ID = "action-battle-action-bar";
|
|
18
31
|
const DEFAULT_ATTACK_LOCK_DURATION_MS = 350;
|
|
@@ -31,7 +44,8 @@ export const DEFAULT_PLAYER_ATTACK_HITBOXES = {
|
|
|
31
44
|
const beginPlayerAttackLock = (
|
|
32
45
|
player: RpgPlayer,
|
|
33
46
|
map: ReturnType<RpgPlayer["getCurrentMap"]> | undefined,
|
|
34
|
-
durationMs: number
|
|
47
|
+
durationMs: number,
|
|
48
|
+
locks: { movement: boolean; direction: boolean }
|
|
35
49
|
): boolean => {
|
|
36
50
|
if (durationMs <= 0) return true;
|
|
37
51
|
|
|
@@ -53,11 +67,15 @@ const beginPlayerAttackLock = (
|
|
|
53
67
|
const previousDirectionFixed = player.directionFixed;
|
|
54
68
|
const previousAnimationFixed = player.animationFixed;
|
|
55
69
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
if (locks.movement) {
|
|
71
|
+
player.pendingInputs = [];
|
|
72
|
+
player.lastProcessedInputTs = 0;
|
|
73
|
+
(map as any)?.stopMovement?.(player);
|
|
74
|
+
player.canMove.set(false);
|
|
75
|
+
}
|
|
76
|
+
if (locks.direction) {
|
|
77
|
+
player.directionFixed = true;
|
|
78
|
+
}
|
|
61
79
|
|
|
62
80
|
setTimeout(() => {
|
|
63
81
|
if (runtimePlayer.__actionBattleAttackLockId !== lockId) return;
|
|
@@ -115,6 +133,16 @@ const getVisibleActionEvents = (
|
|
|
115
133
|
collisions.forEach((id: string) => addEvent(map.getEvent(id)));
|
|
116
134
|
}
|
|
117
135
|
|
|
136
|
+
const direction =
|
|
137
|
+
typeof player.getDirection === "function" ? player.getDirection() : undefined;
|
|
138
|
+
const interactionCollisions = (map as any).getInteractionCollisions?.(
|
|
139
|
+
player.id,
|
|
140
|
+
direction
|
|
141
|
+
);
|
|
142
|
+
if (Array.isArray(interactionCollisions)) {
|
|
143
|
+
interactionCollisions.forEach((id: string) => addEvent(map.getEvent(id)));
|
|
144
|
+
}
|
|
145
|
+
|
|
118
146
|
for (const event of map.getEvents()) {
|
|
119
147
|
const rect = eventRect(event);
|
|
120
148
|
if (hitboxes.some((hitbox) => rectsOverlap(hitbox, rect))) {
|
|
@@ -200,7 +228,8 @@ export function getPlayerWeaponKnockbackForce(player: RpgPlayer): number {
|
|
|
200
228
|
export function applyPlayerHitToEvent(
|
|
201
229
|
player: RpgPlayer,
|
|
202
230
|
target: RpgEvent,
|
|
203
|
-
hooks?: ApplyHitHooks
|
|
231
|
+
hooks?: ApplyHitHooks,
|
|
232
|
+
metadata?: Record<string, any>
|
|
204
233
|
): HitResult | undefined {
|
|
205
234
|
const ai = (target as any).battleAi as BattleAi;
|
|
206
235
|
if (!ai) return undefined;
|
|
@@ -243,6 +272,8 @@ export function applyPlayerHitToEvent(
|
|
|
243
272
|
{
|
|
244
273
|
attacker: player,
|
|
245
274
|
target,
|
|
275
|
+
metadata,
|
|
276
|
+
reaction: metadata?.reaction,
|
|
246
277
|
}
|
|
247
278
|
);
|
|
248
279
|
|
|
@@ -251,6 +282,7 @@ export function applyPlayerHitToEvent(
|
|
|
251
282
|
damage: result.damage,
|
|
252
283
|
defeated: result.defeated,
|
|
253
284
|
raw: result.rawDamage,
|
|
285
|
+
reaction: result.reaction,
|
|
254
286
|
});
|
|
255
287
|
}
|
|
256
288
|
|
|
@@ -269,11 +301,13 @@ const toLegacyHitResult = (context: any): HitResult => ({
|
|
|
269
301
|
const resolvePlayerAttackHitboxes = (
|
|
270
302
|
player: RpgPlayer,
|
|
271
303
|
directionKey: string,
|
|
272
|
-
options: ActionBattleOptions
|
|
304
|
+
options: ActionBattleOptions,
|
|
305
|
+
profile: NormalizedActionBattleAttackProfile
|
|
273
306
|
): ActionBattleHitbox[] => {
|
|
274
307
|
const configuredHitboxes = {
|
|
275
308
|
...DEFAULT_PLAYER_ATTACK_HITBOXES,
|
|
276
309
|
...options.attack?.hitboxes,
|
|
310
|
+
...profile.hitboxes,
|
|
277
311
|
};
|
|
278
312
|
const hitboxConfig =
|
|
279
313
|
configuredHitboxes[
|
|
@@ -296,6 +330,39 @@ const resolvePlayerAttackHitboxes = (
|
|
|
296
330
|
);
|
|
297
331
|
};
|
|
298
332
|
|
|
333
|
+
const mergeAttackProfileOverrides = (
|
|
334
|
+
base: NormalizedActionBattleAttackProfile,
|
|
335
|
+
override: ActionBattleAttackProfile
|
|
336
|
+
): ActionBattleAttackProfile => ({
|
|
337
|
+
...base,
|
|
338
|
+
...override,
|
|
339
|
+
reaction: {
|
|
340
|
+
...base.reaction,
|
|
341
|
+
...override.reaction,
|
|
342
|
+
},
|
|
343
|
+
hitboxes: {
|
|
344
|
+
...base.hitboxes,
|
|
345
|
+
...override.hitboxes,
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const resolvePlayerAttackProfile = (
|
|
350
|
+
player: RpgPlayer,
|
|
351
|
+
options: ActionBattleOptions
|
|
352
|
+
): NormalizedActionBattleAttackProfile => {
|
|
353
|
+
const baseProfile = getNormalizedActionBattleAttackProfile(options);
|
|
354
|
+
const weaponProfile = resolveActionBattleWeaponAttackProfile(player);
|
|
355
|
+
if (!weaponProfile) return baseProfile;
|
|
356
|
+
return normalizeActionBattleAttackProfile(
|
|
357
|
+
mergeAttackProfileOverrides(baseProfile, weaponProfile),
|
|
358
|
+
{
|
|
359
|
+
lockMovement: options.attack?.lockMovement,
|
|
360
|
+
lockDurationMs: options.attack?.lockDurationMs,
|
|
361
|
+
hitboxes: options.attack?.hitboxes,
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
299
366
|
const resolveSignal = (value: any) =>
|
|
300
367
|
typeof value === "function" ? value() : value;
|
|
301
368
|
|
|
@@ -423,7 +490,7 @@ const ensureActionBarGui = (
|
|
|
423
490
|
const gui = existing || player.gui(ACTION_BATTLE_ACTION_BAR_GUI_ID);
|
|
424
491
|
if (!(gui as any).__actionBattleReady) {
|
|
425
492
|
(gui as any).__actionBattleReady = true;
|
|
426
|
-
gui.on("useItem", ({ id }) => {
|
|
493
|
+
gui.on("useItem", ({ id }: { id: string }) => {
|
|
427
494
|
try {
|
|
428
495
|
player.useItem(id);
|
|
429
496
|
} catch {
|
|
@@ -431,10 +498,13 @@ const ensureActionBarGui = (
|
|
|
431
498
|
}
|
|
432
499
|
gui.update(buildActionBarData(player, options));
|
|
433
500
|
});
|
|
434
|
-
gui.on(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
501
|
+
gui.on(
|
|
502
|
+
"useSkill",
|
|
503
|
+
({ id, target }: { id: string; target?: { x: number; y: number } }) => {
|
|
504
|
+
handleActionBattleSkillUse(player, id, target, options);
|
|
505
|
+
gui.update(buildActionBarData(player, options));
|
|
506
|
+
}
|
|
507
|
+
);
|
|
438
508
|
gui.on("refresh", () => {
|
|
439
509
|
gui.update(buildActionBarData(player, options));
|
|
440
510
|
});
|
|
@@ -523,7 +593,7 @@ const handleActionBattleSkillUse = (
|
|
|
523
593
|
const targets: any[] = [];
|
|
524
594
|
const affects = options.targeting?.affects || "events";
|
|
525
595
|
if (affects === "events" || affects === "both") {
|
|
526
|
-
map.getEvents().forEach((event) => {
|
|
596
|
+
map.getEvents().forEach((event: RpgEvent) => {
|
|
527
597
|
const tile = getEntityTile(event, tileSize);
|
|
528
598
|
if (affected.has(`${tile.x},${tile.y}`)) {
|
|
529
599
|
targets.push(event);
|
|
@@ -531,7 +601,7 @@ const handleActionBattleSkillUse = (
|
|
|
531
601
|
});
|
|
532
602
|
}
|
|
533
603
|
if (affects === "players" || affects === "both") {
|
|
534
|
-
map.getPlayers().forEach((other) => {
|
|
604
|
+
map.getPlayers().forEach((other: RpgPlayer) => {
|
|
535
605
|
if (other.id === player.id) return;
|
|
536
606
|
const tile = getEntityTile(other, tileSize);
|
|
537
607
|
if (affected.has(`${tile.x},${tile.y}`)) {
|
|
@@ -574,6 +644,7 @@ export const createActionBattleServer = (
|
|
|
574
644
|
if (input.action == Control.Action) {
|
|
575
645
|
const map = player.getCurrentMap();
|
|
576
646
|
const direction = player.getDirection();
|
|
647
|
+
const attackProfile = resolvePlayerAttackProfile(player, options);
|
|
577
648
|
|
|
578
649
|
// Convert Direction enum to string key
|
|
579
650
|
const directionKey = direction as string;
|
|
@@ -581,42 +652,80 @@ export const createActionBattleServer = (
|
|
|
581
652
|
const hitboxes = resolvePlayerAttackHitboxes(
|
|
582
653
|
player,
|
|
583
654
|
directionKey,
|
|
584
|
-
options
|
|
655
|
+
options,
|
|
656
|
+
attackProfile
|
|
585
657
|
);
|
|
586
658
|
|
|
587
659
|
if (isActionReservedForNormalEvent(player, map, hitboxes)) {
|
|
588
660
|
return;
|
|
589
661
|
}
|
|
590
662
|
|
|
591
|
-
const lockMovement =
|
|
663
|
+
const lockMovement = attackProfile.movementLock;
|
|
664
|
+
const lockDirection = attackProfile.directionLock;
|
|
592
665
|
const lockDurationMs =
|
|
593
|
-
|
|
594
|
-
|
|
666
|
+
attackProfile.totalDurationMs ?? DEFAULT_ATTACK_LOCK_DURATION_MS;
|
|
667
|
+
const actionLocked = (lockMovement || lockDirection) && lockDurationMs > 0;
|
|
595
668
|
|
|
596
669
|
if (
|
|
597
|
-
|
|
598
|
-
!beginPlayerAttackLock(player, map, Math.max(0, lockDurationMs)
|
|
670
|
+
actionLocked &&
|
|
671
|
+
!beginPlayerAttackLock(player, map, Math.max(0, lockDurationMs), {
|
|
672
|
+
movement: lockMovement,
|
|
673
|
+
direction: lockDirection,
|
|
674
|
+
})
|
|
599
675
|
) {
|
|
600
676
|
return;
|
|
601
677
|
}
|
|
602
|
-
movementLocked = lockMovement && lockDurationMs > 0;
|
|
603
678
|
|
|
604
679
|
playActionBattleAnimation("attack", player, options.animations);
|
|
605
|
-
if (
|
|
680
|
+
if (actionLocked) {
|
|
606
681
|
player.animationFixed = true;
|
|
607
682
|
}
|
|
683
|
+
const attackId = createActionBattleAttackId(
|
|
684
|
+
player.id,
|
|
685
|
+
attackProfile.id
|
|
686
|
+
);
|
|
687
|
+
const hitTracker = new ActionBattleHitTracker(
|
|
688
|
+
attackProfile.hitPolicy
|
|
689
|
+
);
|
|
690
|
+
if (options.debug?.attacks) {
|
|
691
|
+
console.log("[ActionBattle] player attack", {
|
|
692
|
+
attackId,
|
|
693
|
+
playerId: player.id,
|
|
694
|
+
profile: attackProfile.id,
|
|
695
|
+
hitboxes,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
608
698
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
699
|
+
scheduleActionBattleStartup(attackProfile, () => {
|
|
700
|
+
map
|
|
701
|
+
?.createMovingHitbox(hitboxes, {
|
|
702
|
+
speed: resolveActionBattleHitboxSpeed(
|
|
703
|
+
attackProfile,
|
|
704
|
+
hitboxes.length
|
|
705
|
+
),
|
|
706
|
+
})
|
|
707
|
+
.subscribe({
|
|
708
|
+
next(hits: any[]) {
|
|
709
|
+
hits.forEach((hit: any) => {
|
|
710
|
+
if (hit instanceof RpgEvent) {
|
|
711
|
+
if (!hitTracker.tryHit(hit)) return;
|
|
712
|
+
const result = applyPlayerHitToEvent(
|
|
713
|
+
player,
|
|
714
|
+
hit,
|
|
715
|
+
undefined,
|
|
716
|
+
{
|
|
717
|
+
attackId,
|
|
718
|
+
attackProfileId: attackProfile.id,
|
|
719
|
+
reaction: attackProfile.reaction,
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
if (result?.defeated) {
|
|
723
|
+
console.log(`Player ${player.id} defeated AI ${hit.id}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
},
|
|
618
728
|
});
|
|
619
|
-
},
|
|
620
729
|
});
|
|
621
730
|
}
|
|
622
731
|
},
|
|
@@ -662,6 +771,44 @@ export const createActionBattleServer = (
|
|
|
662
771
|
|
|
663
772
|
export default createActionBattleServer();
|
|
664
773
|
|
|
774
|
+
export {
|
|
775
|
+
ACTION_BATTLE_HITBOX_FRAME_MS,
|
|
776
|
+
ActionBattleHitTracker,
|
|
777
|
+
createActionBattleAttackId,
|
|
778
|
+
getNormalizedActionBattleAttackProfile,
|
|
779
|
+
resolveActionBattleHitboxSpeed,
|
|
780
|
+
scheduleActionBattleStartup,
|
|
781
|
+
} from "./core/attack-runtime";
|
|
782
|
+
export {
|
|
783
|
+
DEFAULT_ACTION_BATTLE_ATTACK_PROFILE,
|
|
784
|
+
normalizeActionBattleAttackProfile,
|
|
785
|
+
type ActionBattleAttackProfileFallbacks,
|
|
786
|
+
} from "./core/attack-profile";
|
|
787
|
+
export type {
|
|
788
|
+
ActionBattleAttackDirection,
|
|
789
|
+
ActionBattleAttackHitboxConfig,
|
|
790
|
+
ActionBattleAttackHitboxMap,
|
|
791
|
+
ActionBattleAttackHitPolicy,
|
|
792
|
+
ActionBattleAttackProfile,
|
|
793
|
+
ActionBattleDebugOptions,
|
|
794
|
+
ActionBattleHitReactionProfile,
|
|
795
|
+
NormalizedActionBattleHitReactionProfile,
|
|
796
|
+
NormalizedActionBattleAttackProfile,
|
|
797
|
+
} from "./types";
|
|
798
|
+
export {
|
|
799
|
+
DEFAULT_ACTION_BATTLE_HIT_REACTION,
|
|
800
|
+
isActionBattleEntityInvincible,
|
|
801
|
+
normalizeActionBattleHitReaction,
|
|
802
|
+
setActionBattleInvincibility,
|
|
803
|
+
} from "./core/hit-reaction";
|
|
804
|
+
export {
|
|
805
|
+
DEFAULT_ACTION_BATTLE_ENEMY_ATTACK_PROFILES,
|
|
806
|
+
normalizeActionBattleEnemyAttackProfiles,
|
|
807
|
+
type ActionBattleEnemyAttackProfileKey,
|
|
808
|
+
type ActionBattleEnemyAttackProfileMap,
|
|
809
|
+
type NormalizedActionBattleEnemyAttackProfileMap,
|
|
810
|
+
} from "./core/enemy-attack-profiles";
|
|
811
|
+
export { resolveActionBattleWeaponAttackProfile } from "./core/equipment";
|
|
665
812
|
export {
|
|
666
813
|
AiDebug,
|
|
667
814
|
AiState,
|
package/src/types.ts
CHANGED
|
@@ -89,6 +89,61 @@ export interface ActionBattleUiOptions {
|
|
|
89
89
|
targeting?: ActionBattleUiTargetingOptions;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
export type ActionBattleAttackDirection =
|
|
93
|
+
| "up"
|
|
94
|
+
| "down"
|
|
95
|
+
| "left"
|
|
96
|
+
| "right"
|
|
97
|
+
| "default";
|
|
98
|
+
|
|
99
|
+
export interface ActionBattleAttackHitboxConfig {
|
|
100
|
+
offsetX: number;
|
|
101
|
+
offsetY: number;
|
|
102
|
+
width: number;
|
|
103
|
+
height: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ActionBattleAttackHitboxMap = Partial<
|
|
107
|
+
Record<ActionBattleAttackDirection, ActionBattleAttackHitboxConfig>
|
|
108
|
+
>;
|
|
109
|
+
|
|
110
|
+
export type ActionBattleAttackHitPolicy =
|
|
111
|
+
| "oncePerTarget"
|
|
112
|
+
| "allowRepeatHits";
|
|
113
|
+
|
|
114
|
+
export interface ActionBattleHitReactionProfile {
|
|
115
|
+
invincibilityMs?: number;
|
|
116
|
+
hitstunMs?: number;
|
|
117
|
+
staggerPower?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface NormalizedActionBattleHitReactionProfile {
|
|
121
|
+
invincibilityMs: number;
|
|
122
|
+
hitstunMs: number;
|
|
123
|
+
staggerPower: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ActionBattleAttackProfile {
|
|
127
|
+
id?: string;
|
|
128
|
+
startupMs?: number;
|
|
129
|
+
activeMs?: number;
|
|
130
|
+
recoveryMs?: number;
|
|
131
|
+
cooldownMs?: number;
|
|
132
|
+
movementLock?: boolean;
|
|
133
|
+
directionLock?: boolean;
|
|
134
|
+
animationKey?: ActionBattleAnimationKey;
|
|
135
|
+
hitPolicy?: ActionBattleAttackHitPolicy;
|
|
136
|
+
reaction?: ActionBattleHitReactionProfile;
|
|
137
|
+
hitboxes?: ActionBattleAttackHitboxMap;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface NormalizedActionBattleAttackProfile
|
|
141
|
+
extends Required<Omit<ActionBattleAttackProfile, "hitboxes" | "reaction">> {
|
|
142
|
+
reaction: NormalizedActionBattleHitReactionProfile;
|
|
143
|
+
hitboxes?: ActionBattleAttackHitboxMap;
|
|
144
|
+
totalDurationMs: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
92
147
|
export interface ActionBattleSkillOptions {
|
|
93
148
|
getTargeting?: ActionBattleSkillTargetingResolver;
|
|
94
149
|
defaultAoeMask?: ActionBattleAoeMask;
|
|
@@ -99,19 +154,19 @@ export interface ActionBattleTargetingOptions {
|
|
|
99
154
|
allowEmptyTarget?: boolean;
|
|
100
155
|
}
|
|
101
156
|
|
|
157
|
+
export interface ActionBattleDebugOptions {
|
|
158
|
+
attacks?: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
102
161
|
export interface ActionBattleAttackOptions {
|
|
162
|
+
profile?: ActionBattleAttackProfile;
|
|
103
163
|
lockMovement?: boolean;
|
|
104
164
|
lockDurationMs?: number;
|
|
105
165
|
showPreview?: boolean;
|
|
106
166
|
previewDurationMs?: number;
|
|
107
167
|
previewColor?: number;
|
|
108
168
|
previewAccentColor?: number;
|
|
109
|
-
hitboxes?:
|
|
110
|
-
Record<
|
|
111
|
-
"up" | "down" | "left" | "right" | "default",
|
|
112
|
-
{ offsetX: number; offsetY: number; width: number; height: number }
|
|
113
|
-
>
|
|
114
|
-
>;
|
|
169
|
+
hitboxes?: ActionBattleAttackHitboxMap;
|
|
115
170
|
resolveHitboxes?: (context: {
|
|
116
171
|
player: any;
|
|
117
172
|
direction: string;
|
|
@@ -139,6 +194,7 @@ export interface ActionBattleOptions {
|
|
|
139
194
|
skills?: ActionBattleSkillOptions;
|
|
140
195
|
targeting?: ActionBattleTargetingOptions;
|
|
141
196
|
attack?: ActionBattleAttackOptions;
|
|
197
|
+
debug?: ActionBattleDebugOptions;
|
|
142
198
|
animations?: ActionBattleAnimationOptions;
|
|
143
199
|
systems?: ActionBattleSystemOptions;
|
|
144
200
|
}
|