@rpgjs/action-battle 5.0.0-beta.4 → 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/README.md
CHANGED
|
@@ -231,6 +231,24 @@ new BattleAi(event, {
|
|
|
231
231
|
AttackPattern.Combo,
|
|
232
232
|
AttackPattern.DashAttack
|
|
233
233
|
],
|
|
234
|
+
|
|
235
|
+
// Per-pattern enemy attack timing and reactions
|
|
236
|
+
attackProfiles: {
|
|
237
|
+
charged: {
|
|
238
|
+
startupMs: 900,
|
|
239
|
+
activeMs: 140,
|
|
240
|
+
recoveryMs: 300,
|
|
241
|
+
reaction: {
|
|
242
|
+
hitstunMs: 240,
|
|
243
|
+
staggerPower: 2
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// Hit reaction tuning
|
|
249
|
+
poise: 1,
|
|
250
|
+
hitstunMs: 150,
|
|
251
|
+
invincibilityMs: 250,
|
|
234
252
|
|
|
235
253
|
// Patrol waypoints (for idle state)
|
|
236
254
|
patrolWaypoints: [
|
|
@@ -249,6 +267,11 @@ new BattleAi(event, {
|
|
|
249
267
|
});
|
|
250
268
|
```
|
|
251
269
|
|
|
270
|
+
`attackProfiles` lets enemies telegraph attacks with `startupMs`, keep hitboxes
|
|
271
|
+
active for `activeMs`, and apply hit reactions. `poise` controls interruption:
|
|
272
|
+
an incoming hit only stuns the enemy when its `reaction.staggerPower` is greater
|
|
273
|
+
than or equal to the enemy's `poise`.
|
|
274
|
+
|
|
252
275
|
## Enemy Types
|
|
253
276
|
|
|
254
277
|
Types modify AI **behavior** (cooldowns, ranges, dodge), not stats:
|
|
@@ -607,6 +630,98 @@ Player attacks are resolved with `createMovingHitbox()` instead of a passive
|
|
|
607
630
|
contact collision. You can still customize the generated hitboxes with
|
|
608
631
|
`attack.hitboxes` or `attack.resolveHitboxes`.
|
|
609
632
|
|
|
633
|
+
### Attack profile model
|
|
634
|
+
|
|
635
|
+
Use `attack.profile` to describe the timing model of a player attack in one
|
|
636
|
+
typed object. A profile separates the attack into startup, active, and recovery
|
|
637
|
+
phases so combat systems can share the same vocabulary.
|
|
638
|
+
|
|
639
|
+
```ts
|
|
640
|
+
import { provideActionBattle } from "@rpgjs/action-battle/server";
|
|
641
|
+
|
|
642
|
+
export default provideActionBattle({
|
|
643
|
+
attack: {
|
|
644
|
+
profile: {
|
|
645
|
+
id: "iron-sword",
|
|
646
|
+
startupMs: 80,
|
|
647
|
+
activeMs: 120,
|
|
648
|
+
recoveryMs: 180,
|
|
649
|
+
cooldownMs: 380,
|
|
650
|
+
movementLock: true,
|
|
651
|
+
directionLock: true,
|
|
652
|
+
animationKey: "attack",
|
|
653
|
+
hitPolicy: "oncePerTarget",
|
|
654
|
+
reaction: {
|
|
655
|
+
invincibilityMs: 250,
|
|
656
|
+
hitstunMs: 150,
|
|
657
|
+
staggerPower: 1
|
|
658
|
+
},
|
|
659
|
+
hitboxes: {
|
|
660
|
+
right: { offsetX: 18, offsetY: -18, width: 42, height: 36 }
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
lockDurationMs: 380
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
The default profile mirrors the legacy attack lock: no startup, a short active
|
|
669
|
+
window, and recovery that totals `350ms`. The player attack runtime uses
|
|
670
|
+
`startupMs` before creating the hitbox, `activeMs` to keep the hitbox active,
|
|
671
|
+
and `totalDurationMs` for movement and direction locks. `hitPolicy:
|
|
672
|
+
"oncePerTarget"` prevents the same attack window from damaging the same target
|
|
673
|
+
multiple times.
|
|
674
|
+
|
|
675
|
+
`reaction` describes what happens after the hit connects:
|
|
676
|
+
|
|
677
|
+
- `invincibilityMs`: temporary invincibility after damage.
|
|
678
|
+
- `hitstunMs`: stun duration requested by the hit.
|
|
679
|
+
- `staggerPower`: value compared against enemy `poise`.
|
|
680
|
+
|
|
681
|
+
```ts
|
|
682
|
+
import {
|
|
683
|
+
normalizeActionBattleAttackProfile,
|
|
684
|
+
type ActionBattleAttackProfile
|
|
685
|
+
} from "@rpgjs/action-battle/server";
|
|
686
|
+
|
|
687
|
+
const sword: ActionBattleAttackProfile = {
|
|
688
|
+
id: "sword",
|
|
689
|
+
startupMs: 70,
|
|
690
|
+
activeMs: 110,
|
|
691
|
+
recoveryMs: 170
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const normalized = normalizeActionBattleAttackProfile(sword);
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Equipped weapons can override the player attack profile:
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
const Dagger = {
|
|
701
|
+
id: "dagger",
|
|
702
|
+
name: "Dagger",
|
|
703
|
+
_type: "weapon" as const,
|
|
704
|
+
atk: 8,
|
|
705
|
+
knockbackForce: 20,
|
|
706
|
+
attackProfile: {
|
|
707
|
+
id: "dagger",
|
|
708
|
+
startupMs: 40,
|
|
709
|
+
activeMs: 70,
|
|
710
|
+
recoveryMs: 110
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
Enable lightweight attack logs while tuning profiles:
|
|
716
|
+
|
|
717
|
+
```ts
|
|
718
|
+
provideActionBattle({
|
|
719
|
+
debug: {
|
|
720
|
+
attacks: true
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
```
|
|
724
|
+
|
|
610
725
|
When the action targets a normal event with no `BattleAi`, the server lets the
|
|
611
726
|
event handle `onAction` and does not create the combat hitbox. Enemy events
|
|
612
727
|
with `BattleAi` still trigger the A-RPG attack.
|
package/dist/ai.server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RpgEvent, RpgPlayer } from '@rpgjs/server';
|
|
2
|
+
import { ActionBattleEnemyAttackProfileMap } from './core/enemy-attack-profiles';
|
|
2
3
|
import { ActionBattleDamageResult } from './core/contracts';
|
|
3
|
-
import { ActionBattleAnimationOptions } from './types';
|
|
4
|
+
import { NormalizedActionBattleHitReactionProfile, ActionBattleAnimationOptions } from './types';
|
|
4
5
|
type RpgEventWithBattleAi = RpgEvent & {
|
|
5
6
|
battleAi?: BattleAi;
|
|
6
7
|
};
|
|
@@ -14,6 +15,7 @@ export interface BattleAiOptions {
|
|
|
14
15
|
fleeThreshold?: number;
|
|
15
16
|
attackSkill?: any;
|
|
16
17
|
attackPatterns?: AttackPattern[];
|
|
18
|
+
attackProfiles?: ActionBattleEnemyAttackProfileMap;
|
|
17
19
|
patrolWaypoints?: Array<{
|
|
18
20
|
x: number;
|
|
19
21
|
y: number;
|
|
@@ -21,6 +23,9 @@ export interface BattleAiOptions {
|
|
|
21
23
|
groupBehavior?: boolean;
|
|
22
24
|
moveToCooldown?: number;
|
|
23
25
|
retreatCooldown?: number;
|
|
26
|
+
poise?: number;
|
|
27
|
+
hitstunMs?: number;
|
|
28
|
+
invincibilityMs?: number;
|
|
24
29
|
behavior?: {
|
|
25
30
|
baseScore?: number;
|
|
26
31
|
updateInterval?: number;
|
|
@@ -252,6 +257,7 @@ export declare class BattleAi {
|
|
|
252
257
|
private fleeThreshold;
|
|
253
258
|
private attackSkill;
|
|
254
259
|
private attackPatterns;
|
|
260
|
+
private attackProfiles;
|
|
255
261
|
private animations?;
|
|
256
262
|
private comboCount;
|
|
257
263
|
private comboMax;
|
|
@@ -281,6 +287,9 @@ export declare class BattleAi {
|
|
|
281
287
|
private lastRetreatTime;
|
|
282
288
|
private timers;
|
|
283
289
|
private behaviorKey?;
|
|
290
|
+
private poise;
|
|
291
|
+
private hitstunMs;
|
|
292
|
+
private invincibilityMs;
|
|
284
293
|
/**
|
|
285
294
|
* Create a new Battle AI Controller
|
|
286
295
|
*
|
|
@@ -362,6 +371,7 @@ export declare class BattleAi {
|
|
|
362
371
|
* Uses skill if configured, otherwise creates hitbox
|
|
363
372
|
*/
|
|
364
373
|
private performMeleeAttack;
|
|
374
|
+
private executeMeleeAttack;
|
|
365
375
|
/**
|
|
366
376
|
* Perform basic hitbox attack when no skill is set
|
|
367
377
|
*/
|
|
@@ -429,6 +439,9 @@ export declare class BattleAi {
|
|
|
429
439
|
* Perform dash attack
|
|
430
440
|
*/
|
|
431
441
|
private performDashAttack;
|
|
442
|
+
private getAttackProfile;
|
|
443
|
+
private telegraphAttack;
|
|
444
|
+
private scheduleAttackStartup;
|
|
432
445
|
/**
|
|
433
446
|
* Face the current target with hysteresis to prevent animation flickering
|
|
434
447
|
*
|
|
@@ -487,7 +500,9 @@ export declare class BattleAi {
|
|
|
487
500
|
* The actual damage is applied externally via RPGJS API.
|
|
488
501
|
*/
|
|
489
502
|
takeDamage(attacker: RpgPlayer): boolean;
|
|
490
|
-
handleDamage(attacker: RpgPlayer, damageResult: ActionBattleDamageResult
|
|
503
|
+
handleDamage(attacker: RpgPlayer, damageResult: ActionBattleDamageResult & {
|
|
504
|
+
reaction?: NormalizedActionBattleHitReactionProfile;
|
|
505
|
+
}): boolean;
|
|
491
506
|
/**
|
|
492
507
|
* Kill this AI
|
|
493
508
|
*
|
package/dist/client/index.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { DEFAULT_ACTION_BATTLE_HIT_REACTION, isActionBattleEntityInvincible, normalizeActionBattleHitReaction, setActionBattleInvincibility } from "./index2.js";
|
|
2
|
+
import { DEFAULT_ACTION_BATTLE_ATTACK_PROFILE, normalizeActionBattleAttackProfile } from "./index3.js";
|
|
3
|
+
import { ACTION_BATTLE_HITBOX_FRAME_MS, ActionBattleHitTracker, createActionBattleAttackId, getNormalizedActionBattleAttackProfile, resolveActionBattleHitboxSpeed, scheduleActionBattleStartup } from "./index11.js";
|
|
4
|
+
import client_default, { createActionBattleClient } from "./index12.js";
|
|
5
|
+
import { DEFAULT_ZELDA_PLAYER_HITBOXES, createDefaultPlayerHitboxResolver, defaultCombatSystem, defaultEnemyBehaviors, defaultKnockbackResolver, defaultRpgjsDamageResolver } from "./index13.js";
|
|
6
|
+
import { createActionBattleSystems, getActionBattleSystems } from "./index14.js";
|
|
7
|
+
import { DEFAULT_ACTION_BATTLE_ENEMY_ATTACK_PROFILES, normalizeActionBattleEnemyAttackProfiles } from "./index15.js";
|
|
8
|
+
import { AiDebug, AiState, AttackPattern, BattleAi, DEFAULT_KNOCKBACK, EnemyType } from "./index16.js";
|
|
9
|
+
import { resolveActionBattleWeaponAttackProfile } from "./index17.js";
|
|
10
|
+
import { applyActionBattleHit } from "./index18.js";
|
|
11
|
+
import { createActionEnemy } from "./index19.js";
|
|
12
|
+
import { ACTION_BATTLE_ACTION_BAR_GUI_ID, DEFAULT_PLAYER_ATTACK_HITBOXES, applyPlayerHitToEvent, createActionBattleServer, getPlayerWeaponKnockbackForce, openActionBattleActionBar, updateActionBattleActionBar } from "./index20.js";
|
|
8
13
|
import { createModule } from "@rpgjs/common";
|
|
9
14
|
//#region src/index.ts
|
|
10
15
|
var server = null;
|
|
@@ -20,4 +25,4 @@ var src_default = {
|
|
|
20
25
|
client: client_default
|
|
21
26
|
};
|
|
22
27
|
//#endregion
|
|
23
|
-
export { ACTION_BATTLE_ACTION_BAR_GUI_ID, AiDebug, AiState, AttackPattern, BattleAi, DEFAULT_KNOCKBACK, DEFAULT_PLAYER_ATTACK_HITBOXES, DEFAULT_ZELDA_PLAYER_HITBOXES, EnemyType, applyActionBattleHit, applyPlayerHitToEvent, createActionBattleServer, createActionBattleSystems, createActionEnemy, createDefaultPlayerHitboxResolver, src_default as default, defaultCombatSystem, defaultEnemyBehaviors, defaultKnockbackResolver, defaultRpgjsDamageResolver, getActionBattleSystems, getPlayerWeaponKnockbackForce, openActionBattleActionBar, provideActionBattle, updateActionBattleActionBar };
|
|
28
|
+
export { ACTION_BATTLE_ACTION_BAR_GUI_ID, ACTION_BATTLE_HITBOX_FRAME_MS, ActionBattleHitTracker, AiDebug, AiState, AttackPattern, BattleAi, DEFAULT_ACTION_BATTLE_ATTACK_PROFILE, DEFAULT_ACTION_BATTLE_ENEMY_ATTACK_PROFILES, DEFAULT_ACTION_BATTLE_HIT_REACTION, DEFAULT_KNOCKBACK, DEFAULT_PLAYER_ATTACK_HITBOXES, DEFAULT_ZELDA_PLAYER_HITBOXES, EnemyType, applyActionBattleHit, applyPlayerHitToEvent, createActionBattleAttackId, createActionBattleServer, createActionBattleSystems, createActionEnemy, createDefaultPlayerHitboxResolver, src_default as default, defaultCombatSystem, defaultEnemyBehaviors, defaultKnockbackResolver, defaultRpgjsDamageResolver, getActionBattleSystems, getNormalizedActionBattleAttackProfile, getPlayerWeaponKnockbackForce, isActionBattleEntityInvincible, normalizeActionBattleAttackProfile, normalizeActionBattleEnemyAttackProfiles, normalizeActionBattleHitReaction, openActionBattleActionBar, provideActionBattle, resolveActionBattleHitboxSpeed, resolveActionBattleWeaponAttackProfile, scheduleActionBattleStartup, setActionBattleInvincibility, updateActionBattleActionBar };
|
package/dist/client/index10.js
CHANGED
|
@@ -1,143 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
up: {
|
|
22
|
-
offsetX: -16,
|
|
23
|
-
offsetY: -48,
|
|
24
|
-
width: 32,
|
|
25
|
-
height: 32
|
|
26
|
-
},
|
|
27
|
-
down: {
|
|
28
|
-
offsetX: -16,
|
|
29
|
-
offsetY: 16,
|
|
30
|
-
width: 32,
|
|
31
|
-
height: 32
|
|
32
|
-
},
|
|
33
|
-
left: {
|
|
34
|
-
offsetX: -48,
|
|
35
|
-
offsetY: -16,
|
|
36
|
-
width: 32,
|
|
37
|
-
height: 32
|
|
38
|
-
},
|
|
39
|
-
right: {
|
|
40
|
-
offsetX: 16,
|
|
41
|
-
offsetY: -16,
|
|
42
|
-
width: 32,
|
|
43
|
-
height: 32
|
|
44
|
-
},
|
|
45
|
-
default: {
|
|
46
|
-
offsetX: 0,
|
|
47
|
-
offsetY: -32,
|
|
48
|
-
width: 32,
|
|
49
|
-
height: 32
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var resolveEquippedWeapon = (entity) => {
|
|
53
|
-
const equipments = entity?.equipments?.() || [];
|
|
54
|
-
for (const item of equipments) {
|
|
55
|
-
const itemId = item?.id?.() ?? item?.id;
|
|
56
|
-
const itemData = entity?.databaseById?.(itemId);
|
|
57
|
-
if (itemData?._type === "weapon") return itemData;
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
};
|
|
61
|
-
var resolveDirection = (attacker, target) => {
|
|
62
|
-
const dx = target.x() - attacker.x();
|
|
63
|
-
const dy = target.y() - attacker.y();
|
|
64
|
-
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
65
|
-
if (distance <= 0) return void 0;
|
|
66
|
-
return {
|
|
67
|
-
x: dx / distance,
|
|
68
|
-
y: dy / distance
|
|
1
|
+
var DEFAULT_ANIMATION_BY_KEY = {
|
|
2
|
+
attack: "attack",
|
|
3
|
+
hurt: "hurt",
|
|
4
|
+
die: "die",
|
|
5
|
+
castSkill: "skill",
|
|
6
|
+
castSpell: "skill"
|
|
7
|
+
};
|
|
8
|
+
var getConfiguredAnimation = (key, animations) => {
|
|
9
|
+
if (!animations) return {
|
|
10
|
+
hasConfiguredAnimation: false,
|
|
11
|
+
configured: void 0
|
|
12
|
+
};
|
|
13
|
+
const hasConfiguredAnimation = Object.prototype.hasOwnProperty.call(animations, key);
|
|
14
|
+
if (hasConfiguredAnimation) return {
|
|
15
|
+
hasConfiguredAnimation,
|
|
16
|
+
configured: animations[key]
|
|
17
|
+
};
|
|
18
|
+
if (key === "castSkill") return {
|
|
19
|
+
hasConfiguredAnimation: Object.prototype.hasOwnProperty.call(animations, "castSpell"),
|
|
20
|
+
configured: animations.castSpell
|
|
69
21
|
};
|
|
70
|
-
};
|
|
71
|
-
var createDefaultPlayerHitboxResolver = (hitboxes = DEFAULT_ZELDA_PLAYER_HITBOXES) => (context) => {
|
|
72
|
-
const attacker = context.attacker;
|
|
73
|
-
const config = hitboxes[context.direction ?? (typeof attacker.getDirection === "function" ? attacker.getDirection() : "default")] || hitboxes.default;
|
|
74
|
-
return [{
|
|
75
|
-
x: attacker.x() + config.offsetX,
|
|
76
|
-
y: attacker.y() + config.offsetY,
|
|
77
|
-
width: config.width,
|
|
78
|
-
height: config.height
|
|
79
|
-
}];
|
|
80
|
-
};
|
|
81
|
-
var defaultRpgjsDamageResolver = (context) => {
|
|
82
|
-
const target = context.target;
|
|
83
|
-
const raw = target.applyDamage(context.attacker, context.skill);
|
|
84
22
|
return {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
raw
|
|
23
|
+
hasConfiguredAnimation: false,
|
|
24
|
+
configured: void 0
|
|
88
25
|
};
|
|
89
26
|
};
|
|
90
|
-
|
|
91
|
-
const
|
|
27
|
+
function resolveActionBattleAnimation(key, entity, animations, context, defaults = {}) {
|
|
28
|
+
const defaultAnimationName = defaults.animationName ?? DEFAULT_ANIMATION_BY_KEY[key];
|
|
29
|
+
const defaultRepeat = defaults.repeat ?? 1;
|
|
30
|
+
const { hasConfiguredAnimation, configured: configuredAnimation } = getConfiguredAnimation(key, animations);
|
|
31
|
+
if (!hasConfiguredAnimation && key !== "attack") return null;
|
|
32
|
+
const configured = hasConfiguredAnimation ? configuredAnimation : defaultAnimationName;
|
|
33
|
+
const result = typeof configured === "function" ? configured(entity, context) : configured;
|
|
34
|
+
if (result == null) return null;
|
|
35
|
+
if (typeof result === "string") return {
|
|
36
|
+
animationName: result,
|
|
37
|
+
repeat: defaultRepeat,
|
|
38
|
+
waitEnd: false
|
|
39
|
+
};
|
|
92
40
|
return {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
41
|
+
animationName: result.animationName ?? defaultAnimationName,
|
|
42
|
+
graphic: result.graphic,
|
|
43
|
+
repeat: result.repeat ?? defaultRepeat,
|
|
44
|
+
waitEnd: result.waitEnd ?? false,
|
|
45
|
+
delayMs: result.delayMs
|
|
96
46
|
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
]
|
|
111
|
-
}),
|
|
112
|
-
[CoreEnemyType.Defensive]: ({ hpPercent }) => ({
|
|
113
|
-
mode: hpPercent !== null && hpPercent < .3 ? "retreat" : "tactical",
|
|
114
|
-
attackPatterns: [CoreAttackPattern.Melee, CoreAttackPattern.Charged]
|
|
115
|
-
}),
|
|
116
|
-
[CoreEnemyType.Ranged]: ({ distance }) => ({
|
|
117
|
-
mode: distance !== null && distance < 80 ? "retreat" : "tactical",
|
|
118
|
-
attackPatterns: [CoreAttackPattern.Melee, CoreAttackPattern.Zone]
|
|
119
|
-
}),
|
|
120
|
-
[CoreEnemyType.Tank]: () => ({
|
|
121
|
-
mode: "assault",
|
|
122
|
-
attackPatterns: [
|
|
123
|
-
CoreAttackPattern.Melee,
|
|
124
|
-
CoreAttackPattern.Charged,
|
|
125
|
-
CoreAttackPattern.Zone
|
|
126
|
-
]
|
|
127
|
-
}),
|
|
128
|
-
[CoreEnemyType.Berserker]: ({ hpPercent }) => ({
|
|
129
|
-
mode: "assault",
|
|
130
|
-
attackCooldown: hpPercent === null ? void 0 : Math.max(250, 800 * Math.max(.3, hpPercent)),
|
|
131
|
-
attackPatterns: [
|
|
132
|
-
CoreAttackPattern.Melee,
|
|
133
|
-
CoreAttackPattern.Combo,
|
|
134
|
-
CoreAttackPattern.DashAttack
|
|
135
|
-
]
|
|
136
|
-
})
|
|
137
|
-
};
|
|
138
|
-
var defaultActionBattleSystems = {
|
|
139
|
-
combat: defaultCombatSystem,
|
|
140
|
-
ai: { behaviors: defaultEnemyBehaviors }
|
|
141
|
-
};
|
|
47
|
+
}
|
|
48
|
+
function playActionBattleAnimation(key, entity, animations, context, defaults = {}) {
|
|
49
|
+
const animation = resolveActionBattleAnimation(key, entity, animations, context, defaults);
|
|
50
|
+
if (!animation) return null;
|
|
51
|
+
if (animation.graphic !== void 0) entity.setGraphicAnimation(animation.animationName, animation.graphic, animation.repeat);
|
|
52
|
+
else entity.setGraphicAnimation(animation.animationName, animation.repeat);
|
|
53
|
+
return animation;
|
|
54
|
+
}
|
|
55
|
+
function getActionBattleAnimationRemovalDelay(animation) {
|
|
56
|
+
if (!animation) return 0;
|
|
57
|
+
if (animation.delayMs !== void 0) return animation.delayMs;
|
|
58
|
+
return animation.waitEnd ? 500 : 0;
|
|
59
|
+
}
|
|
142
60
|
//#endregion
|
|
143
|
-
export {
|
|
61
|
+
export { getActionBattleAnimationRemovalDelay, playActionBattleAnimation, resolveActionBattleAnimation };
|
package/dist/client/index11.js
CHANGED
|
@@ -1,25 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/core/
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { normalizeActionBattleAttackProfile } from "./index3.js";
|
|
2
|
+
//#region src/core/attack-runtime.ts
|
|
3
|
+
var ACTION_BATTLE_HITBOX_FRAME_MS = 16;
|
|
4
|
+
function getNormalizedActionBattleAttackProfile(options = {}) {
|
|
5
|
+
const attack = options.attack ?? {};
|
|
6
|
+
return normalizeActionBattleAttackProfile(attack.profile, {
|
|
7
|
+
lockMovement: attack.lockMovement,
|
|
8
|
+
lockDurationMs: attack.lockDurationMs,
|
|
9
|
+
hitboxes: attack.hitboxes
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function resolveActionBattleHitboxSpeed(profile, hitboxCount) {
|
|
13
|
+
const positions = Math.max(1, Math.floor(hitboxCount));
|
|
14
|
+
const activeFrames = Math.max(1, Math.ceil(profile.activeMs / 16));
|
|
15
|
+
return Math.max(1, Math.ceil(activeFrames / positions));
|
|
16
|
+
}
|
|
17
|
+
function scheduleActionBattleStartup(profile, callback, scheduler = setTimeout) {
|
|
18
|
+
if (profile.startupMs <= 0) {
|
|
19
|
+
callback();
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return scheduler(callback, profile.startupMs);
|
|
23
|
+
}
|
|
24
|
+
var attackIdCounter = 0;
|
|
25
|
+
function createActionBattleAttackId(attackerId, profileId) {
|
|
26
|
+
attackIdCounter++;
|
|
27
|
+
return `${attackerId ?? "unknown"}:${profileId}:${Date.now()}:${attackIdCounter}`;
|
|
28
|
+
}
|
|
29
|
+
var getTargetKey = (target) => {
|
|
30
|
+
if (!target || target.id === void 0 || target.id === null) return null;
|
|
31
|
+
return String(target.id);
|
|
32
|
+
};
|
|
33
|
+
var ActionBattleHitTracker = class {
|
|
34
|
+
hitTargets = /* @__PURE__ */ new Set();
|
|
35
|
+
constructor(hitPolicy) {
|
|
36
|
+
this.hitPolicy = hitPolicy;
|
|
37
|
+
}
|
|
38
|
+
canHit(target) {
|
|
39
|
+
if (this.hitPolicy === "allowRepeatHits") return true;
|
|
40
|
+
const key = getTargetKey(target);
|
|
41
|
+
return !key || !this.hitTargets.has(key);
|
|
42
|
+
}
|
|
43
|
+
recordHit(target) {
|
|
44
|
+
const key = getTargetKey(target);
|
|
45
|
+
if (key) this.hitTargets.add(key);
|
|
46
|
+
}
|
|
47
|
+
tryHit(target) {
|
|
48
|
+
if (!this.canHit(target)) return false;
|
|
49
|
+
this.recordHit(target);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
21
52
|
};
|
|
22
|
-
var getActionBattleSystems = () => currentActionBattleSystems;
|
|
23
|
-
var createActionBattleSystems = mergeSystems;
|
|
24
53
|
//#endregion
|
|
25
|
-
export {
|
|
54
|
+
export { ACTION_BATTLE_HITBOX_FRAME_MS, ActionBattleHitTracker, createActionBattleAttackId, getNormalizedActionBattleAttackProfile, resolveActionBattleHitboxSpeed, scheduleActionBattleStartup };
|