This commit is contained in:
André Bastos Dias 2026-06-02 14:06:08 +01:00 committed by GitHub
commit dfd9a8ef0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 139 additions and 164 deletions

View File

@ -821,9 +821,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
// Drain/recoil/secondary effect confusion do not happen if the substitute breaks
if (target.volatiles['substitute']) {
if (move.recoil) {
this.damage(this.clampIntRange(Math.floor(uncappedDamage * move.recoil[0] / move.recoil[1]), 1),
source, target, 'recoil');
if (uncappedDamage) {
this.actions.calcRecoilDamage(uncappedDamage, move, source);
}
if (move.drain) {
const amount = this.clampIntRange(Math.floor(uncappedDamage * move.drain[0] / move.drain[1]), 1);

View File

@ -588,6 +588,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
if ((damage || damage === 0) && !target.fainted) {
damage = this.battle.damage(damage, target, pokemon, move);
if (damage) this.calcRecoilDamage(damage, move, pokemon);
if (!(damage || damage === 0)) return false;
didSomething = true;
} else if (damage === false && typeof hitResult === 'undefined') {

View File

@ -47,8 +47,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
// Drain/recoil does not happen if the substitute breaks
if (target.volatiles['substitute']) {
if (move.recoil) {
this.damage(Math.round(uncappedDamage * move.recoil[0] / move.recoil[1]), source, target, 'recoil');
if (uncappedDamage) {
this.actions.calcRecoilDamage(uncappedDamage, move, source);
}
if (move.drain) {
this.heal(Math.ceil(uncappedDamage * move.drain[0] / move.drain[1]), source, target, 'drain');

View File

@ -433,6 +433,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
if ((damage || damage === 0) && !target.fainted) {
damage = this.battle.damage(damage, target, pokemon, move);
if (damage && target.hp > 0) this.calcRecoilDamage(damage, move, pokemon);
if (!(damage || damage === 0)) return false;
didSomething = true;
} else if (damage === false && typeof hitResult === 'undefined') {

View File

@ -295,8 +295,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
if (move.recoil && move.totalDamage) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
if (move.totalDamage) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
return damage;
},

View File

@ -233,8 +233,8 @@ export const Scripts: ModdedBattleScriptsData = {
// Implementing Recoil mechanics from Stadium 2.
// If a pokemon caused the other to faint with a recoil move and only one pokemon remains on both sides,
// recoil damage will not be taken.
if (move.recoil && move.totalDamage && (pokemon.side.pokemonLeft > 1 || target.side.pokemonLeft > 1 || target.hp)) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
if (move.totalDamage && (pokemon.side.pokemonLeft > 1 || target.side.pokemonLeft > 1 || target.hp)) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
return damage;
},

View File

@ -188,7 +188,6 @@ export const Scripts: ModdedBattleScriptsData = {
if (!this.battle.singleEvent('TryMove', move, null, pokemon, target, move) ||
!this.battle.runEvent('TryMove', pokemon, target, move)) {
move.mindBlownRecoil = false;
return false;
}
@ -457,8 +456,8 @@ export const Scripts: ModdedBattleScriptsData = {
move.totalDamage = damage;
}
if (move.recoil && move.totalDamage) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
if (move.totalDamage) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
if (target && pokemon !== target) target.gotAttacked(move, damage, pokemon);
@ -476,9 +475,5 @@ export const Scripts: ModdedBattleScriptsData = {
return damage;
},
calcRecoilDamage(damageDealt, move) {
return this.battle.clampIntRange(Math.floor(damageDealt * move.recoil![0] / move.recoil![1]), 1);
},
},
};

View File

@ -1307,8 +1307,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-activate', target, 'Substitute', '[damage]');
}
if (move.ohko) this.add('-ohko');
if (move.recoil && damage) {
this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil');
if (damage) {
this.actions.calcRecoilDamage(damage, move, source);
}
if (move.drain) {
this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain');

View File

@ -199,8 +199,19 @@ export const Scripts: ModdedBattleScriptsData = {
}
return hitResults;
},
calcRecoilDamage(damageDealt, move) {
return this.battle.clampIntRange(Math.floor(damageDealt * move.recoil![0] / move.recoil![1]), 1);
calcRecoilDamage(damageDealt: number, move: Move, pokemon: Pokemon): number | null {
let recoilDamage = 0;
if (move.struggleRecoil) recoilDamage = this.battle.clampIntRange(Math.floor(pokemon.baseMaxhp / 4), 1);
else if (move.recoil) {
recoilDamage = this.battle.clampIntRange(Math.floor(damageDealt * move.recoil[0] / move.recoil[1]), 1);
} else return null;
if (move.struggleRecoil) {
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
} else {
this.battle.damage(recoilDamage, pokemon, pokemon, 'recoil');
}
return recoilDamage;
},
},
};

View File

@ -872,8 +872,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
} else {
this.add('-activate', target, 'Substitute', '[damage]');
}
if (move.recoil && damage) {
this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil');
if (damage) {
this.actions.calcRecoilDamage(damage, move, source);
}
if (move.drain) {
this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain');

View File

@ -1314,7 +1314,6 @@ export const Scripts: ModdedBattleScriptsData = {
if (!this.battle.singleEvent('TryMove', move, null, pokemon, target, move) ||
!this.battle.runEvent('TryMove', pokemon, target, move)) {
move.mindBlownRecoil = false;
return false;
}
@ -1476,14 +1475,6 @@ export const Scripts: ModdedBattleScriptsData = {
// Total damage dealt is accumulated for the purposes of recoil (Parental Bond).
move.totalDamage += damage[i];
}
if (move.mindBlownRecoil) {
const hpBeforeRecoil = pokemon.hp;
this.battle.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get(move.id), true);
move.mindBlownRecoil = false;
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
this.battle.eachEvent('Update');
if (!pokemon.hp && targets.length === 1) {
hit++; // report the correct number of hits for multihit moves
@ -1498,26 +1489,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.add('-hitcount', targets[0], hit - 1);
}
if ((move.recoil || move.id === 'chloroblast') && move.totalDamage) {
const hpBeforeRecoil = pokemon.hp;
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, pokemon, 'recoil');
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
if (move.struggleRecoil) {
const hpBeforeRecoil = pokemon.hp;
let recoilDamage;
if (this.dex.gen >= 5) {
recoilDamage = this.battle.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1);
} else {
recoilDamage = this.battle.clampIntRange(this.battle.trunc(pokemon.maxhp / 4), 1);
}
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
if (move.totalDamage) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
// smartTarget messes up targetsCopy, but smartTarget should in theory ensure that targets will never fail, anyway

View File

@ -140,15 +140,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
mindblown: {
inherit: true,
onAfterMove(pokemon, target, move) {
if (move.mindBlownRecoil && !move.multihit) {
const hpBeforeRecoil = pokemon.hp;
const calc = calculate(this, pokemon, pokemon, 'mindblown');
this.damage(Math.round(calc * pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Mind Blown'), true);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.runEvent('EmergencyExit', pokemon, pokemon);
}
}
onMoveFail(target, source, move) {
if (move.multihit) return;
const calc = calculate(this, source, source, 'mindblown');
this.damage(Math.round(calc * source.maxhp / 2), source, source, this.dex.conditions.get('Mind Blown'));
},
},
nightmare: {
@ -244,15 +239,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
steelbeam: {
inherit: true,
onAfterMove(pokemon, target, move) {
if (move.mindBlownRecoil && !move.multihit) {
const hpBeforeRecoil = pokemon.hp;
const calc = calculate(this, pokemon, pokemon, 'steelbeam');
this.damage(Math.round(calc * pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Steel Beam'), true);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.runEvent('EmergencyExit', pokemon, pokemon);
}
}
onMoveFail(target, source, move) {
if (move.multihit) return;
const calc = calculate(this, source, source, 'steelbeam');
this.damage(Math.round(calc * source.maxhp / 2), source, source, this.dex.conditions.get('Mind Blown'));
},
},
supercellslam: {

View File

@ -110,15 +110,6 @@ export const Scripts: ModdedBattleScriptsData = {
// Total damage dealt is accumulated for the purposes of recoil (Parental Bond).
move.totalDamage += damage[i];
}
if (move.mindBlownRecoil) {
const hpBeforeRecoil = pokemon.hp;
const calc = calculate(this.battle, pokemon, pokemon, move.id);
this.battle.damage(Math.round(calc * pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get(move.id), true);
move.mindBlownRecoil = false;
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
this.battle.eachEvent('Update');
if (!pokemon.hp && targets.length === 1) {
hit++; // report the correct number of hits for multihit moves
@ -133,27 +124,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.add('-hitcount', targets[0], hit - 1);
}
if ((move.recoil || move.id === 'chloroblast') && move.totalDamage) {
const hpBeforeRecoil = pokemon.hp;
const recoilDamage = this.calcRecoilDamage(move.totalDamage, move, pokemon);
if (recoilDamage !== 1.1) this.battle.damage(recoilDamage, pokemon, pokemon, 'recoil');
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
if (move.struggleRecoil) {
const hpBeforeRecoil = pokemon.hp;
let recoilDamage;
if (this.dex.gen >= 5) {
recoilDamage = this.battle.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1);
} else {
recoilDamage = this.battle.clampIntRange(this.battle.trunc(pokemon.maxhp / 4), 1);
}
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
if (move.totalDamage) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
// smartTarget messes up targetsCopy, but smartTarget should in theory ensure that targets will never fail, anyway
@ -194,12 +166,28 @@ export const Scripts: ModdedBattleScriptsData = {
return damage;
},
calcRecoilDamage(damageDealt, move, pokemon): number {
calcRecoilDamage(damageDealt: number, move: Move, pokemon: Pokemon): number | null {
const calc = calculate(this.battle, pokemon, pokemon, move.id);
if (calc === 0) return 1.1;
if (move.id === 'chloroblast') return Math.round(calc * pokemon.maxhp / 2);
const recoil = Math.round(damageDealt * calc * move.recoil![0] / move.recoil![1]);
return this.battle.clampIntRange(recoil, 1);
if (!calc) return 0;
let recoilDamage = 0;
if (move.struggleRecoil) recoilDamage = this.battle.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1);
else if (move.mindBlownRecoil) recoilDamage = Math.round(calc * pokemon.maxhp / 2);
else if (move.recoil) {
recoilDamage = this.battle.clampIntRange(Math.round(damageDealt * calc * move.recoil[0] / move.recoil[1]), 1);
} else return null;
const hpBeforeRecoil = pokemon.hp;
if (move.struggleRecoil) {
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
} else {
this.battle.damage(recoilDamage, pokemon, pokemon, 'recoil');
}
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
return recoilDamage;
},
},
};

View File

@ -939,7 +939,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
hasCrashDamage: true,
onMoveFail(target, source, move) {
this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get('High Jump Kick'));
this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get('Axe Kick'));
},
secondary: {
chance: 30,
@ -2443,7 +2443,8 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
pp: 5,
priority: 0,
flags: { protect: 1, mirror: 1, metronome: 1 },
// Recoil implemented in battle-actions.ts
mindBlownRecoil: true,
// Contrary to Mind Blown, Chloroblast does not implement the MoveFail event
target: "normal",
type: "Grass",
},
@ -11885,14 +11886,9 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
priority: 0,
flags: { protect: 1, mirror: 1 },
mindBlownRecoil: true,
onAfterMove(pokemon, target, move) {
if (move.mindBlownRecoil && !move.multihit) {
const hpBeforeRecoil = pokemon.hp;
this.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Mind Blown'), true);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.runEvent('EmergencyExit', pokemon, pokemon);
}
}
onMoveFail(target, source, move) {
if (move.multihit) return;
this.damage(Math.round(source.maxhp / 2), source, source, this.dex.conditions.get('Mind Blown'));
},
target: "allAdjacent",
type: "Fire",
@ -17888,14 +17884,9 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
priority: 0,
flags: { protect: 1, mirror: 1 },
mindBlownRecoil: true,
onAfterMove(pokemon, target, move) {
if (move.mindBlownRecoil && !move.multihit) {
const hpBeforeRecoil = pokemon.hp;
this.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get('Steel Beam'), true);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.runEvent('EmergencyExit', pokemon, pokemon);
}
}
onMoveFail(target, source, move) {
if (move.multihit) return;
this.damage(Math.round(source.maxhp / 2), source, source, this.dex.conditions.get('Steel Beam'));
},
target: "normal",
type: "Steel",
@ -18369,8 +18360,8 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
} else {
this.add('-activate', target, 'move: Substitute', '[damage]');
}
if (move.recoil || move.id === 'chloroblast') {
this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil');
if (damage) {
this.actions.calcRecoilDamage(damage, move, source);
}
if (move.drain) {
this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain');

View File

@ -488,7 +488,6 @@ export class BattleActions {
tryMoveResult = this.battle.runEvent('TryMove', pokemon, target, move);
}
if (!tryMoveResult) {
move.mindBlownRecoil = false;
return tryMoveResult;
}
@ -524,7 +523,13 @@ export class BattleActions {
}
if (!moveResult) {
const originalHp = pokemon.hp;
this.battle.singleEvent('MoveFail', move, null, target, pokemon, move);
if (pokemon && pokemon !== target && move.category !== 'Status') {
if (pokemon.hp <= pokemon.maxhp / 2 && originalHp > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
return false;
}
@ -959,14 +964,6 @@ export class BattleActions {
// Total damage dealt is accumulated for the purposes of recoil (Parental Bond).
move.totalDamage += damage[i];
}
if (move.mindBlownRecoil) {
const hpBeforeRecoil = pokemon.hp;
this.battle.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get(move.id), true);
move.mindBlownRecoil = false;
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
this.battle.eachEvent('Update');
if (!pokemon.hp && targets.length === 1) {
hit++; // report the correct number of hits for multihit moves
@ -981,26 +978,8 @@ export class BattleActions {
this.battle.add('-hitcount', targets[0], hit - 1);
}
if ((move.recoil || move.id === 'chloroblast') && move.totalDamage) {
const hpBeforeRecoil = pokemon.hp;
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, pokemon, 'recoil');
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
if (move.struggleRecoil) {
const hpBeforeRecoil = pokemon.hp;
let recoilDamage;
if (this.dex.gen >= 5) {
recoilDamage = this.battle.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1);
} else {
recoilDamage = this.battle.clampIntRange(this.battle.trunc(pokemon.maxhp / 4), 1);
}
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
if (move.totalDamage) {
this.calcRecoilDamage(move.totalDamage, move, pokemon);
}
// smartTarget messes up targetsCopy, but smartTarget should in theory ensure that targets will never fail, anyway
@ -1397,9 +1376,25 @@ export class BattleActions {
return retVal === true ? undefined : retVal;
}
calcRecoilDamage(damageDealt: number, move: Move, pokemon: Pokemon): number {
if (move.id === 'chloroblast') return Math.round(pokemon.maxhp / 2);
return this.battle.clampIntRange(Math.round(damageDealt * move.recoil![0] / move.recoil![1]), 1);
calcRecoilDamage(damageDealt: number, move: Move, pokemon: Pokemon): number | null {
let recoilDamage = null;
if (move.struggleRecoil) recoilDamage = this.battle.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1);
else if (move.mindBlownRecoil) recoilDamage = Math.round(pokemon.maxhp / 2);
else if (move.recoil) {
recoilDamage = this.battle.clampIntRange(Math.round(damageDealt * move.recoil[0] / move.recoil[1]), 1);
} else return null;
const hpBeforeRecoil = pokemon.hp;
if (move.struggleRecoil) {
this.battle.directDamage(recoilDamage, pokemon, pokemon, { id: 'strugglerecoil' } as Condition);
} else {
this.battle.damage(recoilDamage, pokemon, pokemon, 'recoil');
}
if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
return recoilDamage;
}
getZMove(move: Move, pokemon: Pokemon, skipChecks?: boolean): string | undefined {

View File

@ -2109,7 +2109,7 @@ export class Battle {
}
if (targetDamage !== 0) targetDamage = this.clampIntRange(targetDamage, 1);
if (effect.id !== 'struggle-recoil') { // Struggle recoil is not affected by effects
if (effect.id !== 'strugglerecoil') { // Struggle recoil is not affected by effects
if (effect.effectType === 'Weather' && !target.runStatusImmunity(effect.id)) {
this.debug('weather immunity');
retVals[i] = 0;
@ -2158,12 +2158,6 @@ export class Battle {
}
if (targetDamage && effect.effectType === 'Move') {
if (this.gen <= 1 && effect.recoil && source) {
if (this.dex.currentMod !== 'gen1stadium' || target.hp > 0) {
const amount = this.clampIntRange(Math.floor(targetDamage * effect.recoil[0] / effect.recoil[1]), 1);
this.damage(amount, source, target, 'recoil');
}
}
if (this.gen <= 4 && effect.drain && source) {
const amount = this.clampIntRange(Math.floor(targetDamage * effect.drain[0] / effect.drain[1]), 1);
// Draining can be countered in gen 1

View File

@ -154,7 +154,7 @@ interface BattleScriptsData {
interface ModdedBattleActions {
inherit?: true;
afterMoveSecondaryEvent?: (this: BattleActions, targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => undefined;
calcRecoilDamage?: (this: BattleActions, damageDealt: number, move: Move, pokemon: Pokemon) => number;
calcRecoilDamage?: (this: BattleActions, damageDealt: number, move: Move, pokemon: Pokemon) => number | null;
canMegaEvo?: (this: BattleActions, pokemon: Pokemon) => string | undefined | null;
canMegaEvoX?: (this: BattleActions, pokemon: Pokemon) => string | undefined | null;
canMegaEvoY?: (this: BattleActions, pokemon: Pokemon) => string | undefined | null;

View File

@ -401,6 +401,32 @@ describe(`Emergency Exit`, () => {
assert.equal(battle.requestState, 'switch');
});
it(`should request a switchout after crash damage`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', ability: 'Emergency Exit', moves: ['highjumpkick'], evs: { hp: 4 } },
{ species: 'Wynaut', moves: ['sleeptalk'] },
], [
{ species: 'Chansey', moves: ['protect'] },
]]);
const eePokemon = battle.p1.active[0];
battle.makeChoices();
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
});
it(`should request a switchout after Mind Blown recoil damage`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', ability: 'Emergency Exit', moves: ['mindblown'], evs: { hp: 4 } },
{ species: 'Wynaut', moves: ['sleeptalk'] },
], [
{ species: 'Chansey', moves: ['protect'] },
]]);
const eePokemon = battle.p1.active[0];
battle.makeChoices();
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
});
it(`should request a switchout after taking struggle recoil damage`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', item: 'Assault Vest', ability: 'Emergency Exit', moves: ['protect'] },

View File

@ -27,4 +27,15 @@ describe('Mind Blown', () => {
]]);
assert.hurtsBy(battle.p1.active[0], Math.ceil(battle.p1.active[0].maxhp / 2), () => battle.makeChoices());
});
it('should not deal damage to the user if there is no target', () => {
battle = common.createBattle([[
{ species: 'Dugtrio', ability: 'sandveil', moves: ['memento'] },
{ species: 'Dugtrio', ability: 'sandveil', moves: ['memento'] },
], [
{ species: 'Blacephalon', ability: 'limber', moves: ['mindblown'] },
]]);
assert.false.hurts(battle.p2.active[0], () => battle.makeChoices());
});
});