From 55cbc52bba8c786eeb960004a90e9ceef441b933 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Fri, 7 Feb 2020 10:57:28 -0800 Subject: [PATCH] Fix bugs with secondary/ability order Fixes #6346 The `AfterDamage` event has been replaced with `DamagingHit`, which which happens for damaging moves after secondaries. The `AfterHit` event has also been moved after `DamagingHit`, to make sure Knock Off still procs after Rocky Helmet. `AfterHit` is no longer a valid event on `secondary` and `self` blocks, because it's meaningless in those blocks, anyway. All `self.onAfterHit` and `secondary.onAfterHit` handlers have been moved to `onHit`, which should have the same timing in practice. --- data/abilities.js | 140 ++++++++++++++------------------- data/items.js | 37 ++++----- data/mods/gen2/moves.js | 2 +- data/mods/gen3/abilities.js | 24 +++--- data/mods/gen4/abilities.js | 8 +- data/mods/gen4/items.js | 22 ------ data/mods/gen6/abilities.js | 14 ++-- data/mods/gen6/items.js | 12 +-- data/mods/gen7/abilities.js | 4 +- data/mods/gennext/abilities.js | 22 +++--- data/mods/ssb/abilities.js | 27 +++---- data/mods/ssb/statuses.js | 2 +- data/moves.js | 16 ++-- data/scripts.js | 24 +++++- sim/battle.ts | 16 ++-- sim/global-types.ts | 14 ++-- simulator-doc.txt | 2 - 17 files changed, 176 insertions(+), 210 deletions(-) diff --git a/data/abilities.js b/data/abilities.js index 85a6e472d5..696af140fc 100644 --- a/data/abilities.js +++ b/data/abilities.js @@ -79,9 +79,9 @@ let BattleAbilities = { shortDesc: "If this Pokemon is KOed with a contact move, that move's user loses 1/4 its max HP.", id: "aftermath", name: "Aftermath", - onAfterDamageOrder: 1, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact'] && !target.hp) { + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && !target.hp) { this.damage(source.baseMaxhp / 4, source, target); } }, @@ -531,13 +531,11 @@ let BattleAbilities = { "cottondown": { desc: "When the Pokémon is hit by an attack, it scatters cotton fluff around and lowers the Speed stat of all Pokémon except itself.", shortDesc: "Lowers Speed of all Pokémon except itself when hit by an attack.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.id !== 'confused') { - this.add('-ability', target, 'Cotton Down'); - for (let pokemon of this.getAllActive()) { - if (pokemon === target) continue; - this.boost({spe: -1}, pokemon, target, null, true); - } + onDamagingHit(damage, target, source, move) { + this.add('-ability', target, 'Cotton Down'); + for (let pokemon of this.getAllActive()) { + if (pokemon === target) continue; + this.boost({spe: -1}, pokemon, target, null, true); } }, id: "cottondown", @@ -548,9 +546,9 @@ let BattleAbilities = { "cursedbody": { desc: "If this Pokemon is hit by an attack, there is a 30% chance that move gets disabled unless one of the attacker's moves is already disabled.", shortDesc: "If this Pokemon is hit by an attack, there is a 30% chance that move gets disabled.", - onAfterDamage(damage, target, source, move) { - if (!source || source.volatiles['disable']) return; - if (source !== target && move && move.effectType === 'Move' && !move.isFutureMove) { + onDamagingHit(damage, target, source, move) { + if (source.volatiles['disable']) return; + if (!move.isFutureMove) { if (this.randomChance(3, 10)) { source.addVolatile('disable', this.effectData.target); } @@ -564,8 +562,8 @@ let BattleAbilities = { "cutecharm": { desc: "There is a 30% chance a Pokemon making contact with this Pokemon will become infatuated if it is of the opposite gender.", shortDesc: "30% chance of infatuating Pokemon of the opposite gender if they make contact.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(3, 10)) { source.addVolatile('attract', this.effectData.target); } @@ -863,8 +861,8 @@ let BattleAbilities = { "effectspore": { desc: "30% chance a Pokemon making contact with this Pokemon will be poisoned, paralyzed, or fall asleep.", shortDesc: "30% chance of poison/paralysis/sleep on others making contact with this Pokemon.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact'] && !source.status && source.runStatusImmunity('powder')) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && !source.status && source.runStatusImmunity('powder')) { let r = this.random(100); if (r < 11) { source.setStatus('slp', target); @@ -905,14 +903,6 @@ let BattleAbilities = { this.add('-activate', target, 'ability: Emergency Exit'); } }, - onAfterDamage(damage, target, source, effect) { - if (!target.hp || effect.effectType === 'Move') return; - if (target.hp <= target.maxhp / 2 && target.hp + damage > target.maxhp / 2) { - if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.switchFlag) return; - target.switchFlag = true; - this.add('-activate', target, 'ability: Emergency Exit'); - } - }, id: "emergencyexit", name: "Emergency Exit", rating: 1, @@ -951,8 +941,8 @@ let BattleAbilities = { }, "flamebody": { shortDesc: "30% chance a Pokemon making contact with this Pokemon will be burned.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(3, 10)) { source.trySetStatus('brn', target); } @@ -1275,8 +1265,8 @@ let BattleAbilities = { }, "gooey": { shortDesc: "Pokemon making contact with this Pokemon have their Speed lowered by 1 stage.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.add('-ability', target, 'Gooey'); this.boost({spe: -1}, source, target, null, true); } @@ -1354,15 +1344,15 @@ let BattleAbilities = { "gulpmissile": { desc: "If this Pokemon is a Cramorant, it changes forme when it hits a target with Surf or uses the first turn of Dive successfully. It becomes Gulping Form with an Arrokuda in its mouth if it has more than 1/2 of its maximum HP remaining, or Gorging Form with a Pikachu in its mouth if it has 1/2 or less of its maximum HP remaining. If Cramorant gets hit in Gulping or Gorging Form, it spits the Arrokuda or Pikachu at its attacker, even if it has no HP remaining. The projectile deals damage equal to 1/4 of the target's maximum HP, rounded down; this damage is blocked by the Magic Guard Ability but not by a substitute. An Arrokuda also lowers the target's Defense by 1 stage, and a Pikachu paralyzes the target. Cramorant will return to normal if it spits out a projectile, switches out, or Dynamaxes.", shortDesc: "When hit after Surf/Dive, attacker takes 1/4 max HP and -1 Defense or paralysis.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.id !== 'confused' && ['cramorantgulping', 'cramorantgorging'].includes(target.template.speciesid) && !target.transformed && !target.isSemiInvulnerable()) { + onDamagingHit(damage, target, source, move) { + if (move.effectType === 'Move' && ['cramorantgulping', 'cramorantgorging'].includes(target.template.speciesid) && !target.transformed && !target.isSemiInvulnerable()) { this.damage(source.baseMaxhp / 4, source, target); if (target.template.speciesid === 'cramorantgulping') { this.boost({def: -1}, source, target, null, true); } else { - source.trySetStatus('par', target, effect); + source.trySetStatus('par', target, move); } - target.formeChange('cramorant', effect); + target.formeChange('cramorant', move); } }, // The Dive part of this mechanic is implemented in Dive's `onTryMove` in moves.js @@ -1641,9 +1631,9 @@ let BattleAbilities = { if (pokemon === pokemon.side.pokemon[i]) return; pokemon.illusion = pokemon.side.pokemon[i]; }, - onAfterDamage(damage, target, source, effect) { - if (target.illusion && effect && effect.effectType === 'Move' && effect.id !== 'confused') { - this.singleEvent('End', this.dex.getAbility('Illusion'), target.abilityData, target, source, effect); + onDamagingHit(damage, target, source, move) { + if (target.illusion) { + this.singleEvent('End', this.dex.getAbility('Illusion'), target.abilityData, target, source, move); } }, onEnd(pokemon) { @@ -1714,9 +1704,9 @@ let BattleAbilities = { shortDesc: "If this Pokemon is KOed with a move, that move's user loses an equal amount of HP.", id: "innardsout", name: "Innards Out", - onAfterDamageOrder: 1, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.effectType === 'Move' && !target.hp) { + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (!target.hp) { this.damage(target.getUndynamaxedHP(damage), source, target); } }, @@ -1790,9 +1780,9 @@ let BattleAbilities = { "ironbarbs": { desc: "Pokemon making contact with this Pokemon lose 1/8 of their maximum HP, rounded down.", shortDesc: "Pokemon making contact with this Pokemon lose 1/8 of their max HP.", - onAfterDamageOrder: 1, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 8, source, target); } }, @@ -1818,8 +1808,8 @@ let BattleAbilities = { }, "justified": { shortDesc: "This Pokemon's Attack is raised by 1 stage after it is damaged by a Dark-type move.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.type === 'Dark') { + onDamagingHit(damage, target, source, move) { + if (move.type === 'Dark') { this.boost({atk: 1}); } }, @@ -2352,8 +2342,8 @@ let BattleAbilities = { shortDesc: "Pokemon making contact with this Pokemon have their Ability changed to Mummy.", id: "mummy", name: "Mummy", - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact'] && source.ability !== 'mummy') { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && source.ability !== 'mummy') { let oldAbility = source.setAbility('mummy', target); if (oldAbility) { this.add('-activate', target, 'ability: Mummy', this.dex.getAbility(oldAbility).name, '[of] ' + source); @@ -2670,7 +2660,7 @@ let BattleAbilities = { "perishbody": { desc: "When hit by a move that makes direct contact, the Pokémon and the attacker will faint after three turns unless they switch out of battle.", shortDesc: "When hit by a contact move, the Pokémon and the attacker faint in 3 turns.", - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (!move.flags['contact']) return; let announced = false; @@ -2792,8 +2782,8 @@ let BattleAbilities = { }, "poisonpoint": { shortDesc: "30% chance a Pokemon making contact with this Pokemon will be poisoned.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(3, 10)) { source.trySetStatus('psn', target); } @@ -3056,8 +3046,8 @@ let BattleAbilities = { "rattled": { desc: "This Pokemon's Speed is raised by 1 stage if hit by a Bug-, Dark-, or Ghost-type attack, or Intimidate.", shortDesc: "Speed is raised 1 stage if hit by a Bug-, Dark-, or Ghost-type attack, or Intimidated.", - onAfterDamage(damage, target, source, effect) { - if (effect && (effect.type === 'Dark' || effect.type === 'Bug' || effect.type === 'Ghost')) { + onDamagingHit(damage, target, source, move) { + if (['Dark', 'Bug', 'Ghost'].includes(move.type)) { this.boost({spe: 1}); } }, @@ -3215,9 +3205,9 @@ let BattleAbilities = { "roughskin": { desc: "Pokemon making contact with this Pokemon lose 1/8 of their maximum HP, rounded down.", shortDesc: "Pokemon making contact with this Pokemon lose 1/8 of their max HP.", - onAfterDamageOrder: 1, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 8, source, target); } }, @@ -3271,8 +3261,8 @@ let BattleAbilities = { }, "sandspit": { shortDesc: "The Pokémon creates a sandstorm when it's hit by an attack.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.id !== 'confused' && this.field.getWeather().id !== 'sandstorm') { + onDamagingHit(damage, target, source, move) { + if (this.field.getWeather().id !== 'sandstorm') { this.field.setWeather('sandstorm'); } }, @@ -3798,10 +3788,8 @@ let BattleAbilities = { }, "stamina": { shortDesc: "This Pokemon's Defense is raised by 1 stage after it is damaged by a move.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.id !== 'confused') { - this.boost({def: 1}); - } + onDamagingHit(damage, target, source, effect) { + this.boost({def: 1}); }, id: "stamina", name: "Stamina", @@ -3825,8 +3813,8 @@ let BattleAbilities = { }, "static": { shortDesc: "30% chance a Pokemon making contact with this Pokemon will be paralyzed.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(3, 10)) { source.trySetStatus('par', target); } @@ -3849,8 +3837,8 @@ let BattleAbilities = { }, "steamengine": { shortDesc: "This Pokemon's Speed is raised by 6 stages after it is damaged by Fire/Water moves.", - onAfterDamage(damage, target, source, effect) { - if (effect && ['Water', 'Fire'].includes(effect.type)) { + onDamagingHit(damage, target, source, move) { + if (['Water', 'Fire'].includes(move.type)) { this.boost({spe: 6}); } }, @@ -4133,8 +4121,8 @@ let BattleAbilities = { }, "tanglinghair": { shortDesc: "Pokemon making contact with this Pokemon have their Speed lowered by 1 stage.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.add('-ability', target, 'Tangling Hair'); this.boost({spe: -1}, source, target, null, true); } @@ -4455,10 +4443,10 @@ let BattleAbilities = { "wanderingspirit": { desc: "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", shortDesc: "Exchanges abilities when hit with a contact move.", - onAfterDamage(damage, target, source, effect) { - if (!source || source.ability === 'wanderingspirit') return; + onDamagingHit(damage, target, source, move) { + if (source.ability === 'wanderingspirit') return; if (target.volatiles['dynamax'] || target.ability === 'illusion' || target.ability === 'wonderguard') return; - if (effect && effect.effectType === 'Move' && effect.flags['contact']) { + if (move.flags['contact']) { let sourceAbility = source.setAbility('wanderingspirit', target); if (!sourceAbility) return; if (target.side === source.side) { @@ -4534,8 +4522,8 @@ let BattleAbilities = { }, "watercompaction": { shortDesc: "This Pokemon's Defense is raised 2 stages after it is damaged by a Water-type move.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.type === 'Water') { + onDamagingHit(damage, target, source, move) { + if (move.type === 'Water') { this.boost({def: 2}); } }, @@ -4566,7 +4554,7 @@ let BattleAbilities = { "weakarmor": { desc: "If a physical attack hits this Pokemon, its Defense is lowered by 1 stage and its Speed is raised by 2 stages.", shortDesc: "If a physical attack hits this Pokemon, Defense is lowered by 1, Speed is raised by 2.", - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.category === 'Physical') { this.boost({def: -1, spe: 2}, target, target); } @@ -4613,14 +4601,6 @@ let BattleAbilities = { this.add('-activate', target, 'ability: Wimp Out'); } }, - onAfterDamage(damage, target, source, effect) { - if (!target.hp || effect.effectType === 'Move') return; - if (target.hp <= target.maxhp / 2 && target.hp + damage > target.maxhp / 2) { - if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.switchFlag) return; - target.switchFlag = true; - this.add('-activate', target, 'ability: Wimp Out'); - } - }, id: "wimpout", name: "Wimp Out", rating: 1, diff --git a/data/items.js b/data/items.js index 23bd571034..25d89b649b 100644 --- a/data/items.js +++ b/data/items.js @@ -41,7 +41,7 @@ let BattleItems = { fling: { basePower: 30, }, - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.type === 'Water') { target.useItem(); } @@ -162,18 +162,15 @@ let BattleItems = { } }, // airborneness implemented in sim/pokemon.js:Pokemon#isGrounded - onAfterDamage(damage, target, source, effect) { - this.debug('effect: ' + effect.id); - if (effect.effectType === 'Move' && effect.id !== 'confused') { - this.add('-enditem', target, 'Air Balloon'); - target.item = ''; - target.itemData = {id: '', target}; - this.runEvent('AfterUseItem', target, null, null, this.dex.getItem('airballoon')); - } + onDamagingHit(damage, target, source, move) { + this.add('-enditem', target, 'Air Balloon'); + target.item = ''; + target.itemData = {id: '', target}; + this.runEvent('AfterUseItem', target, null, null, this.dex.getItem('airballoon')); }, onAfterSubDamage(damage, target, source, effect) { this.debug('effect: ' + effect.id); - if (effect.effectType === 'Move' && effect.id !== 'confused') { + if (effect.effectType === 'Move') { this.add('-enditem', target, 'Air Balloon'); target.item = ''; target.itemData = {id: '', target}; @@ -736,7 +733,7 @@ let BattleItems = { fling: { basePower: 30, }, - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.type === 'Electric') { target.useItem(); } @@ -2900,8 +2897,8 @@ let BattleItems = { basePower: 100, type: "Dragon", }, - onAfterDamage(damage, target, source, move) { - if (source && source.hp && source !== target && move && move.category === 'Physical') { + onDamagingHit(damage, target, source, move) { + if (move.category === 'Physical') { if (target.eatItem()) { this.damage(source.baseMaxhp / 8, source, target); } @@ -3404,7 +3401,7 @@ let BattleItems = { fling: { basePower: 30, }, - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.type === 'Water') { target.useItem(); } @@ -5151,9 +5148,9 @@ let BattleItems = { fling: { basePower: 60, }, - onAfterDamageOrder: 2, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHitOrder: 2, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 6, source, target); } }, @@ -5242,8 +5239,8 @@ let BattleItems = { basePower: 100, type: "Dark", }, - onAfterDamage(damage, target, source, move) { - if (source && source.hp && source !== target && move && move.category === 'Special') { + onDamagingHit(damage, target, source, move) { + if (move.category === 'Special') { if (target.eatItem()) { this.damage(source.baseMaxhp / 8, source, target); } @@ -5711,7 +5708,7 @@ let BattleItems = { fling: { basePower: 30, }, - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.type === 'Ice') { target.useItem(); } diff --git a/data/mods/gen2/moves.js b/data/mods/gen2/moves.js index 5e240ee66d..08ac9a2a2c 100644 --- a/data/mods/gen2/moves.js +++ b/data/mods/gen2/moves.js @@ -956,7 +956,7 @@ let BattleMovedex = { onAfterHit() {}, secondary: { chance: 100, - onAfterHit(target, source) { + onHit(target, source) { if (source.item || source.volatiles['gem']) { return; } diff --git a/data/mods/gen3/abilities.js b/data/mods/gen3/abilities.js index 9489305250..27433d541d 100644 --- a/data/mods/gen3/abilities.js +++ b/data/mods/gen3/abilities.js @@ -6,8 +6,8 @@ let BattleAbilities = { inherit: true, desc: "There is a 1/3 chance a Pokemon making contact with this Pokemon will become infatuated if it is of the opposite gender.", shortDesc: "1/3 chance of infatuating Pokemon of the opposite gender if they make contact.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(1, 3)) { source.addVolatile('attract', target); } @@ -18,8 +18,8 @@ let BattleAbilities = { inherit: true, desc: "10% chance a Pokemon making contact with this Pokemon will be poisoned, paralyzed, or fall asleep.", shortDesc: "10% chance of poison/paralysis/sleep on others making contact with this Pokemon.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact'] && !source.status) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && !source.status) { let r = this.random(300); if (r < 10) { source.setStatus('slp', target); @@ -34,8 +34,8 @@ let BattleAbilities = { "flamebody": { inherit: true, shortDesc: "1/3 chance a Pokemon making contact with this Pokemon will be burned.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(1, 3)) { source.trySetStatus('brn', target); } @@ -129,8 +129,8 @@ let BattleAbilities = { "poisonpoint": { inherit: true, shortDesc: "1/3 chance a Pokemon making contact with this Pokemon will be poisoned.", - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(1, 3)) { source.trySetStatus('psn', target); } @@ -147,8 +147,8 @@ let BattleAbilities = { inherit: true, desc: "Pokemon making contact with this Pokemon lose 1/16 of their maximum HP, rounded down.", shortDesc: "Pokemon making contact with this Pokemon lose 1/16 of their max HP.", - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 16, source, target); } }, @@ -164,8 +164,8 @@ let BattleAbilities = { "static": { inherit: true, shortDesc: "1/3 chance a Pokemon making contact with this Pokemon will be paralyzed.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { if (this.randomChance(1, 3)) { source.trySetStatus('par', target); } diff --git a/data/mods/gen4/abilities.js b/data/mods/gen4/abilities.js index 1524ef2f00..4004e4b5df 100644 --- a/data/mods/gen4/abilities.js +++ b/data/mods/gen4/abilities.js @@ -33,10 +33,10 @@ let BattleAbilities = { "colorchange": { inherit: true, desc: "This Pokemon's type changes to match the type of the last move that hit it, unless that type is already one of its types. This effect applies after each hit from a multi-hit move.", - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (!target.hp) return; let type = move.type; - if (target.isActive && move.effectType === 'Move' && move.category !== 'Status' && type !== '???' && !target.hasType(type)) { + if (target.isActive && move.category !== 'Status' && type !== '???' && !target.hasType(type)) { if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type, '[from] ability: Color Change'); } @@ -45,8 +45,8 @@ let BattleAbilities = { }, "effectspore": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact'] && !source.status) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && !source.status) { let r = this.random(100); if (r < 10) { source.setStatus('slp', target); diff --git a/data/mods/gen4/items.js b/data/mods/gen4/items.js index af57222dd3..8e083fae9c 100644 --- a/data/mods/gen4/items.js +++ b/data/mods/gen4/items.js @@ -125,17 +125,6 @@ let BattleItems = { onEffectiveness() {}, desc: "Holder's Speed is halved and it becomes grounded.", }, - "jabocaberry": { - inherit: true, - onAfterDamage() {}, - onAfterMoveSecondary(target, source, move) { - if (source && source !== target && move && move.category === 'Physical') { - if (target.eatItem()) { - this.damage(source.baseMaxhp / 8, source, target, null, true); - } - } - }, - }, "kingsrock": { inherit: true, onModifyMove(move) { @@ -258,17 +247,6 @@ let BattleItems = { } }, }, - "rowapberry": { - inherit: true, - onAfterDamage() {}, - onAfterMoveSecondary(target, source, move) { - if (source && source !== target && move && move.category === 'Special') { - if (target.eatItem()) { - this.damage(source.baseMaxhp / 8, source, target, null, true); - } - } - }, - }, "stick": { inherit: true, onModifyCritRatio(critRatio, user) { diff --git a/data/mods/gen6/abilities.js b/data/mods/gen6/abilities.js index 320d09bd43..49b76ecea5 100644 --- a/data/mods/gen6/abilities.js +++ b/data/mods/gen6/abilities.js @@ -13,8 +13,8 @@ let BattleAbilities = { }, "aftermath": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact'] && !target.hp) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && !target.hp) { this.damage(source.baseMaxhp / 4, source, target, null, true); } }, @@ -47,8 +47,8 @@ let BattleAbilities = { }, "ironbarbs": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 8, source, target, null, true); } }, @@ -124,8 +124,8 @@ let BattleAbilities = { }, "roughskin": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 8, source, target, null, true); } }, @@ -142,7 +142,7 @@ let BattleAbilities = { inherit: true, desc: "If a physical attack hits this Pokemon, its Defense is lowered by 1 stage and its Speed is raised by 1 stage.", shortDesc: "If a physical attack hits this Pokemon, Defense is lowered by 1, Speed is raised by 1.", - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (move.category === 'Physical') { this.boost({def: -1, spe: 1}, target, target); } diff --git a/data/mods/gen6/items.js b/data/mods/gen6/items.js index 6ae1e08901..884a9efee1 100644 --- a/data/mods/gen6/items.js +++ b/data/mods/gen6/items.js @@ -76,8 +76,8 @@ let BattleItems = { }, jabocaberry: { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.category === 'Physical') { + onDamagingHit(damage, target, source, move) { + if (move.category === 'Physical') { if (target.eatItem()) { this.damage(source.baseMaxhp / 8, source, target, null, true); } @@ -145,16 +145,16 @@ let BattleItems = { }, rockyhelmet: { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.damage(source.baseMaxhp / 6, source, target, null, true); } }, }, rowapberry: { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.category === 'Special') { + onDamagingHit(damage, target, source, move) { + if (move.category === 'Special') { if (target.eatItem()) { this.damage(source.baseMaxhp / 8, source, target, null, true); } diff --git a/data/mods/gen7/abilities.js b/data/mods/gen7/abilities.js index e72ca9d84c..c624d2d7c8 100644 --- a/data/mods/gen7/abilities.js +++ b/data/mods/gen7/abilities.js @@ -110,8 +110,8 @@ let BattleAbilities = { "rattled": { desc: "This Pokemon's Speed is raised by 1 stage if hit by a Bug-, Dark-, or Ghost-type attack.", shortDesc: "This Pokemon's Speed is raised 1 stage if hit by a Bug-, Dark-, or Ghost-type attack.", - onAfterDamage(damage, target, source, effect) { - if (effect && (effect.type === 'Dark' || effect.type === 'Bug' || effect.type === 'Ghost')) { + onDamagingHit(damage, target, source, move) { + if (['Dark', 'Bug', 'Ghost'].includes(move.type)) { this.boost({spe: 1}); } }, diff --git a/data/mods/gennext/abilities.js b/data/mods/gennext/abilities.js index a3160e112d..9d8ef63268 100644 --- a/data/mods/gennext/abilities.js +++ b/data/mods/gennext/abilities.js @@ -123,8 +123,8 @@ let BattleAbilities = { onResidual(target, source, effect) { this.heal(target.baseMaxhp / 16); }, - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact'] && this.field.isWeather('hail')) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact'] && this.field.isWeather('hail')) { if (this.randomChance(3, 10)) { source.trySetStatus('frz', target); } @@ -141,8 +141,8 @@ let BattleAbilities = { }, "static": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { source.trySetStatus('par', target); } }, @@ -150,8 +150,8 @@ let BattleAbilities = { }, "cutecharm": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { source.addVolatile('Attract', target); } }, @@ -160,8 +160,8 @@ let BattleAbilities = { }, "poisonpoint": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (move && move.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { source.trySetStatus('psn', target); } }, @@ -416,7 +416,7 @@ let BattleAbilities = { return damage; } }, - onAfterDamage() {}, + onDamagingHit() {}, desc: "This ability reduces incoming move damage by 1/10 of the user's max HP and increases the user's Speed for the first hit after switch-in (and does not activate again until the next switch-in).", shortDesc: "Reduces incoming move damage by 1/10 of the user's max HP and increases the user's Spe for the 1st hit after switch-in (doesn't activate until next switch-in).", }, @@ -482,8 +482,8 @@ let BattleAbilities = { }, "aftermath": { inherit: true, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && !target.hp) { + onDamagingHit(damage, target, source, move) { + if (!target.hp) { this.damage(source.baseMaxhp / 3, source, target, null, true); } }, diff --git a/data/mods/ssb/abilities.js b/data/mods/ssb/abilities.js index 7354460e93..76cba7c3bf 100644 --- a/data/mods/ssb/abilities.js +++ b/data/mods/ssb/abilities.js @@ -278,9 +278,8 @@ let BattleAbilities = { if ((target === source || move.category === 'Status') && target.template.speciesid !== 'shayminsky' && target.transformed) return; target.formeChange('Shaymin', this.effect); }, - onAfterDamage(damage, target, source, effect) { - if (source === target) return; - if (target && target.template.speciesid === 'shaymin') { + onDamagingHit(damage, target, source, move) { + if (target.template.speciesid === 'shaymin') { target.formeChange('Shaymin-Sky', this.effect); } }, @@ -333,9 +332,9 @@ let BattleAbilities = { pokemon.maybeTrapped = true; } }, - onAfterDamageOrder: 1, - onAfterDamage(damage, target, source, move) { - if (source && source !== target && move && move.effectType === 'Move' && !target.hp) { + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (!target.hp) { this.damage(damage, source, target); } }, @@ -407,8 +406,8 @@ let BattleAbilities = { giblovepls: { desc: "After being damaged by a contact move, this Pokemon is healed by 20% of its maximum HP and has its Defense raised by one stage.", shortDesc: "Defense +1 and heal 20% after hit by contact move.", - onAfterDamage(damage, target, source, effect) { - if (effect && effect.flags['contact']) { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.boost({def: 1}, target); this.heal(target.baseMaxhp / 5, target); } @@ -452,10 +451,10 @@ let BattleAbilities = { if (pokemon === pokemon.side.pokemon[i]) return; pokemon.illusion = pokemon.side.pokemon[i]; }, - onAfterDamage(damage, target, source, effect) { + onDamagingHit(damage, target, source, move) { // Illusion that only breaks when hit with a move that is super effective VS dark - if (target.illusion && effect && effect.effectType === 'Move' && effect.id !== 'confused' && this.dex.getEffectiveness(effect.type, target.getTypes()) > 0) { - this.singleEvent('End', this.dex.getAbility('Illusion'), target.abilityData, target, source, effect); + if (target.illusion && this.dex.getEffectiveness(move.type, target.getTypes()) > 0) { + this.singleEvent('End', this.dex.getAbility('Illusion'), target.abilityData, target, source, move); } }, onEnd(pokemon) { @@ -632,8 +631,8 @@ let BattleAbilities = { return this.chainModify([0x1333, 0x1000]); } }, - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.flags.contact && effect.id !== 'confused') { + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { this.boost({spe: 1}); } }, @@ -917,7 +916,7 @@ let BattleAbilities = { return false; } }, - onAfterDamage(damage, target, source, move) { + onDamagingHit(damage, target, source, move) { if (target.getMoveHitData(move).typeMod > 0) { if (target.m.heavilydamaged && !target.m.quoteplayed) { this.add(`c|@Ransei|Yo really? Why do you keep hitting me with super effective moves?`); diff --git a/data/mods/ssb/statuses.js b/data/mods/ssb/statuses.js index 2d5fef2b4b..b6bb0a6419 100644 --- a/data/mods/ssb/statuses.js +++ b/data/mods/ssb/statuses.js @@ -1404,7 +1404,7 @@ let BattleStatuses = { // Prevent Snaquaza from fainting while using a fake claim to prevent visual bug if (pokemon.hp - damage <= 0) return (pokemon.hp - 1); }, - onAfterDamage(damage, pokemon) { + onDamagingHit(damage, pokemon) { // Hack for Snaquaza's Z move if (!pokemon.m.claimHP || pokemon.hp > 1) return; // Now we handle the fake claim "fainting" diff --git a/data/moves.js b/data/moves.js index d6f24169cd..4e02a58113 100644 --- a/data/moves.js +++ b/data/moves.js @@ -2974,8 +2974,8 @@ let BattleMovedex = { if (source !== this.effectData.target) return; return source.side.foe.active[this.effectData.position]; }, - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect) === 'Physical') { + onDamagingHit(damage, target, source, move) { + if (source.side !== target.side && this.getCategory(move) === 'Physical') { this.effectData.position = source.position; this.effectData.damage = 2 * damage; } @@ -6941,7 +6941,7 @@ let BattleMovedex = { flags: {}, isMax: "Duraludon", self: { - onAfterHit(source) { + onHit(source) { for (let pokemon of source.side.foe.active) { const move = pokemon.lastMove; if (move && !move.isZ && !move.isMax) { @@ -6975,7 +6975,7 @@ let BattleMovedex = { flags: {}, isMax: "Alcremie", self: { - onAfterHit(target, source, move) { + onHit(target, source, move) { for (let pokemon of source.side.active) { this.heal(pokemon.maxhp / 6, pokemon, source, move); } @@ -11944,8 +11944,8 @@ let BattleMovedex = { if (source !== this.effectData.target) return; return source.side.foe.active[this.effectData.position]; }, - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && source.side !== target.side) { + onDamagingHit(damage, target, source, effect) { + if (source.side !== target.side) { this.effectData.position = source.position; this.effectData.damage = 1.5 * damage; } @@ -12301,8 +12301,8 @@ let BattleMovedex = { if (source !== this.effectData.target) return; return source.side.foe.active[this.effectData.position]; }, - onAfterDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect) === 'Special') { + onDamagingHit(damage, target, source, move) { + if (source.side !== target.side && this.getCategory(move) === 'Special') { this.effectData.position = source.position; this.effectData.damage = 2 * damage; } diff --git a/data/scripts.js b/data/scripts.js index cd1ce5145b..5dc9507d78 100644 --- a/data/scripts.js +++ b/data/scripts.js @@ -291,6 +291,7 @@ let BattleScripts = { return true; }, + /** NOTE: includes single-target moves */ trySpreadMoveHit(targets, pokemon, move) { if (targets.length > 1) move.spreadHit = true; @@ -549,6 +550,7 @@ let BattleScripts = { } return undefined; }, + /** NOTE: used only for moves that target sides/fields rather than pokemon */ tryMoveHit(target, pokemon, move) { this.setActiveMove(move, pokemon, target); @@ -790,6 +792,24 @@ let BattleScripts = { if (!damage[j] && damage[j] !== 0) targets[j] = false; } + /** @type {Pokemon[]} */ + let damagedTargets = []; + let damagedDamage = []; + for (let i = 0; i < targets.length; i++) { + if (typeof damage[i] === 'number') { + damagedTargets.push(/** @type {Pokemon} */ (targets[i])); + damagedDamage.push(damage[i]); + } + } + if (damagedDamage.length) { + this.runEvent('DamagingHit', damagedTargets, pokemon, move, damagedDamage); + if (moveData.onAfterHit) { + for (const target of damagedTargets) { + this.singleEvent('AfterHit', moveData, {}, target, pokemon, move); + } + } + } + return [damage, targets]; }, tryPrimaryHitEvent(damage, targets, pokemon, move, moveData, isSecondary) { @@ -941,10 +961,6 @@ let BattleScripts = { if (!isSelf && !isSecondary) { this.runEvent('Hit', target, pokemon, move); } - if (moveData.onAfterHit) { - hitResult = this.singleEvent('AfterHit', moveData, {}, target, pokemon, move); - didSomething = this.combineResults(didSomething, hitResult); - } } } if (moveData.selfSwitch) { diff --git a/sim/battle.ts b/sim/battle.ts index 78529192d6..bb7900a645 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -416,7 +416,8 @@ export class Battle { singleEvent( eventid: string, effect: Effect, effectData: AnyObject | null, target: string | Pokemon | Side | Field | Battle | null, source?: string | Pokemon | Effect | false | null, - sourceEffect?: Effect | string | null, relayVar?: any) { + sourceEffect?: Effect | string | null, relayVar?: any + ) { if (this.eventDepth >= 8) { // oh fuck this.add('message', 'STACK LIMIT EXCEEDED'); @@ -588,7 +589,8 @@ export class Battle { */ runEvent( eventid: string, target?: Pokemon | Pokemon[] | Side | Battle | null, source?: string | Pokemon | false | null, - effect?: Effect | null, relayVar?: any, onEffect?: boolean, fastExit?: boolean) { + effect?: Effect | null, relayVar?: any, onEffect?: boolean, fastExit?: boolean + ) { // if (Battle.eventCounter) { // if (!Battle.eventCounter[eventid]) Battle.eventCounter[eventid] = 0; // Battle.eventCounter[eventid]++; @@ -605,7 +607,7 @@ export class Battle { let effectSource = null; if (source instanceof Pokemon) effectSource = source; const handlers = this.findEventHandlers(target, eventid, effectSource); - if (eventid === 'Invulnerability' || eventid === 'TryHit' || eventid === 'AfterDamage') { + if (eventid === 'Invulnerability' || eventid === 'TryHit' || eventid === 'DamagingHit') { handlers.sort(Battle.compareLeftToRightOrder); } else if (fastExit) { handlers.sort(Battle.compareRedirectOrder); @@ -650,7 +652,7 @@ export class Battle { if (handler.index !== undefined) { // TODO: find a better way to do this if (!targetRelayVars[handler.index] && !(targetRelayVars[handler.index] === 0 && - eventid === 'AfterDamage')) continue; + eventid === 'DamagingHit')) continue; if (handler.target) { args[hasRelayVar] = handler.target; this.event.target = handler.target; @@ -763,7 +765,8 @@ export class Battle { */ priorityEvent( eventid: string, target: Pokemon | Side | Battle, source?: Pokemon | null, - effect?: Effect, relayVar?: any, onEffect?: boolean): any { + effect?: Effect, relayVar?: any, onEffect?: boolean + ): any { return this.runEvent(eventid, target, source, effect, relayVar, onEffect, true); } @@ -1750,8 +1753,6 @@ export class Battle { } } - // @ts-ignore - FIXME AfterDamage passes an Effect, not an ActiveMove - if (!effect.flags) effect.flags = {}; if (instafaint) { for (const [i, target] of targetArray.entries()) { if (!retVals[i] || !target) continue; @@ -1766,7 +1767,6 @@ export class Battle { } } } - retVals = this.runEvent('AfterDamage', (targetArray.filter(val => !!val)) as Pokemon[], source, effect, retVals); return retVals; } diff --git a/sim/global-types.ts b/sim/global-types.ts index 5c12b26c63..72ebf71c2d 100644 --- a/sim/global-types.ts +++ b/sim/global-types.ts @@ -109,7 +109,6 @@ interface SelfEffect { terrain?: string volatileStatus?: string weather?: string - onAfterHit?: MoveEventMethods['onAfterHit'] onHit?: MoveEventMethods['onHit'] } @@ -122,7 +121,6 @@ interface SecondaryEffect { self?: SelfEffect status?: string volatileStatus?: string - onAfterHit?: MoveEventMethods['onAfterHit'] onHit?: MoveEventMethods['onHit'] } @@ -196,7 +194,7 @@ interface PureEffectEventMethods { } interface EventMethods { - onAfterDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void + onDamagingHit?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void onAfterEachBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon) => void onAfterHit?: MoveEventMethods['onAfterHit'] onAfterSetStatus?: (this: Battle, status: PureEffect, target: Pokemon, source: Pokemon, effect: Effect) => void @@ -283,7 +281,7 @@ interface EventMethods { onWeatherModifyDamage?: CommonHandlers['ModifierSourceMove'] onModifyDamagePhase1?: CommonHandlers['ModifierSourceMove'] onModifyDamagePhase2?: CommonHandlers['ModifierSourceMove'] - onAllyAfterDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void + onAllyDamagingHit?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void onAllyAfterEachBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon) => void onAllyAfterHit?: MoveEventMethods['onAfterHit'] onAllyAfterSetStatus?: (this: Battle, status: PureEffect, target: Pokemon, source: Pokemon, effect: Effect) => void @@ -368,7 +366,7 @@ interface EventMethods { onAllyWeatherModifyDamage?: CommonHandlers['ModifierSourceMove'] onAllyModifyDamagePhase1?: CommonHandlers['ModifierSourceMove'] onAllyModifyDamagePhase2?: CommonHandlers['ModifierSourceMove'] - onFoeAfterDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void + onFoeDamagingHit?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void onFoeAfterEachBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon) => void onFoeAfterHit?: MoveEventMethods['onAfterHit'] onFoeAfterSetStatus?: (this: Battle, status: PureEffect, target: Pokemon, source: Pokemon, effect: Effect) => void @@ -453,7 +451,7 @@ interface EventMethods { onFoeWeatherModifyDamage?: CommonHandlers['ModifierSourceMove'] onFoeModifyDamagePhase1?: CommonHandlers['ModifierSourceMove'] onFoeModifyDamagePhase2?: CommonHandlers['ModifierSourceMove'] - onSourceAfterDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void + onSourceDamagingHit?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void onSourceAfterEachBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon) => void onSourceAfterHit?: MoveEventMethods['onAfterHit'] onSourceAfterSetStatus?: (this: Battle, status: PureEffect, target: Pokemon, source: Pokemon, effect: Effect) => void @@ -538,7 +536,7 @@ interface EventMethods { onSourceWeatherModifyDamage?: CommonHandlers['ModifierSourceMove'] onSourceModifyDamagePhase1?: CommonHandlers['ModifierSourceMove'] onSourceModifyDamagePhase2?: CommonHandlers['ModifierSourceMove'] - onAnyAfterDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void + onAnyDamagingHit?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void onAnyAfterEachBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon) => void onAnyAfterHit?: MoveEventMethods['onAfterHit'] onAnyAfterSetStatus?: (this: Battle, status: PureEffect, target: Pokemon, source: Pokemon, effect: Effect) => void @@ -626,7 +624,7 @@ interface EventMethods { // Priorities (incomplete list) onAccuracyPriority?: number - onAfterDamageOrder?: number + onDamagingHitOrder?: number onAfterMoveSecondaryPriority?: number onAfterMoveSecondarySelfPriority?: number onAfterMoveSelfPriority?: number diff --git a/simulator-doc.txt b/simulator-doc.txt index a406305e2d..d9af85566e 100644 --- a/simulator-doc.txt +++ b/simulator-doc.txt @@ -82,13 +82,11 @@ runAction() - runs runSwitch, runAfterSwitch, and runMove in priority order, the [Damage] (A,U) -> false => exit damage() damage - [AfterDamage] (U) } heal() { [Heal] (A) -> false => exit heal() heal - [AfterHeal] } status() (U) { [Status] (A)