@rpgjs/action-battle 5.0.0-beta.11 → 5.0.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +11 -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 +5 -5
  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/config.ts +84 -37
  86. package/src/core/action-use.spec.ts +317 -0
  87. package/src/core/action-use.ts +386 -0
  88. package/src/core/ai-behavior-tree.spec.ts +116 -0
  89. package/src/core/ai-behavior-tree.ts +272 -0
  90. package/src/core/attack-profile.spec.ts +46 -0
  91. package/src/core/attack-runtime.spec.ts +35 -0
  92. package/src/core/attack-runtime.ts +32 -0
  93. package/src/core/context.ts +9 -0
  94. package/src/core/contracts.ts +146 -1
  95. package/src/core/defaults.ts +56 -0
  96. package/src/core/equipment.ts +9 -5
  97. package/src/core/targets.spec.ts +112 -0
  98. package/src/core/targets.ts +147 -0
  99. package/src/enemies/factory.ts +8 -0
  100. package/src/index.ts +111 -2
  101. package/src/locomotion.spec.ts +51 -0
  102. package/src/locomotion.ts +48 -0
  103. package/src/movement.spec.ts +78 -0
  104. package/src/movement.ts +46 -0
  105. package/src/server.ts +242 -66
  106. package/src/types.ts +105 -35
  107. package/src/ui.ts +113 -0
  108. package/src/visual.spec.ts +166 -0
  109. package/src/visual.ts +285 -0
  110. package/README.md +0 -1242
@@ -1,55 +1,102 @@
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
1
+ import { actionBattleTargetingState, actionBattleUiOptions } from "./index5.js";
2
+ import { parseAoeMask } from "./index10.js";
3
+ import { RpgClientEngine, inject } from "@rpgjs/client";
4
+ import { Container, Graphics, computed, cond, h, useDefineProps, useProps } from "canvasengine";
5
+ //#region src/components/targeting-overlay.ce
6
+ function component($$props) {
7
+ useProps($$props);
8
+ const { object } = useDefineProps($$props)();
9
+ const engine = inject(RpgClientEngine);
10
+ const isCurrentPlayer = computed(() => {
11
+ if (!object?.id) return false;
12
+ return (typeof object.id === "function" ? object.id() : object.id) === engine.playerId;
10
13
  });
14
+ const uiOptions = computed(() => actionBattleUiOptions());
15
+ const targetingState = computed(() => actionBattleTargetingState());
16
+ const shouldRender = computed(() => {
17
+ if (!isCurrentPlayer()) return false;
18
+ if (!uiOptions().targeting?.enabled) return false;
19
+ return targetingState().active;
20
+ });
21
+ const getTileSize = () => {
22
+ const uiTile = uiOptions().targeting?.tileSize;
23
+ if (uiTile?.width && uiTile?.height) return uiTile;
24
+ const hitbox = object.hitbox?.() || {
25
+ w: 32,
26
+ h: 32
27
+ };
28
+ return {
29
+ width: hitbox.w || 32,
30
+ height: hitbox.h || 32
31
+ };
32
+ };
33
+ const getOriginTile = () => {
34
+ const hitbox = object.hitbox?.() || {
35
+ w: 32,
36
+ h: 32
37
+ };
38
+ const tileSize = getTileSize();
39
+ return {
40
+ x: Math.floor((object.x() + hitbox.w / 2) / tileSize.width),
41
+ y: Math.floor((object.y() + hitbox.h / 2) / tileSize.height)
42
+ };
43
+ };
44
+ const toColor = (value, fallback) => {
45
+ if (typeof value === "number") return value;
46
+ return fallback;
47
+ };
48
+ const drawGrid = (g) => {
49
+ const state = targetingState();
50
+ if (!state.active) return;
51
+ const tileSize = getTileSize();
52
+ const origin = getOriginTile();
53
+ const target = {
54
+ x: origin.x + state.offset.x,
55
+ y: origin.y + state.offset.y
56
+ };
57
+ const mask = parseAoeMask(state.aoeMask);
58
+ const colors = uiOptions().targeting?.colors || {};
59
+ const areaColor = toColor(colors.area, 3120887);
60
+ const edgeColor = toColor(colors.edge, 1796760);
61
+ const cursorColor = toColor(colors.cursor, 16765286);
62
+ const showGrid = uiOptions().targeting?.showGrid !== false;
63
+ const playerX = object.x();
64
+ const playerY = object.y();
65
+ g.clear();
66
+ mask.cells.forEach((cell) => {
67
+ const tileX = target.x + cell.dx;
68
+ const tileY = target.y + cell.dy;
69
+ const worldX = tileX * tileSize.width;
70
+ const worldY = tileY * tileSize.height;
71
+ const relX = worldX - playerX;
72
+ const relY = worldY - playerY;
73
+ g.rect(relX, relY, tileSize.width, tileSize.height);
74
+ g.fill({
75
+ color: areaColor,
76
+ alpha: .35
77
+ });
78
+ if (showGrid) {
79
+ g.rect(relX, relY, tileSize.width, tileSize.height);
80
+ g.stroke({
81
+ color: edgeColor,
82
+ alpha: .9,
83
+ width: 1
84
+ });
85
+ }
86
+ });
87
+ const cursorWorldX = target.x * tileSize.width;
88
+ const cursorWorldY = target.y * tileSize.height;
89
+ const cursorRelX = cursorWorldX - playerX;
90
+ const cursorRelY = cursorWorldY - playerY;
91
+ g.rect(cursorRelX, cursorRelY, tileSize.width, tileSize.height);
92
+ g.stroke({
93
+ color: cursorColor,
94
+ alpha: 1,
95
+ width: 2
96
+ });
97
+ };
98
+ return h(Container, null, cond(shouldRender, () => h(Graphics, { draw: drawGrid })));
11
99
  }
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
- hitPolicy;
35
- hitTargets = /* @__PURE__ */ new Set();
36
- constructor(hitPolicy) {
37
- this.hitPolicy = hitPolicy;
38
- }
39
- canHit(target) {
40
- if (this.hitPolicy === "allowRepeatHits") return true;
41
- const key = getTargetKey(target);
42
- return !key || !this.hitTargets.has(key);
43
- }
44
- recordHit(target) {
45
- const key = getTargetKey(target);
46
- if (key) this.hitTargets.add(key);
47
- }
48
- tryHit(target) {
49
- if (!this.canHit(target)) return false;
50
- this.recordHit(target);
51
- return true;
52
- }
53
- };
100
+ var __ce_component = component;
54
101
  //#endregion
