From e218cb9dbbf1fcc1841663b0e1e1ab77a3314804 Mon Sep 17 00:00:00 2001 From: Karthik <32044378+Karthik99999@users.noreply.github.com> Date: Sat, 15 Jul 2023 15:54:49 -0400 Subject: [PATCH] Properly implement Chloroblast recoil (#9648) --- data/mods/gen2/scripts.ts | 2 +- data/mods/gen2stadium2/scripts.ts | 2 +- data/mods/gen3/scripts.ts | 2 +- data/mods/gen4/moves.ts | 2 +- data/mods/gen5/moves.ts | 2 +- data/mods/ssb/scripts.ts | 2 +- data/moves.ts | 15 ++------- sim/battle-actions.ts | 7 ++-- sim/global-types.ts | 2 +- test/sim/moves/chloroblast.js | 54 +++++++++++++++++++++++++++++++ 10 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 test/sim/moves/chloroblast.js diff --git a/data/mods/gen2/scripts.ts b/data/mods/gen2/scripts.ts index 964e34e307..3ea800cea3 100644 --- a/data/mods/gen2/scripts.ts +++ b/data/mods/gen2/scripts.ts @@ -302,7 +302,7 @@ export const Scripts: ModdedBattleScriptsData = { } if (move.recoil && move.totalDamage) { - this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil'); + this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil'); } return damage; }, diff --git a/data/mods/gen2stadium2/scripts.ts b/data/mods/gen2stadium2/scripts.ts index baa37ee97d..6992fc9d43 100644 --- a/data/mods/gen2stadium2/scripts.ts +++ b/data/mods/gen2stadium2/scripts.ts @@ -239,7 +239,7 @@ export const Scripts: ModdedBattleScriptsData = { // 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, target, 'recoil'); + this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil'); } return damage; }, diff --git a/data/mods/gen3/scripts.ts b/data/mods/gen3/scripts.ts index f85af9d99e..e0b41fd6e8 100644 --- a/data/mods/gen3/scripts.ts +++ b/data/mods/gen3/scripts.ts @@ -451,7 +451,7 @@ export const Scripts: ModdedBattleScriptsData = { } if (move.recoil && move.totalDamage) { - this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil'); + this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil'); } if (target && pokemon !== target) target.gotAttacked(move, damage, pokemon); diff --git a/data/mods/gen4/moves.ts b/data/mods/gen4/moves.ts index 29556e122e..6be90e81cb 100644 --- a/data/mods/gen4/moves.ts +++ b/data/mods/gen4/moves.ts @@ -1513,7 +1513,7 @@ export const Moves: {[k: string]: ModdedMoveData} = { this.add('-activate', target, 'Substitute', '[damage]'); } if (move.recoil && damage) { - this.damage(this.actions.calcRecoilDamage(damage, move), source, target, 'recoil'); + this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil'); } if (move.drain) { this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain'); diff --git a/data/mods/gen5/moves.ts b/data/mods/gen5/moves.ts index d5d0263d0a..26cc616d84 100644 --- a/data/mods/gen5/moves.ts +++ b/data/mods/gen5/moves.ts @@ -890,7 +890,7 @@ export const Moves: {[k: string]: ModdedMoveData} = { this.add('-activate', target, 'Substitute', '[damage]'); } if (move.recoil && damage) { - this.damage(this.actions.calcRecoilDamage(damage, move), source, target, 'recoil'); + this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil'); } if (move.drain) { this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain'); diff --git a/data/mods/ssb/scripts.ts b/data/mods/ssb/scripts.ts index 778c403aaf..0c48686776 100644 --- a/data/mods/ssb/scripts.ts +++ b/data/mods/ssb/scripts.ts @@ -527,7 +527,7 @@ export const Scripts: ModdedBattleScriptsData = { } if (move.recoil && move.totalDamage) { - this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, pokemon, 'recoil'); + this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, pokemon, 'recoil'); } if (move.struggleRecoil) { diff --git a/data/moves.ts b/data/moves.ts index 19fe760aec..a142abcc3d 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -2396,16 +2396,7 @@ export const Moves: {[moveid: string]: MoveData} = { pp: 5, 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('Chloroblast'), true); - if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) { - this.runEvent('EmergencyExit', pokemon, pokemon); - } - } - }, + // Recoil implemented in battle-actions.ts secondary: null, target: "normal", type: "Grass", @@ -18788,8 +18779,8 @@ export const Moves: {[moveid: string]: MoveData} = { } else { this.add('-activate', target, 'move: Substitute', '[damage]'); } - if (move.recoil) { - this.damage(this.actions.calcRecoilDamage(damage, move), source, target, 'recoil'); + if (move.recoil || move.id === 'chloroblast') { + this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil'); } if (move.drain) { this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain'); diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 9fb34f3e68..6ade6e9b27 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -945,9 +945,9 @@ export class BattleActions { this.battle.add('-hitcount', targets[0], hit - 1); } - if (move.recoil && move.totalDamage) { + if ((move.recoil || move.id === 'chloroblast') && move.totalDamage) { const hpBeforeRecoil = pokemon.hp; - this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, pokemon, 'recoil'); + 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); } @@ -1359,7 +1359,8 @@ export class BattleActions { return retVal === true ? undefined : retVal; } - calcRecoilDamage(damageDealt: number, move: Move): number { + 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); } diff --git a/sim/global-types.ts b/sim/global-types.ts index 5076cfee0b..cebe44c4b4 100644 --- a/sim/global-types.ts +++ b/sim/global-types.ts @@ -214,7 +214,7 @@ interface BattleScriptsData { interface ModdedBattleActions { inherit?: true; afterMoveSecondaryEvent?: (this: BattleActions, targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => undefined; - calcRecoilDamage?: (this: BattleActions, damageDealt: number, move: Move) => number; + calcRecoilDamage?: (this: BattleActions, damageDealt: number, move: Move, pokemon: Pokemon) => number; canMegaEvo?: (this: BattleActions, pokemon: Pokemon) => string | undefined | null; canUltraBurst?: (this: BattleActions, pokemon: Pokemon) => string | null; canZMove?: (this: BattleActions, pokemon: Pokemon) => ZMoveOptions | void; diff --git a/test/sim/moves/chloroblast.js b/test/sim/moves/chloroblast.js new file mode 100644 index 0000000000..aeaaa453dc --- /dev/null +++ b/test/sim/moves/chloroblast.js @@ -0,0 +1,54 @@ +'use strict'; + +const assert = require('./../../assert'); +const common = require('./../../common'); + +let battle; + +describe('Chloroblast', function () { + afterEach(function () { + battle.destroy(); + }); + + it('should deal recoil damage to the user equal to half its max HP, rounded up', function () { + battle = common.createBattle([[ + {species: "Electrode-Hisui", item: 'widelens', moves: ['chloroblast']}, + ], [ + {species: "Blissey", moves: ['sleeptalk']}, + ]]); + assert.hurtsBy(battle.p1.active[0], Math.round(battle.p1.active[0].maxhp / 2), () => battle.makeChoices()); + }); + + it('should not deal recoil damage to the user if it misses or is blocked by Protect', function () { + battle = common.createBattle([[ + {species: "Electrode-Hisui", item: 'widelens', moves: ['chloroblast', 'protect']}, + ], [ + {species: "Talonflame", ability: 'galewings', moves: ['fly', 'protect']}, + ]]); + battle.makeChoices('move chloroblast', 'move fly'); + battle.makeChoices('move protect', 'auto'); + battle.makeChoices('move chloroblast', 'move protect'); + assert.fullHP(battle.p1.active[0]); + }); + + it('should have its recoil damage negated by Rock Head', function () { + battle = common.createBattle([[ + {species: "Electrode-Hisui", ability: 'rockhead', item: 'widelens', moves: ['chloroblast']}, + ], [ + {species: "Blissey", moves: ['sleeptalk']}, + ]]); + battle.makeChoices(); + assert.fullHP(battle.p1.active[0]); + }); + + it('should not have its base power boosted by Reckless', function () { + battle = common.createBattle([[ + {species: "Electrode-Hisui", ability: 'reckless', item: 'widelens', moves: ['chloroblast']}, + ], [ + {species: "Blissey", ability: 'shellarmor', moves: ['sleeptalk']}, + ]]); + battle.makeChoices(); + const damage = battle.p2.active[0].maxhp - battle.p2.active[0].hp; + assert.bounded(damage, [103, 123]); + }); +});