@rpgjs/action-battle 5.0.0-beta.10 → 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.
Files changed (111) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/client/ai.server.d.ts +45 -8
  3. package/dist/client/attack-input.d.ts +3 -0
  4. package/dist/client/core/action-use.d.ts +18 -0
  5. package/dist/client/core/ai-behavior-tree.d.ts +99 -0
  6. package/dist/client/core/attack-runtime.d.ts +2 -0
  7. package/dist/client/core/defaults.d.ts +2 -1
  8. package/dist/client/core/equipment.d.ts +1 -0
  9. package/dist/client/core/targets.d.ts +15 -0
  10. package/dist/client/enemies/factory.d.ts +2 -0
  11. package/dist/client/index.d.ts +12 -7
  12. package/dist/client/index.js +16 -11
  13. package/dist/client/index10.js +32 -56
  14. package/dist/client/index11.js +99 -52
  15. package/dist/client/index12.js +76 -103
  16. package/dist/client/index13.js +72 -135
  17. package/dist/client/index14.js +67 -23
  18. package/dist/client/index15.js +197 -63
  19. package/dist/client/index16.js +112 -1337
  20. package/dist/client/index17.js +193 -7
  21. package/dist/client/index18.js +32 -58
  22. package/dist/client/index19.js +70 -8
  23. package/dist/client/index20.js +57 -501
  24. package/dist/client/index21.js +69 -0
  25. package/dist/client/index22.js +225 -0
  26. package/dist/client/index23.js +16 -0
  27. package/dist/client/index24.js +25 -0
  28. package/dist/client/index25.js +107 -0
  29. package/dist/client/index26.js +1707 -0
  30. package/dist/client/index27.js +12 -0
  31. package/dist/client/index28.js +589 -0
  32. package/dist/client/index4.js +79 -38
  33. package/dist/client/index6.js +65 -306
  34. package/dist/client/index7.js +33 -33
  35. package/dist/client/index8.js +24 -100
  36. package/dist/client/index9.js +293 -61
  37. package/dist/client/locomotion.d.ts +16 -0
  38. package/dist/client/movement.d.ts +14 -0
  39. package/dist/client/server.d.ts +7 -3
  40. package/dist/client/ui.d.ts +22 -0
  41. package/dist/client/visual.d.ts +15 -0
  42. package/dist/server/ai.server.d.ts +45 -8
  43. package/dist/server/attack-input.d.ts +3 -0
  44. package/dist/server/core/action-use.d.ts +18 -0
  45. package/dist/server/core/ai-behavior-tree.d.ts +99 -0
  46. package/dist/server/core/attack-runtime.d.ts +2 -0
  47. package/dist/server/core/defaults.d.ts +2 -1
  48. package/dist/server/core/equipment.d.ts +1 -0
  49. package/dist/server/core/targets.d.ts +15 -0
  50. package/dist/server/enemies/factory.d.ts +2 -0
  51. package/dist/server/index.d.ts +12 -7
  52. package/dist/server/index.js +14 -9
  53. package/dist/server/index10.js +64 -1336
  54. package/dist/server/index11.js +33 -33
  55. package/dist/server/index13.js +66 -11
  56. package/dist/server/index14.js +206 -484
  57. package/dist/server/index15.js +15 -9
  58. package/dist/server/index16.js +26 -0
  59. package/dist/server/index17.js +25 -0
  60. package/dist/server/index18.js +107 -0
  61. package/dist/server/index19.js +1707 -0
  62. package/dist/server/index2.js +10 -2
  63. package/dist/server/index20.js +37 -0
  64. package/dist/server/index21.js +588 -0
  65. package/dist/server/index22.js +78 -0
  66. package/dist/server/index23.js +12 -0
  67. package/dist/server/index5.js +79 -38
  68. package/dist/server/index6.js +192 -129
  69. package/dist/server/index7.js +198 -24
  70. package/dist/server/index8.js +28 -66
  71. package/dist/server/index9.js +68 -51
  72. package/dist/server/locomotion.d.ts +16 -0
  73. package/dist/server/movement.d.ts +14 -0
  74. package/dist/server/server.d.ts +7 -3
  75. package/dist/server/ui.d.ts +22 -0
  76. package/dist/server/visual.d.ts +15 -0
  77. package/package.json +10 -10
  78. package/src/ai.server.spec.ts +233 -0
  79. package/src/ai.server.ts +627 -108
  80. package/src/animations.spec.ts +40 -0
  81. package/src/animations.ts +31 -9
  82. package/src/attack-input.spec.ts +51 -0
  83. package/src/attack-input.ts +59 -0
  84. package/src/client.ts +75 -62
  85. package/src/components/action-bar.ce +2 -2
  86. package/src/config.ts +84 -37
  87. package/src/core/action-use.spec.ts +317 -0
  88. package/src/core/action-use.ts +386 -0
  89. package/src/core/ai-behavior-tree.spec.ts +116 -0
  90. package/src/core/ai-behavior-tree.ts +272 -0
  91. package/src/core/attack-profile.spec.ts +46 -0
  92. package/src/core/attack-runtime.spec.ts +35 -0
  93. package/src/core/attack-runtime.ts +32 -0
  94. package/src/core/context.ts +9 -0
  95. package/src/core/contracts.ts +146 -1
  96. package/src/core/defaults.ts +56 -0
  97. package/src/core/equipment.ts +9 -5
  98. package/src/core/targets.spec.ts +112 -0
  99. package/src/core/targets.ts +147 -0
  100. package/src/enemies/factory.ts +8 -0
  101. package/src/index.ts +111 -2
  102. package/src/locomotion.spec.ts +51 -0
  103. package/src/locomotion.ts +48 -0
  104. package/src/movement.spec.ts +78 -0
  105. package/src/movement.ts +46 -0
  106. package/src/server.ts +242 -66
  107. package/src/types.ts +105 -35
  108. package/src/ui.ts +113 -0
  109. package/src/visual.spec.ts +166 -0
  110. package/src/visual.ts +285 -0
  111. package/README.md +0 -1242
