@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/esm/sim/battle.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import { Side } from './side.mjs';
|
|
|
22
22
|
import { State } from './state.mjs';
|
|
23
23
|
import { BattleQueue } from './battle-queue.mjs';
|
|
24
24
|
import { BattleActions } from './battle-actions.mjs';
|
|
25
|
-
import { Utils } from '../lib/
|
|
25
|
+
import { Utils } from '../lib/utils.mjs';
|
|
26
26
|
const splitRegex = /^\|split\|p([1234])\n(.*)\n(.*)|.+/gm;
|
|
27
27
|
export function extractChannelMessages(message, channelIds) {
|
|
28
28
|
const channelIdSet = new Set(channelIds);
|
|
@@ -75,7 +75,7 @@ export class Battle {
|
|
|
75
75
|
options.forceRandomChance : null;
|
|
76
76
|
this.deserialized = !!options.deserialized;
|
|
77
77
|
this.strictChoices = !!options.strictChoices;
|
|
78
|
-
this.formatData = { id: format.id };
|
|
78
|
+
this.formatData = this.initEffectState({ id: format.id });
|
|
79
79
|
this.gameType = (format.gameType || 'singles');
|
|
80
80
|
this.field = new Field(this);
|
|
81
81
|
this.sides = Array(format.playerCount).fill(null);
|
|
@@ -101,7 +101,7 @@ export class Battle {
|
|
|
101
101
|
this.started = false;
|
|
102
102
|
this.ended = false;
|
|
103
103
|
this.effect = { id: '' };
|
|
104
|
-
this.effectState = { id: '' };
|
|
104
|
+
this.effectState = this.initEffectState({ id: '' });
|
|
105
105
|
this.event = { id: '' };
|
|
106
106
|
this.events = null;
|
|
107
107
|
this.eventDepth = 0;
|
|
@@ -112,8 +112,12 @@ export class Battle {
|
|
|
112
112
|
this.lastMoveLine = -1;
|
|
113
113
|
this.lastSuccessfulMoveThisTurn = null;
|
|
114
114
|
this.lastDamage = 0;
|
|
115
|
-
this.
|
|
115
|
+
this.effectOrder = 0;
|
|
116
116
|
this.quickClawRoll = false;
|
|
117
|
+
this.speedOrder = [];
|
|
118
|
+
for (let i = 0; i < this.activePerHalf * 2; i++) {
|
|
119
|
+
this.speedOrder.push(i);
|
|
120
|
+
}
|
|
117
121
|
this.teamGenerator = null;
|
|
118
122
|
this.hints = new Set();
|
|
119
123
|
this.NOT_FAIL = '';
|
|
@@ -226,6 +230,7 @@ export class Battle {
|
|
|
226
230
|
* 2. Priority, high to low (default 0)
|
|
227
231
|
* 3. Speed, high to low (default 0)
|
|
228
232
|
* 4. SubOrder, low to high (default 0)
|
|
233
|
+
* 5. EffectOrder, low to high (default 0)
|
|
229
234
|
*
|
|
230
235
|
* Doesn't reference `this` so doesn't need to be bound.
|
|
231
236
|
*/
|
|
@@ -234,12 +239,14 @@ export class Battle {
|
|
|
234
239
|
((b.priority || 0) - (a.priority || 0)) ||
|
|
235
240
|
((b.speed || 0) - (a.speed || 0)) ||
|
|
236
241
|
-((b.subOrder || 0) - (a.subOrder || 0)) ||
|
|
242
|
+
-((b.effectOrder || 0) - (a.effectOrder || 0)) ||
|
|
237
243
|
0;
|
|
238
244
|
}
|
|
239
245
|
static compareRedirectOrder(a, b) {
|
|
240
246
|
return ((b.priority || 0) - (a.priority || 0)) ||
|
|
241
247
|
((b.speed || 0) - (a.speed || 0)) ||
|
|
242
|
-
((a.effectHolder && b.effectHolder) ?
|
|
248
|
+
((a.effectHolder?.abilityState && b.effectHolder?.abilityState) ?
|
|
249
|
+
-(b.effectHolder.abilityState.effectOrder - a.effectHolder.abilityState.effectOrder) : 0) ||
|
|
243
250
|
0;
|
|
244
251
|
}
|
|
245
252
|
static compareLeftToRightOrder(a, b) {
|
|
@@ -304,22 +311,32 @@ export class Battle {
|
|
|
304
311
|
/**
|
|
305
312
|
* Runs an event with no source on each effect on the field, in Speed order.
|
|
306
313
|
*
|
|
307
|
-
* Unlike `eachEvent`, this contains a lot of other handling and is intended
|
|
314
|
+
* Unlike `eachEvent`, this contains a lot of other handling and is only intended for
|
|
315
|
+
* the 'Residual' and 'SwitchIn' events.
|
|
308
316
|
*/
|
|
309
|
-
|
|
317
|
+
fieldEvent(eventid, targets) {
|
|
310
318
|
const callbackName = `on${eventid}`;
|
|
311
|
-
let
|
|
312
|
-
|
|
319
|
+
let getKey;
|
|
320
|
+
if (eventid === 'Residual') {
|
|
321
|
+
getKey = 'duration';
|
|
322
|
+
}
|
|
323
|
+
let handlers = this.findFieldEventHandlers(this.field, `onField${eventid}`, getKey);
|
|
313
324
|
for (const side of this.sides) {
|
|
314
325
|
if (side.n < 2 || !side.allySide) {
|
|
315
|
-
handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`,
|
|
326
|
+
handlers = handlers.concat(this.findSideEventHandlers(side, `onSide${eventid}`, getKey));
|
|
316
327
|
}
|
|
317
328
|
for (const active of side.active) {
|
|
318
329
|
if (!active)
|
|
319
330
|
continue;
|
|
320
|
-
|
|
331
|
+
if (eventid === 'SwitchIn') {
|
|
332
|
+
handlers = handlers.concat(this.findPokemonEventHandlers(active, `onAny${eventid}`));
|
|
333
|
+
}
|
|
334
|
+
if (targets && !targets.includes(active))
|
|
335
|
+
continue;
|
|
336
|
+
handlers = handlers.concat(this.findPokemonEventHandlers(active, callbackName, getKey));
|
|
321
337
|
handlers = handlers.concat(this.findSideEventHandlers(side, callbackName, undefined, active));
|
|
322
338
|
handlers = handlers.concat(this.findFieldEventHandlers(this.field, callbackName, undefined, active));
|
|
339
|
+
handlers = handlers.concat(this.findBattleEventHandlers(callbackName, getKey, active));
|
|
323
340
|
}
|
|
324
341
|
}
|
|
325
342
|
this.speedSort(handlers);
|
|
@@ -327,9 +344,11 @@ export class Battle {
|
|
|
327
344
|
const handler = handlers[0];
|
|
328
345
|
handlers.shift();
|
|
329
346
|
const effect = handler.effect;
|
|
330
|
-
if (handler.effectHolder.fainted)
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
if (handler.effectHolder.fainted) {
|
|
348
|
+
if (!(handler.state?.isSlotCondition))
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (eventid === 'Residual' && handler.end && handler.state && handler.state.duration) {
|
|
333
352
|
handler.state.duration--;
|
|
334
353
|
if (!handler.state.duration) {
|
|
335
354
|
const endCallArgs = handler.endCallArgs || [handler.effectHolder, effect.id];
|
|
@@ -345,7 +364,7 @@ export class Battle {
|
|
|
345
364
|
if (handler.effectHolder.pseudoWeather)
|
|
346
365
|
handlerEventid = `Field${eventid}`;
|
|
347
366
|
if (handler.callback) {
|
|
348
|
-
this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null,
|
|
367
|
+
this.singleEvent(handlerEventid, effect, handler.state, handler.effectHolder, null, null, undefined, handler.callback);
|
|
349
368
|
}
|
|
350
369
|
this.faintMessages();
|
|
351
370
|
if (this.ended)
|
|
@@ -379,8 +398,8 @@ export class Battle {
|
|
|
379
398
|
// it's changed; call it off
|
|
380
399
|
return relayVar;
|
|
381
400
|
}
|
|
382
|
-
if (eventid !== 'Start' && eventid !== 'TakeItem' &&
|
|
383
|
-
|
|
401
|
+
if (eventid !== 'Start' && eventid !== 'TakeItem' && effect.effectType === 'Item' &&
|
|
402
|
+
(target instanceof Pokemon) && target.ignoringItem()) {
|
|
384
403
|
this.debug(eventid + ' handler suppressed by Embargo, Klutz or Magic Room');
|
|
385
404
|
return relayVar;
|
|
386
405
|
}
|
|
@@ -400,7 +419,7 @@ export class Battle {
|
|
|
400
419
|
const parentEffectState = this.effectState;
|
|
401
420
|
const parentEvent = this.event;
|
|
402
421
|
this.effect = effect;
|
|
403
|
-
this.effectState = state || {};
|
|
422
|
+
this.effectState = state || this.initEffectState({});
|
|
404
423
|
this.event = { id: eventid, target, source, effect: sourceEffect };
|
|
405
424
|
this.eventDepth++;
|
|
406
425
|
const args = [target, source, sourceEffect];
|
|
@@ -551,7 +570,7 @@ export class Battle {
|
|
|
551
570
|
if (Array.isArray(target))
|
|
552
571
|
throw new Error("");
|
|
553
572
|
handlers.unshift(this.resolvePriority({
|
|
554
|
-
effect: sourceEffect, callback, state: {}, end: null, effectHolder: target,
|
|
573
|
+
effect: sourceEffect, callback, state: this.initEffectState({}), end: null, effectHolder: target,
|
|
555
574
|
}, `on${eventid}`));
|
|
556
575
|
}
|
|
557
576
|
}
|
|
@@ -670,7 +689,7 @@ export class Battle {
|
|
|
670
689
|
const parentEffect = this.effect;
|
|
671
690
|
const parentEffectState = this.effectState;
|
|
672
691
|
this.effect = handler.effect;
|
|
673
|
-
this.effectState = handler.state || {};
|
|
692
|
+
this.effectState = handler.state || this.initEffectState({});
|
|
674
693
|
this.effectState.target = effectHolder;
|
|
675
694
|
returnVal = handler.callback.apply(this, args);
|
|
676
695
|
this.effect = parentEffect;
|
|
@@ -712,15 +731,74 @@ export class Battle {
|
|
|
712
731
|
priorityEvent(eventid, target, source, effect, relayVar, onEffect) {
|
|
713
732
|
return this.runEvent(eventid, target, source, effect, relayVar, onEffect, true);
|
|
714
733
|
}
|
|
715
|
-
resolvePriority(
|
|
734
|
+
resolvePriority(h, callbackName) {
|
|
735
|
+
const handler = h;
|
|
716
736
|
// @ts-ignore
|
|
717
737
|
handler.order = handler.effect[`${callbackName}Order`] || false;
|
|
718
738
|
// @ts-ignore
|
|
719
739
|
handler.priority = handler.effect[`${callbackName}Priority`] || 0;
|
|
720
740
|
// @ts-ignore
|
|
721
741
|
handler.subOrder = handler.effect[`${callbackName}SubOrder`] || 0;
|
|
742
|
+
if (!handler.subOrder) {
|
|
743
|
+
// https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-59#post-8685465
|
|
744
|
+
const effectTypeOrder = {
|
|
745
|
+
// Z-Move: 1,
|
|
746
|
+
Condition: 2,
|
|
747
|
+
// Slot Condition: 3,
|
|
748
|
+
// Side Condition: 4,
|
|
749
|
+
// Field Condition: 5, (includes weather but also terrains and pseudoweathers)
|
|
750
|
+
Weather: 5,
|
|
751
|
+
Format: 5,
|
|
752
|
+
Rule: 5,
|
|
753
|
+
Ruleset: 5,
|
|
754
|
+
// Poison Touch: 6, (also includes Perish Body)
|
|
755
|
+
Ability: 7,
|
|
756
|
+
Item: 8,
|
|
757
|
+
// Stall: 9,
|
|
758
|
+
};
|
|
759
|
+
handler.subOrder = effectTypeOrder[handler.effect.effectType] || 0;
|
|
760
|
+
if (handler.effect.effectType === 'Condition') {
|
|
761
|
+
if (handler.state?.target instanceof Side) {
|
|
762
|
+
if (handler.state.isSlotCondition) {
|
|
763
|
+
// slot condition
|
|
764
|
+
handler.subOrder = 3;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
// side condition
|
|
768
|
+
handler.subOrder = 4;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
else if (handler.state?.target instanceof Field) {
|
|
772
|
+
// field condition
|
|
773
|
+
handler.subOrder = 5;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
else if (handler.effect.effectType === 'Ability') {
|
|
777
|
+
if (handler.effect.name === 'Poison Touch' || handler.effect.name === 'Perish Body') {
|
|
778
|
+
handler.subOrder = 6;
|
|
779
|
+
}
|
|
780
|
+
else if (handler.effect.name === 'Stall') {
|
|
781
|
+
handler.subOrder = 9;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (callbackName.endsWith('SwitchIn') || callbackName.endsWith('RedirectTarget')) {
|
|
786
|
+
// If multiple hazards are present on one side, their event handlers all perfectly tie in speed, priority,
|
|
787
|
+
// and subOrder. They should activate in the order they were created, which is where effectOrder comes in.
|
|
788
|
+
// This also applies to speed ties for which ability like Lightning Rod redirects moves.
|
|
789
|
+
// TODO: In-game, other events are also sorted this way, but that's an implementation for another refactor
|
|
790
|
+
handler.effectOrder = handler.state?.effectOrder;
|
|
791
|
+
}
|
|
722
792
|
if (handler.effectHolder && handler.effectHolder.getStat) {
|
|
723
|
-
|
|
793
|
+
const pokemon = handler.effectHolder;
|
|
794
|
+
handler.speed = pokemon.speed;
|
|
795
|
+
if (callbackName.endsWith('SwitchIn')) {
|
|
796
|
+
// Pokemon speeds including ties are resolved before all onSwitchIn handlers and aren't re-sorted in-between
|
|
797
|
+
// so we subtract a fractional speed from each Pokemon's respective event handlers by using the index of their
|
|
798
|
+
// unique field position in a pre-sorted-by-speed array
|
|
799
|
+
const fieldPositionValue = pokemon.side.n * this.sides.length + pokemon.position;
|
|
800
|
+
handler.speed -= this.speedOrder.indexOf(fieldPositionValue) / (this.activePerHalf * 2);
|
|
801
|
+
}
|
|
724
802
|
}
|
|
725
803
|
return handler;
|
|
726
804
|
}
|
|
@@ -795,7 +873,22 @@ export class Battle {
|
|
|
795
873
|
effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
|
|
796
874
|
}, callbackName));
|
|
797
875
|
}
|
|
876
|
+
else if (['ability', 'item'].includes(volatile.id.split(':')[0])) {
|
|
877
|
+
// Innate abilities/items; see comment below
|
|
878
|
+
// @ts-ignore - dynamic lookup
|
|
879
|
+
if (this.gen >= 5 && callbackName === 'onSwitchIn' && !volatile.onAnySwitchIn) {
|
|
880
|
+
callback = volatile.onStart;
|
|
881
|
+
if (callback !== undefined || (getKey && volatileState[getKey])) {
|
|
882
|
+
handlers.push(this.resolvePriority({
|
|
883
|
+
effect: volatile, callback, state: volatileState, end: pokemon.removeVolatile, effectHolder: pokemon,
|
|
884
|
+
}, callbackName));
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
798
888
|
}
|
|
889
|
+
// Abilities and items Start at different times during the SwitchIn event, so we run their onStart handlers
|
|
890
|
+
// during the SwitchIn event instead of running the Start event during switch-ins
|
|
891
|
+
// gens 4 and before still use the old system, though
|
|
799
892
|
const ability = pokemon.getAbility();
|
|
800
893
|
// @ts-ignore - dynamic lookup
|
|
801
894
|
callback = ability[callbackName];
|
|
@@ -803,6 +896,16 @@ export class Battle {
|
|
|
803
896
|
handlers.push(this.resolvePriority({
|
|
804
897
|
effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
|
|
805
898
|
}, callbackName));
|
|
899
|
+
// @ts-ignore - dynamic lookup
|
|
900
|
+
}
|
|
901
|
+
else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !ability.onAnySwitchIn) {
|
|
902
|
+
// @ts-ignore - dynamic lookup
|
|
903
|
+
callback = ability.onStart;
|
|
904
|
+
if (callback !== undefined || (getKey && pokemon.abilityState[getKey])) {
|
|
905
|
+
handlers.push(this.resolvePriority({
|
|
906
|
+
effect: ability, callback, state: pokemon.abilityState, end: pokemon.clearAbility, effectHolder: pokemon,
|
|
907
|
+
}, callbackName));
|
|
908
|
+
}
|
|
806
909
|
}
|
|
807
910
|
const item = pokemon.getItem();
|
|
808
911
|
// @ts-ignore - dynamic lookup
|
|
@@ -811,6 +914,16 @@ export class Battle {
|
|
|
811
914
|
handlers.push(this.resolvePriority({
|
|
812
915
|
effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
|
|
813
916
|
}, callbackName));
|
|
917
|
+
// @ts-ignore - dynamic lookup
|
|
918
|
+
}
|
|
919
|
+
else if (this.gen >= 5 && callbackName === 'onSwitchIn' && !item.onAnySwitchIn) {
|
|
920
|
+
// @ts-ignore - dynamic lookup
|
|
921
|
+
callback = item.onStart;
|
|
922
|
+
if (callback !== undefined || (getKey && pokemon.itemState[getKey])) {
|
|
923
|
+
handlers.push(this.resolvePriority({
|
|
924
|
+
effect: item, callback, state: pokemon.itemState, end: pokemon.clearItem, effectHolder: pokemon,
|
|
925
|
+
}, callbackName));
|
|
926
|
+
}
|
|
814
927
|
}
|
|
815
928
|
const species = pokemon.baseSpecies;
|
|
816
929
|
// @ts-ignore - dynamic lookup
|
|
@@ -833,13 +946,13 @@ export class Battle {
|
|
|
833
946
|
state: slotConditionState,
|
|
834
947
|
end: side.removeSlotCondition,
|
|
835
948
|
endCallArgs: [side, pokemon, slotCondition.id],
|
|
836
|
-
effectHolder:
|
|
949
|
+
effectHolder: pokemon,
|
|
837
950
|
}, callbackName));
|
|
838
951
|
}
|
|
839
952
|
}
|
|
840
953
|
return handlers;
|
|
841
954
|
}
|
|
842
|
-
findBattleEventHandlers(callbackName, getKey) {
|
|
955
|
+
findBattleEventHandlers(callbackName, getKey, customHolder) {
|
|
843
956
|
const handlers = [];
|
|
844
957
|
let callback;
|
|
845
958
|
const format = this.format;
|
|
@@ -847,15 +960,15 @@ export class Battle {
|
|
|
847
960
|
callback = format[callbackName];
|
|
848
961
|
if (callback !== undefined || (getKey && this.formatData[getKey])) {
|
|
849
962
|
handlers.push(this.resolvePriority({
|
|
850
|
-
effect: format, callback, state: this.formatData, end: null, effectHolder: this,
|
|
963
|
+
effect: format, callback, state: this.formatData, end: null, effectHolder: customHolder || this,
|
|
851
964
|
}, callbackName));
|
|
852
965
|
}
|
|
853
966
|
if (this.events && (callback = this.events[callbackName]) !== undefined) {
|
|
854
967
|
for (const handler of callback) {
|
|
855
968
|
const state = (handler.target.effectType === 'Format') ? this.formatData : null;
|
|
856
969
|
handlers.push({
|
|
857
|
-
effect: handler.target, callback: handler.callback, state, end: null,
|
|
858
|
-
|
|
970
|
+
effect: handler.target, callback: handler.callback, state, end: null, effectHolder: customHolder || this,
|
|
971
|
+
priority: handler.priority, order: handler.order, subOrder: handler.subOrder,
|
|
859
972
|
});
|
|
860
973
|
}
|
|
861
974
|
}
|
|
@@ -998,11 +1111,11 @@ export class Battle {
|
|
|
998
1111
|
}
|
|
999
1112
|
return pokemonList;
|
|
1000
1113
|
}
|
|
1001
|
-
getAllActive() {
|
|
1114
|
+
getAllActive(includeFainted) {
|
|
1002
1115
|
const pokemonList = [];
|
|
1003
1116
|
for (const side of this.sides) {
|
|
1004
1117
|
for (const pokemon of side.active) {
|
|
1005
|
-
if (pokemon && !pokemon.fainted) {
|
|
1118
|
+
if (pokemon && (includeFainted || !pokemon.fainted)) {
|
|
1006
1119
|
pokemonList.push(pokemon);
|
|
1007
1120
|
}
|
|
1008
1121
|
}
|
|
@@ -1397,24 +1510,6 @@ export class Battle {
|
|
|
1397
1510
|
this.quickClawRoll = this.randomChance(60, 256);
|
|
1398
1511
|
if (this.gen === 3)
|
|
1399
1512
|
this.quickClawRoll = this.randomChance(1, 5);
|
|
1400
|
-
// Crazyhouse Progress checker because sidebars has trouble keeping track of Pokemon.
|
|
1401
|
-
// Please remove me once there is client support.
|
|
1402
|
-
if (this.ruleTable.has('crazyhouserule')) {
|
|
1403
|
-
for (const side of this.sides) {
|
|
1404
|
-
let buf = `raw|${Utils.escapeHTML(side.name)}'s team:<br />`;
|
|
1405
|
-
for (const pokemon of side.pokemon) {
|
|
1406
|
-
if (!buf.endsWith('<br />'))
|
|
1407
|
-
buf += '/</span>​';
|
|
1408
|
-
if (pokemon.fainted) {
|
|
1409
|
-
buf += `<span style="white-space:nowrap;"><span style="opacity:.3"><psicon pokemon="${pokemon.species.id}" /></span>`;
|
|
1410
|
-
}
|
|
1411
|
-
else {
|
|
1412
|
-
buf += `<span style="white-space:nowrap"><psicon pokemon="${pokemon.species.id}" />`;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
this.add(`${buf}</span>`);
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
1513
|
this.makeRequest('move');
|
|
1419
1514
|
}
|
|
1420
1515
|
maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide) {
|
|
@@ -2145,12 +2240,23 @@ export class Battle {
|
|
|
2145
2240
|
pokemon.side.totalFainted++;
|
|
2146
2241
|
this.runEvent('Faint', pokemon, faintData.source, faintData.effect);
|
|
2147
2242
|
this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon);
|
|
2243
|
+
this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
|
|
2244
|
+
if (pokemon.regressionForme) {
|
|
2245
|
+
// before clearing volatiles
|
|
2246
|
+
pokemon.baseSpecies = this.dex.species.get(pokemon.set.species || pokemon.set.name);
|
|
2247
|
+
pokemon.baseAbility = toID(pokemon.set.ability);
|
|
2248
|
+
}
|
|
2148
2249
|
pokemon.clearVolatile(false);
|
|
2149
2250
|
pokemon.fainted = true;
|
|
2150
2251
|
pokemon.illusion = null;
|
|
2151
2252
|
pokemon.isActive = false;
|
|
2152
2253
|
pokemon.isStarted = false;
|
|
2153
2254
|
delete pokemon.terastallized;
|
|
2255
|
+
if (pokemon.regressionForme) {
|
|
2256
|
+
// after clearing volatiles
|
|
2257
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2258
|
+
pokemon.regressionForme = false;
|
|
2259
|
+
}
|
|
2154
2260
|
pokemon.side.faintedThisTurn = pokemon;
|
|
2155
2261
|
if (this.faintQueue.length >= faintQueueLeft)
|
|
2156
2262
|
checkWin = true;
|
|
@@ -2270,8 +2376,7 @@ export class Battle {
|
|
|
2270
2376
|
if (!species)
|
|
2271
2377
|
continue;
|
|
2272
2378
|
pokemon.baseSpecies = rawSpecies;
|
|
2273
|
-
pokemon.details =
|
|
2274
|
-
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
|
2379
|
+
pokemon.details = pokemon.getUpdatedDetails();
|
|
2275
2380
|
pokemon.setAbility(species.abilities['0'], null, true);
|
|
2276
2381
|
pokemon.baseAbility = pokemon.ability;
|
|
2277
2382
|
const behemothMove = {
|
|
@@ -2424,17 +2529,9 @@ export class Battle {
|
|
|
2424
2529
|
this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
|
|
2425
2530
|
action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
|
|
2426
2531
|
break;
|
|
2427
|
-
case 'runUnnerve':
|
|
2428
|
-
this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
|
|
2429
|
-
break;
|
|
2430
2532
|
case 'runSwitch':
|
|
2431
2533
|
this.actions.runSwitch(action.pokemon);
|
|
2432
2534
|
break;
|
|
2433
|
-
case 'runPrimal':
|
|
2434
|
-
if (!action.pokemon.transformed) {
|
|
2435
|
-
this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
|
|
2436
|
-
}
|
|
2437
|
-
break;
|
|
2438
2535
|
case 'shift':
|
|
2439
2536
|
if (!action.pokemon.isActive)
|
|
2440
2537
|
return false;
|
|
@@ -2450,7 +2547,7 @@ export class Battle {
|
|
|
2450
2547
|
this.clearActiveMove(true);
|
|
2451
2548
|
this.updateSpeed();
|
|
2452
2549
|
residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()]);
|
|
2453
|
-
this.
|
|
2550
|
+
this.fieldEvent('Residual');
|
|
2454
2551
|
this.add('upkeep');
|
|
2455
2552
|
break;
|
|
2456
2553
|
}
|
|
@@ -2491,7 +2588,7 @@ export class Battle {
|
|
|
2491
2588
|
else if (this.queue.peek()?.choice === 'instaswitch') {
|
|
2492
2589
|
return false;
|
|
2493
2590
|
}
|
|
2494
|
-
if (this.gen >= 5) {
|
|
2591
|
+
if (this.gen >= 5 && action.choice !== 'start') {
|
|
2495
2592
|
this.eachEvent('Update');
|
|
2496
2593
|
for (const [pokemon, originalHP] of residualPokemon) {
|
|
2497
2594
|
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
|
@@ -2916,6 +3013,34 @@ export class Battle {
|
|
|
2916
3013
|
getOverflowedTurnCount() {
|
|
2917
3014
|
return this.gen >= 8 ? (this.turn - 1) % 256 : this.turn - 1;
|
|
2918
3015
|
}
|
|
3016
|
+
initEffectState(obj, effectOrder) {
|
|
3017
|
+
if (!obj.id)
|
|
3018
|
+
obj.id = '';
|
|
3019
|
+
if (effectOrder !== undefined) {
|
|
3020
|
+
obj.effectOrder = effectOrder;
|
|
3021
|
+
}
|
|
3022
|
+
else if (obj.id && obj.target && (!(obj.target instanceof Pokemon) || obj.target.isActive)) {
|
|
3023
|
+
obj.effectOrder = this.effectOrder++;
|
|
3024
|
+
}
|
|
3025
|
+
else {
|
|
3026
|
+
obj.effectOrder = 0;
|
|
3027
|
+
}
|
|
3028
|
+
return obj;
|
|
3029
|
+
}
|
|
3030
|
+
clearEffectState(state) {
|
|
3031
|
+
state.id = '';
|
|
3032
|
+
for (const k in state) {
|
|
3033
|
+
if (k === 'id' || k === 'target') {
|
|
3034
|
+
continue;
|
|
3035
|
+
}
|
|
3036
|
+
else if (k === 'effectOrder') {
|
|
3037
|
+
state.effectOrder = 0;
|
|
3038
|
+
}
|
|
3039
|
+
else {
|
|
3040
|
+
delete state[k];
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2919
3044
|
destroy() {
|
|
2920
3045
|
// deallocate ourself
|
|
2921
3046
|
// deallocate children and get rid of references to them
|