@pkmn/sim 0.9.27 → 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 +51 -51
- package/build/cjs/config/formats.js.map +1 -1
- package/build/cjs/data/abilities.js +83 -84
- 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 +6 -6
- 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/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/moves.js +31 -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/formats-data.js +1 -1
- 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/moves.js +1 -1
- package/build/cjs/data/mods/gen8/moves.js.map +1 -1
- package/build/cjs/data/moves.js +15 -9
- 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 +2 -0
- package/build/cjs/lib/utils.js +1 -1
- package/build/cjs/lib/utils.js.map +1 -1
- package/build/cjs/sim/battle-actions.js +25 -36
- 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 +185 -60
- 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 +61 -46
- package/build/cjs/sim/pokemon.js.map +1 -1
- package/build/cjs/sim/prng.d.ts +7 -4
- package/build/cjs/sim/prng.js +67 -16
- 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/team-validator.js +16 -17
- package/build/cjs/sim/team-validator.js.map +1 -1
- package/build/cjs/sim/tools/exhaustive-runner.js +3 -4
- package/build/cjs/sim/tools/exhaustive-runner.js.map +1 -1
- package/build/cjs/sim/tools/random-player-ai.js +1 -1
- package/build/cjs/sim/tools/random-player-ai.js.map +1 -1
- package/build/cjs/sim/tools/runner.js +2 -3
- package/build/cjs/sim/tools/runner.js.map +1 -1
- package/build/esm/config/formats.mjs +51 -51
- package/build/esm/config/formats.mjs.map +1 -1
- package/build/esm/data/abilities.mjs +83 -84
- 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 +6 -6
- 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/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/moves.mjs +31 -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/formats-data.mjs +1 -1
- 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/moves.mjs +1 -1
- package/build/esm/data/mods/gen8/moves.mjs.map +1 -1
- package/build/esm/data/moves.mjs +15 -9
- 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 +2 -0
- package/build/esm/lib/utils.mjs +1 -1
- package/build/esm/lib/utils.mjs.map +1 -1
- package/build/esm/sim/battle-actions.mjs +25 -36
- 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 +184 -59
- 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 +61 -46
- package/build/esm/sim/pokemon.mjs.map +1 -1
- package/build/esm/sim/prng.d.mts +7 -4
- package/build/esm/sim/prng.mjs +34 -16
- 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/team-validator.mjs +10 -11
- package/build/esm/sim/team-validator.mjs.map +1 -1
- package/build/esm/sim/tools/exhaustive-runner.mjs +3 -4
- package/build/esm/sim/tools/exhaustive-runner.mjs.map +1 -1
- package/build/esm/sim/tools/random-player-ai.mjs +1 -1
- package/build/esm/sim/tools/random-player-ai.mjs.map +1 -1
- package/build/esm/sim/tools/runner.mjs +2 -3
- package/build/esm/sim/tools/runner.mjs.map +1 -1
- package/package.json +2 -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);
|
|
@@ -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 = '';
|
|
@@ -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) {
|
|
@@ -2149,12 +2244,23 @@ class Battle {
|
|
|
2149
2244
|
pokemon.side.totalFainted++;
|
|
2150
2245
|
this.runEvent('Faint', pokemon, faintData.source, faintData.effect);
|
|
2151
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
|
+
}
|
|
2152
2253
|
pokemon.clearVolatile(false);
|
|
2153
2254
|
pokemon.fainted = true;
|
|
2154
2255
|
pokemon.illusion = null;
|
|
2155
2256
|
pokemon.isActive = false;
|
|
2156
2257
|
pokemon.isStarted = false;
|
|
2157
2258
|
delete pokemon.terastallized;
|
|
2259
|
+
if (pokemon.regressionForme) {
|
|
2260
|
+
// after clearing volatiles
|
|
2261
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2262
|
+
pokemon.regressionForme = false;
|
|
2263
|
+
}
|
|
2158
2264
|
pokemon.side.faintedThisTurn = pokemon;
|
|
2159
2265
|
if (this.faintQueue.length >= faintQueueLeft)
|
|
2160
2266
|
checkWin = true;
|
|
@@ -2274,8 +2380,7 @@ class Battle {
|
|
|
2274
2380
|
if (!species)
|
|
2275
2381
|
continue;
|
|
2276
2382
|
pokemon.baseSpecies = rawSpecies;
|
|
2277
|
-
pokemon.details =
|
|
2278
|
-
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
|
2383
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2279
2384
|
pokemon.setAbility(species.abilities['0'], null, true);
|
|
2280
2385
|
pokemon.baseAbility = pokemon.ability;
|
|
2281
2386
|
const behemothMove = {
|
|
@@ -2428,17 +2533,9 @@ class Battle {
|
|
|
2428
2533
|
this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
|
|
2429
2534
|
action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
|
|
2430
2535
|
break;
|
|
2431
|
-
case 'runUnnerve':
|
|
2432
|
-
this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
|
|
2433
|
-
break;
|
|
2434
2536
|
case 'runSwitch':
|
|
2435
2537
|
this.actions.runSwitch(action.pokemon);
|
|
2436
2538
|
break;
|
|
2437
|
-
case 'runPrimal':
|
|
2438
|
-
if (!action.pokemon.transformed) {
|
|
2439
|
-
this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
|
|
2440
|
-
}
|
|
2441
|
-
break;
|
|
2442
2539
|
case 'shift':
|
|
2443
2540
|
if (!action.pokemon.isActive)
|
|
2444
2541
|
return false;
|
|
@@ -2454,7 +2551,7 @@ class Battle {
|
|
|
2454
2551
|
this.clearActiveMove(true);
|
|
2455
2552
|
this.updateSpeed();
|
|
2456
2553
|
residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()]);
|
|
2457
|
-
this.
|
|
2554
|
+
this.fieldEvent('Residual');
|
|
2458
2555
|
this.add('upkeep');
|
|
2459
2556
|
break;
|
|
2460
2557
|
}
|
|
@@ -2495,7 +2592,7 @@ class Battle {
|
|
|
2495
2592
|
else if (this.queue.peek()?.choice === 'instaswitch') {
|
|
2496
2593
|
return false;
|
|
2497
2594
|
}
|
|
2498
|
-
if (this.gen >= 5) {
|
|
2595
|
+
if (this.gen >= 5 && action.choice !== 'start') {
|
|
2499
2596
|
this.eachEvent('Update');
|
|
2500
2597
|
for (const [pokemon, originalHP] of residualPokemon) {
|
|
2501
2598
|
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
|
@@ -2920,6 +3017,34 @@ class Battle {
|
|
|
2920
3017
|
getOverflowedTurnCount() {
|
|
2921
3018
|
return this.gen >= 8 ? (this.turn - 1) % 256 : this.turn - 1;
|
|
2922
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
|
+
}
|
|
2923
3048
|
destroy() {
|
|
2924
3049
|
// deallocate ourself
|
|
2925
3050
|
// deallocate children and get rid of references to them
|