@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/build/config/formats.js +93 -72
- package/build/config/formats.js.map +1 -1
- package/build/data/mods/gen1/scripts.js +72 -38
- package/build/data/mods/gen1/scripts.js.map +1 -1
- package/build/data/mods/gen2/scripts.js +1 -1
- package/build/data/rulesets.js +58 -0
- package/build/data/rulesets.js.map +1 -1
- package/build/sim/battle.js +8 -4
- package/build/sim/battle.js.map +1 -1
- package/config/formats.ts +96 -69
- package/data/mods/gen1/scripts.ts +71 -37
- package/data/mods/gen2/scripts.ts +1 -1
- package/data/rulesets.ts +59 -0
- package/package.json +1 -1
- package/sim/battle.ts +6 -4
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', '
|
|
817
|
-
'
|
|
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', '
|
|
1673
|
-
'
|
|
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
|
|
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: '
|
|
2124
|
+
mod: 'gen4',
|
|
2179
2125
|
team: 'random',
|
|
2180
|
-
ruleset: ['[Gen
|
|
2181
|
-
|
|
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', '!
|
|
2552
|
-
banlist: ['Uber'],
|
|
2553
|
-
unbanlist: ['Deoxys-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
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
|