hrbattle 0.1.0

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 (45) hide show
  1. package/README.md +103 -0
  2. package/dist/battle/battleCore.d.ts +83 -0
  3. package/dist/battle/battleCore.js +779 -0
  4. package/dist/battle/config/json/designed-buffs.json +608 -0
  5. package/dist/battle/config/json/designed-hero-skill-books.json +146 -0
  6. package/dist/battle/config/json/designed-monster-skill-books.json +308 -0
  7. package/dist/battle/config/json/designed-roster.json +438 -0
  8. package/dist/battle/config/json/designed-skill-templates.json +81 -0
  9. package/dist/battle/config/jsonConfigLoader.d.ts +26 -0
  10. package/dist/battle/config/jsonConfigLoader.js +77 -0
  11. package/dist/battle/effectSystem.d.ts +26 -0
  12. package/dist/battle/effectSystem.js +175 -0
  13. package/dist/battle/eventBus.d.ts +8 -0
  14. package/dist/battle/eventBus.js +20 -0
  15. package/dist/battle/formula.d.ts +19 -0
  16. package/dist/battle/formula.js +92 -0
  17. package/dist/battle/logger.d.ts +28 -0
  18. package/dist/battle/logger.js +66 -0
  19. package/dist/battle/random.d.ts +6 -0
  20. package/dist/battle/random.js +16 -0
  21. package/dist/battle/script/designedScripts.d.ts +2 -0
  22. package/dist/battle/script/designedScripts.js +1013 -0
  23. package/dist/battle/script/monsterScripts.d.ts +2 -0
  24. package/dist/battle/script/monsterScripts.js +277 -0
  25. package/dist/battle/script/monsterScripts18xx.d.ts +2 -0
  26. package/dist/battle/script/monsterScripts18xx.js +330 -0
  27. package/dist/battle/script/monsterScripts19xx.d.ts +2 -0
  28. package/dist/battle/script/monsterScripts19xx.js +271 -0
  29. package/dist/battle/script/monsterScripts19xxPart2.d.ts +2 -0
  30. package/dist/battle/script/monsterScripts19xxPart2.js +400 -0
  31. package/dist/battle/skillEngine.d.ts +36 -0
  32. package/dist/battle/skillEngine.js +246 -0
  33. package/dist/battle/targeting.d.ts +3 -0
  34. package/dist/battle/targeting.js +38 -0
  35. package/dist/battle/turnbar.d.ts +8 -0
  36. package/dist/battle/turnbar.js +40 -0
  37. package/dist/battle/types.d.ts +302 -0
  38. package/dist/battle/types.js +1 -0
  39. package/dist/cocos-adapter/BattleFacade.d.ts +8 -0
  40. package/dist/cocos-adapter/BattleFacade.js +22 -0
  41. package/dist/cocos-adapter/clientBattleResult.d.ts +127 -0
  42. package/dist/cocos-adapter/clientBattleResult.js +413 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.js +8 -0
  45. package/package.json +32 -0
