@rpgjs/action-battle 5.0.0-beta.5 → 5.0.0-beta.7

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 (66) hide show
  1. package/README.md +161 -22
  2. package/dist/ai.server.d.ts +55 -4
  3. package/dist/client/index.js +13 -8
  4. package/dist/client/index10.js +54 -136
  5. package/dist/client/index11.js +52 -23
  6. package/dist/client/index12.js +101 -1217
  7. package/dist/client/index13.js +139 -42
  8. package/dist/client/index14.js +23 -8
  9. package/dist/client/index15.js +68 -444
  10. package/dist/client/index16.js +1343 -0
  11. package/dist/client/index17.js +13 -0
  12. package/dist/client/index18.js +60 -0
  13. package/dist/client/index19.js +10 -0
  14. package/dist/client/index2.js +25 -87
  15. package/dist/client/index20.js +504 -0
  16. package/dist/client/index3.js +45 -83
  17. package/dist/client/index4.js +98 -297
  18. package/dist/client/index5.js +81 -33
  19. package/dist/client/index6.js +284 -78
  20. package/dist/client/index7.js +33 -74
  21. package/dist/client/index8.js +95 -55
  22. package/dist/client/index9.js +75 -96
  23. package/dist/core/attack-profile.d.ts +9 -0
  24. package/dist/core/attack-runtime.d.ts +20 -0
  25. package/dist/core/enemy-attack-profiles.d.ts +6 -0
  26. package/dist/core/equipment.d.ts +2 -0
  27. package/dist/core/hit-reaction.d.ts +5 -0
  28. package/dist/index.d.ts +7 -2
  29. package/dist/server/index.js +12 -7
  30. package/dist/server/index10.js +1340 -8
  31. package/dist/server/index11.js +37 -0
  32. package/dist/server/index12.js +60 -0
  33. package/dist/server/index13.js +13 -0
  34. package/dist/server/index14.js +503 -0
  35. package/dist/server/index15.js +10 -0
  36. package/dist/server/index2.js +1 -1
  37. package/dist/server/index3.js +25 -87
  38. package/dist/server/index4.js +45 -141
  39. package/dist/server/index5.js +104 -21
  40. package/dist/server/index6.js +137 -1215
  41. package/dist/server/index7.js +22 -34
  42. package/dist/server/index8.js +70 -44
  43. package/dist/server/index9.js +44 -437
  44. package/dist/server.d.ts +8 -2
  45. package/dist/ui/state.d.ts +5 -5
  46. package/package.json +5 -5
  47. package/src/ai.server.spec.ts +120 -0
  48. package/src/ai.server.ts +362 -56
  49. package/src/client.ts +21 -12
  50. package/src/config.ts +17 -2
  51. package/src/core/attack-profile.spec.ts +118 -0
  52. package/src/core/attack-profile.ts +100 -0
  53. package/src/core/attack-runtime.spec.ts +103 -0
  54. package/src/core/attack-runtime.ts +83 -0
  55. package/src/core/contracts.ts +3 -0
  56. package/src/core/enemy-attack-profiles.spec.ts +35 -0
  57. package/src/core/enemy-attack-profiles.ts +103 -0
  58. package/src/core/equipment.spec.ts +37 -0
  59. package/src/core/equipment.ts +17 -0
  60. package/src/core/hit-reaction.spec.ts +43 -0
  61. package/src/core/hit-reaction.ts +70 -0
  62. package/src/core/hit.spec.ts +54 -1
  63. package/src/core/hit.ts +26 -0
  64. package/src/index.ts +48 -1
  65. package/src/server.ts +192 -34
  66. package/src/types.ts +62 -6
