@pkmn/sim 0.6.2 → 0.6.3

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/config/formats.ts CHANGED
@@ -691,7 +691,7 @@ export const Formats: FormatList = [
691
691
  'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Base', 'Zekrom', 'Zeraora', 'Zygarde-Base', 'Arena Trap', 'Comatose', 'Contrary', 'Fluffy',
692
692
  'Fur Coat', 'Gorilla Tactics', 'Huge Power', 'Ice Scales', 'Illusion', 'Imposter', 'Innards Out', 'Intrepid Sword', 'Libero', 'Magic Bounce',
693
693
  'Magnet Pull', 'Moody', 'Neutralizing Gas', 'Parental Bond', 'Poison Heal', 'Protean', 'Pure Power', 'Shadow Tag', 'Simple', 'Stakeout',
694
- 'Speed Boost', 'Unburden', 'Water Bubble', 'Wonder Guard', 'King\'s Rock', 'Baton Pass',
694
+ 'Speed Boost', 'Unburden', 'Water Bubble', 'Wonder Guard', 'King\'s Rock', 'Baton Pass', 'Electrify',
695
695
  ],
696
696
  },
697
697
  {
@@ -810,67 +810,12 @@ export const Formats: FormatList = [
810
810
  ],
811
811
 
812
812
  mod: 'gen8',
813
- ruleset: ['Standard OMs', 'Sleep Moves Clause'],
813
+ ruleset: ['Standard OMs', 'Sleep Moves Clause', 'Godly Gift Mod'],
814
814
  banlist: [
815
815
  'Blissey', 'Calyrex-Shadow', 'Chansey', 'Crawdaunt', 'Dragapult', 'Eternatus', 'Hawlucha', 'Kyogre', 'Marowak-Alola', 'Melmetal',
816
- 'Nidoking', 'Nidoqueen', 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', 'Uber > 1', 'AG ++ Uber > 1', 'Arena Trap',
817
- 'Huge Power', 'Moody', 'Pure Power', 'Shadow Tag', 'Swift Swim', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense',
818
- 'Quick Claw', 'Baton Pass',
816
+ 'Nidoking', 'Nidoqueen', 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', 'Arena Trap', 'Huge Power', 'Moody',
817
+ 'Pure Power', 'Shadow Tag', 'Swift Swim', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense', 'Quick Claw', 'Baton Pass',
819
818
  ],
820
- onValidateTeam(team) {
821
- const gods = new Set<string>();
822
- for (const set of team) {
823
- let species = this.dex.species.get(set.species);
824
- if (typeof species.battleOnly === 'string') species = this.dex.species.get(species.battleOnly);
825
- if (set.item && this.dex.items.get(set.item).megaStone) {
826
- const item = this.dex.items.get(set.item);
827
- if (item.megaEvolves === species.baseSpecies) {
828
- species = this.dex.species.get(item.megaStone);
829
- }
830
- }
831
- if (this.ruleTable.has('standardnatdex')) {
832
- const format = this.dex.formats.getRuleTable(this.dex.formats.get('gen8nationaldex'));
833
- if (format.isBannedSpecies(species)) gods.add(species.name);
834
- } else {
835
- if (['ag', 'uber'].includes(this.toID(species.tier)) || this.toID(set.ability) === 'powerconstruct') {
836
- gods.add(species.name);
837
- }
838
- }
839
- }
840
- if (gods.size > 1) {
841
- return [`You have too many Gods.`, `(${Array.from(gods).join(', ')} are Gods.)`];
842
- }
843
- },
844
- onModifySpeciesPriority: 3,
845
- onModifySpecies(species, target, source) {
846
- if (source || !target?.side) return;
847
- const god = target.side.team.find(set => {
848
- let godSpecies = this.dex.species.get(set.species);
849
- const isNatDex = this.format.ruleTable?.has('standardnatdex');
850
- const validator = this.dex.formats.getRuleTable(
851
- this.dex.formats.get(`gen${isNatDex && this.gen < 8 ? 8 : this.gen}${isNatDex ? 'nationaldex' : 'ou'}`)
852
- );
853
- if (this.toID(set.ability) === 'powerconstruct') {
854
- return true;
855
- }
856
- if (set.item) {
857
- const item = this.dex.items.get(set.item);
858
- if (item.megaEvolves === set.species) godSpecies = this.dex.species.get(item.megaStone);
859
- }
860
- const isBanned = validator.isBannedSpecies(godSpecies);
861
- return isBanned;
862
- }) || target.side.team[0];
863
- const stat = Dex.stats.ids()[target.side.team.indexOf(target.set)];
864
- const newSpecies = this.dex.deepClone(species);
865
- let godSpecies = this.dex.species.get(god.species);
866
- if (typeof godSpecies.battleOnly === 'string') {
867
- godSpecies = this.dex.species.get(godSpecies.battleOnly);
868
- }
869
- newSpecies.bst -= newSpecies.baseStats[stat];
870
- newSpecies.baseStats[stat] = godSpecies.baseStats[stat];
871
- newSpecies.bst += newSpecies.baseStats[stat];
872
- return newSpecies;
873
- },
874
819
  },
