@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.
- package/CHANGELOG.md +11 -0
- package/dist/client/ai.server.d.ts +45 -8
- package/dist/client/attack-input.d.ts +3 -0
- package/dist/client/core/action-use.d.ts +18 -0
- package/dist/client/core/ai-behavior-tree.d.ts +99 -0
- package/dist/client/core/attack-runtime.d.ts +2 -0
- package/dist/client/core/defaults.d.ts +2 -1
- package/dist/client/core/equipment.d.ts +1 -0
- package/dist/client/core/targets.d.ts +15 -0
- package/dist/client/enemies/factory.d.ts +2 -0
- package/dist/client/index.d.ts +12 -7
- package/dist/client/index.js +16 -11
- package/dist/client/index10.js +32 -56
- package/dist/client/index11.js +99 -52
- package/dist/client/index12.js +76 -103
- package/dist/client/index13.js +72 -135
- package/dist/client/index14.js +67 -23
- package/dist/client/index15.js +197 -63
- package/dist/client/index16.js +112 -1337
- package/dist/client/index17.js +193 -7
- package/dist/client/index18.js +32 -58
- package/dist/client/index19.js +70 -8
- package/dist/client/index20.js +57 -501
- package/dist/client/index21.js +69 -0
- package/dist/client/index22.js +225 -0
- package/dist/client/index23.js +16 -0
- package/dist/client/index24.js +25 -0
- package/dist/client/index25.js +107 -0
- package/dist/client/index26.js +1707 -0
- package/dist/client/index27.js +12 -0
- package/dist/client/index28.js +589 -0
- package/dist/client/index4.js +79 -38
- package/dist/client/index6.js +65 -306
- package/dist/client/index7.js +33 -33
- package/dist/client/index8.js +24 -100
- package/dist/client/index9.js +293 -61
- package/dist/client/locomotion.d.ts +16 -0
- package/dist/client/movement.d.ts +14 -0
- package/dist/client/server.d.ts +7 -3
- package/dist/client/ui.d.ts +22 -0
- package/dist/client/visual.d.ts +15 -0
- package/dist/server/ai.server.d.ts +45 -8
- package/dist/server/attack-input.d.ts +3 -0
- package/dist/server/core/action-use.d.ts +18 -0
- package/dist/server/core/ai-behavior-tree.d.ts +99 -0
- package/dist/server/core/attack-runtime.d.ts +2 -0
- package/dist/server/core/defaults.d.ts +2 -1
- package/dist/server/core/equipment.d.ts +1 -0
- package/dist/server/core/targets.d.ts +15 -0
- package/dist/server/enemies/factory.d.ts +2 -0
- package/dist/server/index.d.ts +12 -7
- package/dist/server/index.js +14 -9
- package/dist/server/index10.js +64 -1336
- package/dist/server/index11.js +33 -33
- package/dist/server/index13.js +66 -11
- package/dist/server/index14.js +206 -484
- package/dist/server/index15.js +15 -9
- package/dist/server/index16.js +26 -0
- package/dist/server/index17.js +25 -0
- package/dist/server/index18.js +107 -0
- package/dist/server/index19.js +1707 -0
- package/dist/server/index2.js +10 -2
- package/dist/server/index20.js +37 -0
- package/dist/server/index21.js +588 -0
- package/dist/server/index22.js +78 -0
- package/dist/server/index23.js +12 -0
- package/dist/server/index5.js +79 -38
- package/dist/server/index6.js +192 -129
- package/dist/server/index7.js +198 -24
- package/dist/server/index8.js +28 -66
- package/dist/server/index9.js +68 -51
- package/dist/server/locomotion.d.ts +16 -0
- package/dist/server/movement.d.ts +14 -0
- package/dist/server/server.d.ts +7 -3
- package/dist/server/ui.d.ts +22 -0
- package/dist/server/visual.d.ts +15 -0
- package/package.json +5 -5
- package/src/ai.server.spec.ts +233 -0
- package/src/ai.server.ts +627 -108
- package/src/animations.spec.ts +40 -0
- package/src/animations.ts +31 -9
- package/src/attack-input.spec.ts +51 -0
- package/src/attack-input.ts +59 -0
- package/src/client.ts +75 -62
- package/src/config.ts +84 -37
- package/src/core/action-use.spec.ts +317 -0
- package/src/core/action-use.ts +386 -0
- package/src/core/ai-behavior-tree.spec.ts +116 -0
- package/src/core/ai-behavior-tree.ts +272 -0
- package/src/core/attack-profile.spec.ts +46 -0
- package/src/core/attack-runtime.spec.ts +35 -0
- package/src/core/attack-runtime.ts +32 -0
- package/src/core/context.ts +9 -0
- package/src/core/contracts.ts +146 -1
- package/src/core/defaults.ts +56 -0
- package/src/core/equipment.ts +9 -5
- package/src/core/targets.spec.ts +112 -0
- package/src/core/targets.ts +147 -0
- package/src/enemies/factory.ts +8 -0
- package/src/index.ts +111 -2
- package/src/locomotion.spec.ts +51 -0
- package/src/locomotion.ts +48 -0
- package/src/movement.spec.ts +78 -0
- package/src/movement.ts +46 -0
- package/src/server.ts +242 -66
- package/src/types.ts +105 -35
- package/src/ui.ts +113 -0
- package/src/visual.spec.ts +166 -0
- package/src/visual.ts +285 -0
- 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 };
|