package/src/server.ts CHANGED
@@ -8,7 +8,15 @@ import {
8
8
  } from "./types";
9
9
  import { normalizeActionBattleOptions, setActionBattleOptions } from "./config";
10
10
  import { manhattanDistance, parseAoeMask } from "./targeting";
11
- import { playActionBattleAnimation } from "./animations";
11
+ import { emitActionBattleClientVisual } from "./visual";
12
+ import {
13
+ applyActionBattleAttackDirection,
14
+ resolveActionBattleAttackDirection,
15
+ } from "./attack-input";
16
+ import {
17
+ forceActionBattleLocomotionAnimation,
18
+ withActionBattleAnimationUnlocked,
19
+ } from "./locomotion";
12
20
  import { getActionBattleSystems, setActionBattleSystems } from "./core/context";
13
21
  import { applyActionBattleHit } from "./core/hit";
14
22
  import { DEFAULT_ZELDA_PLAYER_HITBOXES } from "./core/defaults";
@@ -16,12 +24,25 @@ import {
16
24
  ActionBattleHitTracker,
17
25
  createActionBattleAttackId,
18
26
  getNormalizedActionBattleAttackProfile,
19
- resolveActionBattleHitboxSpeed,
20
- scheduleActionBattleStartup,
27
+ runActionBattleActiveHitbox,
21
28
  } from "./core/attack-runtime";
29
+ import {
30
+ canActionBattleUseTarget,
31
+ executeActionBattleUse,
32
+ getActionBattleActionConfig,
33
+ handleActionBattleProjectileDestroy,
34
+ handleActionBattleProjectileImpact,
35
+ } from "./core/action-use";
22
36
  import { normalizeActionBattleAttackProfile } from "./core/attack-profile";
23
- import { resolveActionBattleWeaponAttackProfile } from "./core/equipment";
37
+ import {
38
+ resolveActionBattleWeapon,
39
+ resolveActionBattleWeaponAttackProfile,
40
+ } from "./core/equipment";
24
41
  import type { ActionBattleHitbox } from "./core/contracts";
42
+ import {
43
+ canActionBattleTarget,
44
+ getActionBattleTargets,
45
+ } from "./core/targets";
25
46
  import type {
26
47
  ActionBattleAttackProfile,
27
48
  NormalizedActionBattleAttackProfile,
@@ -82,6 +103,9 @@ const beginPlayerAttackLock = (
82
103
  player.canMove = previousCanMove;
83
104
  player.directionFixed = previousDirectionFixed;
84
105
  player.animationFixed = previousAnimationFixed;
106
+ if (locks.movement && !previousAnimationFixed) {
107
+ forceActionBattleLocomotionAnimation(player, "stand");
108
+ }
85
109
  }, durationMs);
