@rpgjs/action-battle 5.0.0-beta.1 → 5.0.0-beta.11
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 +49 -0
- package/LICENSE +19 -0
- package/README.md +392 -22
- package/dist/{ai.server.d.ts → client/ai.server.d.ts} +90 -28
- package/dist/client/animations.d.ts +16 -0
- package/dist/{client.d.ts → client/client.d.ts} +3 -2
- package/dist/{config.d.ts → client/config.d.ts} +2 -0
- package/dist/client/core/attack-profile.d.ts +9 -0
- package/dist/client/core/attack-runtime.d.ts +20 -0
- package/dist/client/core/context.d.ts +5 -0
- package/dist/client/core/defaults.d.ts +81 -0
- package/dist/client/core/enemy-attack-profiles.d.ts +6 -0
- package/dist/client/core/equipment.d.ts +2 -0
- package/dist/client/core/hit-reaction.d.ts +5 -0
- package/dist/client/core/hit.d.ts +2 -0
- package/dist/client/enemies/factory.d.ts +7 -0
- package/dist/client/index.d.ts +21 -0
- package/dist/client/index.js +24 -31
- package/dist/client/index10.js +61 -0
- package/dist/client/index11.js +55 -0
- package/dist/client/index12.js +106 -0
- package/dist/client/index13.js +143 -0
- package/dist/client/index14.js +25 -0
- package/dist/client/index15.js +72 -0
- package/dist/client/index16.js +1343 -0
- package/dist/client/index17.js +13 -0
- package/dist/client/index18.js +60 -0
- package/dist/client/index19.js +10 -0
- package/dist/client/index2.js +30 -45
- package/dist/client/index20.js +504 -0
- package/dist/client/index3.js +45 -1288
- package/dist/client/index4.js +105 -330
- package/dist/client/index5.js +84 -291
- package/dist/client/index6.js +309 -95
- package/dist/client/index7.js +35 -59
- package/dist/client/index8.js +101 -54
- package/dist/client/index9.js +79 -30
- package/dist/{server.d.ts → client/server.d.ts} +12 -4
- package/dist/client/ui/state.d.ts +35 -0
- package/dist/server/ai.server.d.ts +569 -0
- package/dist/server/animations.d.ts +16 -0
- package/dist/server/config.d.ts +5 -0
- package/dist/server/core/attack-profile.d.ts +9 -0
- package/dist/server/core/attack-runtime.d.ts +20 -0
- package/dist/server/core/context.d.ts +5 -0
- package/dist/server/core/defaults.d.ts +81 -0
- package/dist/server/core/enemy-attack-profiles.d.ts +6 -0
- package/dist/server/core/equipment.d.ts +2 -0
- package/dist/server/core/hit-reaction.d.ts +5 -0
- package/dist/server/core/hit.d.ts +2 -0
- package/dist/server/enemies/factory.d.ts +7 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.js +23 -31
- package/dist/server/index10.js +1342 -0
- package/dist/server/index11.js +37 -0
- package/dist/server/index12.js +60 -0
- package/dist/server/index13.js +13 -0
- package/dist/server/index14.js +503 -0
- package/dist/server/index15.js +10 -0
- package/dist/server/index2.js +59 -332
- package/dist/server/index3.js +29 -1286
- package/dist/server/index4.js +45 -53
- package/dist/server/index5.js +107 -29
- package/dist/server/index6.js +143 -0
- package/dist/server/index7.js +25 -0
- package/dist/server/index8.js +72 -0
- package/dist/server/index9.js +55 -0
- package/dist/server/server.d.ts +106 -0
- package/dist/server/targeting.d.ts +19 -0
- package/package.json +15 -15
- package/src/ai.server.spec.ts +120 -0
- package/src/ai.server.ts +515 -91
- package/src/animations.ts +149 -0
- package/src/canvas-engine-shim.ts +4 -0
- package/src/client.ts +130 -2
- package/src/components/action-bar.ce +7 -5
- package/src/components/attack-preview.ce +90 -0
- package/src/config.ts +61 -0
- package/src/core/attack-profile.spec.ts +118 -0
- package/src/core/attack-profile.ts +100 -0
- package/src/core/attack-runtime.spec.ts +103 -0
- package/src/core/attack-runtime.ts +83 -0
- package/src/core/context.ts +35 -0
- package/src/core/contracts.ts +126 -0
- package/src/core/defaults.ts +162 -0
- package/src/core/enemy-attack-profiles.spec.ts +35 -0
- package/src/core/enemy-attack-profiles.ts +103 -0
- package/src/core/equipment.spec.ts +37 -0
- package/src/core/equipment.ts +17 -0
- package/src/core/hit-reaction.spec.ts +43 -0
- package/src/core/hit-reaction.ts +70 -0
- package/src/core/hit.spec.ts +111 -0
- package/src/core/hit.ts +92 -0
- package/src/enemies/factory.ts +25 -0
- package/src/index.ts +94 -1
- package/src/server.ts +427 -93
- package/src/targeting.spec.ts +24 -0
- package/src/types/canvas-engine.d.ts +4 -0
- package/src/types.ts +148 -0
- package/src/ui/state.ts +57 -0
- package/dist/index.d.ts +0 -11
- package/dist/ui/state.d.ts +0 -18
- /package/dist/{targeting.d.ts → client/targeting.d.ts} +0 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import { RpgEvent, RpgPlayer } from '@rpgjs/server';
|
|
2
|
+
import { ActionBattleEnemyAttackProfileMap } from './core/enemy-attack-profiles';
|
|
3
|
+
import { ActionBattleDamageResult } from './core/contracts';
|
|
4
|
+
import { NormalizedActionBattleHitReactionProfile, ActionBattleAnimationOptions } from './types';
|
|
5
|
+
type RpgEventWithBattleAi = RpgEvent & {
|
|
6
|
+
battleAi?: BattleAi;
|
|
7
|
+
};
|
|
8
|
+
export interface BattleAiRewardItem {
|
|
9
|
+
item?: any;
|
|
10
|
+
itemId?: string;
|
|
11
|
+
amount?: number;
|
|
12
|
+
chance?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface BattleAiRewards {
|
|
15
|
+
exp?: number;
|
|
16
|
+
gold?: number;
|
|
17
|
+
items?: Array<BattleAiRewardItem | string>;
|
|
18
|
+
showNotification?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface BattleAiDefeatReward {
|
|
21
|
+
readonly awarded: boolean;
|
|
22
|
+
giveTo(player?: RpgPlayer | null): void;
|
|
23
|
+
}
|
|
24
|
+
export interface BattleAiDefeatedContext {
|
|
25
|
+
event: RpgEvent;
|
|
26
|
+
attacker?: RpgPlayer;
|
|
27
|
+
reward: BattleAiDefeatReward;
|
|
28
|
+
remove: () => void;
|
|
29
|
+
}
|
|
30
|
+
export type BattleAiDefeatedCallback = (context: BattleAiDefeatedContext) => void;
|
|
31
|
+
export type BattleAiLegacyDefeatedCallback = (event: RpgEvent, attacker?: RpgPlayer) => void;
|
|
32
|
+
export interface BattleAiBaseOptions {
|
|
33
|
+
enemyType?: EnemyType;
|
|
34
|
+
attackCooldown?: number;
|
|
35
|
+
visionRange?: number;
|
|
36
|
+
attackRange?: number;
|
|
37
|
+
dodgeChance?: number;
|
|
38
|
+
dodgeCooldown?: number;
|
|
39
|
+
fleeThreshold?: number;
|
|
40
|
+
attackSkill?: any;
|
|
41
|
+
attackPatterns?: AttackPattern[];
|
|
42
|
+
attackProfiles?: ActionBattleEnemyAttackProfileMap;
|
|
43
|
+
patrolWaypoints?: Array<{
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
}>;
|
|
47
|
+
groupBehavior?: boolean;
|
|
48
|
+
moveToCooldown?: number;
|
|
49
|
+
retreatCooldown?: number;
|
|
50
|
+
poise?: number;
|
|
51
|
+
hitstunMs?: number;
|
|
52
|
+
invincibilityMs?: number;
|
|
53
|
+
behavior?: {
|
|
54
|
+
baseScore?: number;
|
|
55
|
+
updateInterval?: number;
|
|
56
|
+
minStateDuration?: number;
|
|
57
|
+
assaultThreshold?: number;
|
|
58
|
+
retreatThreshold?: number;
|
|
59
|
+
};
|
|
60
|
+
behaviorKey?: string;
|
|
61
|
+
animations?: ActionBattleAnimationOptions;
|
|
62
|
+
rewards?: BattleAiRewards;
|
|
63
|
+
autoAwardRewards?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface BattleAiOptions extends BattleAiBaseOptions {
|
|
66
|
+
/** Callback called when the AI is defeated */
|
|
67
|
+
onDefeated?: BattleAiDefeatedCallback;
|
|
68
|
+
}
|
|
69
|
+
export interface BattleAiLegacyOptions extends BattleAiBaseOptions {
|
|
70
|
+
/** @deprecated Use the context callback signature instead. */
|
|
71
|
+
onDefeated?: BattleAiLegacyDefeatedCallback;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Hit result data returned after applying damage
|
|
75
|
+
*
|
|
76
|
+
* Contains information about the hit including damage dealt,
|
|
77
|
+
* knockback parameters, and whether the target was defeated.
|
|
78
|
+
* Used by hooks to customize hit behavior.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const hitResult: HitResult = {
|
|
83
|
+
* damage: 25,
|
|
84
|
+
* knockbackForce: 50,
|
|
85
|
+
* knockbackDuration: 300,
|
|
86
|
+
* defeated: false,
|
|
87
|
+
* attacker: this.event,
|
|
88
|
+
* target: player
|
|
89
|
+
* };
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export interface HitResult {
|
|
93
|
+
/** Damage dealt to the target */
|
|
94
|
+
damage: number;
|
|
95
|
+
/** Knockback force applied (from weapon or default) */
|
|
96
|
+
knockbackForce: number;
|
|
97
|
+
/** Knockback duration in milliseconds */
|
|
98
|
+
knockbackDuration: number;
|
|
99
|
+
/** Whether the target was defeated */
|
|
100
|
+
defeated: boolean;
|
|
101
|
+
/** The entity that attacked */
|
|
102
|
+
attacker: RpgEvent | RpgPlayer;
|
|
103
|
+
/** The entity that was hit */
|
|
104
|
+
target: RpgPlayer | RpgEvent;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Hook options for customizing hit behavior
|
|
108
|
+
*
|
|
109
|
+
* Allows overriding knockback parameters and adding custom effects
|
|
110
|
+
* when a hit is applied.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const hooks: ApplyHitHooks = {
|
|
115
|
+
* onBeforeHit(result) {
|
|
116
|
+
* // Reduce knockback for armored enemies
|
|
117
|
+
* if (result.target.hasState('armored')) {
|
|
118
|
+
* result.knockbackForce *= 0.5;
|
|
119
|
+
* }
|
|
120
|
+
* return result;
|
|
121
|
+
* },
|
|
122
|
+
* onAfterHit(result) {
|
|
123
|
+
* // Add poison effect on hit
|
|
124
|
+
* if (Math.random() < 0.3) {
|
|
125
|
+
* result.target.addState('poison');
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* };
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export interface ApplyHitHooks {
|
|
132
|
+
/**
|
|
133
|
+
* Called before the hit is applied
|
|
134
|
+
* Can modify the hit result before damage and knockback
|
|
135
|
+
*
|
|
136
|
+
* @param result - The hit result data
|
|
137
|
+
* @returns Modified hit result or void to use original
|
|
138
|
+
*/
|
|
139
|
+
onBeforeHit?: (result: HitResult) => HitResult | void;
|
|
140
|
+
/**
|
|
141
|
+
* Called after the hit is applied
|
|
142
|
+
* Used for side effects like adding states, playing sounds, etc.
|
|
143
|
+
*
|
|
144
|
+
* @param result - The final hit result data
|
|
145
|
+
*/
|
|
146
|
+
onAfterHit?: (result: HitResult) => void;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* AI Debug Logger
|
|
150
|
+
*
|
|
151
|
+
* Conditional logging utility for AI behavior debugging.
|
|
152
|
+
* Enable by setting `AiDebug.enabled = true` or via environment variable `RPGJS_DEBUG_AI=1`
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* // Enable debug logging
|
|
157
|
+
* AiDebug.enabled = true;
|
|
158
|
+
*
|
|
159
|
+
* // Or filter by event ID
|
|
160
|
+
* AiDebug.filterEventId = 'goblin-1';
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export declare const AiDebug: {
|
|
164
|
+
/** Enable/disable all AI debug logs */
|
|
165
|
+
enabled: boolean;
|
|
166
|
+
/** Filter logs to a specific event ID (null = all events) */
|
|
167
|
+
filterEventId: string | null;
|
|
168
|
+
/** Log categories to enable (empty = all) */
|
|
169
|
+
categories: string[];
|
|
170
|
+
/**
|
|
171
|
+
* Log an AI debug message
|
|
172
|
+
*
|
|
173
|
+
* @param category - Log category (e.g., 'state', 'attack', 'movement', 'damage')
|
|
174
|
+
* @param eventId - Event ID for filtering
|
|
175
|
+
* @param message - Log message
|
|
176
|
+
* @param data - Optional additional data
|
|
177
|
+
*/
|
|
178
|
+
log(category: string, eventId: string | undefined, message: string, data?: any): void;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* AI State enumeration
|
|
182
|
+
*
|
|
183
|
+
* Defines the different states an AI can be in, each with its own behavior.
|
|
184
|
+
*/
|
|
185
|
+
export declare enum AiState {
|
|
186
|
+
Idle = "idle",
|
|
187
|
+
Alert = "alert",
|
|
188
|
+
Combat = "combat",
|
|
189
|
+
Flee = "flee",
|
|
190
|
+
Stunned = "stunned"
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Enemy Type enumeration
|
|
194
|
+
*
|
|
195
|
+
* Defines different enemy archetypes with unique behaviors.
|
|
196
|
+
* Stats (HP, ATK, etc.) should be set on the event itself via onInit.
|
|
197
|
+
*/
|
|
198
|
+
export declare enum EnemyType {
|
|
199
|
+
Aggressive = "aggressive",
|
|
200
|
+
Defensive = "defensive",
|
|
201
|
+
Ranged = "ranged",
|
|
202
|
+
Tank = "tank",
|
|
203
|
+
Berserker = "berserker"
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Attack Pattern enumeration
|
|
207
|
+
*
|
|
208
|
+
* Different attack patterns the AI can use.
|
|
209
|
+
*/
|
|
210
|
+
export declare enum AttackPattern {
|
|
211
|
+
Melee = "melee",
|
|
212
|
+
Combo = "combo",
|
|
213
|
+
Charged = "charged",
|
|
214
|
+
Zone = "zone",
|
|
215
|
+
DashAttack = "dashAttack"
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Default knockback configuration
|
|
219
|
+
*
|
|
220
|
+
* Used when no weapon is equipped or weapon doesn't specify knockback.
|
|
221
|
+
*/
|
|
222
|
+
export declare const DEFAULT_KNOCKBACK: {
|
|
223
|
+
/** Default knockback force */
|
|
224
|
+
force: number;
|
|
225
|
+
/** Default knockback duration in milliseconds */
|
|
226
|
+
duration: number;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Advanced Battle AI Controller for events
|
|
230
|
+
*
|
|
231
|
+
* This class provides intelligent combat behavior control for events.
|
|
232
|
+
* It uses the existing RPGJS API for stats, skills, items, etc.
|
|
233
|
+
* The AI only manages behavior - the event's stats should be configured
|
|
234
|
+
* in onInit using standard RPGJS methods.
|
|
235
|
+
*
|
|
236
|
+
* ## Usage with RPGJS API
|
|
237
|
+
*
|
|
238
|
+
* Configure the event stats using standard RPGJS methods:
|
|
239
|
+
* - `this.hp = 100` - Set health
|
|
240
|
+
* - `this.learnSkill(FireBall)` - Learn a skill
|
|
241
|
+
* - `this.addItem(Potion, 3)` - Add items
|
|
242
|
+
* - `this.equip(Sword)` - Equip items
|
|
243
|
+
* - `this.setClass(WarriorClass)` - Set class
|
|
244
|
+
* - `this.param[ATK] = 20` - Set parameters
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* function GoblinEnemy() {
|
|
249
|
+
* return {
|
|
250
|
+
* name: "Goblin",
|
|
251
|
+
* onInit() {
|
|
252
|
+
* this.setGraphic("goblin");
|
|
253
|
+
*
|
|
254
|
+
* // Configure stats using RPGJS API
|
|
255
|
+
* this.hp = 80;
|
|
256
|
+
* this.param[ATK] = 15;
|
|
257
|
+
* this.param[PDEF] = 5;
|
|
258
|
+
* this.learnSkill(Slash);
|
|
259
|
+
*
|
|
260
|
+
* // Apply AI behavior
|
|
261
|
+
* new BattleAi(this, {
|
|
262
|
+
* enemyType: EnemyType.Aggressive,
|
|
263
|
+
* attackSkill: Slash
|
|
264
|
+
* });
|
|
265
|
+
* }
|
|
266
|
+
* };
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export declare class BattleAi {
|
|
271
|
+
private event;
|
|
272
|
+
private target;
|
|
273
|
+
private lastAttackTime;
|
|
274
|
+
private updateInterval?;
|
|
275
|
+
/**
|
|
276
|
+
* Log AI debug message for this event
|
|
277
|
+
*/
|
|
278
|
+
private debugLog;
|
|
279
|
+
private state;
|
|
280
|
+
private stateStartTime;
|
|
281
|
+
private stunnedUntil;
|
|
282
|
+
private enemyType;
|
|
283
|
+
private attackCooldown;
|
|
284
|
+
private visionRange;
|
|
285
|
+
private attackRange;
|
|
286
|
+
private dodgeChance;
|
|
287
|
+
private dodgeCooldown;
|
|
288
|
+
private lastDodgeTime;
|
|
289
|
+
private fleeThreshold;
|
|
290
|
+
private attackSkill;
|
|
291
|
+
private attackPatterns;
|
|
292
|
+
private attackProfiles;
|
|
293
|
+
private animations?;
|
|
294
|
+
private comboCount;
|
|
295
|
+
private comboMax;
|
|
296
|
+
private chargingAttack;
|
|
297
|
+
private groupBehavior;
|
|
298
|
+
private nearbyEnemies;
|
|
299
|
+
private groupUpdateInterval;
|
|
300
|
+
private patrolWaypoints;
|
|
301
|
+
private currentPatrolIndex;
|
|
302
|
+
private lastHpCheck;
|
|
303
|
+
private recentDamageTaken;
|
|
304
|
+
private damageCheckInterval;
|
|
305
|
+
private isMovingToTarget;
|
|
306
|
+
private onDefeatedCallback?;
|
|
307
|
+
private rewards?;
|
|
308
|
+
private autoAwardRewards;
|
|
309
|
+
private defeated;
|
|
310
|
+
private lastFacingDirection;
|
|
311
|
+
private behaviorScore;
|
|
312
|
+
private behaviorMode;
|
|
313
|
+
private behaviorLastUpdate;
|
|
314
|
+
private behaviorUpdateInterval;
|
|
315
|
+
private behaviorAssaultThreshold;
|
|
316
|
+
private behaviorRetreatThreshold;
|
|
317
|
+
private behaviorMinStateDuration;
|
|
318
|
+
private behaviorEnabled;
|
|
319
|
+
private moveToCooldown;
|
|
320
|
+
private lastMoveToTime;
|
|
321
|
+
private retreatCooldown;
|
|
322
|
+
private lastRetreatTime;
|
|
323
|
+
private timers;
|
|
324
|
+
private behaviorKey?;
|
|
325
|
+
private poise;
|
|
326
|
+
private hitstunMs;
|
|
327
|
+
private invincibilityMs;
|
|
328
|
+
/**
|
|
329
|
+
* Create a new Battle AI Controller
|
|
330
|
+
*
|
|
331
|
+
* The AI controls behavior only. Stats should be set on the event
|
|
332
|
+
* using standard RPGJS methods (hp, param, learnSkill, etc.)
|
|
333
|
+
*
|
|
334
|
+
* @param event - The event to control
|
|
335
|
+
* @param options - AI behavior configuration
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```ts
|
|
339
|
+
* // In your event's onInit
|
|
340
|
+
* this.hp = 100;
|
|
341
|
+
* this.param[ATK] = 20;
|
|
342
|
+
* this.learnSkill(FireBall);
|
|
343
|
+
*
|
|
344
|
+
* new BattleAi(this, {
|
|
345
|
+
* enemyType: EnemyType.Ranged,
|
|
346
|
+
* attackSkill: FireBall,
|
|
347
|
+
* visionRange: 200,
|
|
348
|
+
* fleeThreshold: 0.2
|
|
349
|
+
* });
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
constructor(event: RpgEventWithBattleAi, options?: BattleAiOptions);
|
|
353
|
+
constructor(event: RpgEventWithBattleAi, options?: BattleAiLegacyOptions);
|
|
354
|
+
/**
|
|
355
|
+
* Apply enemy type-specific behavior modifiers
|
|
356
|
+
*
|
|
357
|
+
* This only affects AI behavior (cooldowns, ranges, dodge).
|
|
358
|
+
* Stats should be set on the event itself.
|
|
359
|
+
*/
|
|
360
|
+
private applyEnemyTypeBehavior;
|
|
361
|
+
/**
|
|
362
|
+
* Setup vision detection
|
|
363
|
+
*/
|
|
364
|
+
private setupVision;
|
|
365
|
+
/**
|
|
366
|
+
* Start the AI behavior loop
|
|
367
|
+
*/
|
|
368
|
+
private startAiBehaviorLoop;
|
|
369
|
+
/**
|
|
370
|
+
* Change AI state with validated transitions
|
|
371
|
+
*/
|
|
372
|
+
private changeState;
|
|
373
|
+
/**
|
|
374
|
+
* Main AI behavior update loop
|
|
375
|
+
*/
|
|
376
|
+
private updateAiBehavior;
|
|
377
|
+
/**
|
|
378
|
+
* Update idle behavior (patrolling)
|
|
379
|
+
*/
|
|
380
|
+
private updateIdleBehavior;
|
|
381
|
+
/**
|
|
382
|
+
* Update alert behavior
|
|
383
|
+
*/
|
|
384
|
+
private updateAlertBehavior;
|
|
385
|
+
/**
|
|
386
|
+
* Update combat behavior
|
|
387
|
+
*/
|
|
388
|
+
private updateCombatBehavior;
|
|
389
|
+
/**
|
|
390
|
+
* Update flee behavior
|
|
391
|
+
*/
|
|
392
|
+
private updateFleeBehavior;
|
|
393
|
+
/**
|
|
394
|
+
* Select and perform an attack pattern
|
|
395
|
+
*/
|
|
396
|
+
private selectAndPerformAttack;
|
|
397
|
+
/**
|
|
398
|
+
* Select attack pattern with weighted probability
|
|
399
|
+
*/
|
|
400
|
+
private selectAttackPattern;
|
|
401
|
+
/**
|
|
402
|
+
* Perform attack pattern
|
|
403
|
+
*/
|
|
404
|
+
private performAttackPattern;
|
|
405
|
+
/**
|
|
406
|
+
* Perform melee attack
|
|
407
|
+
* Uses skill if configured, otherwise creates hitbox
|
|
408
|
+
*/
|
|
409
|
+
private performMeleeAttack;
|
|
410
|
+
private executeMeleeAttack;
|
|
411
|
+
/**
|
|
412
|
+
* Perform basic hitbox attack when no skill is set
|
|
413
|
+
*/
|
|
414
|
+
private performBasicHitbox;
|
|
415
|
+
/**
|
|
416
|
+
* Apply hit to target using RPGJS damage system with knockback
|
|
417
|
+
*
|
|
418
|
+
* Calculates damage using RPGJS formula, applies knockback based on
|
|
419
|
+
* equipped weapon's knockbackForce property, and triggers visual effects.
|
|
420
|
+
* Supports hooks for customizing behavior.
|
|
421
|
+
*
|
|
422
|
+
* @param target - The player or entity being hit
|
|
423
|
+
* @param hooks - Optional hooks for customizing hit behavior
|
|
424
|
+
* @returns The hit result containing damage and knockback info
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* // Basic hit
|
|
429
|
+
* this.applyHit(player);
|
|
430
|
+
*
|
|
431
|
+
* // With custom hooks
|
|
432
|
+
* this.applyHit(player, {
|
|
433
|
+
* onBeforeHit(result) {
|
|
434
|
+
* result.knockbackForce *= 1.5; // Increase knockback
|
|
435
|
+
* return result;
|
|
436
|
+
* },
|
|
437
|
+
* onAfterHit(result) {
|
|
438
|
+
* console.log(`Dealt ${result.damage} damage!`);
|
|
439
|
+
* }
|
|
440
|
+
* });
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
private applyHit;
|
|
444
|
+
/**
|
|
445
|
+
* Get knockback force from equipped weapon
|
|
446
|
+
*
|
|
447
|
+
* Retrieves the knockbackForce property from the event's equipped weapon.
|
|
448
|
+
* Falls back to DEFAULT_KNOCKBACK.force if no weapon or property is set.
|
|
449
|
+
*
|
|
450
|
+
* @returns Knockback force value
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* // Weapon with knockbackForce: 80
|
|
455
|
+
* const force = this.getWeaponKnockbackForce(); // 80
|
|
456
|
+
*
|
|
457
|
+
* // No weapon equipped
|
|
458
|
+
* const force = this.getWeaponKnockbackForce(); // 50 (default)
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
private getWeaponKnockbackForce;
|
|
462
|
+
/**
|
|
463
|
+
* Perform combo attack
|
|
464
|
+
*/
|
|
465
|
+
private performComboAttack;
|
|
466
|
+
/**
|
|
467
|
+
* Perform charged attack
|
|
468
|
+
*/
|
|
469
|
+
private performChargedAttack;
|
|
470
|
+
/**
|
|
471
|
+
* Perform zone attack (360 degrees)
|
|
472
|
+
*/
|
|
473
|
+
private performZoneAttack;
|
|
474
|
+
/**
|
|
475
|
+
* Perform dash attack
|
|
476
|
+
*/
|
|
477
|
+
private performDashAttack;
|
|
478
|
+
private getAttackProfile;
|
|
479
|
+
private telegraphAttack;
|
|
480
|
+
private scheduleAttackStartup;
|
|
481
|
+
/**
|
|
482
|
+
* Face the current target with hysteresis to prevent animation flickering
|
|
483
|
+
*
|
|
484
|
+
* Uses multiple strategies to prevent flickering:
|
|
485
|
+
* 1. When very close to target (collision), keep current direction
|
|
486
|
+
* 2. When near diagonal, require significant difference to change
|
|
487
|
+
* 3. Only change if direction is clearly wrong (opposite)
|
|
488
|
+
*/
|
|
489
|
+
private faceTarget;
|
|
490
|
+
/**
|
|
491
|
+
* Try to dodge
|
|
492
|
+
*/
|
|
493
|
+
private tryDodge;
|
|
494
|
+
private canDodge;
|
|
495
|
+
private shouldDodge;
|
|
496
|
+
/**
|
|
497
|
+
* Flee from target
|
|
498
|
+
*/
|
|
499
|
+
private fleeFromTarget;
|
|
500
|
+
/**
|
|
501
|
+
* Retreat from target (temporary)
|
|
502
|
+
*/
|
|
503
|
+
private retreatFromTarget;
|
|
504
|
+
/**
|
|
505
|
+
* Check damage taken for retreat decision
|
|
506
|
+
*/
|
|
507
|
+
private checkDamageTaken;
|
|
508
|
+
/**
|
|
509
|
+
* Start patrol
|
|
510
|
+
*/
|
|
511
|
+
private startPatrol;
|
|
512
|
+
/**
|
|
513
|
+
* Update group behavior
|
|
514
|
+
*/
|
|
515
|
+
private updateGroupBehavior;
|
|
516
|
+
/**
|
|
517
|
+
* Find nearby enemies
|
|
518
|
+
*/
|
|
519
|
+
private findNearbyEnemies;
|
|
520
|
+
/**
|
|
521
|
+
* Apply formation around target
|
|
522
|
+
*/
|
|
523
|
+
private applyFormation;
|
|
524
|
+
/**
|
|
525
|
+
* Handle player entering vision
|
|
526
|
+
*/
|
|
527
|
+
onDetectInShape(player: InstanceType<typeof RpgPlayer>, shape: any): void;
|
|
528
|
+
/**
|
|
529
|
+
* Handle player leaving vision
|
|
530
|
+
*/
|
|
531
|
+
onDetectOutShape(player: InstanceType<typeof RpgPlayer>, shape: any): void;
|
|
532
|
+
/**
|
|
533
|
+
* Handle taking damage (called from server.ts)
|
|
534
|
+
*
|
|
535
|
+
* This triggers state changes like stun and flee check.
|
|
536
|
+
* The actual damage is applied externally via RPGJS API.
|
|
537
|
+
*/
|
|
538
|
+
takeDamage(attacker: RpgPlayer): boolean;
|
|
539
|
+
handleDamage(attacker: RpgPlayer, damageResult: ActionBattleDamageResult & {
|
|
540
|
+
reaction?: NormalizedActionBattleHitReactionProfile;
|
|
541
|
+
}): boolean;
|
|
542
|
+
/**
|
|
543
|
+
* Kill this AI
|
|
544
|
+
*
|
|
545
|
+
* Stops all movements, cleans up resources, calls the onDefeated hook,
|
|
546
|
+
* and removes the event from the map.
|
|
547
|
+
*/
|
|
548
|
+
private kill;
|
|
549
|
+
/**
|
|
550
|
+
* Get distance between entities
|
|
551
|
+
*/
|
|
552
|
+
private getDistance;
|
|
553
|
+
private updateBehavior;
|
|
554
|
+
private applyCustomBehavior;
|
|
555
|
+
private handleTacticalMovement;
|
|
556
|
+
private handleAssaultMovement;
|
|
557
|
+
private requestMoveTo;
|
|
558
|
+
private schedule;
|
|
559
|
+
getHealth(): number;
|
|
560
|
+
getMaxHealth(): number;
|
|
561
|
+
getTarget(): InstanceType<typeof RpgPlayer> | null;
|
|
562
|
+
getState(): AiState;
|
|
563
|
+
getEnemyType(): EnemyType;
|
|
564
|
+
/**
|
|
565
|
+
* Clean up
|
|
566
|
+
*/
|
|
567
|
+
destroy(): void;
|
|
568
|
+
}
|
|
569
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionBattleAnimationContext, ActionBattleAnimationEntity, ActionBattleAnimationKey, ActionBattleAnimationOptions } from './types';
|
|
2
|
+
export declare const DEFAULT_DIE_ANIMATION_DELAY_MS = 500;
|
|
3
|
+
export interface ResolvedActionBattleAnimation {
|
|
4
|
+
animationName: string;
|
|
5
|
+
graphic?: string | string[];
|
|
6
|
+
repeat: number;
|
|
7
|
+
waitEnd: boolean;
|
|
8
|
+
delayMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ActionBattleAnimationDefaults {
|
|
11
|
+
animationName?: string;
|
|
12
|
+
repeat?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveActionBattleAnimation(key: ActionBattleAnimationKey, entity: ActionBattleAnimationEntity, animations?: ActionBattleAnimationOptions, context?: ActionBattleAnimationContext, defaults?: ActionBattleAnimationDefaults): ResolvedActionBattleAnimation | null;
|
|
15
|
+
export declare function playActionBattleAnimation(key: ActionBattleAnimationKey, entity: ActionBattleAnimationEntity, animations?: ActionBattleAnimationOptions, context?: ActionBattleAnimationContext, defaults?: ActionBattleAnimationDefaults): ResolvedActionBattleAnimation | null;
|
|
16
|
+
export declare function getActionBattleAnimationRemovalDelay(animation: ResolvedActionBattleAnimation | null): number;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ActionBattleOptions } from './types';
|
|
2
|
+
export declare const DEFAULT_ACTION_BATTLE_OPTIONS: ActionBattleOptions;
|
|
3
|
+
export declare function normalizeActionBattleOptions(options?: ActionBattleOptions): ActionBattleOptions;
|
|
4
|
+
export declare function setActionBattleOptions(options: ActionBattleOptions): void;
|
|
5
|
+
export declare function getActionBattleOptions(): ActionBattleOptions;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ActionBattleAttackHitboxMap, ActionBattleAttackProfile, NormalizedActionBattleAttackProfile } from '../types';
|
|
2
|
+
export declare const DEFAULT_ACTION_BATTLE_ATTACK_PROFILE: NormalizedActionBattleAttackProfile;
|
|
3
|
+
export interface ActionBattleAttackProfileFallbacks {
|
|
4
|
+
id?: string;
|
|
5
|
+
lockMovement?: boolean;
|
|
6
|
+
lockDurationMs?: number;
|
|
7
|
+
hitboxes?: ActionBattleAttackHitboxMap;
|
|
8
|
+
}
|
|
9
|
+
export declare function normalizeActionBattleAttackProfile(profile?: ActionBattleAttackProfile | undefined, fallbacks?: ActionBattleAttackProfileFallbacks): NormalizedActionBattleAttackProfile;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ActionBattleAttackHitPolicy, ActionBattleOptions, NormalizedActionBattleAttackProfile } from '../types';
|
|
2
|
+
export declare const ACTION_BATTLE_HITBOX_FRAME_MS = 16;
|
|
3
|
+
export declare function getNormalizedActionBattleAttackProfile(options?: ActionBattleOptions): NormalizedActionBattleAttackProfile;
|
|
4
|
+
export declare function resolveActionBattleHitboxSpeed(profile: NormalizedActionBattleAttackProfile, hitboxCount: number): number;
|
|
5
|
+
export declare function scheduleActionBattleStartup(profile: NormalizedActionBattleAttackProfile, callback: () => void, scheduler?: (callback: () => void, delayMs: number) => unknown): unknown;
|
|
6
|
+
export declare function createActionBattleAttackId(attackerId: string | number | undefined, profileId: string): string;
|
|
7
|
+
export declare class ActionBattleHitTracker {
|
|
8
|
+
private readonly hitPolicy;
|
|
9
|
+
private hitTargets;
|
|
10
|
+
constructor(hitPolicy: ActionBattleAttackHitPolicy);
|
|
11
|
+
canHit(target: {
|
|
12
|
+
id?: string | number;
|
|
13
|
+
} | undefined): boolean;
|
|
14
|
+
recordHit(target: {
|
|
15
|
+
id?: string | number;
|
|
16
|
+
} | undefined): void;
|
|
17
|
+
tryHit(target: {
|
|
18
|
+
id?: string | number;
|
|
19
|
+
} | undefined): boolean;
|
|
20
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ActionBattleOptions } from '../types';
|
|
2
|
+
import { ActionBattleSystems } from './contracts';
|
|
3
|
+
export declare const setActionBattleSystems: (options?: ActionBattleOptions) => void;
|
|
4
|
+
export declare const getActionBattleSystems: () => ActionBattleSystems;
|
|
5
|
+
export declare const createActionBattleSystems: (options?: ActionBattleOptions) => ActionBattleSystems;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { RpgPlayer } from '@rpgjs/server';
|
|
2
|
+
import { ActionBattleAiBehavior, ActionBattleAttackContext, ActionBattleCombatSystem, ActionBattleDamageContext, ActionBattleKnockbackContext, ActionBattleKnockbackResult, ActionBattleSystems } from './contracts';
|
|
3
|
+
export declare const DEFAULT_ZELDA_PLAYER_HITBOXES: {
|
|
4
|
+
up: {
|
|
5
|
+
offsetX: number;
|
|
6
|
+
offsetY: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
down: {
|
|
11
|
+
offsetX: number;
|
|
12
|
+
offsetY: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
left: {
|
|
17
|
+
offsetX: number;
|
|
18
|
+
offsetY: number;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
right: {
|
|
23
|
+
offsetX: number;
|
|
24
|
+
offsetY: number;
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
};
|
|
28
|
+
default: {
|
|
29
|
+
offsetX: number;
|
|
30
|
+
offsetY: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export declare const createDefaultPlayerHitboxResolver: (hitboxes?: {
|
|
36
|
+
up: {
|
|
37
|
+
offsetX: number;
|
|
38
|
+
offsetY: number;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
};
|
|
42
|
+
down: {
|
|
43
|
+
offsetX: number;
|
|
44
|
+
offsetY: number;
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
};
|
|
48
|
+
left: {
|
|
49
|
+
offsetX: number;
|
|
50
|
+
offsetY: number;
|
|
51
|
+
width: number;
|
|
52
|
+
height: number;
|
|
53
|
+
};
|
|
54
|
+
right: {
|
|
55
|
+
offsetX: number;
|
|
56
|
+
offsetY: number;
|
|
57
|
+
width: number;
|
|
58
|
+
height: number;
|
|
59
|
+
};
|
|
60
|
+
default: {
|
|
61
|
+
offsetX: number;
|
|
62
|
+
offsetY: number;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
};
|
|
66
|
+
}) => (context: ActionBattleAttackContext) => {
|
|
67
|
+
x: any;
|
|
68
|
+
y: any;
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
}[];
|
|
72
|
+
export declare const defaultRpgjsDamageResolver: (context: ActionBattleDamageContext) => {
|
|
73
|
+
damage: any;
|
|
74
|
+
defeated: boolean;
|
|
75
|
+
raw: any;
|
|
76
|
+
};
|
|
77
|
+
export declare const defaultKnockbackResolver: (context: ActionBattleKnockbackContext) => ActionBattleKnockbackResult;
|
|
78
|
+
export declare const defaultCombatSystem: ActionBattleCombatSystem;
|
|
79
|
+
export declare const defaultEnemyBehaviors: Record<string, ActionBattleAiBehavior>;
|
|
80
|
+
export declare const defaultActionBattleSystems: ActionBattleSystems;
|
|
81
|
+
export declare const getEntityWeaponKnockbackForce: (entity: RpgPlayer) => number;
|