@pkmn/sim 0.9.26 → 0.9.28
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/build/cjs/config/formats.js +79 -86
- package/build/cjs/config/formats.js.map +1 -1
- package/build/cjs/data/abilities.js +100 -116
- package/build/cjs/data/abilities.js.map +1 -1
- package/build/cjs/data/aliases.js +3 -2
- package/build/cjs/data/aliases.js.map +1 -1
- package/build/cjs/data/conditions.js +3 -3
- package/build/cjs/data/conditions.js.map +1 -1
- package/build/cjs/data/formats-data.js +12 -12
- package/build/cjs/data/formats-data.js.map +1 -1
- package/build/cjs/data/items.js +55 -25
- package/build/cjs/data/items.js.map +1 -1
- package/build/cjs/data/legality.js +275 -23
- package/build/cjs/data/legality.js.map +1 -1
- package/build/cjs/data/mods/gen1/moves.js.map +1 -1
- package/build/cjs/data/mods/gen1/scripts.js +2 -0
- package/build/cjs/data/mods/gen1/scripts.js.map +1 -1
- package/build/cjs/data/mods/gen2/moves.js +1 -1
- package/build/cjs/data/mods/gen2/moves.js.map +1 -1
- package/build/cjs/data/mods/gen3/abilities.js +0 -2
- package/build/cjs/data/mods/gen3/abilities.js.map +1 -1
- package/build/cjs/data/mods/gen3/moves.js.map +1 -1
- package/build/cjs/data/mods/gen4/abilities.js +1 -1
- package/build/cjs/data/mods/gen4/abilities.js.map +1 -1
- package/build/cjs/data/mods/gen4/formats-data.js +1 -1
- package/build/cjs/data/mods/gen4/formats-data.js.map +1 -1
- package/build/cjs/data/mods/gen4/moves.js +88 -0
- package/build/cjs/data/mods/gen4/moves.js.map +1 -1
- package/build/cjs/data/mods/gen4/scripts.js +26 -0
- package/build/cjs/data/mods/gen4/scripts.js.map +1 -1
- package/build/cjs/data/mods/gen5/conditions.js +1 -2
- package/build/cjs/data/mods/gen5/conditions.js.map +1 -1
- package/build/cjs/data/mods/gen5/formats-data.js +2 -2
- package/build/cjs/data/mods/gen5/formats-data.js.map +1 -1
- package/build/cjs/data/mods/gen6/moves.js.map +1 -1
- package/build/cjs/data/mods/gen8/abilities.js +0 -2
- package/build/cjs/data/mods/gen8/abilities.js.map +1 -1
- package/build/cjs/data/mods/gen8/moves.js +1 -1
- package/build/cjs/data/mods/gen8/moves.js.map +1 -1
- package/build/cjs/data/moves.js +22 -18
- package/build/cjs/data/moves.js.map +1 -1
- package/build/cjs/data/pokemongo.js +1 -1
- package/build/cjs/data/text/moves.js +1 -1
- package/build/cjs/data/text/moves.js.map +1 -1
- package/build/cjs/lib/utils.d.ts +8 -0
- package/build/cjs/lib/utils.js +21 -2
- package/build/cjs/lib/utils.js.map +1 -1
- package/build/cjs/sim/battle-actions.js +30 -39
- package/build/cjs/sim/battle-actions.js.map +1 -1
- package/build/cjs/sim/battle-queue.d.ts +1 -1
- package/build/cjs/sim/battle-queue.js +0 -2
- package/build/cjs/sim/battle-queue.js.map +1 -1
- package/build/cjs/sim/battle-stream.js +2 -3
- package/build/cjs/sim/battle-stream.js.map +1 -1
- package/build/cjs/sim/battle.d.ts +13 -7
- package/build/cjs/sim/battle.js +197 -70
- package/build/cjs/sim/battle.js.map +1 -1
- package/build/cjs/sim/dex-abilities.d.ts +0 -1
- package/build/cjs/sim/dex-abilities.js +2 -2
- package/build/cjs/sim/dex-abilities.js.map +1 -1
- package/build/cjs/sim/dex-conditions.d.ts +11 -3
- package/build/cjs/sim/dex-conditions.js +2 -2
- package/build/cjs/sim/dex-conditions.js.map +1 -1
- package/build/cjs/sim/dex-data.js +7 -7
- package/build/cjs/sim/dex-data.js.map +1 -1
- package/build/cjs/sim/dex-formats.d.ts +2 -1
- package/build/cjs/sim/dex-formats.js.map +1 -1
- package/build/cjs/sim/dex-items.d.ts +3 -1
- package/build/cjs/sim/dex-items.js +3 -2
- package/build/cjs/sim/dex-items.js.map +1 -1
- package/build/cjs/sim/dex-moves.js +4 -4
- package/build/cjs/sim/dex-moves.js.map +1 -1
- package/build/cjs/sim/exported-global-types.d.ts +6 -0
- package/build/cjs/sim/field.js +9 -9
- package/build/cjs/sim/field.js.map +1 -1
- package/build/cjs/sim/global-types.d.ts +6 -0
- package/build/cjs/sim/pokemon.d.ts +8 -4
- package/build/cjs/sim/pokemon.js +62 -47
- package/build/cjs/sim/pokemon.js.map +1 -1
- package/build/cjs/sim/prng.d.ts +57 -18
- package/build/cjs/sim/prng.js +171 -38
- package/build/cjs/sim/prng.js.map +1 -1
- package/build/cjs/sim/side.js +9 -6
- package/build/cjs/sim/side.js.map +1 -1
- package/build/cjs/sim/state.js +1 -1
- package/build/cjs/sim/state.js.map +1 -1
- package/build/cjs/sim/team-validator.js +16 -17
- package/build/cjs/sim/team-validator.js.map +1 -1
- package/build/cjs/sim/tools/exhaustive-runner.js +11 -12
- package/build/cjs/sim/tools/exhaustive-runner.js.map +1 -1
- package/build/cjs/sim/tools/random-player-ai.js +5 -5
- package/build/cjs/sim/tools/random-player-ai.js.map +1 -1
- package/build/cjs/sim/tools/runner.js +7 -8
- package/build/cjs/sim/tools/runner.js.map +1 -1
- package/build/esm/config/formats.mjs +79 -86
- package/build/esm/config/formats.mjs.map +1 -1
- package/build/esm/data/abilities.mjs +100 -116
- package/build/esm/data/abilities.mjs.map +1 -1
- package/build/esm/data/aliases.mjs +3 -2
- package/build/esm/data/aliases.mjs.map +1 -1
- package/build/esm/data/conditions.mjs +3 -3
- package/build/esm/data/conditions.mjs.map +1 -1
- package/build/esm/data/formats-data.mjs +12 -12
- package/build/esm/data/formats-data.mjs.map +1 -1
- package/build/esm/data/items.mjs +55 -25
- package/build/esm/data/items.mjs.map +1 -1
- package/build/esm/data/legality.mjs +275 -23
- package/build/esm/data/legality.mjs.map +1 -1
- package/build/esm/data/mods/gen1/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen1/scripts.mjs +2 -0
- package/build/esm/data/mods/gen1/scripts.mjs.map +1 -1
- package/build/esm/data/mods/gen2/moves.mjs +1 -1
- package/build/esm/data/mods/gen2/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen3/abilities.mjs +0 -2
- package/build/esm/data/mods/gen3/abilities.mjs.map +1 -1
- package/build/esm/data/mods/gen3/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen4/abilities.mjs +1 -1
- package/build/esm/data/mods/gen4/abilities.mjs.map +1 -1
- package/build/esm/data/mods/gen4/formats-data.mjs +1 -1
- package/build/esm/data/mods/gen4/formats-data.mjs.map +1 -1
- package/build/esm/data/mods/gen4/moves.mjs +88 -0
- package/build/esm/data/mods/gen4/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen4/scripts.mjs +26 -0
- package/build/esm/data/mods/gen4/scripts.mjs.map +1 -1
- package/build/esm/data/mods/gen5/conditions.mjs +1 -2
- package/build/esm/data/mods/gen5/conditions.mjs.map +1 -1
- package/build/esm/data/mods/gen5/formats-data.mjs +2 -2
- package/build/esm/data/mods/gen5/formats-data.mjs.map +1 -1
- package/build/esm/data/mods/gen6/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen8/abilities.mjs +0 -2
- package/build/esm/data/mods/gen8/abilities.mjs.map +1 -1
- package/build/esm/data/mods/gen8/moves.mjs +1 -1
- package/build/esm/data/mods/gen8/moves.mjs.map +1 -1
- package/build/esm/data/moves.mjs +22 -18
- package/build/esm/data/moves.mjs.map +1 -1
- package/build/esm/data/pokemongo.mjs +1 -1
- package/build/esm/data/text/moves.mjs +1 -1
- package/build/esm/data/text/moves.mjs.map +1 -1
- package/build/esm/lib/utils.d.mts +8 -0
- package/build/esm/lib/utils.mjs +18 -2
- package/build/esm/lib/utils.mjs.map +1 -1
- package/build/esm/sim/battle-actions.mjs +30 -39
- package/build/esm/sim/battle-actions.mjs.map +1 -1
- package/build/esm/sim/battle-queue.d.mts +1 -1
- package/build/esm/sim/battle-queue.mjs +0 -2
- package/build/esm/sim/battle-queue.mjs.map +1 -1
- package/build/esm/sim/battle-stream.mjs +2 -3
- package/build/esm/sim/battle-stream.mjs.map +1 -1
- package/build/esm/sim/battle.d.mts +13 -7
- package/build/esm/sim/battle.mjs +196 -69
- package/build/esm/sim/battle.mjs.map +1 -1
- package/build/esm/sim/dex-abilities.d.mts +0 -1
- package/build/esm/sim/dex-abilities.mjs +1 -1
- package/build/esm/sim/dex-abilities.mjs.map +1 -1
- package/build/esm/sim/dex-conditions.d.mts +11 -3
- package/build/esm/sim/dex-conditions.mjs +1 -1
- package/build/esm/sim/dex-conditions.mjs.map +1 -1
- package/build/esm/sim/dex-data.mjs +1 -1
- package/build/esm/sim/dex-data.mjs.map +1 -1
- package/build/esm/sim/dex-formats.d.mts +2 -1
- package/build/esm/sim/dex-formats.mjs.map +1 -1
- package/build/esm/sim/dex-items.d.mts +3 -1
- package/build/esm/sim/dex-items.mjs +2 -1
- package/build/esm/sim/dex-items.mjs.map +1 -1
- package/build/esm/sim/dex-moves.mjs +1 -1
- package/build/esm/sim/dex-moves.mjs.map +1 -1
- package/build/esm/sim/exported-global-types.d.mts +6 -0
- package/build/esm/sim/field.mjs +9 -9
- package/build/esm/sim/field.mjs.map +1 -1
- package/build/esm/sim/global-types.d.mts +6 -0
- package/build/esm/sim/pokemon.d.mts +8 -4
- package/build/esm/sim/pokemon.mjs +62 -47
- package/build/esm/sim/pokemon.mjs.map +1 -1
- package/build/esm/sim/prng.d.mts +57 -18
- package/build/esm/sim/prng.mjs +134 -36
- package/build/esm/sim/prng.mjs.map +1 -1
- package/build/esm/sim/side.mjs +8 -5
- package/build/esm/sim/side.mjs.map +1 -1
- package/build/esm/sim/state.mjs +1 -1
- package/build/esm/sim/state.mjs.map +1 -1
- package/build/esm/sim/team-validator.mjs +10 -11
- package/build/esm/sim/team-validator.mjs.map +1 -1
- package/build/esm/sim/tools/exhaustive-runner.mjs +11 -12
- package/build/esm/sim/tools/exhaustive-runner.mjs.map +1 -1
- package/build/esm/sim/tools/random-player-ai.mjs +5 -5
- package/build/esm/sim/tools/random-player-ai.mjs.map +1 -1
- package/build/esm/sim/tools/runner.mjs +7 -8
- package/build/esm/sim/tools/runner.mjs.map +1 -1
- package/package.json +3 -2
package/build/cjs/sim/battle.js
CHANGED
|
@@ -26,7 +26,7 @@ const side_1 = require("./side");
|
|
|
26
26
|
const state_1 = require("./state");
|
|
27
27
|
const battle_queue_1 = require("./battle-queue");
|
|
28
28
|
const battle_actions_1 = require("./battle-actions");
|
|
29
|
-
const
|
|
29
|
+
const utils_1 = require("../lib/utils");
|
|
30
30
|
const splitRegex = /^\|split\|p([1234])\n(.*)\n(.*)|.+/gm;
|
|
31
31
|
function extractChannelMessages(message, channelIds) {
|
|
32
32
|
const channelIdSet = new Set(channelIds);
|
|
@@ -63,7 +63,7 @@ class Battle {
|
|
|
63
63
|
this.gen = this.dex.gen;
|
|
64
64
|
this.ruleTable = this.dex.formats.getRuleTable(format);
|
|
65
65
|
this.trunc = this.dex.trunc;
|
|
66
|
-
this.clampIntRange =
|
|
66
|
+
this.clampIntRange = utils_1.Utils.clampIntRange;
|
|
67
67
|
// Object.assign(this, this.dex.data.Scripts);
|
|
68
68
|
for (const i in this.dex.data.Scripts) {
|
|
69
69
|
const entry = this.dex.data.Scripts[i];
|
|
@@ -79,7 +79,7 @@ class Battle {
|
|
|
79
79
|
options.forceRandomChance : null;
|
|
80
80
|
this.deserialized = !!options.deserialized;
|
|
81
81
|
this.strictChoices = !!options.strictChoices;
|
|
82
|
-
this.formatData = { id: format.id };
|
|
82
|
+
this.formatData = this.initEffectState({ id: format.id });
|
|
83
83
|
this.gameType = (format.gameType || 'singles');
|
|
84
84
|
this.field = new field_1.Field(this);
|
|
85
85
|
this.sides = Array(format.playerCount).fill(null);
|
|
@@ -87,7 +87,7 @@ class Battle {
|
|
|
87
87
|
(format.playerCount > 2 || this.gameType === 'doubles') ? 2 :
|
|
88
88
|
1;
|
|
89
89
|
this.prng = options.prng || new prng_1.PRNG(options.seed || undefined);
|
|
90
|
-
this.prngSeed = this.prng.startingSeed
|
|
90
|
+
this.prngSeed = this.prng.startingSeed;
|
|
91
91
|
this.rated = options.rated || !!options.rated;
|
|
92
92
|
this.reportExactHP = !!format.debug;
|
|
93
93
|
this.reportPercentages = false;
|
|
@@ -105,7 +105,7 @@ class Battle {
|
|
|
105
105
|
this.started = false;
|
|
106
106
|
this.ended = false;
|
|
107
107
|
this.effect = { id: '' };
|
|
108
|
-
this.effectState = { id: '' };
|
|
108
|
+
this.effectState = this.initEffectState({ id: '' });
|
|
109
109
|
this.event = { id: '' };
|
|
110
110
|
this.events = null;
|
|
111
111
|
this.eventDepth = 0;
|
|
@@ -116,8 +116,12 @@ class Battle {
|
|
|
116
116
|
this.lastMoveLine = -1;
|
|
117
117
|
this.lastSuccessfulMoveThisTurn = null;
|
|
118
118
|
this.lastDamage = 0;
|
|
119
|
-
this.
|
|
119
|
+
this.effectOrder = 0;
|
|
120
120
|
this.quickClawRoll = false;
|
|
121
|
+
this.speedOrder = [];
|
|
122
|
+
for (let i = 0; i < this.activePerHalf * 2; i++) {
|
|
123
|
+
this.speedOrder.push(i);
|
|
124
|
+
}
|
|
121
125
|
this.teamGenerator = null;
|
|
122
126
|
this.hints = new Set();
|
|
123
127
|
this.NOT_FAIL = '';
|
|
@@ -126,7 +130,7 @@ class Battle {
|
|
|
126
130
|
this.SILENT_FAIL = null;
|
|
127
131
|
this.send = options.send || (() => { });
|
|
128
132
|
const inputOptions = {
|
|
129
|
-
formatid: options.formatid, seed: this.
|
|
133
|
+
formatid: options.formatid, seed: this.prngSeed,
|
|
130
134
|
};
|
|
131
135
|
if (this.rated)
|
|
132
136
|
inputOptions.rated = this.rated;
|
|
@@ -184,7 +188,7 @@ class Battle {
|
|
|
184
188
|
return `Battle: ${this.format}`;
|
|
185
189
|
}
|
|
186
190
|
random(m, n) {
|
|
187
|
-
return this.prng.
|
|
191
|
+
return this.prng.random(m, n);
|
|
188
192
|
}
|
|
189
193
|
randomChance(numerator, denominator) {
|
|
190
194
|
if (this.forceRandomChance !== null)
|
|
@@ -195,7 +199,7 @@ class Battle {
|
|
|
195
199
|
return this.prng.sample(items);
|
|
196
200
|
}
|
|
197
201
|
/** Note that passing `undefined` resets to the starting seed, but `null` will roll a new seed */
|
|
198
|
-
resetRNG(seed = this.
|
|
202
|
+
resetRNG(seed = this.prngSeed) {
|
|
199
203
|
this.prng = new prng_1.PRNG(seed);
|
|
200
204
|
this.add('message', "The battle's RNG was reset.");
|
|
201
205
|
}
|
|
@@ -230,6 +234,7 @@ class Battle {
|
|
|
230
234
|
* 2. Priority, high to low (default 0)
|
|
231
235
|
* 3. Speed, high to low (default 0)
|
|
232
236
|
* 4. SubOrder, low to high (default 0)
|
|
237
|
+
* 5. EffectOrder, low to high (default 0)
|
|
233
238
|
*
|
|
234
239
|
* Doesn't reference `this` so doesn't need to be bound.
|
|
235
240
|
*/
|
|
@@ -238,12 +243,14 @@ class Battle {
|
|
|
238
243
|
((b.priority || 0) - (a.priority || 0)) ||
|
|
239
244
|
((b.speed || 0) - (a.speed || 0)) ||
|
|
240
245
|
-((b.subOrder || 0) - (a.subOrder || 0)) ||
|
|
246
|
+
-((b.effectOrder || 0) - (a.effectOrder || 0)) ||
|
|
241
247
|
0;
|
|
242
248
|
}
|
|
243
249
|
static compareRedirectOrder(a, b) {
|
|
244
250
|
return ((b.priority || 0) - (a.priority || 0)) ||
|
|
245
251
|
((b.speed || 0) - (a.speed || 0)) ||
|
|
246
|
-
((a.effectHolder && b.effectHolder) ?
|
|
252
|
+
((a.effectHolder?.abilityState && b.effectHolder?.abilityState) ?
|
|
253
|
+
-(b.effectHolder.abilityState.effectOrder - a.effectHolder.abilityState.effectOrder) : 0) ||
|
|
247
254
|
0;
|
|
248
255
|
}
|
|
249
256
|
static compareLeftToRightOrder(a, b) {
|
|
@@ -308,22 +315,32 @@ class Battle {
|
|
|
308
315
|
/**
|
|
309
316
|
* Runs an event with no source on each effect on the field, in Speed order.
|
|
310
317
|
*
|
|
311
|
-
* Unlike `eachEvent`, this contains a lot of other handling and is intended
|
|
318
|
+
* Unlike `eachEvent`, this contains a lot of other handling and is only intended for
|
|
319
|
+
* the 'Residual' and 'SwitchIn' events.
|
|
312
320
|
*/
|
|
313
|
-
|
|
321
|
+
fieldEvent(eventid, targets) {
|
|
314
322
|
const callbackName = `on${eventid}`;
|
|
315
|
-
let
|
|
316
|
-
|
|
323
|
+
let getKey;
|
|
324
|
+
if (eventid === 'Residual') {
|
|
325
|
+
getKey = 'duration';
|
|
326
|
+
}
|
|
327
|
+
let handlers = this.findFieldEventHandlers(this.field, `onField${eventid}`, getKey);
|
|
317
328
|
for (const side of this.sides) {
|
|
318
329
|
if (side.n < 2 || !side.allySide) {
|
|
319
|
-
handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`,
|
|
330
|
+
handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, getKey));
|
|
320
331
|
}
|
|
321
332
|
for (const active of side.active) {
|
|
322
333
|
if (!active)
|
|
323
334
|
continue;
|
|
324
|
-
|
|
335
|
+
if (eventid === 'SwitchIn') {
|
|
336
|
+
handlers = handlers.concat(this.findPokemonEventHandlers(active, `onAny${eventid}`));
|
|
337
|
+
}
|
|
338
|
+
if (targets && !targets.includes(active))
|
|
339
|
+
continue;
|
|
340
|
+
handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, getKey));
|
|
325
341
|
handlers = handlers.concat(this.findSideEventHandlers(side, callbackName, undefined, active));
|
|
326
342
|
handlers = handlers.concat(this.findFieldEventHandlers(this.field, callbackName, undefined, active));
|
|
343
|
+
handlers = handlers.concat(this.findBattleEventHandlers(callbackName, getKey, active));
|
|
327
344
|
}
|
|
328
345
|
}
|
|
329
346
|
this.speedSort(handlers);
|
|
@@ -331,9 +348,11 @@ class Battle {
|
|
|
331
348
|
const handler = handlers[0];
|
|
332
349
|
handlers.shift();
|
|
333
350
|
const effect = handler.effect;
|
|
334
|
-
if (handler.effectHolder.fainted)
|
|
335
|
-
|
|
336
|
-
|
|
351
|
+
if (handler.effectHolder.fainted) {
|
|
352
|
+
if (!(handler.state?.isSlotCondition))
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (eventid === 'Residual' && handler.end && handler.state && handler.state.duration) {
|
|
337
356
|
handler.state.duration--;
|
|
338
357
|
if (!handler.state.duration) {
|
|
339
358
|
const endCallArgs = handler.endCallArgs || [handler.effectHolder, effect.id];
|
|
@@ -349,7 +368,7 @@ class Battle {
|
|
|
349
368
|
if (handler.effectHolder.pseudoWeather)
|
|
350
369
|
handlerEventid = `Field${eventid}`;
|
|
351
370
|
if (handler.callback) {
|
|
352
|
-
this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null,
|
|
371
|
+
this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, undefined, handler.callback);
|
|
353
372
|
}
|
|
354
373
|
this.faintMessages();
|
|
355
374
|
if (this.ended)
|
|
@@ -383,8 +402,8 @@ class Battle {
|
|
|
383
402
|
// it's changed; call it off
|
|
384
403
|
return relayVar;
|
|
385
404
|
}
|
|
386
|
-
if (eventid !== 'Start' && eventid !== 'TakeItem' &&
|
|
387
|
-
|
|
405
|
+
if (eventid !== 'Start' && eventid !== 'TakeItem' && effect.effectType === 'Item' &&
|
|
406
|
+
(target instanceof pokemon_1.Pokemon) && target.ignoringItem()) {
|
|
388
407
|
this.debug(eventid + ' handler suppressed by Embargo, Klutz or Magic Room');
|
|
389
408
|
return relayVar;
|
|
390
409
|
}
|
|
@@ -404,7 +423,7 @@ class Battle {
|
|
|
404
423
|
const parentEffectState = this.effectState;
|
|
405
424
|
const parentEvent = this.event;
|
|
406
425
|
this.effect = effect;
|
|
407
|
-
this.effectState = state || {};
|
|
426
|
+
this.effectState = state || this.initEffectState({});
|
|
408
427
|
this.event = { id: eventid, target, source, effect: sourceEffect };
|
|
409
428
|
this.eventDepth++;
|
|
410
429
|
const args = [target, source, sourceEffect];
|
|
@@ -555,7 +574,7 @@ class Battle {
|
|
|
555
574
|
if (Array.isArray(target))
|
|
556
575
|
throw new Error("");
|
|
557
576
|
handlers.unshift(this.resolvePriority({
|
|
558
|
-
effect: sourceEffect, callback, state: {}, end: null, effectHolder: target,
|
|
577
|
+
effect: sourceEffect, callback, state: this.initEffectState({}), end: null, effectHolder: target,
|
|
559
578
|
}, `on${eventid}`));
|
|
560
579
|
}
|
|
561
580
|
}
|
|
@@ -674,7 +693,7 @@ class Battle {
|
|
|
674
693
|
const parentEffect = this.effect;
|
|
675
694
|
const parentEffectState = this.effectState;
|
|
676
695
|
this.effect = handler.effect;
|
|
677
|
-
this.effectState = handler.state || {};
|
|
696
|
+
this.effectState = handler.state || this.initEffectState({});
|
|
678
697
|
this.effectState.target = effectHolder;
|
|
679
698
|
returnVal = handler.callback.apply(this, args);
|
|
680
699
|
this.effect = parentEffect;
|
|
@@ -716,15 +735,74 @@ class Battle {
|
|
|
716
735
|
priorityEvent(eventid, target, source, effect, relayVar, onEffect) {
|
|
717
736
|
return this.runEvent(eventid, target, source, effect, relayVar, onEffect, true);
|
|
718
737
|
}
|
|
719
|
-
resolvePriority(
|
|
738
|
+
resolvePriority(h, callbackName) {
|
|
739
|
+
const handler = h;
|
|
720
740
|
// @ts-ignore
|
|
721
741
|
handler.order = handler.effect[`${callbackName}Order`] || false;
|
|
722
742
|
// @ts-ignore
|
|
723
743
|
handler.priority = handler.effect[`${callbackName}Priority`] || 0;
|
|
724
744
|
// @ts-ignore
|
|
725
745
|
handler.subOrder = handler.effect[`${callbackName}SubOrder`] || 0;
|
|
746
|
+
if (!handler.subOrder) {
|
|
747
|
+
// https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-59#post-8685465
|
|
748
|
+
const effectTypeOrder = {
|
|
749
|
+
// Z-Move: 1,
|
|
750
|
+
Condition: 2,
|
|
751
|
+
// Slot Condition: 3,
|
|
752
|
+
// Side Condition: 4,
|
|
753
|
+
// Field Condition: 5, (includes weather but also terrains and pseudoweathers)
|
|
754
|
+
Weather: 5,
|
|
755
|
+
Format: 5,
|
|
756
|
+
Rule: 5,
|
|
757
|
+
Ruleset: 5,
|
|
758
|
+
// Poison Touch: 6, (also includes Perish Body)
|
|
759
|
+
Ability: 7,
|
|
760
|
+
Item: 8,
|
|
761
|
+
// Stall: 9,
|
|
762
|
+
};
|
|
763
|
+
handler.subOrder = effectTypeOrder[handler.effect.effectType] || 0;
|
|
764
|
+
if (handler.effect.effectType === 'Condition') {
|
|
765
|
+
if (handler.state?.target instanceof side_1.Side) {
|
|
766
|
+
if (handler.state.isSlotCondition) {
|
|
767
|
+
// slot condition
|
|
768
|
+
handler.subOrder = 3;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
// side condition
|
|
772
|
+
handler.subOrder = 4;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
else if (handler.state?.target instanceof field_1.Field) {
|
|
776
|
+
// field condition
|
|
777
|
+
handler.subOrder = 5;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else if (handler.effect.effectType === 'Ability') {
|
|
781
|
+
if (handler.effect.name === 'Poison Touch' || handler.effect.name === 'Perish Body') {
|
|
782
|
+
handler.subOrder = 6;
|
|
783
|
+
}
|
|
784
|
+
else if (handler.effect.name === 'Stall') {
|
|
785
|
+
handler.subOrder = 9;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (callbackName.endsWith('SwitchIn') || callbackName.endsWith('RedirectTarget')) {
|
|
790
|
+
// If multiple hazards are present on one side, their event handlers all perfectly tie in speed, priority,
|
|
791
|
+
// and subOrder. They should activate in the order they were created, which is where effectOrder comes in.
|
|
792
|
+
// This also applies to speed ties for which ability like Lightning Rod redirects moves.
|
|
793
|
+
// TODO: In-game, other events are also sorted this way, but that's an implementation for another refactor
|
|
794
|
+
handler.effectOrder = handler.state?.effectOrder;
|
|
795
|
+
}
|
|
726
796
|
if (handler.effectHolder && handler.effectHolder.getStat) {
|
|
727
|
-
|
|
797
|
+
const pokemon = handler.effectHolder;
|
|
798
|
+
handler.speed = pokemon.speed;
|
|
799
|
+
if (callbackName.endsWith('SwitchIn')) {
|
|
800
|
+
// Pokemon speeds including ties are resolved before all onSwitchIn handlers and aren't re-sorted in-between
|
|
801
|
+
// so we subtract a fractional speed from each Pokemon's respective event handlers by using the index of their
|
|
802
|
+
// unique field position in a pre-sorted-by-speed array
|
|
803
|
+
const fieldPositionValue = pokemon.side.n * this.sides.length + pokemon.position;
|
|
804
|
+
handler.speed -= this.speedOrder.indexOf(fieldPositionValue) / (this.activePerHalf * 2);
|
|
805
|
+
}
|
|
728
806
|
}
|
|
729
807
|
return handler;
|
|
730
808
|
}
|
|
@@ -799,7 +877,22 @@ class Battle {
|
|
|
799
877
|
effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
|
|
800
878
|
}, callbackName));
|
|
801
879
|
}
|
|
880
|
+
else if (['ability', 'item'].includes(volatile.id.split(':')[0])) {
|
|
881
|
+
// Innate abilities/items; see comment below
|
|
882
|
+
// @ts-ignore - dynamic lookup
|
|
883
|
+
if (this.gen >= 5 && callbackName === 'onSwitchIn' && !volatile.onAnySwitchIn) {
|
|
884
|
+
callback = volatile.onStart;
|
|
885
|
+
if (callback !== undefined || (getKey && volatileState[getKey])) {
|
|
886
|
+
handlers.push(this.resolvePriority({
|
|
887
|
+
effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
|
|
888
|
+
}, callbackName));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
802
892
|
}
|
|
893
|
+
// Abilities and items Start at different times during the SwitchIn event, so we run their onStart handlers
|
|
894
|
+
// during the SwitchIn event instead of running the Start event during switch-ins
|
|
895
|
+
// gens 4 and before still use the old system, though
|
|
803
896
|
const ability = pokemon.getAbility();
|
|
804
897
|
// @ts-ignore - dynamic lookup
|
|
805
898
|
callback = ability[callbackName];
|
|
@@ -807,6 +900,16 @@ class Battle {
|
|
|
807
900
|
handlers.push(this.resolvePriority({
|
|
808
901
|
effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
|
|
809
902
|
}, callbackName));
|
|
903
|
+
// @ts-ignore - dynamic lookup
|
|
904
|
+
}
|
|
905
|
+
else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !ability.onAnySwitchIn) {
|
|
906
|
+
// @ts-ignore - dynamic lookup
|
|
907
|
+
callback = ability.onStart;
|
|
908
|
+
if (callback !== undefined || (getKey && pokemon.abilityState[getKey])) {
|
|
909
|
+
handlers.push(this.resolvePriority({
|
|
910
|
+
effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
|
|
911
|
+
}, callbackName));
|
|
912
|
+
}
|
|
810
913
|
}
|
|
811
914
|
const item = pokemon.getItem();
|
|
812
915
|
// @ts-ignore - dynamic lookup
|
|
@@ -815,6 +918,16 @@ class Battle {
|
|
|
815
918
|
handlers.push(this.resolvePriority({
|
|
816
919
|
effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
|
|
817
920
|
}, callbackName));
|
|
921
|
+
// @ts-ignore - dynamic lookup
|
|
922
|
+
}
|
|
923
|
+
else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !item.onAnySwitchIn) {
|
|
924
|
+
// @ts-ignore - dynamic lookup
|
|
925
|
+
callback = item.onStart;
|
|
926
|
+
if (callback !== undefined || (getKey && pokemon.itemState[getKey])) {
|
|
927
|
+
handlers.push(this.resolvePriority({
|
|
928
|
+
effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
|
|
929
|
+
}, callbackName));
|
|
930
|
+
}
|
|
818
931
|
}
|
|
819
932
|
const species = pokemon.baseSpecies;
|
|
820
933
|
// @ts-ignore - dynamic lookup
|
|
@@ -837,13 +950,13 @@ class Battle {
|
|
|
837
950
|
state: slotConditionState,
|
|
838
951
|
end: side.removeSlotCondition,
|
|
839
952
|
endCallArgs: [side, pokemon, slotCondition.id],
|
|
840
|
-
effectHolder:
|
|
953
|
+
effectHolder: pokemon,
|
|
841
954
|
}, callbackName));
|
|
842
955
|
}
|
|
843
956
|
}
|
|
844
957
|
return handlers;
|
|
845
958
|
}
|
|
846
|
-
findBattleEventHandlers(callbackName, getKey) {
|
|
959
|
+
findBattleEventHandlers(callbackName, getKey, customHolder) {
|
|
847
960
|
const handlers = [];
|
|
848
961
|
let callback;
|
|
849
962
|
const format = this.format;
|
|
@@ -851,15 +964,15 @@ class Battle {
|
|
|
851
964
|
callback = format[callbackName];
|
|
852
965
|
if (callback !== undefined || (getKey && this.formatData[getKey])) {
|
|
853
966
|
handlers.push(this.resolvePriority({
|
|
854
|
-
effect: format, callback, state: this.formatData, end: null, effectHolder: this,
|
|
967
|
+
effect: format, callback, state: this.formatData, end: null, effectHolder: customHolder || this,
|
|
855
968
|
}, callbackName));
|
|
856
969
|
}
|
|
857
970
|
if (this.events && (callback = this.events[callbackName]) !== undefined) {
|
|
858
971
|
for (const handler of callback) {
|
|
859
972
|
const state = (handler.target.effectType === 'Format') ? this.formatData : null;
|
|
860
973
|
handlers.push({
|
|
861
|
-
effect: handler.target, callback: handler.callback, state, end: null,
|
|
862
|
-
|
|
974
|
+
effect: handler.target, callback: handler.callback, state, end: null, effectHolder: customHolder || this,
|
|
975
|
+
priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
|
|
863
976
|
});
|
|
864
977
|
}
|
|
865
978
|
}
|
|
@@ -1002,11 +1115,11 @@ class Battle {
|
|
|
1002
1115
|
}
|
|
1003
1116
|
return pokemonList;
|
|
1004
1117
|
}
|
|
1005
|
-
getAllActive() {
|
|
1118
|
+
getAllActive(includeFainted) {
|
|
1006
1119
|
const pokemonList = [];
|
|
1007
1120
|
for (const side of this.sides) {
|
|
1008
1121
|
for (const pokemon of side.active) {
|
|
1009
|
-
if (pokemon && !pokemon.fainted) {
|
|
1122
|
+
if (pokemon && (includeFainted || !pokemon.fainted)) {
|
|
1010
1123
|
pokemonList.push(pokemon);
|
|
1011
1124
|
}
|
|
1012
1125
|
}
|
|
@@ -1401,24 +1514,6 @@ class Battle {
|
|
|
1401
1514
|
this.quickClawRoll = this.randomChance(60, 256);
|
|
1402
1515
|
if (this.gen === 3)
|
|
1403
1516
|
this.quickClawRoll = this.randomChance(1, 5);
|
|
1404
|
-
// Crazyhouse Progress checker because sidebars has trouble keeping track of Pokemon.
|
|
1405
|
-
// Please remove me once there is client support.
|
|
1406
|
-
if (this.ruleTable.has('crazyhouserule')) {
|
|
1407
|
-
for (const side of this.sides) {
|
|
1408
|
-
let buf = `raw|${lib_1.Utils.escapeHTML(side.name)}'s team:<br />`;
|
|
1409
|
-
for (const pokemon of side.pokemon) {
|
|
1410
|
-
if (!buf.endsWith('<br />'))
|
|
1411
|
-
buf += '/</span>​';
|
|
1412
|
-
if (pokemon.fainted) {
|
|
1413
|
-
buf += `<span style="white-space:nowrap;"><span style="opacity:.3"><psicon pokemon="${pokemon.species.id}" /></span>`;
|
|
1414
|
-
}
|
|
1415
|
-
else {
|
|
1416
|
-
buf += `<span style="white-space:nowrap"><psicon pokemon="${pokemon.species.id}" />`;
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
this.add(`${buf}</span>`);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
1517
|
this.makeRequest('move');
|
|
1423
1518
|
}
|
|
1424
1519
|
maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide) {
|
|
@@ -1823,14 +1918,16 @@ class Battle {
|
|
|
1823
1918
|
this.lastDamage = damage;
|
|
1824
1919
|
if (target.volatiles['substitute']) {
|
|
1825
1920
|
const hint = "In Gen 1, if a Pokemon with a Substitute hurts itself due to confusion or Jump Kick/Hi Jump Kick recoil and the target";
|
|
1826
|
-
if
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1921
|
+
// if the move was a self-targeting move, the source is the same as the target. We need to check the opposing substitute
|
|
1922
|
+
const foe = target.side.foe.active[0];
|
|
1923
|
+
if (foe?.volatiles['substitute']) {
|
|
1924
|
+
foe.volatiles['substitute'].hp -= damage;
|
|
1925
|
+
if (foe.volatiles['substitute'].hp <= 0) {
|
|
1926
|
+
foe.removeVolatile('substitute');
|
|
1927
|
+
foe.subFainted = true;
|
|
1831
1928
|
}
|
|
1832
1929
|
else {
|
|
1833
|
-
this.add('-activate',
|
|
1930
|
+
this.add('-activate', foe, 'Substitute', '[damage]');
|
|
1834
1931
|
}
|
|
1835
1932
|
this.hint(hint + " has a Substitute, the target's Substitute takes the damage.");
|
|
1836
1933
|
return damage;
|
|
@@ -2147,12 +2244,23 @@ class Battle {
|
|
|
2147
2244
|
pokemon.side.totalFainted++;
|
|
2148
2245
|
this.runEvent('Faint', pokemon, faintData.source, faintData.effect);
|
|
2149
2246
|
this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon);
|
|
2247
|
+
this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
|
|
2248
|
+
if (pokemon.regressionForme) {
|
|
2249
|
+
// before clearing volatiles
|
|
2250
|
+
pokemon.baseSpecies = this.dex.species.get(pokemon.set.species || pokemon.set.name);
|
|
2251
|
+
pokemon.baseAbility = (0, dex_1.toID)(pokemon.set.ability);
|
|
2252
|
+
}
|
|
2150
2253
|
pokemon.clearVolatile(false);
|
|
2151
2254
|
pokemon.fainted = true;
|
|
2152
2255
|
pokemon.illusion = null;
|
|
2153
2256
|
pokemon.isActive = false;
|
|
2154
2257
|
pokemon.isStarted = false;
|
|
2155
2258
|
delete pokemon.terastallized;
|
|
2259
|
+
if (pokemon.regressionForme) {
|
|
2260
|
+
// after clearing volatiles
|
|
2261
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2262
|
+
pokemon.regressionForme = false;
|
|
2263
|
+
}
|
|
2156
2264
|
pokemon.side.faintedThisTurn = pokemon;
|
|
2157
2265
|
if (this.faintQueue.length >= faintQueueLeft)
|
|
2158
2266
|
checkWin = true;
|
|
@@ -2272,8 +2380,7 @@ class Battle {
|
|
|
2272
2380
|
if (!species)
|
|
2273
2381
|
continue;
|
|
2274
2382
|
pokemon.baseSpecies = rawSpecies;
|
|
2275
|
-
pokemon.details =
|
|
2276
|
-
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
|
2383
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2277
2384
|
pokemon.setAbility(species.abilities['0'], null, true);
|
|
2278
2385
|
pokemon.baseAbility = pokemon.ability;
|
|
2279
2386
|
const behemothMove = {
|
|
@@ -2426,17 +2533,9 @@ class Battle {
|
|
|
2426
2533
|
this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
|
|
2427
2534
|
action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
|
|
2428
2535
|
break;
|
|
2429
|
-
case 'runUnnerve':
|
|
2430
|
-
this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
|
|
2431
|
-
break;
|
|
2432
2536
|
case 'runSwitch':
|
|
2433
2537
|
this.actions.runSwitch(action.pokemon);
|
|
2434
2538
|
break;
|
|
2435
|
-
case 'runPrimal':
|
|
2436
|
-
if (!action.pokemon.transformed) {
|
|
2437
|
-
this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
|
|
2438
|
-
}
|
|
2439
|
-
break;
|
|
2440
2539
|
case 'shift':
|
|
2441
2540
|
if (!action.pokemon.isActive)
|
|
2442
2541
|
return false;
|
|
@@ -2452,7 +2551,7 @@ class Battle {
|
|
|
2452
2551
|
this.clearActiveMove(true);
|
|
2453
2552
|
this.updateSpeed();
|
|
2454
2553
|
residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()]);
|
|
2455
|
-
this.
|
|
2554
|
+
this.fieldEvent('Residual');
|
|
2456
2555
|
this.add('upkeep');
|
|
2457
2556
|
break;
|
|
2458
2557
|
}
|
|
@@ -2493,7 +2592,7 @@ class Battle {
|
|
|
2493
2592
|
else if (this.queue.peek()?.choice === 'instaswitch') {
|
|
2494
2593
|
return false;
|
|
2495
2594
|
}
|
|
2496
|
-
if (this.gen >= 5) {
|
|
2595
|
+
if (this.gen >= 5 && action.choice !== 'start') {
|
|
2497
2596
|
this.eachEvent('Update');
|
|
2498
2597
|
for (const [pokemon, originalHP] of residualPokemon) {
|
|
2499
2598
|
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
|
@@ -2918,6 +3017,34 @@ class Battle {
|
|
|
2918
3017
|
getOverflowedTurnCount() {
|
|
2919
3018
|
return this.gen >= 8 ? (this.turn - 1) % 256 : this.turn - 1;
|
|
2920
3019
|
}
|
|
3020
|
+
initEffectState(obj, effectOrder) {
|
|
3021
|
+
if (!obj.id)
|
|
3022
|
+
obj.id = '';
|
|
3023
|
+
if (effectOrder !== undefined) {
|
|
3024
|
+
obj.effectOrder = effectOrder;
|
|
3025
|
+
}
|
|
3026
|
+
else if (obj.id && obj.target && (!(obj.target instanceof pokemon_1.Pokemon) || obj.target.isActive)) {
|
|
3027
|
+
obj.effectOrder = this.effectOrder++;
|
|
3028
|
+
}
|
|
3029
|
+
else {
|
|
3030
|
+
obj.effectOrder = 0;
|
|
3031
|
+
}
|
|
3032
|
+
return obj;
|
|
3033
|
+
}
|
|
3034
|
+
clearEffectState(state) {
|
|
3035
|
+
state.id = '';
|
|
3036
|
+
for (const k in state) {
|
|
3037
|
+
if (k === 'id' || k === 'target') {
|
|
3038
|
+
continue;
|
|
3039
|
+
}
|
|
3040
|
+
else if (k === 'effectOrder') {
|
|
3041
|
+
state.effectOrder = 0;
|
|
3042
|
+
}
|
|
3043
|
+
else {
|
|
3044
|
+
delete state[k];
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
2921
3048
|
destroy() {
|
|
2922
3049
|
// deallocate ourself
|
|
2923
3050
|
// deallocate children and get rid of references to them
|