@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.
- package/CHANGELOG.md +22 -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 +10 -10
- 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/components/action-bar.ce +2 -2
- 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
package/dist/client/index20.js
CHANGED
|
@@ -1,504 +1,60 @@
|
|
|
1
|
-
import "./index2.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const hitbox = typeof event.hitbox === "function" ? event.hitbox() : event.hitbox;
|
|
58
|
-
return {
|
|
59
|
-
x: event.x(),
|
|
60
|
-
y: event.y(),
|
|
61
|
-
width: hitbox?.w ?? 32,
|
|
62
|
-
height: hitbox?.h ?? 32
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
var getVisibleActionEvents = (player, map, hitboxes) => {
|
|
66
|
-
if (!map) return [];
|
|
67
|
-
const eventsById = /* @__PURE__ */ new Map();
|
|
68
|
-
const addEvent = (event) => {
|
|
69
|
-
if (!event) return;
|
|
70
|
-
if (!(typeof map.isEventVisibleForPlayer === "function" ? map.isEventVisibleForPlayer(event, player) : true)) return;
|
|
71
|
-
eventsById.set(event.id, event);
|
|
72
|
-
};
|
|
73
|
-
const collisions = map.getCollisions?.(player.id);
|
|
74
|
-
if (Array.isArray(collisions)) collisions.forEach((id) => addEvent(map.getEvent(id)));
|
|
75
|
-
const direction = typeof player.getDirection === "function" ? player.getDirection() : void 0;
|
|
76
|
-
const interactionCollisions = map.getInteractionCollisions?.(player.id, direction);
|
|
77
|
-
if (Array.isArray(interactionCollisions)) interactionCollisions.forEach((id) => addEvent(map.getEvent(id)));
|
|
78
|
-
for (const event of map.getEvents()) {
|
|
79
|
-
const rect = eventRect(event);
|
|
80
|
-
if (hitboxes.some((hitbox) => rectsOverlap(hitbox, rect))) addEvent(event);
|
|
81
|
-
}
|
|
82
|
-
return Array.from(eventsById.values());
|
|
83
|
-
};
|
|
84
|
-
var isActionReservedForNormalEvent = (player, map, hitboxes) => {
|
|
85
|
-
const events = getVisibleActionEvents(player, map, hitboxes);
|
|
86
|
-
return events.length > 0 && !events.some(isBattleEvent);
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* Get knockback force from player's equipped weapon
|
|
90
|
-
*
|
|
91
|
-
* Retrieves the knockbackForce property from the player's equipped weapon.
|
|
92
|
-
* Falls back to DEFAULT_KNOCKBACK.force if no weapon or property is set.
|
|
93
|
-
*
|
|
94
|
-
* @param player - The player to get weapon knockback from
|
|
95
|
-
* @returns Knockback force value
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```ts
|
|
99
|
-
* // Player with weapon having knockbackForce: 80
|
|
100
|
-
* const force = getPlayerWeaponKnockbackForce(player); // 80
|
|
101
|
-
*
|
|
102
|
-
* // No weapon equipped
|
|
103
|
-
* const force = getPlayerWeaponKnockbackForce(player); // 50 (default)
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
function getPlayerWeaponKnockbackForce(player) {
|
|
107
|
-
try {
|
|
108
|
-
const equipments = player.equipments?.() || [];
|
|
109
|
-
for (const item of equipments) {
|
|
110
|
-
const itemData = player.databaseById?.(item.id());
|
|
111
|
-
if (itemData?._type === "weapon" && itemData.knockbackForce !== void 0) return itemData.knockbackForce;
|
|
112
|
-
}
|
|
113
|
-
} catch {}
|
|
114
|
-
return DEFAULT_KNOCKBACK.force;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Apply hit from player to target (event with AI)
|
|
118
|
-
*
|
|
119
|
-
* Handles damage calculation, knockback based on weapon, and visual effects.
|
|
120
|
-
* Can be customized using hooks.
|
|
121
|
-
*
|
|
122
|
-
* @param player - The attacking player
|
|
123
|
-
* @param target - The event being hit
|
|
124
|
-
* @param hooks - Optional hooks for customizing hit behavior
|
|
125
|
-
* @returns Hit result if AI exists, undefined otherwise
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* // Basic hit
|
|
130
|
-
* const result = applyPlayerHitToEvent(player, event);
|
|
131
|
-
*
|
|
132
|
-
* // With custom hooks
|
|
133
|
-
* const result = applyPlayerHitToEvent(player, event, {
|
|
134
|
-
* onBeforeHit(result) {
|
|
135
|
-
* result.knockbackForce *= 2; // Double knockback
|
|
136
|
-
* return result;
|
|
137
|
-
* },
|
|
138
|
-
* onAfterHit(result) {
|
|
139
|
-
* if (result.defeated) {
|
|
140
|
-
* player.gold += 10;
|
|
141
|
-
* }
|
|
142
|
-
* }
|
|
143
|
-
* });
|
|
144
|
-
* ```
|
|
145
|
-
*/
|
|
146
|
-
function applyPlayerHitToEvent(player, target, hooks, metadata) {
|
|
147
|
-
const ai = target.battleAi;
|
|
148
|
-
if (!ai) return void 0;
|
|
149
|
-
const systems = getActionBattleSystems();
|
|
150
|
-
const result = applyActionBattleHit({
|
|
151
|
-
...systems.combat,
|
|
152
|
-
hooks: hooks ? {
|
|
153
|
-
...systems.combat.hooks,
|
|
154
|
-
beforeHit(context) {
|
|
155
|
-
const before = systems.combat.hooks?.beforeHit?.(context);
|
|
156
|
-
if (before === false) return false;
|
|
157
|
-
const nextContext = before || context;
|
|
158
|
-
const legacyResult = toLegacyHitResult(nextContext);
|
|
159
|
-
const modified = hooks.onBeforeHit?.(legacyResult);
|
|
160
|
-
if (!modified) return nextContext;
|
|
161
|
-
return {
|
|
162
|
-
...nextContext,
|
|
163
|
-
damage: {
|
|
164
|
-
damage: modified.damage,
|
|
165
|
-
defeated: modified.defeated,
|
|
166
|
-
raw: nextContext.damage?.raw
|
|
167
|
-
},
|
|
168
|
-
knockback: {
|
|
169
|
-
force: modified.knockbackForce,
|
|
170
|
-
duration: modified.knockbackDuration,
|
|
171
|
-
direction: nextContext.knockback?.direction
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
},
|
|
175
|
-
afterHit(result) {
|
|
176
|
-
systems.combat.hooks?.afterHit?.(result);
|
|
177
|
-
hooks.onAfterHit?.(result);
|
|
178
|
-
}
|
|
179
|
-
} : systems.combat.hooks
|
|
180
|
-
}, {
|
|
181
|
-
attacker: player,
|
|
182
|
-
target,
|
|
183
|
-
metadata,
|
|
184
|
-
reaction: metadata?.reaction
|
|
185
|
-
});
|
|
186
|
-
if (!result.cancelled) ai.handleDamage(player, {
|
|
187
|
-
damage: result.damage,
|
|
188
|
-
defeated: result.defeated,
|
|
189
|
-
raw: result.rawDamage,
|
|
190
|
-
reaction: result.reaction
|
|
191
|
-
});
|
|
1
|
+
import { isActionBattleEntityInvincible, setActionBattleInvincibility } from "./index2.js";
|
|
2
|
+
//#region src/core/hit.ts
|
|
3
|
+
var applyActionBattleHit = (system, context) => {
|
|
4
|
+
let hitContext = { ...context };
|
|
5
|
+
const before = system.hooks?.beforeHit?.(hitContext);
|
|
6
|
+
if (before === false) return {
|
|
7
|
+
damage: 0,
|
|
8
|
+
knockbackForce: 0,
|
|
9
|
+
knockbackDuration: 0,
|
|
10
|
+
defeated: false,
|
|
11
|
+
attacker: hitContext.attacker,
|
|
12
|
+
target: hitContext.target,
|
|
13
|
+
cancelled: true,
|
|
14
|
+
metadata: hitContext.metadata
|
|
15
|
+
};
|
|
16
|
+
if (before) hitContext = before;
|
|
17
|
+
if (isActionBattleEntityInvincible(hitContext.target)) return {
|
|
18
|
+
damage: 0,
|
|
19
|
+
knockbackForce: 0,
|
|
20
|
+
knockbackDuration: 0,
|
|
21
|
+
defeated: false,
|
|
22
|
+
attacker: hitContext.attacker,
|
|
23
|
+
target: hitContext.target,
|
|
24
|
+
cancelled: true,
|
|
25
|
+
metadata: hitContext.metadata,
|
|
26
|
+
reaction: hitContext.reaction
|
|
27
|
+
};
|
|
28
|
+
const damage = hitContext.damage ?? system.resolveDamage({
|
|
29
|
+
attacker: hitContext.attacker,
|
|
30
|
+
target: hitContext.target,
|
|
31
|
+
skill: hitContext.skill,
|
|
32
|
+
pattern: hitContext.pattern
|
|
33
|
+
});
|
|
34
|
+
hitContext.damage = damage;
|
|
35
|
+
const afterDamage = system.hooks?.afterDamage?.(hitContext);
|
|
36
|
+
if (afterDamage) hitContext = afterDamage;
|
|
37
|
+
const knockback = hitContext.knockback ?? system.resolveKnockback({
|
|
38
|
+
attacker: hitContext.attacker,
|
|
39
|
+
target: hitContext.target,
|
|
40
|
+
damage
|
|
41
|
+
});
|
|
42
|
+
hitContext.knockback = knockback;
|
|
43
|
+
if (!damage.defeated && knockback.force > 0 && knockback.direction) hitContext.target.knockback?.(knockback.direction, knockback.force, knockback.duration);
|
|
44
|
+
if (!damage.defeated && hitContext.reaction?.invincibilityMs) setActionBattleInvincibility(hitContext.target, hitContext.reaction.invincibilityMs);
|
|
45
|
+
const result = {
|
|
46
|
+
damage: damage.damage,
|
|
47
|
+
knockbackForce: knockback.force,
|
|
48
|
+
knockbackDuration: knockback.duration,
|
|
49
|
+
defeated: damage.defeated,
|
|
50
|
+
attacker: hitContext.attacker,
|
|
51
|
+
target: hitContext.target,
|
|
52
|
+
rawDamage: damage.raw,
|
|
53
|
+
reaction: hitContext.reaction,
|
|
54
|
+
metadata: hitContext.metadata
|
|
55
|
+
};
|
|
56
|
+
system.hooks?.afterHit?.(result);
|
|
192
57
|
return result;
|
|
193
|
-
}
|
|
194
|
-
var toLegacyHitResult = (context) => ({
|
|
195
|
-
damage: context.damage?.damage ?? 0,
|
|
196
|
-
knockbackForce: context.knockback?.force ?? getPlayerWeaponKnockbackForce(context.attacker),
|
|
197
|
-
knockbackDuration: context.knockback?.duration ?? DEFAULT_KNOCKBACK.duration,
|
|
198
|
-
defeated: context.damage?.defeated ?? false,
|
|
199
|
-
attacker: context.attacker,
|
|
200
|
-
target: context.target
|
|
201
|
-
});
|
|
202
|
-
var resolvePlayerAttackHitboxes = (player, directionKey, options, profile) => {
|
|
203
|
-
const configuredHitboxes = {
|
|
204
|
-
...DEFAULT_PLAYER_ATTACK_HITBOXES,
|
|
205
|
-
...options.attack?.hitboxes,
|
|
206
|
-
...profile.hitboxes
|
|
207
|
-
};
|
|
208
|
-
const hitboxConfig = configuredHitboxes[directionKey] || configuredHitboxes.default;
|
|
209
|
-
const defaultHitboxes = [{
|
|
210
|
-
x: player.x() + hitboxConfig.offsetX,
|
|
211
|
-
y: player.y() + hitboxConfig.offsetY,
|
|
212
|
-
width: hitboxConfig.width,
|
|
213
|
-
height: hitboxConfig.height
|
|
214
|
-
}];
|
|
215
|
-
return options.attack?.resolveHitboxes?.({
|
|
216
|
-
player,
|
|
217
|
-
direction: directionKey,
|
|
218
|
-
defaultHitboxes
|
|
219
|
-
}) ?? defaultHitboxes;
|
|
220
|
-
};
|
|
221
|
-
var mergeAttackProfileOverrides = (base, override) => ({
|
|
222
|
-
...base,
|
|
223
|
-
...override,
|
|
224
|
-
reaction: {
|
|
225
|
-
...base.reaction,
|
|
226
|
-
...override.reaction
|
|
227
|
-
},
|
|
228
|
-
hitboxes: {
|
|
229
|
-
...base.hitboxes,
|
|
230
|
-
...override.hitboxes
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
var resolvePlayerAttackProfile = (player, options) => {
|
|
234
|
-
const baseProfile = getNormalizedActionBattleAttackProfile(options);
|
|
235
|
-
const weaponProfile = resolveActionBattleWeaponAttackProfile(player);
|
|
236
|
-
if (!weaponProfile) return baseProfile;
|
|
237
|
-
return normalizeActionBattleAttackProfile(mergeAttackProfileOverrides(baseProfile, weaponProfile), {
|
|
238
|
-
lockMovement: options.attack?.lockMovement,
|
|
239
|
-
lockDurationMs: options.attack?.lockDurationMs,
|
|
240
|
-
hitboxes: options.attack?.hitboxes
|
|
241
|
-
});
|
|
242
|
-
};
|
|
243
|
-
var resolveSignal = (value) => typeof value === "function" ? value() : value;
|
|
244
|
-
var resolveItemData = (player, itemId) => {
|
|
245
|
-
try {
|
|
246
|
-
return player.databaseById?.(itemId);
|
|
247
|
-
} catch {
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
var resolveSkillData = (player, skillId) => {
|
|
252
|
-
try {
|
|
253
|
-
return player.databaseById?.(skillId);
|
|
254
|
-
} catch {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
var resolveSkillTargeting = (player, skillId, options) => {
|
|
259
|
-
const skillsOptions = options.skills;
|
|
260
|
-
const skillData = resolveSkillData(player, skillId);
|
|
261
|
-
if (skillsOptions?.getTargeting) return skillsOptions.getTargeting(skillData);
|
|
262
|
-
const range = skillData?.range ?? skillData?.targeting?.range ?? skillData?.targeting?.distance;
|
|
263
|
-
const aoeMask = skillData?.aoeMask ?? skillData?.targeting?.aoeMask ?? skillData?.targeting?.mask;
|
|
264
|
-
if (range === void 0 && aoeMask === void 0) return null;
|
|
265
|
-
return {
|
|
266
|
-
range: range ?? 0,
|
|
267
|
-
aoeMask
|
|
268
|
-
};
|
|
269
|
-
};
|
|
270
|
-
var normalizeMaskRows = (mask) => {
|
|
271
|
-
if (!mask) return [];
|
|
272
|
-
if (Array.isArray(mask)) return mask;
|
|
273
|
-
return mask.trim().split("\n").map((row) => row.replace(/\r/g, ""));
|
|
274
|
-
};
|
|
275
|
-
var buildActionBarData = (player, options) => {
|
|
276
|
-
return {
|
|
277
|
-
items: (player.items?.() || []).map((item) => {
|
|
278
|
-
const id = item.id?.() ?? item.id;
|
|
279
|
-
const data = resolveItemData(player, id);
|
|
280
|
-
const name = resolveSignal(data?.name) ?? resolveSignal(item.name) ?? id;
|
|
281
|
-
const description = resolveSignal(data?.description) ?? resolveSignal(item.description) ?? "";
|
|
282
|
-
const icon = resolveSignal(data?.icon) ?? resolveSignal(item.icon);
|
|
283
|
-
const quantity = resolveSignal(item.quantity) ?? 1;
|
|
284
|
-
const consumable = resolveSignal(data?.consumable);
|
|
285
|
-
const itemType = resolveSignal(data?._type);
|
|
286
|
-
return {
|
|
287
|
-
id,
|
|
288
|
-
name,
|
|
289
|
-
description,
|
|
290
|
-
icon,
|
|
291
|
-
quantity,
|
|
292
|
-
usable: quantity > 0 && consumable !== false && (itemType ? itemType === "item" : true)
|
|
293
|
-
};
|
|
294
|
-
}),
|
|
295
|
-
skills: (player.skills?.() || []).map((skill) => {
|
|
296
|
-
const id = skill.id?.() ?? skill.id;
|
|
297
|
-
const data = resolveSkillData(player, id) || skill;
|
|
298
|
-
const name = resolveSignal(data?.name) ?? resolveSignal(skill.name) ?? id;
|
|
299
|
-
const description = resolveSignal(data?.description) ?? resolveSignal(skill.description) ?? "";
|
|
300
|
-
const icon = resolveSignal(data?.icon) ?? resolveSignal(skill.icon);
|
|
301
|
-
const spCost = resolveSignal(data?.spCost) ?? resolveSignal(skill.spCost) ?? 0;
|
|
302
|
-
const usable = spCost <= player.sp;
|
|
303
|
-
const targeting = resolveSkillTargeting(player, id, options);
|
|
304
|
-
const skillEntry = {
|
|
305
|
-
id,
|
|
306
|
-
name,
|
|
307
|
-
description,
|
|
308
|
-
icon,
|
|
309
|
-
spCost,
|
|
310
|
-
usable,
|
|
311
|
-
range: targeting?.range ?? 0
|
|
312
|
-
};
|
|
313
|
-
if (targeting) {
|
|
314
|
-
const mask = targeting.aoeMask ?? options.skills?.defaultAoeMask;
|
|
315
|
-
if (mask) skillEntry.aoeMask = normalizeMaskRows(mask);
|
|
316
|
-
}
|
|
317
|
-
return skillEntry;
|
|
318
|
-
})
|
|
319
|
-
};
|
|
320
|
-
};
|
|
321
|
-
var ensureActionBarGui = (player, options) => {
|
|
322
|
-
const gui = player.getGui?.("action-battle-action-bar") || player.gui("action-battle-action-bar");
|
|
323
|
-
if (!gui.__actionBattleReady) {
|
|
324
|
-
gui.__actionBattleReady = true;
|
|
325
|
-
gui.on("useItem", ({ id }) => {
|
|
326
|
-
try {
|
|
327
|
-
player.useItem(id);
|
|
328
|
-
} catch {}
|
|
329
|
-
gui.update(buildActionBarData(player, options));
|
|
330
|
-
});
|
|
331
|
-
gui.on("useSkill", ({ id, target }) => {
|
|
332
|
-
handleActionBattleSkillUse(player, id, target, options);
|
|
333
|
-
gui.update(buildActionBarData(player, options));
|
|
334
|
-
});
|
|
335
|
-
gui.on("refresh", () => {
|
|
336
|
-
gui.update(buildActionBarData(player, options));
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
return gui;
|
|
340
|
-
};
|
|
341
|
-
var openActionBattleActionBar = (player, rawOptions = {}) => {
|
|
342
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
343
|
-
ensureActionBarGui(player, options).open(buildActionBarData(player, options));
|
|
344
|
-
};
|
|
345
|
-
var updateActionBattleActionBar = (player, rawOptions = {}) => {
|
|
346
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
347
|
-
const gui = player.getGui?.(ACTION_BATTLE_ACTION_BAR_GUI_ID);
|
|
348
|
-
if (gui) gui.update(buildActionBarData(player, options));
|
|
349
|
-
};
|
|
350
|
-
var getTileSize = (map) => ({
|
|
351
|
-
width: map?.tileWidth ?? 32,
|
|
352
|
-
height: map?.tileHeight ?? 32
|
|
353
|
-
});
|
|
354
|
-
var getEntityTile = (entity, tileSize) => {
|
|
355
|
-
const hitbox = entity.hitbox?.() || {
|
|
356
|
-
w: tileSize.width,
|
|
357
|
-
h: tileSize.height
|
|
358
|
-
};
|
|
359
|
-
return {
|
|
360
|
-
x: Math.floor((entity.x() + hitbox.w / 2) / tileSize.width),
|
|
361
|
-
y: Math.floor((entity.y() + hitbox.h / 2) / tileSize.height)
|
|
362
|
-
};
|
|
363
|
-
};
|
|
364
|
-
var handleActionBattleSkillUse = (player, skillId, target, options) => {
|
|
365
|
-
const skillData = resolveSkillData(player, skillId);
|
|
366
|
-
const map = player.getCurrentMap();
|
|
367
|
-
if (!map) {
|
|
368
|
-
playActionBattleAnimation("castSkill", player, options.animations, { skill: skillData });
|
|
369
|
-
player.useSkill(skillId);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
const targeting = resolveSkillTargeting(player, skillId, options);
|
|
373
|
-
if (!targeting || !target) {
|
|
374
|
-
playActionBattleAnimation("castSkill", player, options.animations, { skill: skillData });
|
|
375
|
-
player.useSkill(skillId);
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
const tileSize = getTileSize(map);
|
|
379
|
-
const origin = getEntityTile(player, tileSize);
|
|
380
|
-
const targetTile = {
|
|
381
|
-
x: target.x,
|
|
382
|
-
y: target.y
|
|
383
|
-
};
|
|
384
|
-
if (manhattanDistance(origin, targetTile) > targeting.range) return;
|
|
385
|
-
const mask = parseAoeMask(targeting.aoeMask || options.skills?.defaultAoeMask);
|
|
386
|
-
const affected = /* @__PURE__ */ new Set();
|
|
387
|
-
mask.cells.forEach((cell) => {
|
|
388
|
-
const x = targetTile.x + cell.dx;
|
|
389
|
-
const y = targetTile.y + cell.dy;
|
|
390
|
-
affected.add(`${x},${y}`);
|
|
391
|
-
});
|
|
392
|
-
const targets = [];
|
|
393
|
-
const affects = options.targeting?.affects || "events";
|
|
394
|
-
if (affects === "events" || affects === "both") map.getEvents().forEach((event) => {
|
|
395
|
-
const tile = getEntityTile(event, tileSize);
|
|
396
|
-
if (affected.has(`${tile.x},${tile.y}`)) targets.push(event);
|
|
397
|
-
});
|
|
398
|
-
if (affects === "players" || affects === "both") map.getPlayers().forEach((other) => {
|
|
399
|
-
if (other.id === player.id) return;
|
|
400
|
-
const tile = getEntityTile(other, tileSize);
|
|
401
|
-
if (affected.has(`${tile.x},${tile.y}`)) targets.push(other);
|
|
402
|
-
});
|
|
403
|
-
if (!options.targeting?.allowEmptyTarget && targets.length === 0) return;
|
|
404
|
-
playActionBattleAnimation("castSkill", player, options.animations, {
|
|
405
|
-
skill: skillData,
|
|
406
|
-
target: targets[0]
|
|
407
|
-
});
|
|
408
|
-
player.useSkill(skillId, targets);
|
|
409
|
-
};
|
|
410
|
-
var createActionBattleServer = (rawOptions = {}) => {
|
|
411
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
412
|
-
setActionBattleOptions(options);
|
|
413
|
-
setActionBattleSystems(options);
|
|
414
|
-
return defineModule({
|
|
415
|
-
player: {
|
|
416
|
-
/**
|
|
417
|
-
* Handle player input for combat actions
|
|
418
|
-
*
|
|
419
|
-
* When a player presses the action key, create an attack hitbox
|
|
420
|
-
* that can damage AI enemies within range and knockback the event.
|
|
421
|
-
* Knockback force is based on the player's equipped weapon.
|
|
422
|
-
* Triggers attack animation and visual effects.
|
|
423
|
-
*
|
|
424
|
-
* @param player - The player performing the action
|
|
425
|
-
* @param input - Input data containing pressed keys
|
|
426
|
-
*/
|
|
427
|
-
onInput(player, input) {
|
|
428
|
-
if (input.action == Control.Action) {
|
|
429
|
-
const map = player.getCurrentMap();
|
|
430
|
-
const direction = player.getDirection();
|
|
431
|
-
const attackProfile = resolvePlayerAttackProfile(player, options);
|
|
432
|
-
const hitboxes = resolvePlayerAttackHitboxes(player, direction, options, attackProfile);
|
|
433
|
-
if (isActionReservedForNormalEvent(player, map, hitboxes)) return;
|
|
434
|
-
const lockMovement = attackProfile.movementLock;
|
|
435
|
-
const lockDirection = attackProfile.directionLock;
|
|
436
|
-
const lockDurationMs = attackProfile.totalDurationMs ?? DEFAULT_ATTACK_LOCK_DURATION_MS;
|
|
437
|
-
const actionLocked = (lockMovement || lockDirection) && lockDurationMs > 0;
|
|
438
|
-
if (actionLocked && !beginPlayerAttackLock(player, map, Math.max(0, lockDurationMs), {
|
|
439
|
-
movement: lockMovement,
|
|
440
|
-
direction: lockDirection
|
|
441
|
-
})) return;
|
|
442
|
-
playActionBattleAnimation("attack", player, options.animations);
|
|
443
|
-
if (actionLocked) player.animationFixed = true;
|
|
444
|
-
const attackId = createActionBattleAttackId(player.id, attackProfile.id);
|
|
445
|
-
const hitTracker = new ActionBattleHitTracker(attackProfile.hitPolicy);
|
|
446
|
-
if (options.debug?.attacks) console.log("[ActionBattle] player attack", {
|
|
447
|
-
attackId,
|
|
448
|
-
playerId: player.id,
|
|
449
|
-
profile: attackProfile.id,
|
|
450
|
-
hitboxes
|
|
451
|
-
});
|
|
452
|
-
scheduleActionBattleStartup(attackProfile, () => {
|
|
453
|
-
map?.createMovingHitbox(hitboxes, { speed: resolveActionBattleHitboxSpeed(attackProfile, hitboxes.length) }).subscribe({ next(hits) {
|
|
454
|
-
hits.forEach((hit) => {
|
|
455
|
-
if (hit instanceof RpgEvent) {
|
|
456
|
-
if (!hitTracker.tryHit(hit)) return;
|
|
457
|
-
if (applyPlayerHitToEvent(player, hit, void 0, {
|
|
458
|
-
attackId,
|
|
459
|
-
attackProfileId: attackProfile.id,
|
|
460
|
-
reaction: attackProfile.reaction
|
|
461
|
-
})?.defeated) console.log(`Player ${player.id} defeated AI ${hit.id}`);
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
} });
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
onConnected(player) {
|
|
469
|
-
if (options.ui?.actionBar?.enabled && options.ui?.actionBar?.autoOpen) openActionBattleActionBar(player, options);
|
|
470
|
-
}
|
|
471
|
-
},
|
|
472
|
-
event: {
|
|
473
|
-
/**
|
|
474
|
-
* Handle player detection when entering AI vision
|
|
475
|
-
*
|
|
476
|
-
* Called when a player enters an AI event's vision range.
|
|
477
|
-
* The AI will start pursuing and attacking the player.
|
|
478
|
-
*
|
|
479
|
-
* @param event - The AI event
|
|
480
|
-
* @param player - The player entering vision
|
|
481
|
-
* @param shape - The vision shape
|
|
482
|
-
*/
|
|
483
|
-
onDetectInShape(event, player, shape) {
|
|
484
|
-
event.battleAi?.onDetectInShape(player, shape);
|
|
485
|
-
},
|
|
486
|
-
/**
|
|
487
|
-
* Handle player leaving AI vision
|
|
488
|
-
*
|
|
489
|
-
* Called when a player leaves an AI event's vision range.
|
|
490
|
-
* The AI will stop pursuing the player.
|
|
491
|
-
*
|
|
492
|
-
* @param event - The AI event
|
|
493
|
-
* @param player - The player leaving vision
|
|
494
|
-
* @param shape - The vision shape
|
|
495
|
-
*/
|
|
496
|
-
onDetectOutShape(event, player, shape) {
|
|
497
|
-
event.battleAi?.onDetectOutShape(player, shape);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
58
|
};
|
|
502
|
-
createActionBattleServer();
|
|
503
59
|
//#endregion
|
|
504
|
-
export {
|
|
60
|
+
export { applyActionBattleHit };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/core/targets.ts
|
|
2
|
+
var RpgEvent = null;
|
|
3
|
+
var RpgPlayer = null;
|
|
4
|
+
var ACTION_BATTLE_PLAYER_FACTION = "players";
|
|
5
|
+
var ACTION_BATTLE_ENEMY_FACTION = "enemies";
|
|
6
|
+
var getBattleAi = (entity) => entity?.battleAi;
|
|
7
|
+
var getActionBattleEntityKind = (entity) => {
|
|
8
|
+
if (getBattleAi(entity)) return "event";
|
|
9
|
+
if (entity instanceof RpgEvent) return "event";
|
|
10
|
+
if (typeof entity?.attachShape === "function") return "event";
|
|
11
|
+
if (entity instanceof RpgPlayer) return "player";
|
|
12
|
+
return "player";
|
|
13
|
+
};
|
|
14
|
+
var isActionBattlePlayer = (entity) => getActionBattleEntityKind(entity) === "player";
|
|
15
|
+
var isActionBattleEvent = (entity) => getActionBattleEntityKind(entity) === "event";
|
|
16
|
+
var isActionBattleCombatEntity = (entity) => {
|
|
17
|
+
if (!entity) return false;
|
|
18
|
+
if (isActionBattlePlayer(entity)) return true;
|
|
19
|
+
return !!getBattleAi(entity);
|
|
20
|
+
};
|
|
21
|
+
var getActionBattleFaction = (entity, options = {}) => {
|
|
22
|
+
const configured = options.getFaction?.(entity);
|
|
23
|
+
if (configured !== void 0) return configured;
|
|
24
|
+
const battleAi = getBattleAi(entity);
|
|
25
|
+
if (battleAi && typeof battleAi.getFaction === "function") {
|
|
26
|
+
const faction = battleAi.getFaction();
|
|
27
|
+
if (faction !== void 0) return faction;
|
|
28
|
+
}
|
|
29
|
+
const entityFaction = entity.actionBattleFaction ?? entity.faction;
|
|
30
|
+
if (entityFaction !== void 0) return String(entityFaction);
|
|
31
|
+
if (isActionBattlePlayer(entity)) return ACTION_BATTLE_PLAYER_FACTION;
|
|
32
|
+
if (battleAi) return ACTION_BATTLE_ENEMY_FACTION;
|
|
33
|
+
};
|
|
34
|
+
var getActionBattleTargets = (entity, fallback) => {
|
|
35
|
+
const battleAi = getBattleAi(entity);
|
|
36
|
+
if (battleAi && typeof battleAi.getTargets === "function") return battleAi.getTargets();
|
|
37
|
+
return entity.actionBattleTargets ?? fallback;
|
|
38
|
+
};
|
|
39
|
+
var isActionBattleTargetDefeated = (target) => {
|
|
40
|
+
if (!target) return true;
|
|
41
|
+
const hp = target.hp;
|
|
42
|
+
return typeof hp === "number" && hp <= 0;
|
|
43
|
+
};
|
|
44
|
+
var matchesActionBattleTargetSelector = (selector, context) => {
|
|
45
|
+
if (!selector) return false;
|
|
46
|
+
if (typeof selector === "function") return selector(context);
|
|
47
|
+
if (selector === "all") return true;
|
|
48
|
+
if (selector === "players") return isActionBattlePlayer(context.target);
|
|
49
|
+
if (selector === "events") return isActionBattleEvent(context.target);
|
|
50
|
+
if (selector === "hostile") return !!context.attackerFaction && !!context.targetFaction && context.attackerFaction !== context.targetFaction;
|
|
51
|
+
if (Array.isArray(selector)) return !!context.targetFaction && selector.includes(context.targetFaction);
|
|
52
|
+
return false;
|
|
53
|
+
};
|
|
54
|
+
var canActionBattleTarget = (attacker, target, selector, options = {}) => {
|
|
55
|
+
if (attacker === target) return false;
|
|
56
|
+
if (!isActionBattleCombatEntity(target)) return false;
|
|
57
|
+
if (isActionBattleTargetDefeated(target)) return false;
|
|
58
|
+
const context = {
|
|
59
|
+
attacker,
|
|
60
|
+
target,
|
|
61
|
+
attackerFaction: getActionBattleFaction(attacker, options),
|
|
62
|
+
targetFaction: getActionBattleFaction(target, options)
|
|
63
|
+
};
|
|
64
|
+
const allowed = options.canTarget?.(context);
|
|
65
|
+
if (allowed !== void 0) return allowed;
|
|
66
|
+
return matchesActionBattleTargetSelector(selector, context);
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
export { ACTION_BATTLE_ENEMY_FACTION, ACTION_BATTLE_PLAYER_FACTION, canActionBattleTarget, getActionBattleFaction, getActionBattleTargets, isActionBattleCombatEntity, isActionBattleEvent, isActionBattlePlayer, isActionBattleTargetDefeated, matchesActionBattleTargetSelector };
|