875
820
  {
876
821
  name: "[Gen 8] Free-For-All",
@@ -1669,9 +1614,9 @@ export const Formats: FormatList = [
1669
1614
  'Calyrex-Ice', 'Calyrex-Shadow', 'Dialga', 'Eternatus', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh',
1670
1615
  'Jirachi', 'Kyogre', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna', 'Marshadow', 'Melmetal', 'Mewtwo',
1671
1616
  'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Rayquaza', 'Reshiram', 'Solgaleo', 'Urshifu-Base',
1672
- 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Emergency Exit',
1673
- 'Huge Power', 'Moody', 'Power Construct', 'Shadow Tag', 'Wimp Out', 'Wonder Guard', 'Bolt Beak', 'Fishious Rend',
1674
- 'Shell Smash', 'Swagger',
1617
+ 'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Contrary',
1618
+ 'Emergency Exit', 'Huge Power', 'Moody', 'Power Construct', 'Serene Grace', 'Shadow Tag', 'Wimp Out',
1619
+ 'Wonder Guard', 'Ally Switch', 'Bolt Beak', 'Fishious Rend', 'Shell Smash', 'Swagger',
1675
1620
  ],
1676
1621
  onBeforeSwitchIn(pokemon) {
1677
1622
  pokemon.m.curMoves = this.dex.deepClone(pokemon.moves);
@@ -2173,12 +2118,94 @@ export const Formats: FormatList = [
2173
2118
  column: 2,
2174
2119
  },
2175
2120
  {
2176
- name: "[Gen 7] Pick-Your-Team Random Battle",
2121
+ name: "[Gen 4] Shared Power Random Battle",
2122
+ desc: `[Gen 4] Random Battle with aspects of [Gen 8] Shared Power`,
2177
2123
 
2178
- mod: 'gen7',
2124
+ mod: 'gen4',
2179
2125
  team: 'random',
2180
- ruleset: ['[Gen 7] Random Battle', 'Max Team Size = 12', 'Picked Team Size = 6', 'Team Preview'],
2181
- desc: `Twelve Pokémon sets are randomly chosen, then you pick six for your team!`,
2126
+ ruleset: ['[Gen 4] Random Battle', 'Team Preview'],
2127
+ getSharedPower(pokemon) {
2128
+ const sharedPower = new Set<string>();
2129
+ for (const ally of pokemon.side.pokemon) {
2130
+ if (ally.previouslySwitchedIn > 0) {
2131
+ if (pokemon.battle.dex.currentMod !== 'sharedpower' && ['trace', 'mirrorarmor'].includes(ally.baseAbility)) {
2132
+ sharedPower.add('noability');
2133
+ continue;
2134
+ }
2135
+ sharedPower.add(ally.baseAbility);
2136
+ }
2137
+ }
2138
+ sharedPower.delete(pokemon.baseAbility);
2139
+ return sharedPower;
2140
+ },
2141
+ onBeforeSwitchIn(pokemon) {
2142
+ let format = this.format;
2143
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
2144
+ for (const ability of format.getSharedPower!(pokemon)) {
2145
+ const effect = 'ability:' + ability;
2146
+ pokemon.volatiles[effect] = {id: this.toID(effect), target: pokemon};
2147
+ if (!pokemon.m.abils) pokemon.m.abils = [];
2148
+ if (!pokemon.m.abils.includes(effect)) pokemon.m.abils.push(effect);
2149
+ }
2150
+ },
2151
+ onSwitchInPriority: 2,
2152
+ onSwitchIn(pokemon) {
2153
+ let format = this.format;
2154
+ if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
2155
+ for (const ability of format.getSharedPower!(pokemon)) {
2156
+ if (ability === 'noability') {
2157
+ this.hint(`Mirror Armor and Trace break in Shared Power formats that don't use Shared Power as a base, so they get removed from non-base users.`);
2158
+ }
2159
+ const effect = 'ability:' + ability;
2160
+ delete pokemon.volatiles[effect];
2161
+ pokemon.addVolatile(effect);
2162
+ }
2163
+ },
2164
+ field: {
2165
+ suppressingWeather() {
2166
+ for (const pokemon of this.battle.getAllActive()) {
2167
+ const innates = Object.keys(pokemon.volatiles).filter(x => x.startsWith('ability:'));
2168
+ if (pokemon && !pokemon.ignoringAbility() &&
2169
+ (pokemon.getAbility().suppressWeather || innates.some(x => (
2170
+ this.battle.dex.abilities.get(x.replace('ability:', '')).suppressWeather
2171
+ )))) {
2172
+ return true;
2173
+ }
2174
+ }
2175
+ return false;
2176
+ },
2177
+ },
2178
+ pokemon: {
2179
+ hasAbility(ability) {
2180
+ if (this.ignoringAbility()) return false;
2181
+ if (Array.isArray(ability)) return ability.some(abil => this.hasAbility(abil));
2182
+ const abilityid = this.battle.toID(ability);
2183
+ return this.ability === abilityid || !!this.volatiles['ability:' + abilityid];
2184
+ },
2185
+ ignoringAbility() {
2186
+ // Check if any active pokemon have the ability Neutralizing Gas
2187
+ let neutralizinggas = false;
2188
+ for (const pokemon of this.battle.getAllActive()) {
2189
+ // can't use hasAbility because it would lead to infinite recursion
2190
+ if (
2191
+ (pokemon.ability === ('neutralizinggas' as ID) || pokemon.m.abils?.includes('ability:neutralizinggas')) &&
2192
+ !pokemon.volatiles['gastroacid'] && !pokemon.abilityState.ending
2193
+ ) {
2194
+ neutralizinggas = true;
2195
+ break;
2196
+ }
2197
+ }
2198
+
2199
+ return !!(
2200
+ (this.battle.gen >= 5 && !this.isActive) ||
2201
+ ((this.volatiles['gastroacid'] ||
2202
+ (neutralizinggas && (this.ability !== ('neutralizinggas' as ID) ||
2203
+ this.m.abils?.includes('ability:neutralizinggas'))
2204
+ )) && !this.getAbility().isPermanent
2205
+ )
2206
+ );
2207
+ },
2208
+ },
2182
2209
  },
2183
2210
 
2184
2211
  // Randomized Metas
@@ -2548,9 +2575,9 @@ export const Formats: FormatList = [
2548
2575
  mod: 'gen3',
2549
2576
  // searchShow: false,
2550
2577
  gameType: 'doubles',
2551
- ruleset: ['Standard', '!Sleep Clause Mod', '!Switch Priority Clause Mod'],
2552
- banlist: ['Uber'],
2553
- unbanlist: ['Deoxys-Speed', 'Wobbuffet', 'Wynaut'],
2578
+ ruleset: ['Standard', '!Switch Priority Clause Mod'],
2579
+ banlist: ['Uber', 'Soul Dew', 'Swagger'],
2580
+ unbanlist: ['Deoxys-Defense', 'Latias', 'Wobbuffet', 'Wynaut'],
2554
2581
  },
2555
2582
  {
2556
2583
  name: "[Gen 3] UU",
@@ -67,8 +67,7 @@ export const Scripts: ModdedBattleScriptsData = {
67
67
  },
68
68
  actions: {
69
69
  // This function is the main one when running a move.
70
- // It deals with the beforeMove and AfterMoveSelf events.
71
- // This leads with partial trapping moves shennanigans after the move has been used.
70
+ // It deals with the beforeMove event.
72
71
  // It also deals with how PP reduction works on gen 1.
73
72
  runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) {
74
73
  const target = this.battle.getTarget(pokemon, moveOrMoveName, targetLoc);
@@ -115,46 +114,79 @@ export const Scripts: ModdedBattleScriptsData = {
115
114
  }
116
115
  }
117
116
  this.useMove(move, pokemon, target, sourceEffect);
118
- this.battle.singleEvent('AfterMove', move, null, pokemon, target, move);
117
+ },
118
+ // This function deals with AfterMoveSelf events.
119
+ // This leads with partial trapping moves shenanigans after the move has been used.
120
+ useMove(moveOrMoveName, pokemon, target, sourceEffect) {
121
+ const moveResult = this.useMoveInner(moveOrMoveName, pokemon, target, sourceEffect);
119
122
 
120
- // If target fainted
121
- if (target && target.hp <= 0) {
122
- // We remove recharge
123
- if (pokemon.volatiles['mustrecharge']) pokemon.removeVolatile('mustrecharge');
124
- delete pokemon.volatiles['partialtrappinglock'];
125
- // We remove screens
126
- target.side.removeSideCondition('reflect');
127
- target.side.removeSideCondition('lightscreen');
128
- pokemon.removeVolatile('twoturnmove');
129
- } else if (pokemon.hp) {
130
- this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
123
+ if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
124
+ const baseMove = this.battle.dex.moves.get(moveOrMoveName);
125
+ let move = this.battle.dex.getActiveMove(baseMove);
126
+ if (target === undefined) target = this.battle.getRandomTarget(pokemon, move);
127
+ if (move.target === 'self') {
128
+ target = pokemon;
129
+ }
130
+ if (sourceEffect) move.sourceEffect = sourceEffect.id;
131
+
132
+ this.battle.singleEvent('ModifyMove', move, null, pokemon, target, move, move);
133
+ if (baseMove.target !== move.target) {
134
+ // Target changed in ModifyMove, so we must adjust it here
135
+ target = this.battle.getRandomTarget(pokemon, move);
136
+ }
137
+ move = this.battle.runEvent('ModifyMove', pokemon, target, move, move);
138
+ if (baseMove.target !== move.target) {
139
+ // Check again, this shouldn't ever happen on Gen 1.
140
+ target = this.battle.getRandomTarget(pokemon, move);
131
141
  }
132
- if (pokemon.volatiles['mustrecharge']) this.battle.add('-mustrecharge', pokemon);
133
-
134
- // For partial trapping moves, we are saving the target
135
- if (move.volatileStatus === 'partiallytrapped' && target && target.hp > 0) {
136
- // Let's check if the lock exists
137
- if (pokemon.volatiles['partialtrappinglock'] && target.volatiles['partiallytrapped']) {
138
- // Here the partialtrappinglock volatile has been already applied
139
- const sourceVolatile = pokemon.volatiles['partialtrappinglock'];
140
- const targetVolatile = target.volatiles['partiallytrapped'];
141
- if (!sourceVolatile.locked) {
142
- // If it's the first hit, we save the target
143
- sourceVolatile.locked = target;
144
- } else if (target !== pokemon && target !== sourceVolatile.locked) {
145
- // Our target switched out! Re-roll the duration, damage, and accuracy.
146
- const duration = this.battle.sample([2, 2, 2, 3, 3, 3, 4, 5]);
147
- sourceVolatile.duration = duration;
148
- sourceVolatile.locked = target;
149
- // Duration reset thus partially trapped at 2 always.
150
- targetVolatile.duration = 2;
142
+
143
+ if (move.id !== 'metronome') {
144
+ if (move.id !== 'mirrormove' ||
145
+ (!pokemon.side.foe.active[0]?.lastMove || pokemon.side.foe.active[0].lastMove?.id === 'mirrormove')) {
146
+ // The move is our 'final' move (a failed Mirror Move, or any move that isn't Metronome or Mirror Move).
147
+ this.battle.singleEvent('AfterMove', move, null, pokemon, target, move);
148
+
149
+ // If target fainted
150
+ if (target && target.hp <= 0) {
151
+ // We remove recharge
152
+ if (pokemon.volatiles['mustrecharge']) pokemon.removeVolatile('mustrecharge');
153
+ delete pokemon.volatiles['partialtrappinglock'];
154
+ // We remove screens
155
+ target.side.removeSideCondition('reflect');
156
+ target.side.removeSideCondition('lightscreen');
157
+ pokemon.removeVolatile('twoturnmove');
158
+ } else if (pokemon.hp) {
159
+ this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
151
160
  }
152
- } // If we move to here, the move failed and there's no partial trapping lock.
161
+ if (pokemon.volatiles['mustrecharge']) this.battle.add('-mustrecharge', pokemon);
162
+
163
+ // For partial trapping moves, we are saving the target
164
+ if (move.volatileStatus === 'partiallytrapped' && target && target.hp > 0) {
165
+ // Let's check if the lock exists
166
+ if (pokemon.volatiles['partialtrappinglock'] && target.volatiles['partiallytrapped']) {
167
+ // Here the partialtrappinglock volatile has been already applied
168
+ const sourceVolatile = pokemon.volatiles['partialtrappinglock'];
169
+ const targetVolatile = target.volatiles['partiallytrapped'];
170
+ if (!sourceVolatile.locked) {
171
+ // If it's the first hit, we save the target
172
+ sourceVolatile.locked = target;
173
+ } else if (target !== pokemon && target !== sourceVolatile.locked) {
174
+ // Our target switched out! Re-roll the duration, damage, and accuracy.
175
+ const duration = this.battle.sample([2, 2, 2, 3, 3, 3, 4, 5]);
176
+ sourceVolatile.duration = duration;
177
+ sourceVolatile.locked = target;
178
+ // Duration reset thus partially trapped at 2 always.
179
+ targetVolatile.duration = 2;
180
+ }
181
+ } // If we move to here, the move failed and there's no partial trapping lock.
182
+ }
183
+ }
153
184
  }
185
+ return moveResult;
154
186
  },
155
187
  // This is the function that actually uses the move, running ModifyMove events.
156
188
  // It uses the move and then deals with the effects after the move.
157
- useMove(moveOrMoveName, pokemon, target, sourceEffect) {
189
+ useMoveInner(moveOrMoveName, pokemon, target, sourceEffect) {
158
190
  if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
159
191
  const baseMove = this.battle.dex.moves.get(moveOrMoveName);
160
192
  let move = this.battle.dex.getActiveMove(baseMove);
@@ -393,6 +425,7 @@ export const Scripts: ModdedBattleScriptsData = {
393
425
  // We get the sub to the target to see if it existed
394
426
  const targetSub = (target) ? target.volatiles['substitute'] : false;
395
427
  const targetHadSub = (targetSub !== null && targetSub !== false && (typeof targetSub !== 'undefined'));
428
+ let targetHasSub: boolean;
396
429
 
397
430
  if (target) {
398
431
  hitResult = this.battle.singleEvent('TryHit', moveData, {}, target, pokemon, move);
@@ -425,6 +458,7 @@ export const Scripts: ModdedBattleScriptsData = {
425
458
  }
426
459
 
427
460
  if (hitResult === 0) {
461
+ targetHasSub = !!(target?.volatiles['substitute']);
428
462
  target = null;
429
463
  } else if (!hitResult) {
430
464
  if (hitResult === false) this.battle.add('-fail', target);
@@ -554,7 +588,7 @@ export const Scripts: ModdedBattleScriptsData = {
554
588
  return false;
555
589
  }
556
590
  }
557
- const targetHasSub = !!(target?.volatiles['substitute']);
591
+ targetHasSub ??= !!(target?.volatiles['substitute']);
558
592
 
559
593
  // Here's where self effects are applied.
560
594
  const doSelf = (targetHadSub && targetHasSub) || !targetHadSub;
@@ -798,7 +832,7 @@ export const Scripts: ModdedBattleScriptsData = {
798
832
  // This occurs when damage was either 2 or 3 prior to applying STAB/Type matchup, and target is 4x resistant to the move.
799
833
  if (damage === 0) return damage;
800
834
 
801
- // Apply random factor is damage is greater than 1
835
+ // Apply random factor if damage is greater than 1
802
836
  if (damage > 1) {
803
837
  damage *= this.battle.random(217, 256);
804
838
  damage = Math.floor(damage / 255);
@@ -706,7 +706,7 @@ export const Scripts: ModdedBattleScriptsData = {
706
706
  }
707
707
  }
708
708
 
709
- // Apply random factor is damage is greater than 1, except for Flail and Reversal
709
+ // Apply random factor if damage is greater than 1, except for Flail and Reversal
710
710
  if (!move.noDamageVariance && damage > 1) {
711
711
  damage *= this.battle.random(217, 256);
712
712
  damage = Math.floor(damage / 255);
package/data/rulesets.ts CHANGED
@@ -2097,4 +2097,63 @@ export const Rulesets: {[k: string]: FormatData} = {
2097
2097
  }
2098
2098
  },
2099
2099
  },
2100
+ godlygiftmod: {
2101
+ effectType: 'Rule',
2102
+ name: "Godly Gift Mod",
2103
+ onValidateTeam(team) {
2104
+ const gods = new Set<string>();
2105
+ for (const set of team) {
2106
+ let species = this.dex.species.get(set.species);
2107
+ if (typeof species.battleOnly === 'string') species = this.dex.species.get(species.battleOnly);
2108
+ if (set.item && this.dex.items.get(set.item).megaStone) {
2109
+ const item = this.dex.items.get(set.item);
2110
+ if (item.megaEvolves === species.baseSpecies) {
2111
+ species = this.dex.species.get(item.megaStone);
2112
+ }
2113
+ }
2114
+ if (
2115
+ ['ag', 'uber'].includes(this.toID(this.ruleTable.has('standardnatdex') ? species.natDexTier : species.tier)) ||
2116
+ this.toID(set.ability) === 'powerconstruct'
2117
+ ) {
2118
+ gods.add(species.name);
2119
+ }
2120
+ }
2121
+ if (gods.size > 1) {
2122
+ return [`You have too many Gods.`, `(${Array.from(gods).join(', ')} are Gods.)`];
2123
+ }
2124
+ },
2125
+ onModifySpeciesPriority: 3,
2126
+ onModifySpecies(species, target, source) {
2127
+ if (source || !target?.side) return;
2128
+ const god = target.side.team.find(set => {
2129
+ let godSpecies = this.dex.species.get(set.species);
2130
+ const isNatDex = this.format.ruleTable?.has('standardnatdex');
2131
+ const validator = this.dex.formats.getRuleTable(
2132
+ this.dex.formats.get(`gen${this.gen}${isNatDex && this.gen >= 8 ? 'nationaldex' : 'ou'}`)
2133
+ );
2134
+ if (this.toID(set.ability) === 'powerconstruct') {
2135
+ return true;
2136
+ }
2137
+ if (set.item) {
2138
+ const item = this.dex.items.get(set.item);
2139
+ if (item.megaEvolves === set.species) godSpecies = this.dex.species.get(item.megaStone);
2140
+ }
2141
+ const isBanned = validator.isBannedSpecies(godSpecies);
2142
+ return isBanned;
2143
+ }) || target.side.team[0];
2144
+ const stat = Dex.stats.ids()[target.side.team.indexOf(target.set)];
2145
+ const newSpecies = this.dex.deepClone(species);
2146
+ let godSpecies = this.dex.species.get(god.species);
2147
+ if (typeof godSpecies.battleOnly === 'string') {
2148
+ godSpecies = this.dex.species.get(godSpecies.battleOnly);
2149
+ }
2150
+ newSpecies.bst -= newSpecies.baseStats[stat];
2151
+ newSpecies.baseStats[stat] = godSpecies.baseStats[stat];
2152
+ if (this.gen === 1 && (stat === 'spa' || stat === 'spd')) {
2153
+ newSpecies.baseStats['spa'] = newSpecies.baseStats['spd'] = godSpecies.baseStats[stat];
2154
+ }
2155
+ newSpecies.bst += newSpecies.baseStats[stat];
2156
+ return newSpecies;
2157
+ },
2158
+ },
2100
2159
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown",
5
5
  "homepage": "https://psim.us",
6
6
  "main": "build/sim/index.js",
package/sim/battle.ts CHANGED
@@ -2247,13 +2247,15 @@ export class Battle {
2247
2247
  // when used without an explicit target.
2248
2248
 
2249
2249
  move = this.dex.moves.get(move);
2250
- if (move.target === 'adjacentAlly') {
2251
- const adjacentAllies = pokemon.adjacentAllies();
2252
- return adjacentAllies.length ? this.sample(adjacentAllies) : null;
2253
- }
2254
2250
  if (['self', 'all', 'allySide', 'allyTeam', 'adjacentAllyOrSelf'].includes(move.target)) {
2255
2251
  return pokemon;
2252
+ } else if (move.target === 'adjacentAlly') {
2253
+ if (this.gameType === 'singles') return null;
2254
+ const adjacentAllies = pokemon.adjacentAllies();
2255
+ return adjacentAllies.length ? this.sample(adjacentAllies) : null;
2256
2256
  }
2257
+ if (this.gameType === 'singles') return pokemon.side.foe.active[0];
2258
+
2257
2259
  if (this.activePerHalf > 2) {
2258
2260
  if (move.target === 'adjacentFoe' || move.target === 'normal' || move.target === 'randomNormal') {
2259
2261
  // even if a move can target an ally, auto-resolution will never make it target an ally