55
- export { ACTION_BATTLE_HITBOX_FRAME_MS, ActionBattleHitTracker, createActionBattleAttackId, getNormalizedActionBattleAttackProfile, resolveActionBattleHitboxSpeed, scheduleActionBattleStartup };
102
+ export { __ce_component as default };
@@ -1,106 +1,79 @@
1
- import { normalizeActionBattleOptions } from "./index4.js";
2
- import { setActionBattleOptions, startAttackPreview, stopAttackPreview } from "./index5.js";
3
- import __ce_component from "./index6.js";
4
- import __ce_component$1 from "./index8.js";
5
- import __ce_component$2 from "./index9.js";
6
- import { resolveActionBattleAnimation } from "./index10.js";
7
- import { getNormalizedActionBattleAttackProfile } from "./index11.js";
8
- import { PrebuiltComponentAnimations, RpgClientEngine, RpgGui, inject } from "@rpgjs/client";
9
- import { defineModule } from "@rpgjs/common";
10
- //#region src/client.ts
11
- var DEFAULT_ATTACK_LOCK_DURATION_MS = 350;
12
- var beginLocalPlayerAttackLock = (engine, durationMs, locks) => {
13
- if (durationMs <= 0) return true;
14
- const player = engine.scene?.getCurrentPlayer?.();
15
- if (!player) return true;
16
- const runtimePlayer = player;
17
- const now = Date.now();
18
- if (typeof runtimePlayer.__actionBattleAttackLockedUntil === "number" && runtimePlayer.__actionBattleAttackLockedUntil > now) return false;
19
- const lockId = (runtimePlayer.__actionBattleAttackLockId ?? 0) + 1;
20
- runtimePlayer.__actionBattleAttackLockId = lockId;
21
- runtimePlayer.__actionBattleAttackLockedUntil = now + durationMs;
22
- const previousCanMove = player.canMove;
23
- const previousDirectionFixed = player.directionFixed;
24
- const previousAnimationFixed = player.animationFixed;
25
- if (locks.movement) {
26
- if (typeof engine.interruptCurrentPlayerMovement === "function") engine.interruptCurrentPlayerMovement(player);
27
- else engine.scene?.stopMovement?.(player);
28
- player.canMove = false;
29
- }
30
- if (locks.direction) player.directionFixed = true;
31
- player.animationFixed = true;
32
- setTimeout(() => {
33
- if (runtimePlayer.__actionBattleAttackLockId !== lockId) return;
34
- runtimePlayer.__actionBattleAttackLockedUntil = 0;
35
- player.canMove = previousCanMove;
36
- player.directionFixed = previousDirectionFixed;
37
- player.animationFixed = previousAnimationFixed;
38
- }, durationMs);
39
- return true;
40
- };
41
- var resolveLocalPlayerDirection = (player) => {
42
- if (typeof player.getDirection === "function") return player.getDirection();
43
- if (typeof player.direction === "function") return player.direction();
44
- return player.direction ?? "down";
45
- };
46
- var playLocalPlayerAttackAnimation = (player, options) => {
47
- if (!player || typeof player.setAnimation !== "function") return;
48
- const animation = resolveActionBattleAnimation("attack", player, options.animations);
49
- if (!animation) return;
50
- if (animation.graphic !== void 0) {
51
- player.setAnimation(animation.animationName, animation.graphic, animation.repeat);
52
- return;
53
- }
54
- player.setAnimation(animation.animationName, animation.repeat);
55
- };
56
- var showLocalAttackPreview = (player, options) => {
57
- if (!player || options.attack?.showPreview === false) return;
58
- const durationMs = Math.max(1, options.attack?.previewDurationMs ?? 180);
59
- const previewId = startAttackPreview({
60
- direction: resolveLocalPlayerDirection(player),
61
- durationMs,
62
- color: options.attack?.previewColor,
63
- accentColor: options.attack?.previewAccentColor
1
+ import { actionBattleAttackPreviewState } from "./index5.js";
2
+ import { RpgClientEngine, inject } from "@rpgjs/client";
3
+ import { Container, Graphics, computed, cond, h, signal, tick, useDefineProps, useProps } from "canvasengine";
4
+ //#region src/components/attack-preview.ce
5
+ function component($$props) {
6
+ useProps($$props);
7
+ const { object } = useDefineProps($$props)();
8
+ const engine = inject(RpgClientEngine);
9
+ const now = signal(Date.now());
10
+ tick(() => {
11
+ if (actionBattleAttackPreviewState().active) now.set(Date.now());
64
12
  });
65
- setTimeout(() => stopAttackPreview(previewId), durationMs);
66
- };
67
- var createActionBattleClient = (options = {}) => {
68
- const normalized = normalizeActionBattleOptions(options);
69
- setActionBattleOptions(normalized);
70
- const actionBarEnabled = normalized.ui?.actionBar?.enabled;
71
- const componentsInFront = [...normalized.ui?.targeting?.enabled ? [__ce_component$1] : [], __ce_component$2];
72
- const hitComponent = PrebuiltComponentAnimations?.Hit;
73
- return defineModule({
74
- componentAnimations: hitComponent ? [{
75
- id: "hit",
76
- component: hitComponent
77
- }] : [],
78
- gui: actionBarEnabled ? [{
79
- id: "action-battle-action-bar",
80
- component: __ce_component,
81
- dependencies: () => {
82
- return [inject(RpgClientEngine).scene.currentPlayer];
83
- }
84
- }] : [],
85
- sprite: { componentsInFront },
86
- sceneMap: { onAfterLoading() {
87
- if (actionBarEnabled && normalized.ui?.actionBar?.autoOpen) inject(RpgGui).display("action-battle-action-bar");
88
- } },
89
- engine: { onInput(engine, { input }) {
90
- if (input !== "action") return;
91
- const player = engine.scene?.getCurrentPlayer?.();
92
- if (!player) return;
93
- const attackProfile = getNormalizedActionBattleAttackProfile(normalized);
94
- const lockDurationMs = Math.max(0, attackProfile.totalDurationMs ?? DEFAULT_ATTACK_LOCK_DURATION_MS);
95
- if (attackProfile.movementLock || attackProfile.directionLock) beginLocalPlayerAttackLock(engine, lockDurationMs, {
96
- movement: attackProfile.movementLock,
97
- direction: attackProfile.directionLock
98
- });
99
- playLocalPlayerAttackAnimation(player, normalized);
100
- showLocalAttackPreview(player, normalized);
101
- } }
13
+ const isCurrentPlayer = computed(() => {
14
+ if (!object?.id) return false;
15
+ return (typeof object.id === "function" ? object.id() : object.id) === engine.playerId;
102
16
  });
103
- };
104
- var client_default = createActionBattleClient();
17
+ const preview = computed(() => actionBattleAttackPreviewState());
18
+ const progress = computed(() => {
19
+ const state = preview();
20
+ if (!state.active) return 1;
21
+ const elapsed = now() - state.startedAt;
22
+ return Math.max(0, Math.min(1, elapsed / state.durationMs));
23
+ });
24
+ const shouldRender = computed(() => {
25
+ const state = preview();
26
+ return isCurrentPlayer() && state.active && progress() < 1;
27
+ });
28
+ const getHitbox = () => object.hitbox?.() || {
29
+ w: 32,
30
+ h: 32
31
+ };
32
+ const drawRect = (g, x, y, width, height, color, alpha) => {
33
+ g.rect(x, y, width, height);
34
+ g.fill({
35
+ color,
36
+ alpha
37
+ });
38
+ };
39
+ const drawSlash = (g) => {
40
+ g.clear();
41
+ if (!shouldRender()) return;
42
+ const state = preview();
43
+ const p = progress();
44
+ const alpha = Math.sin(Math.PI * p);
45
+ if (alpha <= 0) return;
46
+ const hitbox = getHitbox();
47
+ const width = hitbox.w || 32;
48
+ const height = hitbox.h || 32;
49
+ const reach = 16 + 18 * p;
50
+ const thickness = 4 + 3 * (1 - p);
51
+ const color = state.color;
52
+ const accent = state.accentColor;
53
+ if (state.direction === "left") {
54
+ drawRect(g, -reach - 6, height * .24, reach, thickness, accent, alpha * .55);
55
+ drawRect(g, -reach - 10, height * .46, reach + 4, thickness + 2, color, alpha);
56
+ drawRect(g, -reach - 6, height * .7, reach, thickness, accent, alpha * .4);
57
+ return;
58
+ }
59
+ if (state.direction === "right") {
60
+ drawRect(g, width + 6, height * .24, reach, thickness, accent, alpha * .55);
61
+ drawRect(g, width + 6, height * .46, reach + 4, thickness + 2, color, alpha);
62
+ drawRect(g, width + 6, height * .7, reach, thickness, accent, alpha * .4);
63
+ return;
64
+ }
65
+ if (state.direction === "up") {
66
+ drawRect(g, width * .24, -reach - 6, thickness, reach, accent, alpha * .55);
67
+ drawRect(g, width * .46, -reach - 10, thickness + 2, reach + 4, color, alpha);
68
+ drawRect(g, width * .7, -reach - 6, thickness, reach, accent, alpha * .4);
69
+ return;
70
+ }
71
+ drawRect(g, width * .24, height + 6, thickness, reach, accent, alpha * .55);
72
+ drawRect(g, width * .46, height + 6, thickness + 2, reach + 4, color, alpha);
73
+ drawRect(g, width * .7, height + 6, thickness, reach, accent, alpha * .4);
74
+ };
75
+ return h(Container, null, cond(shouldRender, () => h(Graphics, { draw: drawSlash })));
76
+ }
77
+ var __ce_component = component;
105
78
  //#endregion
106
- export { createActionBattleClient, client_default as default };
79
+ export { __ce_component as default };
@@ -1,143 +1,80 @@
1
- //#region src/core/defaults.ts
2
- var DEFAULT_CORE_KNOCKBACK = {
3
- force: 50,
4
- duration: 300
1
+ import __ce_component from "./index9.js";
2
+ import __ce_component$1 from "./index11.js";
3
+ import __ce_component$2 from "./index12.js";
4
+ import { RpgClientEngine, inject } from "@rpgjs/client";
5
+ //#region src/ui.ts
6
+ var ActionBattleUi = {
7
+ ActionBar: __ce_component,
8
+ TargetingOverlay: __ce_component$1,
9
+ AttackPreview: __ce_component$2
5
10
  };
6
- var CoreAttackPattern = {
7
- Melee: "melee",
8
- Combo: "combo",
9
- Charged: "charged",
10
- Zone: "zone",
11
- DashAttack: "dashAttack"
12
- };
13
- var CoreEnemyType = {
14
- Aggressive: "aggressive",
15
- Defensive: "defensive",
16
- Ranged: "ranged",
17
- Tank: "tank",
18
- Berserker: "berserker"
19
- };
20
- var DEFAULT_ZELDA_PLAYER_HITBOXES = {
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
11
+ var normalizeToggle = (value, defaults) => {
12
+ if (value === false) return {
13
+ ...defaults,
14
+ enabled: false
69
15
  };
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);
16
+ if (value === true) return {
17
+ ...defaults,
18
+ enabled: true
19
+ };
20
+ if (value === void 0) return { ...defaults };
84
21
  return {
85
- damage: raw?.damage ?? 0,
86
- defeated: target.hp <= 0,
87
- raw
22
+ ...defaults,
23
+ ...value,
24
+ enabled: value.enabled ?? defaults.enabled
88
25
  };
89
26
  };
90
- var defaultKnockbackResolver = (context) => {
91
- const weapon = context.weapon ?? resolveEquippedWeapon(context.attacker);
27
+ function createActionBattleUi(input = "classic") {
28
+ if (input === "classic") return {};
29
+ return input;
30
+ }
31
+ function resolveActionBattleUi(options = {}) {
32
+ const actionBar = normalizeToggle(options.actionBar, {
33
+ enabled: false,
34
+ autoOpen: false,
35
+ mode: "both",
36
+ component: ActionBattleUi.ActionBar
37
+ });
38
+ const targeting = normalizeToggle(options.targeting, {
39
+ enabled: true,
40
+ showGrid: true,
41
+ component: ActionBattleUi.TargetingOverlay,
42
+ colors: {
43
+ area: 3120887,
44
+ edge: 1796760,
45
+ cursor: 16765286
46
+ }
47
+ });
48
+ const attackPreview = normalizeToggle(options.attackPreview, {
49
+ enabled: true,
50
+ component: ActionBattleUi.AttackPreview
51
+ });
52
+ const gui = [...options.gui ?? []];
53
+ if (actionBar.enabled && actionBar.component) gui.unshift({
54
+ id: "action-battle-action-bar",
55
+ component: actionBar.component,
56
+ dependencies: () => {
57
+ return [inject(RpgClientEngine).scene.currentPlayer];
58
+ }
59
+ });
60
+ const configuredSpriteComponents = Array.isArray(options.spriteComponents) ? {
61
+ front: options.spriteComponents,
62
+ back: []
63
+ } : options.spriteComponents ?? {};
92
64
  return {
93
- force: weapon?.knockbackForce ?? DEFAULT_CORE_KNOCKBACK.force,
94
- duration: weapon?.knockbackDuration ?? DEFAULT_CORE_KNOCKBACK.duration,
95
- direction: resolveDirection(context.attacker, context.target)
65
+ gui,
66
+ sprite: {
67
+ componentsInFront: [
68
+ ...targeting.enabled && targeting.component ? [targeting.component] : [],
69
+ ...attackPreview.enabled && attackPreview.component ? [attackPreview.component] : [],
70
+ ...configuredSpriteComponents.front ?? []
71
+ ],
72
+ componentsBehind: configuredSpriteComponents.back ?? []
73
+ },
74
+ actionBar,
75
+ targeting,
76
+ attackPreview
96
77
  };
97
- };
98
- var defaultCombatSystem = {
99
- resolveHitboxes: createDefaultPlayerHitboxResolver(),
100
- resolveDamage: defaultRpgjsDamageResolver,
101
- resolveKnockback: defaultKnockbackResolver
102
- };
103
- var defaultEnemyBehaviors = {
104
- [CoreEnemyType.Aggressive]: ({ hpPercent }) => ({
105
- mode: hpPercent !== null && hpPercent < .15 ? "retreat" : "assault",
106
- attackPatterns: [
107
- CoreAttackPattern.Melee,
108
- CoreAttackPattern.Combo,
109
- CoreAttackPattern.DashAttack
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
- };
78
+ }
142
79
  //#endregion
143
- export { DEFAULT_ZELDA_PLAYER_HITBOXES, createDefaultPlayerHitboxResolver, defaultActionBattleSystems, defaultCombatSystem, defaultEnemyBehaviors, defaultKnockbackResolver, defaultRpgjsDamageResolver };
80
+ export { ActionBattleUi, createActionBattleUi, resolveActionBattleUi };
@@ -1,25 +1,69 @@
1
- import { defaultActionBattleSystems } from "./index13.js";
2
- //#region src/core/context.ts
3
- var mergeSystems = (options = {}) => ({
4
- combat: {
5
- ...defaultActionBattleSystems.combat,
6
- resolveDamage: options.systems?.combat?.damage ?? defaultActionBattleSystems.combat.resolveDamage,
7
- resolveKnockback: options.systems?.combat?.knockback ?? defaultActionBattleSystems.combat.resolveKnockback,
8
- hooks: {
9
- ...defaultActionBattleSystems.combat.hooks,
10
- ...options.systems?.combat?.hooks
11
- }
12
- },
13
- ai: { behaviors: {
14
- ...defaultActionBattleSystems.ai.behaviors,
15
- ...options.systems?.ai?.behaviors
16
- } }
17
- });
18
- var currentActionBattleSystems = mergeSystems();
19
- var setActionBattleSystems = (options = {}) => {
20
- currentActionBattleSystems = mergeSystems(options);
1
+ var playResolvedAnimation = (entity, animation) => {
2
+ if (typeof entity.setGraphicAnimation === "function") {
3
+ if (animation.graphic !== void 0) entity.setGraphicAnimation(animation.animationName, animation.graphic, animation.repeat);
4
+ else entity.setGraphicAnimation(animation.animationName, animation.repeat);
5
+ return;
6
+ }
7
+ if (typeof entity.setAnimation === "function") if (animation.graphic !== void 0) entity.setAnimation(animation.animationName, animation.graphic, animation.repeat);
8
+ else entity.setAnimation(animation.animationName, animation.repeat);
21
9
  };
22
- var getActionBattleSystems = () => currentActionBattleSystems;
23
- var createActionBattleSystems = mergeSystems;
10
+ var DEFAULT_ANIMATION_BY_KEY = {
11
+ attack: "attack",
12
+ hurt: "hurt",
13
+ die: "die",
14
+ castSkill: "skill",
15
+ castSpell: "skill"
16
+ };
17
+ var getConfiguredAnimation = (key, animations) => {
18
+ if (!animations) return {
19
+ hasConfiguredAnimation: false,
20
+ configured: void 0
21
+ };
22
+ const hasConfiguredAnimation = Object.prototype.hasOwnProperty.call(animations, key);
23
+ if (hasConfiguredAnimation) return {
24
+ hasConfiguredAnimation,
25
+ configured: animations[key]
26
+ };
27
+ if (key === "castSkill") return {
28
+ hasConfiguredAnimation: Object.prototype.hasOwnProperty.call(animations, "castSpell"),
29
+ configured: animations.castSpell
30
+ };
31
+ return {
32
+ hasConfiguredAnimation: false,
33
+ configured: void 0
34
+ };
35
+ };
36
+ function resolveActionBattleAnimation(key, entity, animations, context, defaults = {}) {
37
+ const defaultAnimationName = defaults.animationName ?? DEFAULT_ANIMATION_BY_KEY[key];
38
+ const defaultRepeat = defaults.repeat ?? 1;
39
+ const { hasConfiguredAnimation, configured: configuredAnimation } = getConfiguredAnimation(key, animations);
40
+ if (!hasConfiguredAnimation && key !== "attack") return null;
41
+ const configured = hasConfiguredAnimation ? configuredAnimation : defaultAnimationName;
42
+ const result = typeof configured === "function" ? configured(entity, context) : configured;
43
+ if (result == null) return null;
44
+ if (typeof result === "string") return {
45
+ animationName: result,
46
+ repeat: defaultRepeat,
47
+ waitEnd: false
48
+ };
49
+ return {
50
+ animationName: result.animationName ?? defaultAnimationName,
51
+ graphic: result.graphic,
52
+ repeat: result.repeat ?? defaultRepeat,
53
+ waitEnd: result.waitEnd ?? false,
54
+ delayMs: result.delayMs
55
+ };
56
+ }
57
+ function playActionBattleAnimation(key, entity, animations, context, defaults = {}) {
58
+ const animation = resolveActionBattleAnimation(key, entity, animations, context, defaults);
59
+ if (!animation) return null;
60
+ playResolvedAnimation(entity, animation);
61
+ return animation;
62
+ }
63
+ function getActionBattleAnimationRemovalDelay(animation) {
64
+ if (!animation) return 0;
65
+ if (animation.delayMs !== void 0) return animation.delayMs;
66
+ return animation.waitEnd ? 500 : 0;
67
+ }
24
68
  //#endregion
25
- export { createActionBattleSystems, getActionBattleSystems, setActionBattleSystems };
69
+ export { getActionBattleAnimationRemovalDelay, playActionBattleAnimation, resolveActionBattleAnimation };