86
110
 
87
111
  return true;
@@ -98,12 +122,12 @@ const rectsOverlap = (
98
122
  a.y < b.y + b.height &&
99
123
  a.y + a.height > b.y;
100
124
 
101
- const eventRect = (event: RpgEvent) => {
125
+ const entityRect = (entity: RpgPlayer | RpgEvent | any) => {
102
126
  const hitbox =
103
- typeof event.hitbox === "function" ? event.hitbox() : (event as any).hitbox;
127
+ typeof entity.hitbox === "function" ? entity.hitbox() : entity.hitbox;
104
128
  return {
105
- x: event.x(),
106
- y: event.y(),
129
+ x: entity.x(),
130
+ y: entity.y(),
107
131
  width: hitbox?.w ?? 32,
108
132
  height: hitbox?.h ?? 32,
109
133
  };
@@ -143,7 +167,7 @@ const getVisibleActionEvents = (
143
167
  }
144
168
 
145
169
  for (const event of map.getEvents()) {
146
- const rect = eventRect(event);
170
+ const rect = entityRect(event);
147
171
  if (hitboxes.some((hitbox) => rectsOverlap(hitbox, rect))) {
148
172
  addEvent(event);
149
173
  }
@@ -229,9 +253,19 @@ export function applyPlayerHitToEvent(
229
253
  target: RpgEvent,
230
254
  hooks?: ApplyHitHooks,
231
255
  metadata?: Record<string, any>
256
+ ): HitResult | undefined {
257
+ if (!(target as any).battleAi) return undefined;
258
+ return applyActionBattleEntityHit(player, target, hooks, metadata);
259
+ }
260
+
261
+ export function applyActionBattleEntityHit(
262
+ attacker: RpgPlayer | RpgEvent,
263
+ target: RpgPlayer | RpgEvent,
264
+ hooks?: ApplyHitHooks,
265
+ metadata?: Record<string, any>
232
266
  ): HitResult | undefined {
233
267
  const ai = (target as any).battleAi as BattleAi;
234
- if (!ai) return undefined;
268
+ if (target instanceof RpgEvent && !ai) return undefined;
235
269
 
236
270
  const systems = getActionBattleSystems();
237
271
  const result = applyActionBattleHit(
@@ -269,15 +303,15 @@ export function applyPlayerHitToEvent(
269
303
  : systems.combat.hooks,
270
304
  },
271
305
  {
272
- attacker: player,
306
+ attacker,
273
307
  target,
274
308
  metadata,
275
309
  reaction: metadata?.reaction,
276
310
  }
277
311
  );
278
312
 
279
- if (!result.cancelled) {
280
- ai.handleDamage(player, {
313
+ if (!result.cancelled && ai) {
314
+ ai.handleDamage(attacker, {
281
315
  damage: result.damage,
282
316
  defeated: result.defeated,
283
317
  raw: result.rawDamage,
@@ -329,6 +363,39 @@ const resolvePlayerAttackHitboxes = (
329
363
  );
330
364
  };
331
365
 
366
+ const getActionBattleHitboxCandidates = (
367
+ map: ReturnType<RpgPlayer["getCurrentMap"]> | undefined,
368
+ hitboxes: ActionBattleHitbox[],
369
+ options: { excludeIds?: string[]; kinds?: Array<"players" | "events"> } = {}
370
+ ) => {
371
+ if (!map) return [];
372
+ if (typeof (map as any).queryHitbox === "function") {
373
+ const candidates = new Map<string, RpgPlayer | RpgEvent>();
374
+ for (const hitbox of hitboxes) {
375
+ for (const entity of (map as any).queryHitbox(hitbox, options)) {
376
+ if (entity?.id) candidates.set(entity.id, entity);
377
+ }
378
+ }
379
+ return Array.from(candidates.values());
380
+ }
381
+
382
+ const candidates = new Map<string, RpgPlayer | RpgEvent>();
383
+ const excluded = new Set(options.excludeIds ?? []);
384
+ const add = (entity: RpgPlayer | RpgEvent | undefined) => {
385
+ if (!entity?.id) return;
386
+ if (excluded.has(entity.id)) return;
387
+ const rect = entityRect(entity);
388
+ if (hitboxes.some((hitbox) => rectsOverlap(hitbox, rect))) {
389
+ candidates.set(entity.id, entity);
390
+ }
391
+ };
392
+
393
+ const kinds = new Set(options.kinds ?? ["players", "events"]);
394
+ if (kinds.has("players")) map.getPlayers?.().forEach(add);
395
+ if (kinds.has("events")) map.getEvents?.().forEach(add);
396
+ return Array.from(candidates.values());
397
+ };
398
+
332
399
  const mergeAttackProfileOverrides = (
333
400
  base: NormalizedActionBattleAttackProfile,
334
401
  override: ActionBattleAttackProfile
@@ -381,6 +448,16 @@ const resolveSkillData = (player: RpgPlayer, skillId: string) => {
381
448
  }
382
449
  };
383
450
 
451
+ const resolvePlayerSkillUsable = (player: RpgPlayer, skillId: string) => {
452
+ try {
453
+ return (
454
+ (player as any).getSkill?.(skillId) ?? resolveSkillData(player, skillId)
455
+ );
456
+ } catch {
457
+ return resolveSkillData(player, skillId);
458
+ }
459
+ };
460
+
384
461
  const resolveSkillTargeting = (
385
462
  player: RpgPlayer,
386
463
  skillId: string,
@@ -552,11 +629,24 @@ const handleActionBattleSkillUse = (
552
629
  target: { x: number; y: number } | undefined,
553
630
  options: ActionBattleOptions
554
631
  ) => {
555
- const skillData = resolveSkillData(player, skillId);
632
+ const skillData = resolvePlayerSkillUsable(player, skillId);
633
+ const actionConfig = getActionBattleActionConfig(skillData);
634
+
635
+ if (actionConfig?.target === "self") {
636
+ executeActionBattleUse({
637
+ attacker: player,
638
+ target: player,
639
+ usable: skillData,
640
+ skill: skillData,
641
+ });
642
+ return;
643
+ }
556
644
 
557
645
  const map = player.getCurrentMap();
558
646
  if (!map) {
559
- playActionBattleAnimation("castSkill", player, options.animations, {
647
+ emitActionBattleClientVisual({
648
+ moment: "castSkill",
649
+ entity: player,
560
650
  skill: skillData,
561
651
  });
562
652
  player.useSkill(skillId);
@@ -564,7 +654,9 @@ const handleActionBattleSkillUse = (
564
654
  }
565
655
  const targeting = resolveSkillTargeting(player, skillId, options);
566
656
  if (!targeting || !target) {
567
- playActionBattleAnimation("castSkill", player, options.animations, {
657
+ emitActionBattleClientVisual({
658
+ moment: "castSkill",
659
+ entity: player,
568
660
  skill: skillData,
569
661
  });
570
662
  player.useSkill(skillId);
@@ -590,11 +682,20 @@ const handleActionBattleSkillUse = (
590
682
  });
591
683
 
592
684
  const targets: any[] = [];
685
+ const actionTarget = actionConfig?.target ?? "enemy";
593
686
  const affects = options.targeting?.affects || "events";
594
687
  if (affects === "events" || affects === "both") {
595
688
  map.getEvents().forEach((event: RpgEvent) => {
596
689
  const tile = getEntityTile(event, tileSize);
597
- if (affected.has(`${tile.x},${tile.y}`)) {
690
+ if (
691
+ affected.has(`${tile.x},${tile.y}`) &&
692
+ canActionBattleUseTarget(
693
+ player,
694
+ event,
695
+ actionTarget,
696
+ options.combat?.targets
697
+ )
698
+ ) {
598
699
  targets.push(event);
599
700
  }
600
701
  });
@@ -603,7 +704,15 @@ const handleActionBattleSkillUse = (
603
704
  map.getPlayers().forEach((other: RpgPlayer) => {
604
705
  if (other.id === player.id) return;
605
706
  const tile = getEntityTile(other, tileSize);
606
- if (affected.has(`${tile.x},${tile.y}`)) {
707
+ if (
708
+ affected.has(`${tile.x},${tile.y}`) &&
709
+ canActionBattleUseTarget(
710
+ player,
711
+ other,
712
+ actionTarget,
713
+ options.combat?.targets
714
+ )
715
+ ) {
607
716
  targets.push(other);
608
717
  }
609
718
  });
@@ -613,11 +722,12 @@ const handleActionBattleSkillUse = (
613
722
  return;
614
723
  }
615
724
 
616
- playActionBattleAnimation("castSkill", player, options.animations, {
725
+ executeActionBattleUse({
726
+ attacker: player,
727
+ target: targets,
728
+ usable: skillData,
617
729
  skill: skillData,
618
- target: targets[0],
619
730
  });
620
- player.useSkill(skillId, targets as any);
621
731
  };
622
732
 
623
733
  export const createActionBattleServer = (
@@ -642,20 +752,22 @@ export const createActionBattleServer = (
642
752
  onInput(player: RpgPlayer, input: any) {
643
753
  if (input.action == Control.Action) {
644
754
  const map = player.getCurrentMap();
645
- const direction = player.getDirection();
755
+ const direction = resolveActionBattleAttackDirection(player, input);
756
+ applyActionBattleAttackDirection(player, direction);
646
757
  const attackProfile = resolvePlayerAttackProfile(player, options);
647
758
 
648
759
  // Convert Direction enum to string key
649
760
  const directionKey = direction as string;
650
761
 
651
- const hitboxes = resolvePlayerAttackHitboxes(
762
+ const resolveActiveHitboxes = () => resolvePlayerAttackHitboxes(
652
763
  player,
653
764
  directionKey,
654
765
  options,
655
766
  attackProfile
656
767
  );
768
+ const initialHitboxes = resolveActiveHitboxes();
657
769
 
658
- if (isActionReservedForNormalEvent(player, map, hitboxes)) {
770
+ if (isActionReservedForNormalEvent(player, map, initialHitboxes)) {
659
771
  return;
660
772
  }
661
773
 
@@ -675,7 +787,12 @@ export const createActionBattleServer = (
675
787
  return;
676
788
  }
677
789
 
678
- playActionBattleAnimation("attack", player, options.animations);
790
+ withActionBattleAnimationUnlocked(player, () => {
791
+ emitActionBattleClientVisual({
792
+ moment: "attack",
793
+ entity: player,
794
+ });
795
+ });
679
796
  if (actionLocked) {
680
797
  player.animationFixed = true;
681
798
  }
@@ -683,53 +800,60 @@ export const createActionBattleServer = (
683
800
  player.id,
684
801
  attackProfile.id
685
802
  );
803
+ const weapon = resolveActionBattleWeapon(player);
686
804
  const hitTracker = new ActionBattleHitTracker(
687
805
  attackProfile.hitPolicy
688
806
  );
689
- if (options.debug?.attacks) {
690
- console.log("[ActionBattle] player attack", {
691
- attackId,
692
- playerId: player.id,
693
- profile: attackProfile.id,
694
- hitboxes,
807
+ const targetSelector = getActionBattleTargets(player, "events");
808
+
809
+ const processHits = (hits: any[]) => {
810
+ hits.forEach((hit: any) => {
811
+ if (
812
+ !canActionBattleTarget(
813
+ player,
814
+ hit,
815
+ targetSelector,
816
+ options.combat?.targets
817
+ )
818
+ ) {
819
+ return;
820
+ }
821
+ if (!hitTracker.tryHit(hit)) return;
822
+ const handledByWeapon =
823
+ weapon &&
824
+ executeActionBattleUse({
825
+ attacker: player,
826
+ target: hit,
827
+ usable: weapon,
828
+ weapon,
829
+ profile: attackProfile,
830
+ playVisual: false,
831
+ });
832
+ if (handledByWeapon) return;
833
+ applyActionBattleEntityHit(player, hit, undefined, {
834
+ attackId,
835
+ attackProfileId: attackProfile.id,
836
+ reaction: attackProfile.reaction,
837
+ });
695
838
  });
696
- }
697
-
698
- scheduleActionBattleStartup(attackProfile, () => {
699
- map
700
- ?.createMovingHitbox(hitboxes, {
701
- speed: resolveActionBattleHitboxSpeed(
702
- attackProfile,
703
- hitboxes.length
704
- ),
705
- })
706
- .subscribe({
707
- next(hits: any[]) {
708
- hits.forEach((hit: any) => {
709
- if (hit instanceof RpgEvent) {
710
- if (!hitTracker.tryHit(hit)) return;
711
- const result = applyPlayerHitToEvent(
712
- player,
713
- hit,
714
- undefined,
715
- {
716
- attackId,
717
- attackProfileId: attackProfile.id,
718
- reaction: attackProfile.reaction,
719
- }
720
- );
721
- if (result?.defeated) {
722
- console.log(`Player ${player.id} defeated AI ${hit.id}`);
723
- }
724
- }
725
- });
726
- },
839
+ };
840
+
841
+ runActionBattleActiveHitbox(
842
+ attackProfile,
843
+ resolveActiveHitboxes,
844
+ (activeHitboxes) => {
845
+ const candidates = getActionBattleHitboxCandidates(map, activeHitboxes, {
846
+ excludeIds: [player.id],
847
+ kinds: ["players", "events"],
727
848
  });
728
- });
849
+ processHits(candidates);
850
+ }
851
+ );
729
852
  }
730
853
  },
731
854
  onConnected(player: RpgPlayer) {
732
- if (options.ui?.actionBar?.enabled && options.ui?.actionBar?.autoOpen) {
855
+ const actionBar = options.ui?.actionBar as any;
856
+ if (actionBar?.enabled && actionBar?.autoOpen) {
733
857
  openActionBattleActionBar(player, options);
734
858
  }
735
859
  },
@@ -765,6 +889,23 @@ export const createActionBattleServer = (
765
889
  ai?.onDetectOutShape(player, shape);
766
890
  },
767
891
  },
892
+ projectiles: {
893
+ onImpact(context: any) {
894
+ handleActionBattleProjectileImpact({
895
+ attacker:
896
+ context.projectile?.payload?.attackerId
897
+ ? context.map?.getObjectById?.(context.projectile.payload.attackerId)
898
+ : undefined,
899
+ target: context.target,
900
+ projectile: context.projectile,
901
+ hit: context.hit,
902
+ map: context.map,
903
+ } as any);
904
+ },
905
+ onDestroy(context: any) {
906
+ handleActionBattleProjectileDestroy(context.projectile?.id);
907
+ },
908
+ },
768
909
  });
769
910
  };
770
911
 
@@ -776,6 +917,7 @@ export {
776
917
  createActionBattleAttackId,
777
918
  getNormalizedActionBattleAttackProfile,
778
919
  resolveActionBattleHitboxSpeed,
920
+ runActionBattleActiveHitbox,
779
921
  scheduleActionBattleStartup,
780
922
  } from "./core/attack-runtime";
781
923
  export {
@@ -789,11 +931,31 @@ export type {
789
931
  ActionBattleAttackHitboxMap,
790
932
  ActionBattleAttackHitPolicy,
791
933
  ActionBattleAttackProfile,
792
- ActionBattleDebugOptions,
793
934
  ActionBattleHitReactionProfile,
794
935
  NormalizedActionBattleHitReactionProfile,
795
936
  NormalizedActionBattleAttackProfile,
796
937
  } from "./types";
938
+ export type {
939
+ ActionBattleActionConfig,
940
+ ActionBattleActionMode,
941
+ ActionBattleActionTarget,
942
+ ActionBattleProjectileImpactContext,
943
+ ActionBattleProjectileOptions,
944
+ ActionBattleTargetContext,
945
+ ActionBattleTargetOptions,
946
+ ActionBattleTargetSelector,
947
+ ActionBattleUsable,
948
+ ActionBattleUseContext,
949
+ } from "./core/contracts";
950
+ export {
951
+ canActionBattleUseTarget,
952
+ executeActionBattleUse,
953
+ getActionBattleActionConfig,
954
+ getActionBattleActionRange,
955
+ handleActionBattleProjectileDestroy,
956
+ handleActionBattleProjectileImpact,
957
+ shouldUseActionBattleUsable,
958
+ } from "./core/action-use";
797
959
  export {
798
960
  DEFAULT_ACTION_BATTLE_HIT_REACTION,
799
961
  isActionBattleEntityInvincible,
@@ -807,7 +969,21 @@ export {
807
969
  type ActionBattleEnemyAttackProfileMap,
808
970
  type NormalizedActionBattleEnemyAttackProfileMap,
809
971
  } from "./core/enemy-attack-profiles";
810
- export { resolveActionBattleWeaponAttackProfile } from "./core/equipment";
972
+ export {
973
+ resolveActionBattleWeapon,
974
+ resolveActionBattleWeaponAttackProfile,
975
+ } from "./core/equipment";
976
+ export {
977
+ ACTION_BATTLE_ENEMY_FACTION,
978
+ ACTION_BATTLE_PLAYER_FACTION,
979
+ canActionBattleTarget,
980
+ getActionBattleFaction,
981
+ getActionBattleTargets,
982
+ isActionBattleCombatEntity,
983
+ isActionBattleEvent,
984
+ isActionBattlePlayer,
985
+ matchesActionBattleTargetSelector,
986
+ } from "./core/targets";
811
987
  export {
812
988
  AiDebug,
813
989
  AiState,
package/src/types.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type {
2
2
  ActionBattleAiBehavior,
3
+ ActionBattleAiPreset,
4
+ ActionBattleTargetOptions,
3
5
  ActionBattleCombatSystem,
4
6
  ActionBattleHitHooks,
5
7
  ActionBattleHitbox,
@@ -31,12 +33,8 @@ export type ActionBattleAnimationResult =
31
33
  | undefined;
32
34
 
33
35
  export type ActionBattleAnimationEntity = {
34
- setGraphicAnimation(animationName: string, repeat: number): void;
35
- setGraphicAnimation(
36
- animationName: string,
37
- graphic: string | string[],
38
- repeat: number
39
- ): void;
36
+ setGraphicAnimation?: (...args: any[]) => unknown;
37
+ setAnimation?: (...args: any[]) => unknown;
40
38
  [key: string]: any;
41
39
  };
42
40
 
@@ -67,28 +65,6 @@ export type ActionBattleSkillTargetingResolver = (
67
65
  skill: any
68
66
  ) => ActionBattleSkillTargeting | null | undefined;
69
67
 
70
- export interface ActionBattleUiActionBarOptions {
71
- enabled?: boolean;
72
- autoOpen?: boolean;
73
- mode?: ActionBattleActionBarMode;
74
- }
75
-
76
- export interface ActionBattleUiTargetingOptions {
77
- enabled?: boolean;
78
- showGrid?: boolean;
79
- tileSize?: { width: number; height: number };
80
- colors?: {
81
- area?: number;
82
- edge?: number;
83
- cursor?: number;
84
- };
85
- }
86
-
87
- export interface ActionBattleUiOptions {
88
- actionBar?: ActionBattleUiActionBarOptions;
89
- targeting?: ActionBattleUiTargetingOptions;
90
- }
91
-
92
68
  export type ActionBattleAttackDirection =
93
69
  | "up"
94
70
  | "down"
@@ -145,6 +121,7 @@ export interface NormalizedActionBattleAttackProfile
145
121
  }
146
122
 
147
123
  export interface ActionBattleSkillOptions {
124
+ targeting?: ActionBattleSkillTargetingResolver;
148
125
  getTargeting?: ActionBattleSkillTargetingResolver;
149
126
  defaultAoeMask?: ActionBattleAoeMask;
150
127
  }
@@ -154,10 +131,6 @@ export interface ActionBattleTargetingOptions {
154
131
  allowEmptyTarget?: boolean;
155
132
  }
156
133
 
157
- export interface ActionBattleDebugOptions {
158
- attacks?: boolean;
159
- }
160
-
161
134
  export interface ActionBattleAttackOptions {
162
135
  profile?: ActionBattleAttackProfile;
163
136
  lockMovement?: boolean;
@@ -175,26 +148,123 @@ export interface ActionBattleAttackOptions {
175
148
  }
176
149
 
177
150
  export interface ActionBattleCombatOptions {
151
+ attack?: ActionBattleAttackOptions;
178
152
  damage?: ActionBattleCombatSystem["resolveDamage"];
179
153
  knockback?: ActionBattleCombatSystem["resolveKnockback"];
180
154
  hooks?: ActionBattleHitHooks;
155
+ targets?: ActionBattleTargetOptions;
181
156
  }
182
157
 
183
- export interface ActionBattleAiSystemOptions {
158
+ export interface ActionBattleAiOptions {
184
159
  behaviors?: Record<string, ActionBattleAiBehavior>;
160
+ presets?: Record<string, ActionBattleAiPreset>;
185
161
  }
186
162
 
163
+ export type ActionBattleAiSystemOptions = ActionBattleAiOptions;
164
+
187
165
  export interface ActionBattleSystemOptions {
188
166
  combat?: ActionBattleCombatOptions;
189
- ai?: ActionBattleAiSystemOptions;
167
+ ai?: ActionBattleAiOptions;
168
+ }
169
+
170
+ export type ActionBattleVisualMoment =
171
+ | "attack"
172
+ | "castSkill"
173
+ | "hit"
174
+ | "hurt"
175
+ | "defeat"
176
+ | "preview";
177
+
178
+ export interface ActionBattleVisualContext {
179
+ moment: ActionBattleVisualMoment;
180
+ entity?: any;
181
+ target?: any;
182
+ attacker?: any;
183
+ damage?: number;
184
+ defeated?: boolean;
185
+ result?: any;
186
+ skill?: any;
187
+ pattern?: string;
188
+ animations?: ActionBattleAnimationOptions;
189
+ animationDefaults?: {
190
+ animationName?: string;
191
+ repeat?: number;
192
+ };
193
+ }
194
+
195
+ export interface ActionBattleVisualHelpers {
196
+ graphic(entity: any, keyOrOptions: ActionBattleAnimationKey | ActionBattleAnimationResult): void;
197
+ flash(entity: any, options?: Record<string, any>): void;
198
+ damageText(entity: any, damageOrText?: number | string): void;
199
+ component(entity: any, id: string, params?: Record<string, any>): void;
200
+ preview(entity: any, options?: Record<string, any>): void;
201
+ }
202
+
203
+ export type ActionBattleVisualPart = (
204
+ context: ActionBattleVisualContext,
205
+ helpers: ActionBattleVisualHelpers
206
+ ) => void;
207
+
208
+ export type ActionBattleVisualComposer = (
209
+ context: ActionBattleVisualContext
210
+ ) => void;
211
+
212
+ export type ActionBattleVisualPreset = "classic" | "fx" | "none";
213
+
214
+ export type ActionBattleVisualInput =
215
+ | ActionBattleVisualPreset
216
+ | ActionBattleVisualComposer
217
+ | Partial<Record<ActionBattleVisualMoment, ActionBattleVisualPart>>;
218
+
219
+ export interface ActionBattleUiActionBarOptions {
220
+ enabled?: boolean;
221
+ autoOpen?: boolean;
222
+ mode?: ActionBattleActionBarMode;
223
+ component?: any;
224
+ }
225
+
226
+ export interface ActionBattleUiTargetingOptions {
227
+ enabled?: boolean;
228
+ component?: any;
229
+ showGrid?: boolean;
230
+ tileSize?: { width: number; height: number };
231
+ colors?: {
232
+ area?: number;
233
+ edge?: number;
234
+ cursor?: number;
235
+ };
236
+ }
237
+
238
+ export interface ActionBattleUiAttackPreviewOptions {
239
+ enabled?: boolean;
240
+ component?: any;
241
+ }
242
+
243
+ export interface ActionBattleUiGuiEntry {
244
+ id: string;
245
+ component: any;
246
+ dependencies?: Function;
247
+ }
248
+
249
+ export interface ActionBattleUiOptions {
250
+ actionBar?: boolean | ActionBattleUiActionBarOptions;
251
+ targeting?: boolean | ActionBattleUiTargetingOptions;
252
+ attackPreview?: boolean | ActionBattleUiAttackPreviewOptions;
253
+ gui?: ActionBattleUiGuiEntry[];
254
+ spriteComponents?: {
255
+ front?: any[];
256
+ back?: any[];
257
+ } | any[];
190
258
  }
191
259
 
192
260
  export interface ActionBattleOptions {
261
+ combat?: ActionBattleCombatOptions;
262
+ visual?: ActionBattleVisualInput;
193
263
  ui?: ActionBattleUiOptions;
264
+ ai?: ActionBattleAiOptions;
194
265
  skills?: ActionBattleSkillOptions;
195
266
  targeting?: ActionBattleTargetingOptions;
196
267
  attack?: ActionBattleAttackOptions;
197
- debug?: ActionBattleDebugOptions;
198
268
  animations?: ActionBattleAnimationOptions;
199
269
  systems?: ActionBattleSystemOptions;
200
270
  }