@@ -0,0 +1,2 @@
1
+ import type { ISkillScript } from "../types";
2
+ export declare const monsterScripts16xx: Record<string, ISkillScript>;
@@ -0,0 +1,277 @@
1
+ function buildEmptyResult(ctx, targets = ctx.targets) {
2
+ return {
3
+ skillId: ctx.skillId,
4
+ casterId: ctx.caster,
5
+ targets,
6
+ totalDamage: 0,
7
+ totalHeal: 0,
8
+ totalShield: 0,
9
+ triggeredBy: ctx.trigger
10
+ };
11
+ }
12
+ function buildDamageHit(ctx, targetId, finalValue) {
13
+ return {
14
+ targetId,
15
+ effectType: "damage",
16
+ element: ctx.api.getUnit(ctx.caster)?.element ?? "fire",
17
+ skillType: "basic",
18
+ isCritical: false,
19
+ baseValue: finalValue,
20
+ criticalValue: finalValue,
21
+ finalValue,
22
+ isRepression: 0,
23
+ damageBreakdown: { preMitigation: finalValue, postMitigation: finalValue, shieldAbsorbed: 0, hpDamage: finalValue }
24
+ };
25
+ }
26
+ function buildBuffHit(ctx, targetId, buffId, resisted = false) {
27
+ return {
28
+ targetId,
29
+ effectType: "buff",
30
+ element: ctx.api.getUnit(ctx.caster)?.element ?? "fire",
31
+ skillType: "basic",
32
+ isCritical: false,
33
+ baseValue: 0,
34
+ criticalValue: 0,
35
+ finalValue: 0,
36
+ isRepression: 0,
37
+ appliedBuffId: resisted ? undefined : buffId,
38
+ resisted
39
+ };
40
+ }
41
+ function buildHealHit(ctx, targetId, finalValue) {
42
+ return {
43
+ targetId,
44
+ effectType: "heal",
45
+ element: ctx.api.getUnit(ctx.caster)?.element ?? "fire",
46
+ skillType: "basic",
47
+ isCritical: false,
48
+ baseValue: finalValue,
49
+ criticalValue: finalValue,
50
+ finalValue,
51
+ isRepression: 0
52
+ };
53
+ }
54
+ function buildShieldHit(ctx, targetId, finalValue) {
55
+ return {
56
+ targetId,
57
+ effectType: "shield",
58
+ element: ctx.api.getUnit(ctx.caster)?.element ?? "fire",
59
+ skillType: "basic",
60
+ isCritical: false,
61
+ baseValue: finalValue,
62
+ criticalValue: finalValue,
63
+ finalValue,
64
+ isRepression: 0
65
+ };
66
+ }
67
+ // ============================================================
68
+ // 16012 水波波比 技能:对随机一名敌人造成110%水属性伤害,并削减敌人10%行动条
69
+ // ============================================================
70
+ const m16012Script = {
71
+ onCast(ctx) {
72
+ const result = buildEmptyResult(ctx);
73
+ const self = ctx.api.getUnit(ctx.caster);
74
+ if (!self)
75
+ return result;
76
+ const apDrain = (ctx.levelParams.apDrain ?? 1000) / 10000;
77
+ for (const tid of ctx.targets) {
78
+ const t = ctx.api.getUnit(tid);
79
+ if (t && t.teamId !== self.teamId) {
80
+ ctx.api.adjustActionProgress(tid, -apDrain);
81
+ }
82
+ }
83
+ return result;
84
+ }
85
+ };
86
+ // ============================================================
87
+ // 16024 雷波波比 被动:血量低于50%时进入无法行动状态,下一自己回合结束后对全体敌人造成200%雷属性伤害
88
+ // ============================================================
89
+ const m16024Script = {
90
+ onCast(ctx) {
91
+ const selfId = ctx.caster;
92
+ let triggered = false;
93
+ ctx.api.onAnyEvent(selfId, "OnTurnStart", (data) => {
94
+ const { actorId } = data;
95
+ if (actorId !== selfId)
96
+ return;
97
+ const self = ctx.api.getUnit(selfId);
98
+ if (!self || !self.runtime.alive)
99
+ return;
100
+ if (!triggered && self.runtime.Hp * 100 < self.stats.Mhp * 50) {
101
+ triggered = true;
102
+ ctx.api.addBuff(selfId, selfId, "stun");
103
+ }
104
+ });
105
+ ctx.api.onAnyEvent(selfId, "OnTurnEnd", (data) => {
106
+ const { actorId } = data;
107
+ if (actorId !== selfId)
108
+ return;
109
+ const self = ctx.api.getUnit(selfId);
110
+ if (!self || !self.runtime.alive)
111
+ return;
112
+ if (triggered) {
113
+ triggered = false;
114
+ const ratio = (ctx.levelParams.ratio ?? 20000) / 10000;
115
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId);
116
+ for (const enemy of enemies) {
117
+ ctx.api.damage(selfId, enemy.id, Math.floor(self.stats.Att * ratio));
118
+ }
119
+ }
120
+ });
121
+ return buildEmptyResult(ctx, [selfId]);
122
+ }
123
+ };
124
+ // ============================================================
125
+ // 16082 小岩2 技能:为自身及血量最低友军提供相当于自身15%最大生命值的护盾
126
+ // ============================================================
127
+ const m16082Script = {
128
+ onCast(ctx) {
129
+ const self = ctx.api.getUnit(ctx.caster);
130
+ if (!self)
131
+ return buildEmptyResult(ctx);
132
+ const hits = [];
133
+ const shieldRatio = (ctx.levelParams.shieldRatio ?? 1500) / 10000;
134
+ const shieldVal = Math.floor(self.stats.Mhp * shieldRatio);
135
+ // 自身
136
+ ctx.api.shield(ctx.caster, shieldVal);
137
+ hits.push(buildShieldHit(ctx, ctx.caster, shieldVal));
138
+ // 血量最低友军
139
+ const allies = ctx.api.getAliveUnits()
140
+ .filter(u => u.teamId === self.teamId && u.id !== ctx.caster)
141
+ .sort((a, b) => a.runtime.Hp - b.runtime.Hp);
142
+ if (allies.length > 0) {
143
+ ctx.api.shield(allies[0].id, shieldVal);
144
+ hits.push(buildShieldHit(ctx, allies[0].id, shieldVal));
145
+ }
146
+ return { ...buildEmptyResult(ctx, [ctx.caster, ...(allies.length > 0 ? [allies[0].id] : [])]), totalShield: shieldVal * (allies.length > 0 ? 2 : 1), hits };
147
+ }
148
+ };
149
+ // ============================================================
150
+ // 16064 小水2 被动:受到攻击时,有30%概率使攻击者【迟缓】,持续1回合
151
+ // ============================================================
152
+ const m16064Script = {
153
+ onCast(ctx) {
154
+ ctx.api.onAnyEvent(ctx.caster, "OnAfterDamage", (payload) => {
155
+ const data = payload;
156
+ if (data.targetId !== ctx.caster || data.value <= 0 || data.sourceId === data.targetId)
157
+ return;
158
+ const self = ctx.api.getUnit(ctx.caster);
159
+ if (!self || !self.runtime.alive)
160
+ return;
161
+ if (ctx.api.randomInt(10000) < 3000) {
162
+ ctx.api.addBuff(ctx.caster, data.sourceId, "slow");
163
+ }
164
+ });
165
+ return buildEmptyResult(ctx, [ctx.caster]);
166
+ }
167
+ };
168
+ // ============================================================
169
+ // 16104 小火2 被动:自身血量低于50%时,暴击率提升20%,暴击伤害提升20%
170
+ // ============================================================
171
+ const m16104Script = {
172
+ onCast(ctx) {
173
+ let buffApplied = false;
174
+ ctx.api.onAnyEvent(ctx.caster, "OnAfterDamage", (payload) => {
175
+ const data = payload;
176
+ if (data.targetId !== ctx.caster)
177
+ return;
178
+ const self = ctx.api.getUnit(ctx.caster);
179
+ if (!self || !self.runtime.alive)
180
+ return;
181
+ if (!buffApplied && self.runtime.Hp * 100 < self.stats.Mhp * 50) {
182
+ buffApplied = true;
183
+ ctx.api.addBuff(ctx.caster, ctx.caster, "monster-low-hp-crit");
184
+ }
185
+ });
186
+ return buildEmptyResult(ctx, [ctx.caster]);
187
+ }
188
+ };
189
+ // ============================================================
190
+ // 16122 小光2 技能:对随机两名敌人造成60%光属性伤害,并有50%概率赋予【炫目】
191
+ // ============================================================
192
+ const m16122Script = {
193
+ onCast(ctx) {
194
+ const self = ctx.api.getUnit(ctx.caster);
195
+ if (!self)
196
+ return buildEmptyResult(ctx);
197
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId);
198
+ if (enemies.length === 0)
199
+ return buildEmptyResult(ctx);
200
+ const hits = [];
201
+ let totalDamage = 0;
202
+ const hitTargets = [];
203
+ const count = Math.min(2, enemies.length);
204
+ const pool = [...enemies];
205
+ for (let i = 0; i < count; i++) {
206
+ const idx = ctx.api.randomInt(pool.length);
207
+ const target = pool.splice(idx, 1)[0];
208
+ hitTargets.push(target.id);
209
+ const ratio = (ctx.levelParams.ratio ?? 6000) / 10000;
210
+ const dmg = ctx.api.damage(ctx.caster, target.id, Math.floor(self.stats.Att * ratio));
211
+ totalDamage += dmg;
212
+ hits.push(buildDamageHit(ctx, target.id, dmg));
213
+ if (ctx.api.randomInt(10000) < 5000) {
214
+ ctx.api.addBuff(ctx.caster, target.id, "dazzle");
215
+ hits.push(buildBuffHit(ctx, target.id, "dazzle"));
216
+ }
217
+ else {
218
+ hits.push(buildBuffHit(ctx, target.id, "dazzle", true));
219
+ }
220
+ }
221
+ return { ...buildEmptyResult(ctx, hitTargets), totalDamage, hits };
222
+ }
223
+ };
224
+ // ============================================================
225
+ // 16132 小暗 技能:对血量最高的敌人造成100%暗属性伤害,并削减其10%能量
226
+ // (伤害由模板处理,这里处理削减能量)
227
+ // ============================================================
228
+ const m16132Script = {
229
+ onCast(ctx) {
230
+ const self = ctx.api.getUnit(ctx.caster);
231
+ if (!self)
232
+ return buildEmptyResult(ctx);
233
+ for (const tid of ctx.targets) {
234
+ const t = ctx.api.getUnit(tid);
235
+ if (t && t.teamId !== self.teamId) {
236
+ const drain = Math.floor(t.runtime.EnergyMax * 0.1);
237
+ t.runtime.Energy = Math.max(0, t.runtime.Energy - drain);
238
+ }
239
+ }
240
+ return buildEmptyResult(ctx);
241
+ }
242
+ };
243
+ // ============================================================
244
+ // 16144 小暗2 被动:死亡时召唤小光
245
+ // ============================================================
246
+ const m16144Script = {
247
+ onCast(ctx) {
248
+ ctx.api.onAnyEvent(ctx.caster, "OnDeath", (payload) => {
249
+ const data = payload;
250
+ if (data.targetId !== ctx.caster)
251
+ return;
252
+ const self = ctx.api.getUnit(ctx.caster);
253
+ if (!self)
254
+ return;
255
+ const id = `${ctx.caster}_light_spawn_${Date.now()}`;
256
+ const baseStats = { Mhp: 60, Def: 20, Att: 30, Spd: 80, pER: 0, pAR: 0, pEHR: 0, pERes: 0, pBSDI: 0, pESDI: 0, pFSDI: 0, pCTR: 0, pCTD: 150, pCTR_Def: 0, pCTD_Def: 0, pHE: 0, pFDI: 0, pFDI_Def: 0, pTDI: 0, pTDI_Def: 0, pWDI: 0, pWDI_Def: 0, pRDI: 0, pRDI_Def: 0, pADI: 0, pADI_Def: 0, pLDI: 0, pLDI_Def: 0, pDDI: 0, pDDI_Def: 0, pIM: 0, pITM: 0, pVulnerability: 0 };
257
+ ctx.api.spawnUnit({
258
+ id, name: "小光", teamId: self.teamId, position: 1, element: "light", stats: baseStats,
259
+ skills: [
260
+ { id: `${id}_b`, mode: "config", type: "basic", energyCost: 0, energyGain: 20, cooldown: 0, targetRule: "nearest", element: "light", configTemplateId: "m.basic.light.50" },
261
+ { id: `${id}_e`, mode: "config", type: "core", energyCost: 0, energyGain: 30, cooldown: 2, targetRule: "allAllies", element: "light", configTemplateId: "m.16112.heal40" }
262
+ ]
263
+ });
264
+ });
265
+ return buildEmptyResult(ctx, [ctx.caster]);
266
+ }
267
+ };
268
+ export const monsterScripts16xx = {
269
+ "m.16012.script": m16012Script,
270
+ "m.16024.script": m16024Script,
271
+ "m.16064.script": m16064Script,
272
+ "m.16082.script": m16082Script,
273
+ "m.16104.script": m16104Script,
274
+ "m.16122.script": m16122Script,
275
+ "m.16132.script": m16132Script,
276
+ "m.16144.script": m16144Script,
277
+ };
@@ -0,0 +1,2 @@
1
+ import type { ISkillScript } from "../types";
2
+ export declare const monsterScripts18xx: Record<string, ISkillScript>;
@@ -0,0 +1,330 @@
1
+ function buildEmptyResult(ctx, targets = ctx.targets) {
2
+ return { skillId: ctx.skillId, casterId: ctx.caster, targets, totalDamage: 0, totalHeal: 0, totalShield: 0, triggeredBy: ctx.trigger };
3
+ }
4
+ function buildDamageHit(ctx, targetId, v) {
5
+ return { targetId, effectType: "damage", element: ctx.api.getUnit(ctx.caster)?.element ?? "fire", skillType: "basic", isCritical: false, baseValue: v, criticalValue: v, finalValue: v, isRepression: 0, damageBreakdown: { preMitigation: v, postMitigation: v, shieldAbsorbed: 0, hpDamage: v } };
6
+ }
7
+ function buildBuffHit(ctx, targetId, buffId, resisted = false) {
8
+ return { targetId, effectType: "buff", element: ctx.api.getUnit(ctx.caster)?.element ?? "fire", skillType: "basic", isCritical: false, baseValue: 0, criticalValue: 0, finalValue: 0, isRepression: 0, appliedBuffId: resisted ? undefined : buffId, resisted };
9
+ }
10
+ function buildHealHit(ctx, targetId, v) {
11
+ return { targetId, effectType: "heal", element: ctx.api.getUnit(ctx.caster)?.element ?? "fire", skillType: "basic", isCritical: false, baseValue: v, criticalValue: v, finalValue: v, isRepression: 0 };
12
+ }
13
+ // ============================================================
14
+ // 18011 光波比比 普攻:对距离最近的一名敌人造成50%光属性伤害,使自身获得3层【光能】
15
+ // (伤害由模板处理,这里处理光能)
16
+ // ============================================================
17
+ const m18011Script = {
18
+ onCast(ctx) {
19
+ for (let i = 0; i < 3; i++) {
20
+ ctx.api.addBuff(ctx.caster, ctx.caster, "light-energy");
21
+ }
22
+ return buildEmptyResult(ctx);
23
+ }
24
+ };
25
+ // ============================================================
26
+ // 18012 光波比比 技能1:对敌方攻击力最高的单位造成130%光属性伤害,80%使其【炫目】,获得3层【光能】
27
+ // (伤害+炫目由模板处理,这里处理光能)
28
+ // ============================================================
29
+ const m18012Script = {
30
+ onCast(ctx) {
31
+ for (let i = 0; i < 3; i++) {
32
+ ctx.api.addBuff(ctx.caster, ctx.caster, "light-energy");
33
+ }
34
+ return buildEmptyResult(ctx);
35
+ }
36
+ };
37
+ // ============================================================
38
+ // 18013 光波比比 技能2:消耗所有【光能】连续对敌方所有单位造成20%光属性伤害,次数=光能层数
39
+ // ============================================================
40
+ const m18013Script = {
41
+ onCast(ctx) {
42
+ const self = ctx.api.getUnit(ctx.caster);
43
+ if (!self)
44
+ return buildEmptyResult(ctx);
45
+ const buffs = ctx.api.getBuffs(ctx.caster).filter(b => b.id === "light-energy");
46
+ const stacks = buffs.reduce((sum, b) => sum + b.stacks, 0);
47
+ ctx.api.removeBuff(ctx.caster, "light-energy");
48
+ if (stacks <= 0)
49
+ return buildEmptyResult(ctx);
50
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId);
51
+ let totalDamage = 0;
52
+ const hits = [];
53
+ const hitTargets = [];
54
+ for (let i = 0; i < stacks; i++) {
55
+ for (const enemy of enemies) {
56
+ if (!enemy.runtime.alive)
57
+ continue;
58
+ const perHitRatio = (ctx.levelParams.ratio ?? 2000) / 10000;
59
+ const dmg = ctx.api.damage(ctx.caster, enemy.id, Math.floor(self.stats.Att * perHitRatio));
60
+ totalDamage += dmg;
61
+ hits.push(buildDamageHit(ctx, enemy.id, dmg));
62
+ if (!hitTargets.includes(enemy.id))
63
+ hitTargets.push(enemy.id);
64
+ }
65
+ }
66
+ return { ...buildEmptyResult(ctx, hitTargets), totalDamage, hits };
67
+ }
68
+ };
69
+ // ============================================================
70
+ // 18022 暗波比比 技能1:对血量最高及其身后的一名敌人造成40%暗属性伤害,赋予其【侵蚀】
71
+ // ============================================================
72
+ const m18022Script = {
73
+ onCast(ctx) {
74
+ const self = ctx.api.getUnit(ctx.caster);
75
+ if (!self)
76
+ return buildEmptyResult(ctx);
77
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId).sort((a, b) => b.runtime.Hp - a.runtime.Hp);
78
+ if (enemies.length === 0)
79
+ return buildEmptyResult(ctx);
80
+ const primary = enemies[0];
81
+ const targets = [primary];
82
+ // 身后的一名敌人 = position比primary大的最近一个
83
+ const behind = enemies.filter(u => u.position > primary.position).sort((a, b) => a.position - b.position);
84
+ if (behind.length > 0)
85
+ targets.push(behind[0]);
86
+ let totalDamage = 0;
87
+ const hits = [];
88
+ for (const t of targets) {
89
+ const ratio18022 = (ctx.levelParams.ratio ?? 4000) / 10000;
90
+ const dmg = ctx.api.damage(ctx.caster, t.id, Math.floor(self.stats.Att * ratio18022));
91
+ totalDamage += dmg;
92
+ hits.push(buildDamageHit(ctx, t.id, dmg));
93
+ ctx.api.addBuff(ctx.caster, t.id, "erosion");
94
+ hits.push(buildBuffHit(ctx, t.id, "erosion"));
95
+ }
96
+ return { ...buildEmptyResult(ctx, targets.map(t => t.id)), totalDamage, hits };
97
+ }
98
+ };
99
+ // ============================================================
100
+ // 18023 暗波比比 技能2:锁定血量最高敌人扣除50%能量,获得【阴霾】提升下次普攻200%伤害
101
+ // 若敌人有【侵蚀】则额外造成150%暗属性伤害
102
+ // ============================================================
103
+ const m18023Script = {
104
+ onCast(ctx) {
105
+ const self = ctx.api.getUnit(ctx.caster);
106
+ if (!self)
107
+ return buildEmptyResult(ctx);
108
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId).sort((a, b) => b.runtime.Hp - a.runtime.Hp);
109
+ if (enemies.length === 0)
110
+ return buildEmptyResult(ctx);
111
+ const target = enemies[0];
112
+ const hits = [];
113
+ let totalDamage = 0;
114
+ // 扣除50%能量
115
+ const drain = Math.floor(target.runtime.EnergyMax * 0.5);
116
+ target.runtime.Energy = Math.max(0, target.runtime.Energy - drain);
117
+ // 获得阴霾
118
+ ctx.api.addBuff(ctx.caster, ctx.caster, "monster-gloom");
119
+ hits.push(buildBuffHit(ctx, ctx.caster, "monster-gloom"));
120
+ // 若有侵蚀则额外伤害
121
+ if (ctx.api.hasBuff(target.id, "erosion")) {
122
+ const extraRatio = (ctx.levelParams.extraRatio ?? 15000) / 10000;
123
+ const dmg = ctx.api.damage(ctx.caster, target.id, Math.floor(self.stats.Att * extraRatio));
124
+ totalDamage += dmg;
125
+ hits.push(buildDamageHit(ctx, target.id, dmg));
126
+ }
127
+ return { ...buildEmptyResult(ctx, [target.id]), totalDamage, hits };
128
+ }
129
+ };
130
+ // ============================================================
131
+ // 18033 骨团 技能2:对攻击力最高的一名敌人造成30%岩属性伤害,并使其眩晕
132
+ // ============================================================
133
+ const m18033Script = {
134
+ onCast(ctx) {
135
+ const self = ctx.api.getUnit(ctx.caster);
136
+ if (!self)
137
+ return buildEmptyResult(ctx);
138
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId).sort((a, b) => b.stats.Att - a.stats.Att);
139
+ if (enemies.length === 0)
140
+ return buildEmptyResult(ctx);
141
+ const target = enemies[0];
142
+ const hits = [];
143
+ const ratio18033 = (ctx.levelParams.ratio ?? 3000) / 10000;
144
+ const dmg = ctx.api.damage(ctx.caster, target.id, Math.floor(self.stats.Att * ratio18033));
145
+ hits.push(buildDamageHit(ctx, target.id, dmg));
146
+ ctx.api.addBuff(ctx.caster, target.id, "stun");
147
+ hits.push(buildBuffHit(ctx, target.id, "stun"));
148
+ return { ...buildEmptyResult(ctx, [target.id]), totalDamage: dmg, hits };
149
+ }
150
+ };
151
+ // ============================================================
152
+ // 18041 水波比比 普攻:对距离最近的一名敌人造成50%水属性伤害,如果敌人被【迟缓】则额外造成50%
153
+ // ============================================================
154
+ const m18041Script = {
155
+ onCast(ctx) {
156
+ const self = ctx.api.getUnit(ctx.caster);
157
+ const targetId = ctx.targets[0];
158
+ if (!self || !targetId)
159
+ return buildEmptyResult(ctx);
160
+ const hits = [];
161
+ let totalDamage = 0;
162
+ const dmg = ctx.api.damage(ctx.caster, targetId, Math.floor(self.stats.Att * 0.5));
163
+ totalDamage += dmg;
164
+ hits.push(buildDamageHit(ctx, targetId, dmg));
165
+ if (ctx.api.hasBuff(targetId, "slow")) {
166
+ const extra = ctx.api.damage(ctx.caster, targetId, Math.floor(self.stats.Att * 0.5));
167
+ totalDamage += extra;
168
+ hits.push(buildDamageHit(ctx, targetId, extra));
169
+ }
170
+ return { ...buildEmptyResult(ctx, [targetId]), totalDamage, hits };
171
+ }
172
+ };
173
+ // ============================================================
174
+ // 18042 水波比比 技能1:进入蓄力状态,下个回合开始时赋予自身【寒潮】
175
+ // ============================================================
176
+ const m18042Script = {
177
+ onCast(ctx) {
178
+ ctx.api.addBuff(ctx.caster, ctx.caster, "monster-charge");
179
+ // 监听下一回合开始赋予寒潮
180
+ ctx.api.onAnyEvent(ctx.caster, "OnTurnStart", (payload) => {
181
+ const { actorId } = payload;
182
+ if (actorId !== ctx.caster)
183
+ return;
184
+ const self = ctx.api.getUnit(ctx.caster);
185
+ if (!self || !self.runtime.alive)
186
+ return;
187
+ ctx.api.addBuff(ctx.caster, ctx.caster, "monster-cold-wave");
188
+ });
189
+ return buildEmptyResult(ctx, [ctx.caster]);
190
+ }
191
+ };
192
+ // ============================================================
193
+ // 18043 水波比比 技能2:当拥有寒潮时可释放冰河,对全体敌人造成50%水魔法伤害,100%概率赋予【迟缓】
194
+ // ============================================================
195
+ const m18043Script = {
196
+ onCast(ctx) {
197
+ const self = ctx.api.getUnit(ctx.caster);
198
+ if (!self)
199
+ return buildEmptyResult(ctx);
200
+ // 消耗寒潮
201
+ if (!ctx.api.hasBuff(ctx.caster, "monster-cold-wave"))
202
+ return buildEmptyResult(ctx);
203
+ ctx.api.removeBuff(ctx.caster, "monster-cold-wave");
204
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId);
205
+ let totalDamage = 0;
206
+ const hits = [];
207
+ for (const enemy of enemies) {
208
+ const dmg = ctx.api.damage(ctx.caster, enemy.id, Math.floor(self.stats.Att * 0.5));
209
+ totalDamage += dmg;
210
+ hits.push(buildDamageHit(ctx, enemy.id, dmg));
211
+ ctx.api.addBuff(ctx.caster, enemy.id, "slow");
212
+ hits.push(buildBuffHit(ctx, enemy.id, "slow"));
213
+ }
214
+ return { ...buildEmptyResult(ctx, enemies.map(e => e.id)), totalDamage, hits };
215
+ }
216
+ };
217
+ // ============================================================
218
+ // 18054 中火 被动:场上每存在1层【点燃】,自身造成的伤害提升5%
219
+ // ============================================================
220
+ const m18054Script = {
221
+ onCast(ctx) {
222
+ ctx.api.onAnyEvent(ctx.caster, "OnBeforeCast", (payload) => {
223
+ const self = ctx.api.getUnit(ctx.caster);
224
+ if (!self || !self.runtime.alive)
225
+ return;
226
+ // 统计场上所有点燃层数
227
+ const allUnits = ctx.api.getAliveUnits();
228
+ let burnStacks = 0;
229
+ for (const u of allUnits) {
230
+ const buffs = ctx.api.getBuffs(u.id).filter(b => b.id === "burn");
231
+ burnStacks += buffs.reduce((sum, b) => sum + b.stacks, 0);
232
+ }
233
+ // 移除旧的增伤buff再重新添加
234
+ ctx.api.removeBuff(ctx.caster, "monster-burn-empower");
235
+ for (let i = 0; i < burnStacks; i++) {
236
+ ctx.api.addBuff(ctx.caster, ctx.caster, "monster-burn-empower");
237
+ }
238
+ });
239
+ return buildEmptyResult(ctx, [ctx.caster]);
240
+ }
241
+ };
242
+ // ============================================================
243
+ // 18062 中雷 技能:对血量最低敌人造成130%雷伤害,若目标带有【静电】额外造成50%雷属性伤害
244
+ // ============================================================
245
+ const m18062Script = {
246
+ onCast(ctx) {
247
+ const self = ctx.api.getUnit(ctx.caster);
248
+ if (!self)
249
+ return buildEmptyResult(ctx);
250
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId).sort((a, b) => a.runtime.Hp - b.runtime.Hp);
251
+ if (enemies.length === 0)
252
+ return buildEmptyResult(ctx);
253
+ const target = enemies[0];
254
+ let totalDamage = 0;
255
+ const hits = [];
256
+ const dmg = ctx.api.damage(ctx.caster, target.id, Math.floor(self.stats.Att * 1.3));
257
+ totalDamage += dmg;
258
+ hits.push(buildDamageHit(ctx, target.id, dmg));
259
+ if (ctx.api.hasBuff(target.id, "static")) {
260
+ const extra = ctx.api.damage(ctx.caster, target.id, Math.floor(self.stats.Att * 0.5));
261
+ totalDamage += extra;
262
+ hits.push(buildDamageHit(ctx, target.id, extra));
263
+ }
264
+ return { ...buildEmptyResult(ctx, [target.id]), totalDamage, hits };
265
+ }
266
+ };
267
+ // ============================================================
268
+ // 18064 中雷 被动:每次自身攻击产生暴击时,自身行动条提前10%
269
+ // ============================================================
270
+ const m18064Script = {
271
+ onCast(ctx) {
272
+ ctx.api.onAnyEvent(ctx.caster, "OnAfterDamage", (payload) => {
273
+ const data = payload;
274
+ if (data.sourceId !== ctx.caster || data.value <= 0)
275
+ return;
276
+ const self = ctx.api.getUnit(ctx.caster);
277
+ if (!self || !self.runtime.alive)
278
+ return;
279
+ // 简化:每次造成伤害有暴击率概率触发行动条提前
280
+ // 实际暴击判定在公式层,这里用概率模拟
281
+ if (ctx.api.randomInt(10000) < self.stats.pCTR) {
282
+ ctx.api.adjustActionProgress(ctx.caster, 0.1);
283
+ }
284
+ });
285
+ return buildEmptyResult(ctx, [ctx.caster]);
286
+ }
287
+ };
288
+ // ============================================================
289
+ // 18072 中风 技能1:对全体敌人造成70%风属性伤害,并引爆所有【风化】状态立即结算1次伤害
290
+ // (伤害由模板处理,这里处理引爆风化)
291
+ // ============================================================
292
+ const m18072Script = {
293
+ onCast(ctx) {
294
+ const self = ctx.api.getUnit(ctx.caster);
295
+ if (!self)
296
+ return buildEmptyResult(ctx);
297
+ const enemies = ctx.api.getAliveUnits().filter(u => u.teamId !== self.teamId);
298
+ let totalDamage = 0;
299
+ const hits = [];
300
+ for (const enemy of enemies) {
301
+ const weatherBuffs = ctx.api.getBuffs(enemy.id).filter(b => b.id === "weathering");
302
+ const stacks = weatherBuffs.reduce((sum, b) => sum + b.stacks, 0);
303
+ if (stacks > 0) {
304
+ // 引爆:立即结算1次风化DOT伤害
305
+ const dotDmg = Math.floor(enemy.stats.Att * 0.18 * stacks);
306
+ if (dotDmg > 0) {
307
+ const dmg = ctx.api.damage(ctx.caster, enemy.id, dotDmg);
308
+ totalDamage += dmg;
309
+ hits.push(buildDamageHit(ctx, enemy.id, dmg));
310
+ }
311
+ }
312
+ }
313
+ return { ...buildEmptyResult(ctx), totalDamage, hits };
314
+ }
315
+ };
316
+ export const monsterScripts18xx = {
317
+ "m.18011.script": m18011Script,
318
+ "m.18012.script": m18012Script,
319
+ "m.18013.script": m18013Script,
320
+ "m.18022.script": m18022Script,
321
+ "m.18023.script": m18023Script,
322
+ "m.18033.script": m18033Script,
323
+ "m.18041.script": m18041Script,
324
+ "m.18042.script": m18042Script,
325
+ "m.18043.script": m18043Script,
326
+ "m.18054.script": m18054Script,
327
+ "m.18062.script": m18062Script,
328
+ "m.18064.script": m18064Script,
329
+ "m.18072.script": m18072Script,
330
+ };
@@ -0,0 +1,2 @@
1
+ import type { ISkillScript } from "../types";
2
+ export declare const monsterScripts19xxPart1: Record<string, ISkillScript>;