@@ -1,94 +1,32 @@
1
- //#region src/config.ts
2
- var DEFAULT_ACTION_BATTLE_OPTIONS = {
3
- ui: {
4
- actionBar: {
5
- enabled: false,
6
- autoOpen: false,
7
- mode: "both"
8
- },
9
- targeting: {
10
- enabled: true,
11
- showGrid: true,
12
- colors: {
13
- area: 3120887,
14
- edge: 1796760,
15
- cursor: 16765286
16
- }
17
- }
18
- },
19
- skills: { defaultAoeMask: ["#"] },
20
- targeting: {
21
- affects: "events",
22
- allowEmptyTarget: true
23
- },
24
- attack: {
25
- lockMovement: true,
26
- lockDurationMs: 350,
27
- showPreview: true,
28
- previewDurationMs: 180,
29
- previewColor: 16774064,
30
- previewAccentColor: 16777215
31
- },
32
- animations: {}
1
+ //#region src/core/hit-reaction.ts
2
+ var DEFAULT_ACTION_BATTLE_HIT_REACTION = {
3
+ invincibilityMs: 250,
4
+ hitstunMs: 150,
5
+ staggerPower: 1
33
6
  };
34
- var currentActionBattleOptions = DEFAULT_ACTION_BATTLE_OPTIONS;
35
- function normalizeActionBattleOptions(options = {}) {
7
+ var STATE_KEY = "__actionBattleHitReaction";
8
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
9
+ var nonNegativeMs = (value, fallback) => isFiniteNumber(value) ? Math.max(0, value) : fallback;
10
+ var nonNegativeValue = (value, fallback) => isFiniteNumber(value) ? Math.max(0, value) : fallback;
11
+ var getRuntimeState = (entity) => {
12
+ if (!entity[STATE_KEY]) entity[STATE_KEY] = { invincibleUntil: 0 };
13
+ return entity[STATE_KEY];
14
+ };
15
+ function normalizeActionBattleHitReaction(reaction, defaults = DEFAULT_ACTION_BATTLE_HIT_REACTION) {
36
16
  return {
37
- ui: {
38
- actionBar: {
39
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.actionBar,
40
- ...options.ui?.actionBar
41
- },
42
- targeting: {
43
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting,
44
- ...options.ui?.targeting,
45
- colors: {
46
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting?.colors,
47
- ...options.ui?.targeting?.colors
48
- }
49
- }
50
- },
51
- skills: {
52
- ...DEFAULT_ACTION_BATTLE_OPTIONS.skills,
53
- ...options.skills
54
- },
55
- targeting: {
56
- ...DEFAULT_ACTION_BATTLE_OPTIONS.targeting,
57
- ...options.targeting
58
- },
59
- attack: {
60
- ...DEFAULT_ACTION_BATTLE_OPTIONS.attack,
61
- ...options.attack
62
- },
63
- animations: {
64
- ...DEFAULT_ACTION_BATTLE_OPTIONS.animations,
65
- ...options.animations
66
- },
67
- systems: {
68
- combat: {
69
- ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat,
70
- ...options.systems?.combat,
71
- hooks: {
72
- ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat?.hooks,
73
- ...options.systems?.combat?.hooks
74
- }
75
- },
76
- ai: {
77
- ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai,
78
- ...options.systems?.ai,
79
- behaviors: {
80
- ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai?.behaviors,
81
- ...options.systems?.ai?.behaviors
82
- }
83
- }
84
- }
17
+ invincibilityMs: nonNegativeMs(reaction?.invincibilityMs, defaults.invincibilityMs),
18
+ hitstunMs: nonNegativeMs(reaction?.hitstunMs, defaults.hitstunMs),
19
+ staggerPower: nonNegativeValue(reaction?.staggerPower, defaults.staggerPower)
85
20
  };
86
21
  }
87
- function setActionBattleOptions(options) {
88
- currentActionBattleOptions = options;
22
+ function isActionBattleEntityInvincible(entity, now = Date.now()) {
23
+ if (!entity) return false;
24
+ return getRuntimeState(entity).invincibleUntil > now;
89
25
  }
90
- function getActionBattleOptions() {
91
- return currentActionBattleOptions;
26
+ function setActionBattleInvincibility(entity, durationMs, now = Date.now()) {
27
+ if (!entity || durationMs <= 0) return;
28
+ const state = getRuntimeState(entity);
29
+ state.invincibleUntil = Math.max(state.invincibleUntil, now + durationMs);
92
30
  }
93
31
  //#endregion
94
- export { getActionBattleOptions, normalizeActionBattleOptions, setActionBattleOptions };
32
+ export { DEFAULT_ACTION_BATTLE_HIT_REACTION, isActionBattleEntityInvincible, normalizeActionBattleHitReaction, setActionBattleInvincibility };
@@ -1,143 +1,47 @@
1
- //#region src/core/defaults.ts
2
- var DEFAULT_CORE_KNOCKBACK = {
3
- force: 50,
4
- duration: 300
5
- };
6
- var CoreAttackPattern = {
7
- Melee: "melee",
8
- Combo: "combo",
9
- Charged: "charged",
10
- Zone: "zone",
11
- DashAttack: "dashAttack"
12
- };
13
- var CoreEnemyType = {
14
- Aggressive: "aggressive",
15
- Defensive: "defensive",
16
- Ranged: "ranged",
17
- Tank: "tank",
18
- Berserker: "berserker"
19
- };
20
- var DEFAULT_ZELDA_PLAYER_HITBOXES = {
21
- up: {
22
- offsetX: -16,
23
- offsetY: -48,
24
- width: 32,
25
- height: 32
26
- },
27
- down: {
28
- offsetX: -16,
29
- offsetY: 16,
30
- width: 32,
31
- height: 32
32
- },
33
- left: {
34
- offsetX: -48,
35
- offsetY: -16,
36
- width: 32,
37
- height: 32
38
- },
39
- right: {
40
- offsetX: 16,
41
- offsetY: -16,
42
- width: 32,
43
- height: 32
44
- },
45
- default: {
46
- offsetX: 0,
47
- offsetY: -32,
48
- width: 32,
49
- height: 32
50
- }
51
- };
52
- var resolveEquippedWeapon = (entity) => {
53
- const equipments = entity?.equipments?.() || [];
54
- for (const item of equipments) {
55
- const itemId = item?.id?.() ?? item?.id;
56
- const itemData = entity?.databaseById?.(itemId);
57
- if (itemData?._type === "weapon") return itemData;
58
- }
59
- return null;
60
- };
61
- var resolveDirection = (attacker, target) => {
62
- const dx = target.x() - attacker.x();
63
- const dy = target.y() - attacker.y();
64
- const distance = Math.sqrt(dx * dx + dy * dy);
65
- if (distance <= 0) return void 0;
66
- return {
67
- x: dx / distance,
68
- y: dy / distance
69
- };
70
- };
71
- var createDefaultPlayerHitboxResolver = (hitboxes = DEFAULT_ZELDA_PLAYER_HITBOXES) => (context) => {
72
- const attacker = context.attacker;
73
- const config = hitboxes[context.direction ?? (typeof attacker.getDirection === "function" ? attacker.getDirection() : "default")] || hitboxes.default;
74
- return [{
75
- x: attacker.x() + config.offsetX,
76
- y: attacker.y() + config.offsetY,
77
- width: config.width,
78
- height: config.height
79
- }];
80
- };
81
- var defaultRpgjsDamageResolver = (context) => {
82
- const target = context.target;
83
- const raw = target.applyDamage(context.attacker, context.skill);
84
- return {
85
- damage: raw?.damage ?? 0,
86
- defeated: target.hp <= 0,
87
- raw
88
- };
89
- };
90
- var defaultKnockbackResolver = (context) => {
91
- const weapon = context.weapon ?? resolveEquippedWeapon(context.attacker);
92
- return {
93
- force: weapon?.knockbackForce ?? DEFAULT_CORE_KNOCKBACK.force,
94
- duration: weapon?.knockbackDuration ?? DEFAULT_CORE_KNOCKBACK.duration,
95
- direction: resolveDirection(context.attacker, context.target)
1
+ import { DEFAULT_ACTION_BATTLE_HIT_REACTION, normalizeActionBattleHitReaction } from "./index3.js";
2
+ //#region src/core/attack-profile.ts
3
+ var DEFAULT_ACTION_BATTLE_ATTACK_PROFILE = {
4
+ id: "basic",
5
+ startupMs: 0,
6
+ activeMs: 120,
7
+ recoveryMs: 230,
8
+ cooldownMs: 350,
9
+ movementLock: true,
10
+ directionLock: true,
11
+ animationKey: "attack",
12
+ hitPolicy: "oncePerTarget",
13
+ reaction: DEFAULT_ACTION_BATTLE_HIT_REACTION,
14
+ totalDurationMs: 350
15
+ };
16
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
17
+ var nonNegativeMs = (value, fallback) => isFiniteNumber(value) ? Math.max(0, value) : fallback;
18
+ var positiveMs = (value, fallback) => isFiniteNumber(value) ? Math.max(1, value) : fallback;
19
+ var resolveHitPolicy = (value) => value === "allowRepeatHits" ? "allowRepeatHits" : "oncePerTarget";
20
+ var resolveAnimationKey = (value) => value ?? DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.animationKey;
21
+ function normalizeActionBattleAttackProfile(profile = {}, fallbacks = {}) {
22
+ const startupMs = nonNegativeMs(profile.startupMs, DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.startupMs);
23
+ const activeMs = positiveMs(profile.activeMs, DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.activeMs);
24
+ const legacyDuration = nonNegativeMs(fallbacks.lockDurationMs, DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.totalDurationMs);
25
+ const fallbackRecoveryMs = Math.max(0, legacyDuration - startupMs - activeMs);
26
+ const recoveryMs = nonNegativeMs(profile.recoveryMs, fallbackRecoveryMs);
27
+ const totalDurationMs = startupMs + activeMs + recoveryMs;
28
+ const cooldownMs = nonNegativeMs(profile.cooldownMs, totalDurationMs);
29
+ const hitboxes = profile.hitboxes ?? fallbacks.hitboxes;
30
+ const normalized = {
31
+ id: profile.id || fallbacks.id || DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.id,
32
+ startupMs,
33
+ activeMs,
34
+ recoveryMs,
35
+ cooldownMs,
36
+ movementLock: profile.movementLock ?? fallbacks.lockMovement ?? DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.movementLock,
37
+ directionLock: profile.directionLock ?? DEFAULT_ACTION_BATTLE_ATTACK_PROFILE.directionLock,
38
+ animationKey: resolveAnimationKey(profile.animationKey),
39
+ hitPolicy: resolveHitPolicy(profile.hitPolicy),
40
+ reaction: normalizeActionBattleHitReaction(profile.reaction),
41
+ totalDurationMs
96
42
  };
97
- };
98
- var defaultCombatSystem = {
99
- resolveHitboxes: createDefaultPlayerHitboxResolver(),
100
- resolveDamage: defaultRpgjsDamageResolver,
101
- resolveKnockback: defaultKnockbackResolver
102
- };
103
- var defaultEnemyBehaviors = {
104
- [CoreEnemyType.Aggressive]: ({ hpPercent }) => ({
105
- mode: hpPercent !== null && hpPercent < .15 ? "retreat" : "assault",
106
- attackPatterns: [
107
- CoreAttackPattern.Melee,
108
- CoreAttackPattern.Combo,
109
- CoreAttackPattern.DashAttack
110
- ]
111
- }),
112
- [CoreEnemyType.Defensive]: ({ hpPercent }) => ({
113
- mode: hpPercent !== null && hpPercent < .3 ? "retreat" : "tactical",
114
- attackPatterns: [CoreAttackPattern.Melee, CoreAttackPattern.Charged]
115
- }),
116
- [CoreEnemyType.Ranged]: ({ distance }) => ({
117
- mode: distance !== null && distance < 80 ? "retreat" : "tactical",
118
- attackPatterns: [CoreAttackPattern.Melee, CoreAttackPattern.Zone]
119
- }),
120
- [CoreEnemyType.Tank]: () => ({
121
- mode: "assault",
122
- attackPatterns: [
123
- CoreAttackPattern.Melee,
124
- CoreAttackPattern.Charged,
125
- CoreAttackPattern.Zone
126
- ]
127
- }),
128
- [CoreEnemyType.Berserker]: ({ hpPercent }) => ({
129
- mode: "assault",
130
- attackCooldown: hpPercent === null ? void 0 : Math.max(250, 800 * Math.max(.3, hpPercent)),
131
- attackPatterns: [
132
- CoreAttackPattern.Melee,
133
- CoreAttackPattern.Combo,
134
- CoreAttackPattern.DashAttack
135
- ]
136
- })
137
- };
138
- var defaultActionBattleSystems = {
139
- combat: defaultCombatSystem,
140
- ai: { behaviors: defaultEnemyBehaviors }
141
- };
43
+ if (hitboxes) normalized.hitboxes = hitboxes;
44
+ return normalized;
45
+ }
142
46
  //#endregion
143
- export { DEFAULT_ZELDA_PLAYER_HITBOXES, createDefaultPlayerHitboxResolver, defaultActionBattleSystems, defaultCombatSystem, defaultEnemyBehaviors, defaultKnockbackResolver, defaultRpgjsDamageResolver };
47
+ export { DEFAULT_ACTION_BATTLE_ATTACK_PROFILE, normalizeActionBattleAttackProfile };
@@ -1,25 +1,108 @@
1
- import { defaultActionBattleSystems } from "./index4.js";
2
- //#region src/core/context.ts
3
- var mergeSystems = (options = {}) => ({
4
- combat: {
5
- ...defaultActionBattleSystems.combat,
6
- resolveDamage: options.systems?.combat?.damage ?? defaultActionBattleSystems.combat.resolveDamage,
7
- resolveKnockback: options.systems?.combat?.knockback ?? defaultActionBattleSystems.combat.resolveKnockback,
8
- hooks: {
9
- ...defaultActionBattleSystems.combat.hooks,
10
- ...options.systems?.combat?.hooks
1
+ import { normalizeActionBattleAttackProfile } from "./index4.js";
2
+ //#region src/config.ts
3
+ var DEFAULT_ACTION_BATTLE_OPTIONS = {
4
+ ui: {
5
+ actionBar: {
6
+ enabled: false,
7
+ autoOpen: false,
8
+ mode: "both"
9
+ },
10
+ targeting: {
11
+ enabled: true,
12
+ showGrid: true,
13
+ colors: {
14
+ area: 3120887,
15
+ edge: 1796760,
16
+ cursor: 16765286
17
+ }
11
18
  }
12
19
  },
13
- ai: { behaviors: {
14
- ...defaultActionBattleSystems.ai.behaviors,
15
- ...options.systems?.ai?.behaviors
16
- } }
17
- });
18
- var currentActionBattleSystems = mergeSystems();
19
- var setActionBattleSystems = (options = {}) => {
20
- currentActionBattleSystems = mergeSystems(options);
20
+ skills: { defaultAoeMask: ["#"] },
21
+ targeting: {
22
+ affects: "events",
23
+ allowEmptyTarget: true
24
+ },
25
+ attack: {
26
+ lockMovement: true,
27
+ lockDurationMs: 350,
28
+ showPreview: true,
29
+ previewDurationMs: 180,
30
+ previewColor: 16774064,
31
+ previewAccentColor: 16777215
32
+ },
33
+ animations: {}
21
34
  };
22
- var getActionBattleSystems = () => currentActionBattleSystems;
23
- var createActionBattleSystems = mergeSystems;
35
+ var currentActionBattleOptions = DEFAULT_ACTION_BATTLE_OPTIONS;
36
+ function normalizeActionBattleOptions(options = {}) {
37
+ const attack = {
38
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.attack,
39
+ ...options.attack
40
+ };
41
+ const attackProfile = normalizeActionBattleAttackProfile(attack.profile, {
42
+ lockMovement: attack.lockMovement,
43
+ lockDurationMs: attack.lockDurationMs,
44
+ hitboxes: attack.hitboxes
45
+ });
46
+ return {
47
+ ui: {
48
+ actionBar: {
49
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.actionBar,
50
+ ...options.ui?.actionBar
51
+ },
52
+ targeting: {
53
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting,
54
+ ...options.ui?.targeting,
55
+ colors: {
56
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting?.colors,
57
+ ...options.ui?.targeting?.colors
58
+ }
59
+ }
60
+ },
61
+ skills: {
62
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.skills,
63
+ ...options.skills
64
+ },
65
+ targeting: {
66
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.targeting,
67
+ ...options.targeting
68
+ },
69
+ debug: {
70
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.debug,
71
+ ...options.debug
72
+ },
73
+ attack: {
74
+ ...attack,
75
+ profile: attackProfile
76
+ },
77
+ animations: {
78
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.animations,
79
+ ...options.animations
80
+ },
81
+ systems: {
82
+ combat: {
83
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat,
84
+ ...options.systems?.combat,
85
+ hooks: {
86
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.combat?.hooks,
87
+ ...options.systems?.combat?.hooks
88
+ }
89
+ },
90
+ ai: {
91
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai,
92
+ ...options.systems?.ai,
93
+ behaviors: {
94
+ ...DEFAULT_ACTION_BATTLE_OPTIONS.systems?.ai?.behaviors,
95
+ ...options.systems?.ai?.behaviors
96
+ }
97
+ }
98
+ }
99
+ };
100
+ }
101
+ function setActionBattleOptions(options) {
102
+ currentActionBattleOptions = options;
103
+ }
104
+ function getActionBattleOptions() {
105
+ return currentActionBattleOptions;
106
+ }
24
107
  //#endregion
25
- export { createActionBattleSystems, getActionBattleSystems, setActionBattleSystems };
108
+ export { getActionBattleOptions, normalizeActionBattleOptions, setActionBattleOptions };