@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
@@ -0,0 +1,225 @@
1
+ import { getActionBattleOptions } from "./index4.js";
2
+ import { emitActionBattleClientVisual } from "./index15.js";
3
+ import { getActionBattleSystems } from "./index18.js";
4
+ import { applyActionBattleHit } from "./index20.js";
5
+ import { canActionBattleTarget, getActionBattleFaction, getActionBattleTargets, isActionBattleCombatEntity, isActionBattleTargetDefeated } from "./index21.js";
6
+ //#region src/core/action-use.ts
7
+ var MAXHP = null;
8
+ var projectileHandlers = /* @__PURE__ */ new Map();
9
+ var normalizeDirection = (direction) => {
10
+ const distance = Math.sqrt(direction.x * direction.x + direction.y * direction.y);
11
+ if (distance <= 0) return {
12
+ x: 0,
13
+ y: 1
14
+ };
15
+ return {
16
+ x: direction.x / distance,
17
+ y: direction.y / distance
18
+ };
19
+ };
20
+ var directionToTarget = (attacker, target) => {
21
+ const first = firstTarget(target);
22
+ if (!first) return void 0;
23
+ return normalizeDirection({
24
+ x: first.x() - attacker.x(),
25
+ y: first.y() - attacker.y()
26
+ });
27
+ };
28
+ var asArray = (value) => {
29
+ if (!value) return [];
30
+ return Array.isArray(value) ? value : [value];
31
+ };
32
+ var firstTarget = (target) => asArray(target)[0];
33
+ var resolveActionConfig = (usable) => usable?.action ?? usable?.actionBattle ?? usable?._skillInstance?.action ?? usable?._skillInstance?.actionBattle ?? usable?._skillData?.action ?? usable?._skillData?.actionBattle;
34
+ var getUseHookTarget = (usable) => usable?._skillInstance ?? usable?._skillData ?? usable;
35
+ var getUseHook = (usable) => {
36
+ const target = getUseHookTarget(usable);
37
+ return typeof target?.onUse === "function" ? {
38
+ hook: target.onUse,
39
+ target
40
+ } : void 0;
41
+ };
42
+ var isSkill = (usable, explicitSkill) => !!explicitSkill || usable?._type === "skill" || usable?.spCost !== void 0;
43
+ var consumeSkillUse = (attacker, skill) => {
44
+ const spCost = typeof skill?.spCost === "number" ? skill.spCost : 0;
45
+ if (spCost > 0) {
46
+ if (spCost > (attacker.sp ?? 0)) throw new Error(`Not enough SP to use ${skill?.id ?? skill?.name ?? "skill"}`);
47
+ const halfCost = attacker.hasEffect?.("HALF_SP_COST") || attacker.hasEffect?.("half_sp_cost");
48
+ attacker.sp -= spCost / (halfCost ? 2 : 1);
49
+ }
50
+ const hitRate = typeof skill?.hitRate === "number" ? skill.hitRate : 1;
51
+ if (Math.random() > hitRate) throw new Error(`Action battle skill failed: ${skill?.id ?? skill?.name ?? "skill"}`);
52
+ };
53
+ var applyDamageEffect = (attacker, target, skill, reaction, metadata) => {
54
+ const systems = getActionBattleSystems();
55
+ attacker.applyStates?.(target, skill);
56
+ const result = applyActionBattleHit(systems.combat, {
57
+ attacker,
58
+ target,
59
+ skill,
60
+ reaction,
61
+ metadata
62
+ });
63
+ if (!result.cancelled) {
64
+ emitActionBattleClientVisual({
65
+ moment: "hit",
66
+ entity: attacker,
67
+ target,
68
+ damage: result.damage,
69
+ result,
70
+ skill
71
+ });
72
+ target.battleAi?.handleDamage?.(attacker, {
73
+ damage: result.damage,
74
+ defeated: result.defeated,
75
+ raw: result.rawDamage,
76
+ reaction: result.reaction
77
+ });
78
+ }
79
+ return result;
80
+ };
81
+ var buildActionContext = (input) => {
82
+ const action = {};
83
+ Object.assign(action, {
84
+ attacker: input.attacker,
85
+ user: input.attacker,
86
+ target: input.target,
87
+ usable: input.usable,
88
+ skill: input.skill,
89
+ weapon: input.weapon,
90
+ action: input.action,
91
+ pattern: input.pattern,
92
+ defaultEffect(target = input.target) {
93
+ return asArray(target).map((entry) => applyDamageEffect(input.attacker, entry, input.skill, input.profile?.reaction, {
94
+ actionId: input.usable?.id,
95
+ actionType: input.usable?._type,
96
+ pattern: input.pattern
97
+ }));
98
+ },
99
+ damage(target = input.target) {
100
+ const entry = firstTarget(target);
101
+ if (!entry) return void 0;
102
+ return applyDamageEffect(input.attacker, entry, input.skill, input.profile?.reaction, {
103
+ actionId: input.usable?.id,
104
+ actionType: input.usable?._type,
105
+ pattern: input.pattern
106
+ });
107
+ },
108
+ heal(target, amount) {
109
+ if (!target || !Number.isFinite(amount) || amount <= 0) return 0;
110
+ return asArray(target).reduce((total, entry) => {
111
+ const currentHp = Number(entry.hp ?? 0);
112
+ const maxHp = (typeof entry.param === "function" ? entry.param() : entry.param)?.[MAXHP] ?? Number.POSITIVE_INFINITY;
113
+ const nextHp = Math.min(maxHp, currentHp + amount);
114
+ entry.hp = nextHp;
115
+ emitActionBattleClientVisual({
116
+ moment: "hurt",
117
+ entity: entry,
118
+ target: entry,
119
+ damage: Math.max(0, nextHp - currentHp),
120
+ skill: input.skill
121
+ });
122
+ return total + nextHp - currentHp;
123
+ }, 0);
124
+ },
125
+ projectile(options = { type: "action" }) {
126
+ const map = input.attacker.getCurrentMap?.();
127
+ if (!map?.projectiles?.emit) return [];
128
+ const projectile = {
129
+ ...input.action?.projectile ?? {},
130
+ ...options
131
+ };
132
+ const range = projectile.range ?? input.action?.range ?? 160;
133
+ const speed = projectile.speed ?? 180;
134
+ const emitted = map.projectiles.emit({
135
+ type: projectile.type,
136
+ origin: projectile.origin,
137
+ direction: projectile.direction ?? directionToTarget(input.attacker, input.target),
138
+ spreadDegrees: projectile.spreadDegrees,
139
+ accuracy: projectile.accuracy,
140
+ trajectory: projectile.trajectory ?? {
141
+ type: "linear",
142
+ speed,
143
+ range
144
+ },
145
+ collision: projectile.collision,
146
+ repeat: projectile.repeat,
147
+ pattern: projectile.pattern,
148
+ payload: {
149
+ ...projectile.payload,
150
+ actionBattle: true,
151
+ attackerId: input.attacker.id,
152
+ actionId: input.usable?.id
153
+ },
154
+ params: projectile.params,
155
+ canHit: ({ target }) => {
156
+ if (!target) return false;
157
+ return canActionBattleUseTarget(input.attacker, target, input.action?.target ?? "enemy", getActionBattleOptions().combat?.targets);
158
+ }
159
+ }, input.attacker);
160
+ for (const state of emitted) projectileHandlers.set(state.id, {
161
+ action,
162
+ onImpact: projectile.onImpact
163
+ });
164
+ return emitted;
165
+ }
166
+ });
167
+ return action;
168
+ };
169
+ var getActionBattleActionConfig = (usable) => resolveActionConfig(usable);
170
+ var getActionBattleActionRange = (usable) => resolveActionConfig(usable)?.range;
171
+ var canActionBattleUseTarget = (attacker, target, actionTarget = "enemy", options = {}) => {
172
+ if (isActionBattleTargetDefeated(target)) return false;
173
+ if (actionTarget === "self") return attacker === target;
174
+ if (actionTarget === "any") return attacker === target || isActionBattleCombatEntity(target);
175
+ if (attacker === target || !isActionBattleCombatEntity(target)) return false;
176
+ if (actionTarget === "ally") {
177
+ const attackerFaction = getActionBattleFaction(attacker, options);
178
+ const targetFaction = getActionBattleFaction(target, options);
179
+ return !!attackerFaction && attackerFaction === targetFaction;
180
+ }
181
+ return canActionBattleTarget(attacker, target, getActionBattleTargets(attacker, "hostile"), options);
182
+ };
183
+ var shouldUseActionBattleUsable = (usable, explicitSkill) => {
184
+ if (!usable) return false;
185
+ return isSkill(usable, explicitSkill) || !!getUseHook(usable) || !!resolveActionConfig(usable);
186
+ };
187
+ var executeActionBattleUse = (input) => {
188
+ if (!shouldUseActionBattleUsable(input.usable, input.skill)) return false;
189
+ const actionConfig = resolveActionConfig(input.usable);
190
+ if (isSkill(input.usable, input.skill)) consumeSkillUse(input.attacker, input.skill ?? input.usable);
191
+ const action = buildActionContext({
192
+ ...input,
193
+ action: actionConfig
194
+ });
195
+ const hook = getUseHook(input.usable);
196
+ if (input.playVisual !== false) emitActionBattleClientVisual({
197
+ moment: input.skill ? "castSkill" : "attack",
198
+ entity: input.attacker,
199
+ skill: input.skill,
200
+ target: firstTarget(input.target)
201
+ });
202
+ if (hook) {
203
+ hook.hook.call(hook.target, input.attacker, input.target, action);
204
+ return true;
205
+ }
206
+ if (actionConfig?.mode === "projectile") {
207
+ action.projectile(actionConfig.projectile);
208
+ return true;
209
+ }
210
+ action.defaultEffect(input.target);
211
+ return true;
212
+ };
213
+ var handleActionBattleProjectileImpact = (context) => {
214
+ const handler = projectileHandlers.get(context.projectile.id);
215
+ if (!handler) return;
216
+ const target = context.target;
217
+ handler.action.target = target ?? handler.action.target;
218
+ if (handler.onImpact) handler.onImpact(context, handler.action);
219
+ else handler.action.defaultEffect(target ?? void 0);
220
+ };
221
+ var handleActionBattleProjectileDestroy = (projectileId) => {
222
+ projectileHandlers.delete(projectileId);
223
+ };
224
+ //#endregion
225
+ export { canActionBattleUseTarget, executeActionBattleUse, getActionBattleActionConfig, getActionBattleActionRange, handleActionBattleProjectileDestroy, handleActionBattleProjectileImpact, shouldUseActionBattleUsable };
@@ -0,0 +1,16 @@
1
+ //#region src/core/equipment.ts
2
+ var resolveItemId = (item) => item?.id?.() ?? item?.id;
3
+ function resolveActionBattleWeapon(entity) {
4
+ const equipments = entity?.equipments?.() || [];
5
+ for (const item of equipments) {
6
+ const itemId = resolveItemId(item);
7
+ const itemData = entity?.databaseById?.(itemId);
8
+ if (itemData?._type === "weapon") return itemData;
9
+ }
10
+ return null;
11
+ }
12
+ function resolveActionBattleWeaponAttackProfile(entity) {
13
+ return resolveActionBattleWeapon(entity)?.attackProfile ?? null;
14
+ }
15
+ //#endregion
16
+ export { resolveActionBattleWeapon, resolveActionBattleWeaponAttackProfile };
@@ -0,0 +1,25 @@
1
+ //#region src/movement.ts
2
+ var UNRESOLVED_ENTITY_MESSAGE = "unable to resolve entity";
3
+ var isActionBattleMovementResolutionError = (error) => {
4
+ return (error instanceof Error ? error.message : String(error ?? "")).includes(UNRESOLVED_ENTITY_MESSAGE);
5
+ };
6
+ var hasActionBattlePhysicsBody = (entity) => {
7
+ if (!entity) return false;
8
+ const map = entity.getCurrentMap?.();
9
+ if (!map) return false;
10
+ if (typeof map.getBody !== "function" || !entity.id) return true;
11
+ return Boolean(map.getBody(entity.id));
12
+ };
13
+ var safeActionBattleDash = (entity, direction, additionalSpeed, duration) => {
14
+ if (!entity || typeof entity.dash !== "function") return false;
15
+ if (!hasActionBattlePhysicsBody(entity)) return false;
16
+ try {
17
+ entity.dash(direction, additionalSpeed, duration);
18
+ return true;
19
+ } catch (error) {
20
+ if (isActionBattleMovementResolutionError(error)) return false;
21
+ throw error;
22
+ }
23
+ };
24
+ //#endregion
25
+ export { safeActionBattleDash };
@@ -0,0 +1,107 @@
1
+ //#region src/core/ai-behavior-tree.ts
2
+ var isTreeNode = (input) => Boolean(input && typeof input.tick === "function");
3
+ var normalizeTreeResult = (result) => result ?? { status: "failure" };
4
+ var runIntentInput = (input, context) => {
5
+ if (isTreeNode(input)) return input.tick(context);
6
+ return {
7
+ status: "success",
8
+ intent: typeof input === "function" ? input(context) : input
9
+ };
10
+ };
11
+ var defineAiTree = (input) => {
12
+ if (isTreeNode(input)) return input;
13
+ return { tick(context) {
14
+ return normalizeTreeResult(input(context));
15
+ } };
16
+ };
17
+ var selector = (children) => ({ tick(context) {
18
+ for (const child of children) {
19
+ const result = defineAiTree(child).tick(context);
20
+ if (result.status !== "failure") return result;
21
+ }
22
+ return { status: "failure" };
23
+ } });
24
+ var sequence = (children) => ({ tick(context) {
25
+ let last = { status: "success" };
26
+ for (const child of children) {
27
+ last = defineAiTree(child).tick(context);
28
+ if (last.status !== "success") return last;
29
+ }
30
+ return last;
31
+ } });
32
+ var condition = (predicate) => ({ tick(context) {
33
+ return { status: predicate(context) ? "success" : "failure" };
34
+ } });
35
+ var action = (input, status = "success") => ({ tick(context) {
36
+ return {
37
+ ...runIntentInput(input, context),
38
+ status
39
+ };
40
+ } });
41
+ var decision = (resolve) => ({ tick(context) {
42
+ return {
43
+ status: "success",
44
+ decision: typeof resolve === "function" ? resolve(context) : resolve
45
+ };
46
+ } });
47
+ var rule = (predicate, then) => ({
48
+ condition: predicate,
49
+ then
50
+ });
51
+ var defineAiBehavior = (behavior) => {
52
+ const branches = [...(behavior.when ?? []).map((entry) => sequence([condition(entry.condition), action(entry.then)]))];
53
+ if (behavior.otherwise) branches.push(action(behavior.otherwise));
54
+ return selector(branches);
55
+ };
56
+ var hpBelow = (ratio) => {
57
+ return ({ self }) => self.hpPercent !== null && self.hpPercent < ratio;
58
+ };
59
+ var targetVisible = () => {
60
+ return ({ targetInfo }) => Boolean(targetInfo?.visible);
61
+ };
62
+ var targetInRange = (range) => {
63
+ return ({ self, targetInfo }) => {
64
+ if (!targetInfo) return false;
65
+ return targetInfo.distance <= (range ?? self.attackRange);
66
+ };
67
+ };
68
+ var distanceLessThan = (distance) => {
69
+ return ({ targetInfo }) => targetInfo !== null && targetInfo.distance < distance;
70
+ };
71
+ var inState = (state) => {
72
+ return ({ self }) => self.state === state;
73
+ };
74
+ var isEnemyType = (enemyType) => {
75
+ return ({ self }) => self.enemyType === enemyType;
76
+ };
77
+ var idle = () => ({ type: "idle" });
78
+ var patrol = () => ({ type: "patrol" });
79
+ var faceTarget = () => ({ type: "faceTarget" });
80
+ var chase = () => ({ type: "moveToTarget" });
81
+ var moveToTarget = chase;
82
+ var flee = () => ({ type: "fleeFromTarget" });
83
+ var fleeFromTarget = flee;
84
+ var keepDistance = (distance, tolerance) => ({
85
+ type: "keepDistance",
86
+ distance,
87
+ tolerance
88
+ });
89
+ var useAttack = (pattern) => ({
90
+ type: "useAttack",
91
+ pattern
92
+ });
93
+ var useSkill = (skill) => ({
94
+ type: "useSkill",
95
+ skill
96
+ });
97
+ var setMode = (mode) => ({
98
+ type: "setMode",
99
+ mode,
100
+ consume: false
101
+ });
102
+ var ifHpBelow = (ratio, then) => rule(hpBelow(ratio), then);
103
+ var ifTargetVisible = (then) => rule(targetVisible(), then);
104
+ var ifTargetInRange = (then, range) => rule(targetInRange(range), then);
105
+ var ifDistanceLessThan = (distance, then) => rule(distanceLessThan(distance), then);
106
+ //#endregion
107
+ export { action, chase, condition, decision, defineAiBehavior, defineAiTree, distanceLessThan, faceTarget, flee, fleeFromTarget, hpBelow, idle, ifDistanceLessThan, ifHpBelow, ifTargetInRange, ifTargetVisible, inState, isEnemyType, keepDistance, moveToTarget, patrol, rule, selector, sequence, setMode, targetInRange, targetVisible, useAttack, useSkill };