From 2e56120ad993de42c5b42e9287ead3bd3fd10c03 Mon Sep 17 00:00:00 2001 From: Kevin Lau Date: Wed, 8 Apr 2015 00:15:28 -0700 Subject: [PATCH 1/4] Move refactor: affectedByImmunities -> ignoreImmunity While move.affectedByImmunities was always a Boolean value to denote if the move was affected by immunities, move.ignoreImmunity can be a Boolean value if it ignores or is affected by all type immunities, but also can act as an object such that !!move.ignoreImmunity[type] means it ignores immunities of that type. --- battle-engine.js | 2 +- config/commands.js | 4 ++-- config/formats.js | 12 +++++------- data/abilities.js | 6 ++++-- data/moves.js | 14 +++++--------- data/scripts.js | 12 ++++++------ data/statuses.js | 4 ++-- mods/gen1/moves.js | 16 ++++++++-------- mods/gen1/scripts.js | 12 ++++++------ mods/gen1/statuses.js | 4 ++-- mods/gen2/scripts.js | 2 +- mods/gen3/moves.js | 2 +- mods/gen5/moves.js | 2 +- mods/gennext/moves.js | 26 +++++++++++++------------- mods/stadium/scripts.js | 8 ++++---- tools.js | 1 + 16 files changed, 62 insertions(+), 65 deletions(-) diff --git a/battle-engine.js b/battle-engine.js index d5669b180c..64ba9596f2 100644 --- a/battle-engine.js +++ b/battle-engine.js @@ -3013,7 +3013,7 @@ Battle = (function () { flags: {} }; - if (move.affectedByImmunities) { + if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) { if (!target.runImmunity(move.type, !suppressMessages)) { return false; } diff --git a/config/commands.js b/config/commands.js index 0f998443bc..ff247bec89 100644 --- a/config/commands.js +++ b/config/commands.js @@ -1263,8 +1263,8 @@ var commands = exports.commands = { if (!this.canBroadcast()) return; var factor = 0; - if (source.category === "Status" && !source.affectedByImmunities) factor = 1; - if (Tools.getImmunity(source.type || source, defender) || source.affectedByImmunities === false) { + if (source.category === "Status" && (source.ignoreImmunity === true || source.ignoreImmunity[source.type])) factor = 1; + if (Tools.getImmunity(source.type || source, defender) || source.ignoreImmunity === true || source.ignoreImmunity[source.type]) { var totalTypeMod = 0; if (source.effectType !== 'Move' || source.basePower || source.basePowerCallback) { for (var i = 0; i < defender.types.length; i++) { diff --git a/config/formats.js b/config/formats.js index e49cb34d6b..fabb88b81a 100644 --- a/config/formats.js +++ b/config/formats.js @@ -2333,7 +2333,7 @@ exports.Formats = [ move.category = 'Special'; move.type = 'Psychic'; move.negateSecondary = true; - move.affectedByImmunities = false; + move.ignoreImmunity = true; delete move.secondaries; move.onTryHit = function (target, pokemon) { this.attrLastMove('[still]'); @@ -2483,7 +2483,7 @@ exports.Formats = [ } if (move.id === 'triattack' && name === 'ascriptmaster') { move.name = 'Spectrum Beam'; - move.affectedByImmunities = false; + move.ignoreImmunity = true; move.basePower = 8; move.critRatio = 1; move.accuracy = 95; @@ -2540,7 +2540,7 @@ exports.Formats = [ move.onHit = function (pokemon) { pokemon.addVolatile('confusion'); }; - move.affectedByImmunities = false; + move.ignoreImmunity = true; move.type = 'Dark'; } if (move.id === 'bugbuzz' && name === 'beowulf') { @@ -2709,9 +2709,7 @@ exports.Formats = [ this.attrLastMove('[still]'); this.add('-anim', source, "Giga Drain", target); }; - if (move.type === 'Ground') { - move.affectedByImmunities = false; - } + move.ignoreImmunity = {'Ground': true}; } if (move.id === 'partingshot' && name === 'hippopotas') { move.name = 'Hazard Pass'; @@ -3274,7 +3272,7 @@ exports.Formats = [ target.ignore['Ability'] = true; }; move.accuracy = true; - move.affectedByImmunities = false; + move.ignoreImmunity = true; move.ignoreDefensive = true; move.ignoreEvasion = true; } diff --git a/data/abilities.js b/data/abilities.js index 30b3c32729..fc9ca84299 100644 --- a/data/abilities.js +++ b/data/abilities.js @@ -2268,8 +2268,10 @@ exports.BattleAbilities = { shortDesc: "This Pokemon can hit Ghost types with Normal- and Fighting-type moves.", onModifyMovePriority: -5, onModifyMove: function (move) { - if (move.type in {'Fighting':1, 'Normal':1}) { - move.affectedByImmunities = false; + if (!move.ignoreImmunity) move.ignoreImmunity = {}; + if (move.ignoreImmunity !== true) { + move.ignoreImmunity['Fighting'] = true; + move.ignoreImmunity['Normal'] = true; } }, id: "scrappy", diff --git a/data/moves.js b/data/moves.js index 335ea60f7f..1cc919ff26 100644 --- a/data/moves.js +++ b/data/moves.js @@ -952,7 +952,7 @@ exports.BattleMovedex = { priority: 1, flags: {contact: 1, protect: 1}, volatileStatus: 'bide', - affectedByImmunities: false, + ignoreImmunity: true, effect: { duration: 3, onLockMove: 'bide', @@ -4988,7 +4988,7 @@ exports.BattleMovedex = { pp: 10, priority: 0, flags: {}, - affectedByImmunities: false, + ignoreImmunity: true, isFutureMove: true, onTryHit: function (target, source) { source.side.addSideCondition('futuremove'); @@ -5004,7 +5004,7 @@ exports.BattleMovedex = { basePower: 120, category: "Special", flags: {}, - affectedByImmunities: true, + ignoreImmunity: false, type: 'Psychic' } }; @@ -14104,17 +14104,13 @@ exports.BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, nonsky: 1}, isUnreleased: true, - onModifyMove: function (move) { - if (move.type === 'Ground') { - move.affectedByImmunities = false; - } - }, onTryHit: function (target) { // only the attack that grounds the target ignores effectiveness if (target.negateImmunity['Ground']) return; target.negateImmunity['Ground'] = 'IgnoreEffectiveness'; }, volatileStatus: 'smackdown', + ignoreImmunity: {'Ground': true}, secondary: false, target: "allAdjacentFoes", type: "Ground" @@ -14262,7 +14258,7 @@ exports.BattleMovedex = { priority: 0, flags: {protect: 1, reflectable: 1, mirror: 1}, status: 'par', - affectedByImmunities: true, + ignoreImmunity: false, secondary: false, target: "normal", type: "Electric" diff --git a/data/scripts.js b/data/scripts.js index 4d31d18eda..f7d9c1523f 100644 --- a/data/scripts.js +++ b/data/scripts.js @@ -101,8 +101,8 @@ exports.BattleScripts = { return true; } - if (typeof move.affectedByImmunities === 'undefined') { - move.affectedByImmunities = (move.category !== 'Status'); + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); } var damage = false; @@ -209,12 +209,12 @@ exports.BattleScripts = { return this.moveHit(target, pokemon, move); } - if (move.affectedByImmunities && !target.runImmunity(move.type, true)) { - return false; + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); } - if (typeof move.affectedByImmunities === 'undefined') { - move.affectedByImmunities = (move.category !== 'Status'); + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type] && !target.runImmunity(move.type, true)) { + return false; } hitResult = this.runEvent('TryHit', target, pokemon, move); diff --git a/data/statuses.js b/data/statuses.js index 3d6a4b10bc..f28188403a 100644 --- a/data/statuses.js +++ b/data/statuses.js @@ -320,8 +320,8 @@ exports.BattleStatuses = { target.removeVolatile('Protect'); target.removeVolatile('Endure'); - if (typeof posData.moveData.affectedByImmunities === 'undefined') { - posData.moveData.affectedByImmunities = true; + if (posData.moveData.ignoreImmunity === undefined) { + posData.moveData.ignoreImmunity = false; } if (target.hasAbility('wonderguard') && this.gen > 5) { diff --git a/mods/gen1/moves.js b/mods/gen1/moves.js index 883b7dad60..b6660b92e7 100644 --- a/mods/gen1/moves.js +++ b/mods/gen1/moves.js @@ -105,7 +105,7 @@ exports.BattleMovedex = { }, bind: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, volatileStatus: 'partiallytrapped', self: { volatileStatus: 'partialtrappinglock' @@ -199,7 +199,7 @@ exports.BattleMovedex = { }, counter: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, willCrit: false, damageCallback: function (pokemon, target) { // Counter mechanics on gen 1 might be hard to understand. @@ -368,7 +368,7 @@ exports.BattleMovedex = { }, glare: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, growth: { inherit: true, @@ -574,7 +574,7 @@ exports.BattleMovedex = { }, nightshade: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, basePower: 1 }, poisonsting: { @@ -713,12 +713,12 @@ exports.BattleMovedex = { }, sandattack: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, type: "Normal" }, seismictoss: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, basePower: 1 }, selfdestruct: { @@ -867,7 +867,7 @@ exports.BattleMovedex = { }, superfang: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, basePower: 1 }, thunder: { @@ -908,7 +908,7 @@ exports.BattleMovedex = { wrap: { inherit: true, accuracy: 85, - affectedByImmunities: false, + ignoreImmunity: true, volatileStatus: 'partiallytrapped', self: { volatileStatus: 'partialtrappinglock' diff --git a/mods/gen1/scripts.js b/mods/gen1/scripts.js index a79604c3dc..13b483c442 100644 --- a/mods/gen1/scripts.js +++ b/mods/gen1/scripts.js @@ -208,8 +208,8 @@ exports.BattleScripts = { return true; } - if (typeof move.affectedByImmunities === 'undefined') { - move.affectedByImmunities = (move.category !== 'Status'); + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); } var damage = false; @@ -291,7 +291,7 @@ exports.BattleScripts = { } // Check if the Pokémon is immune to this move. - if (move.affectedByImmunities && !target.runImmunity(move.type, true)) { + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type] && !target.runImmunity(move.type, true)) { damage = false; } @@ -371,8 +371,8 @@ exports.BattleScripts = { var hitResult = true; if (!moveData) moveData = move; - if (typeof move.affectedByImmunities === 'undefined') { - move.affectedByImmunities = (move.category !== 'Status'); + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); } // We get the sub to the target to see if it existed @@ -704,7 +704,7 @@ exports.BattleScripts = { }; // Let's see if the target is immune to the move. - if (move.affectedByImmunities) { + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type]) { if (!target.runImmunity(move.type, true)) { return false; } diff --git a/mods/gen1/statuses.js b/mods/gen1/statuses.js index 5ede997f27..9f99da917a 100644 --- a/mods/gen1/statuses.js +++ b/mods/gen1/statuses.js @@ -310,8 +310,8 @@ exports.BattleStatuses = { target.removeVolatile('Protect'); target.removeVolatile('Endure'); - if (typeof posData.moveData.affectedByImmunities === 'undefined') { - posData.moveData.affectedByImmunities = true; + if (posData.moveData.ignoreImmunity === undefined) { + posData.moveData.ignoreImmunity = false; } this.moveHit(target, posData.source, move, posData.moveData); diff --git a/mods/gen2/scripts.js b/mods/gen2/scripts.js index fface12ea7..d23e502135 100644 --- a/mods/gen2/scripts.js +++ b/mods/gen2/scripts.js @@ -286,7 +286,7 @@ exports.BattleScripts = { }; // Let's test for immunities. - if (move.affectedByImmunities) { + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type]) { if (!target.runImmunity(move.type, true)) { return false; } diff --git a/mods/gen3/moves.js b/mods/gen3/moves.js index d6583280f9..c652ef0907 100644 --- a/mods/gen3/moves.js +++ b/mods/gen3/moves.js @@ -357,7 +357,7 @@ exports.BattleMovedex = { glare: { inherit: true, accuracy: 75, - affectedByImmunities: true + ignoreImmunity: false }, growth: { inherit: true, diff --git a/mods/gen5/moves.js b/mods/gen5/moves.js index 598b754f90..ef8cb61b4a 100644 --- a/mods/gen5/moves.js +++ b/mods/gen5/moves.js @@ -266,7 +266,7 @@ exports.BattleMovedex = { basePower: 100, category: "Special", flags: {}, - affectedByImmunities: true, + ignoreImmunity: false, type: 'Psychic' } }; diff --git a/mods/gennext/moves.js b/mods/gennext/moves.js index 5da5643097..952ba6b4e1 100644 --- a/mods/gennext/moves.js +++ b/mods/gennext/moves.js @@ -652,7 +652,7 @@ exports.BattleMovedex = { onBasePower: function (power, user) { if (user.template.id === 'snorlax') return power * 1.5; }, - affectedByImmunities: false + ignoreImmunity: true }, /****************************************************************** Sound-based Normal-type moves: @@ -664,19 +664,19 @@ exports.BattleMovedex = { ******************************************************************/ boomburst: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, hypervoice: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, round: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, uproar: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, /****************************************************************** Bonemerang, Bone Rush, Bone Club moves: @@ -688,18 +688,18 @@ exports.BattleMovedex = { ******************************************************************/ bonemerang: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, accuracy: true }, bonerush: { inherit: true, basePower: 20, - affectedByImmunities: false, + ignoreImmunity: true, accuracy: true }, boneclub: { inherit: true, - affectedByImmunities: false, + ignoreImmunity: true, accuracy: 90 }, /****************************************************************** @@ -712,7 +712,7 @@ exports.BattleMovedex = { relicsong: { inherit: true, basePower: 60, - affectedByImmunities: false, + ignoreImmunity: true, onHit: function (target, pokemon) { if (pokemon.baseTemplate.species !== 'Meloetta' || pokemon.transformed) { return; @@ -1179,7 +1179,7 @@ exports.BattleMovedex = { category: "Special", isViable: true, priority: 0, - affectedByImmunities: false, + ignoreImmunity: true, onHit: function (target, source) { source.side.addSideCondition('futuremove'); if (source.side.sideConditions['futuremove'].positions[source.position]) { @@ -1194,7 +1194,7 @@ exports.BattleMovedex = { basePower: 80, category: "Special", flags: {}, - affectedByImmunities: false, + ignoreImmunity: true, type: 'Normal' } }; @@ -1875,11 +1875,11 @@ exports.BattleMovedex = { }, acid: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, acidspray: { inherit: true, - affectedByImmunities: false + ignoreImmunity: true }, eggbomb: { inherit: true, diff --git a/mods/stadium/scripts.js b/mods/stadium/scripts.js index 1b869cc54c..e3c01b3bbe 100644 --- a/mods/stadium/scripts.js +++ b/mods/stadium/scripts.js @@ -161,7 +161,7 @@ exports.BattleScripts = { } // Check if the Pokémon is immune to this move. - if (move.affectedByImmunities && !target.runImmunity(move.type, true)) { + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type] && !target.runImmunity(move.type, true)) { damage = false; } @@ -238,8 +238,8 @@ exports.BattleScripts = { var hitResult = true; if (!moveData) moveData = move; - if (typeof move.affectedByImmunities === 'undefined') { - move.affectedByImmunities = (move.category !== 'Status'); + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); } // We get the sub to the target to see if it existed @@ -398,7 +398,7 @@ exports.BattleScripts = { }; // Let's see if the target is immune to the move. - if (move.affectedByImmunities) { + if (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type]) { if (!target.runImmunity(move.type, true)) { return false; } diff --git a/tools.js b/tools.js index cb2456a3db..f83651c827 100644 --- a/tools.js +++ b/tools.js @@ -311,6 +311,7 @@ module.exports = (function () { else move.gen = 0; } if (!move.priority) move.priority = 0; + if (!move.ignoreImmunity) move.ignoreImmunity = (move.category === 'Status'); if (!move.flags) move.flags = {}; } return move; From f26b3fd279a577bf6669e6f0bca77a627c84e992 Mon Sep 17 00:00:00 2001 From: Kevin Lau Date: Wed, 8 Apr 2015 23:00:12 -0700 Subject: [PATCH 2/4] Add type immunity tests in Mocha --- test/simulator/misc/statusmoves.js | 35 ++++++++++++ test/simulator/moves/thousandarrows.js | 78 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 test/simulator/misc/statusmoves.js create mode 100644 test/simulator/moves/thousandarrows.js diff --git a/test/simulator/misc/statusmoves.js b/test/simulator/misc/statusmoves.js new file mode 100644 index 0000000000..83fa554aee --- /dev/null +++ b/test/simulator/misc/statusmoves.js @@ -0,0 +1,35 @@ +var assert = require('assert'); +var battle; + +describe('Most status moves', function () { + afterEach(function () { + battle.destroy(); + }); + + it('should ignore type immunities', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Smeargle", ability: 'prankster', item: 'leftovers', moves: ['gastroacid', 'glare', 'confuseray', 'sandattack']}]); + battle.join('p2', 'Guest 2', 1, [ + {species: "Klefki", ability: 'magician', happiness: 0, moves: ['return']}, + {species: "Dusknoir", ability: 'frisk', moves: ['shadowpunch']}, + {species: "Slaking", ability: 'truant', moves: ['shadowclaw']}, + {species: "Tornadus", ability: 'prankster', moves: ['tailwind']}, + {species: "Unown", ability: 'levitate', moves: ['hiddenpower']} + ]); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].item, ''); + battle.choose('p1', 'move 2'); + battle.choose('p2', 'switch 2'); + assert.strictEqual(battle.p2.active[0].status, 'par'); + battle.choose('p1', 'move 3'); + battle.choose('p2', 'switch 3'); + assert.ok(battle.p2.active[0].volatiles['confusion']); + battle.choose('p1', 'move 4'); + battle.choose('p2', 'switch 4'); + assert.strictEqual(battle.p2.active[0].boosts['accuracy'], -1); + battle.choose('p1', 'move 4'); + battle.choose('p2', 'switch 5'); + assert.strictEqual(battle.p2.active[0].boosts['accuracy'], -1); + }); +}); + diff --git a/test/simulator/moves/thousandarrows.js b/test/simulator/moves/thousandarrows.js new file mode 100644 index 0000000000..99cff50dbc --- /dev/null +++ b/test/simulator/moves/thousandarrows.js @@ -0,0 +1,78 @@ +var assert = require('assert'); +var battle; + +describe('Thousand Arrows', function () { + afterEach(function () { + battle.destroy(); + }); + + it('should hit Flying-type Pokemon and remove their Ground immunity', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', item: 'laggingtail', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Tropius", ability: 'harvest', moves: ['synthesis']}]); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + battle.choose('p1', 'move earthquake'); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + }); + + it('should ignore type effectiveness on the first hit against Flying-type Pokemon', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', item: 'laggingtail', moves: ['thousandarrows']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Ho-Oh", ability: 'pressure', item: 'weaknesspolicy', moves: ['recover']}]); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].boosts.atk, 0); + assert.strictEqual(battle.p2.active[0].boosts.spa, 0); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].boosts.atk, 2); + assert.strictEqual(battle.p2.active[0].boosts.spa, 2); + }); + + it('should hit Pokemon with Levitate and remove their Ground immunity', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', item: 'laggingtail', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Cresselia", ability: 'levitate', moves: ['recover']}]); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + battle.choose('p1', 'move earthquake'); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + }); + + it('should hit non-Flying-type Pokemon with Levitate with standard type effectiveness', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Eelektross", ability: 'levitate', item: 'weaknesspolicy', moves: ['thunderwave']}]); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].boosts.atk, 2); + assert.strictEqual(battle.p2.active[0].boosts.spa, 2); + }); + + it('should hit Pokemon with Air Balloon', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', item: 'laggingtail', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Donphan", ability: 'sturdy', item: 'airballoon', moves: ['stealthrock']}]); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + battle.choose('p1', 'move earthquake'); + battle.commitDecisions(); + assert.notStrictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + }); + + it('should not hit Ground-type Pokemon when affected by Electrify', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'aurabreak', item: 'laggingtail', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Stunfisk", ability: 'limber', moves: ['electrify']}]); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + }); + + it('should not hit Ghost-type Pokemon when affected by Normalize', function () { + battle = BattleEngine.Battle.construct(); + battle.join('p1', 'Guest 1', 1, [{species: "Zygarde", ability: 'normalize', item: 'laggingtail', moves: ['thousandarrows', 'earthquake']}]); + battle.join('p2', 'Guest 2', 1, [{species: "Dusknoir", ability: 'pressure', moves: ['haze']}]); + battle.commitDecisions(); + assert.strictEqual(battle.p2.active[0].hp, battle.p2.active[0].maxhp); + }); +}); From 628b870b2f545245fe05b3c971c40b1e89d39e02 Mon Sep 17 00:00:00 2001 From: Kevin Lau Date: Thu, 9 Apr 2015 01:46:22 -0700 Subject: [PATCH 3/4] Move Refactor: Moves track the type effectiveness of the move Set up moves to track the type effectiveness of the move for purposes of 'Hit' and 'ModifyDamage' events. This fixes various glitches resulting from Pokemon with the 'IgnoreEffectiveness' flag set for some types, and also is a slight optimization because it removes the need to run the 'Effectiveness' event one more time to check effectiveness in the moves. --- battle-engine.js | 14 ++++++------- data/abilities.js | 6 +++--- data/items.js | 42 +++++++++++++++++++-------------------- mods/gennext/abilities.js | 12 +++++------ 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/battle-engine.js b/battle-engine.js index 64ba9596f2..7379e49a24 100644 --- a/battle-engine.js +++ b/battle-engine.js @@ -3157,24 +3157,24 @@ Battle = (function () { baseDamage = this.modify(baseDamage, move.stab || 1.5); } // types - var totalTypeMod = 0; + move.typeMod = 0; if (target.negateImmunity[move.type] !== 'IgnoreEffectiveness' || this.getImmunity(move.type, target)) { - totalTypeMod = target.runEffectiveness(move); + move.typeMod = target.runEffectiveness(move); } - totalTypeMod = this.clampIntRange(totalTypeMod, -6, 6); - if (totalTypeMod > 0) { + move.typeMod = this.clampIntRange(move.typeMod, -6, 6); + if (move.typeMod > 0) { if (!suppressMessages) this.add('-supereffective', target); - for (var i = 0; i < totalTypeMod; i++) { + for (var i = 0; i < move.typeMod; i++) { baseDamage *= 2; } } - if (totalTypeMod < 0) { + if (move.typeMod < 0) { if (!suppressMessages) this.add('-resisted', target); - for (var i = 0; i > totalTypeMod; i--) { + for (var i = 0; i > move.typeMod; i--) { baseDamage = Math.floor(baseDamage / 2); } } diff --git a/data/abilities.js b/data/abilities.js index fc9ca84299..65f063c40e 100644 --- a/data/abilities.js +++ b/data/abilities.js @@ -682,7 +682,7 @@ exports.BattleAbilities = { "filter": { shortDesc: "This Pokemon receives 3/4 damage from supereffective attacks.", onSourceModifyDamage: function (damage, source, target, move) { - if (target.runEffectiveness(move) > 0) { + if (move.typeMod > 0) { this.debug('Filter neutralize'); return this.chainModify(0.75); } @@ -2488,7 +2488,7 @@ exports.BattleAbilities = { "solidrock": { shortDesc: "This Pokemon receives 3/4 damage from supereffective attacks.", onSourceModifyDamage: function (damage, source, target, move) { - if (target.runEffectiveness(move) > 0) { + if (move.typeMod > 0) { this.debug('Solid Rock neutralize'); return this.chainModify(0.75); } @@ -2862,7 +2862,7 @@ exports.BattleAbilities = { "tintedlens": { shortDesc: "This Pokemon's attacks that are not very effective on a target deal double damage.", onModifyDamage: function (damage, source, target, move) { - if (target.runEffectiveness(move) < 0) { + if (move.typeMod < 0) { this.debug('Tinted Lens boost'); return this.chainModify(2); } diff --git a/data/items.js b/data/items.js index 6f56f18537..7d24f2486a 100644 --- a/data/items.js +++ b/data/items.js @@ -289,7 +289,7 @@ exports.BattleItems = { type: "Steel" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Steel' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Steel' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -645,7 +645,7 @@ exports.BattleItems = { type: "Rock" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Rock' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Rock' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -832,7 +832,7 @@ exports.BattleItems = { type: "Fighting" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Fighting' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Fighting' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -865,7 +865,7 @@ exports.BattleItems = { type: "Flying" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Flying' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Flying' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -887,7 +887,7 @@ exports.BattleItems = { type: "Dark" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Dark' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Dark' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -1272,8 +1272,8 @@ exports.BattleItems = { basePower: 100, type: "Bug" }, - onSourceBasePower: function (basePower, user, target, move) { - if (move && target.runEffectiveness(move) > 0) { + onSourceModifyDamage: function (damage, source, target, move) { + if (move && move.typeMod > 0) { target.addVolatile('enigmaberry'); } }, @@ -1323,7 +1323,7 @@ exports.BattleItems = { basePower: 10 }, onModifyDamage: function (damage, source, target, move) { - if (move && target.runEffectiveness(move) > 0) { + if (move && move.typeMod > 0) { return this.chainModify(1.2); } }, @@ -1799,7 +1799,7 @@ exports.BattleItems = { type: "Dragon" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Dragon' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Dragon' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -2073,7 +2073,7 @@ exports.BattleItems = { type: "Ghost" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Ghost' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Ghost' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -2095,7 +2095,7 @@ exports.BattleItems = { type: "Poison" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Poison' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Poison' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -3045,7 +3045,7 @@ exports.BattleItems = { type: "Fire" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Fire' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Fire' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -3137,7 +3137,7 @@ exports.BattleItems = { type: "Water" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Water' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Water' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -3159,7 +3159,7 @@ exports.BattleItems = { type: "Psychic" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Psychic' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Psychic' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -3627,7 +3627,7 @@ exports.BattleItems = { type: "Grass" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Grass' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Grass' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -3744,7 +3744,7 @@ exports.BattleItems = { type: "Fairy" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Fairy' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Fairy' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -4001,7 +4001,7 @@ exports.BattleItems = { type: "Ground" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Ground' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Ground' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -4423,7 +4423,7 @@ exports.BattleItems = { type: "Bug" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Bug' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Bug' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -4561,7 +4561,7 @@ exports.BattleItems = { type: "Electric" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Electric' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Electric' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); @@ -4630,7 +4630,7 @@ exports.BattleItems = { basePower: 80 }, onHit: function (target, source, move) { - if (target.hp && move.category !== 'Status' && !move.damage && !move.damageCallback && target.runEffectiveness(move) > 0 && target.useItem()) { + if (target.hp && move.category !== 'Status' && !move.damage && !move.damageCallback && move.typeMod > 0 && target.useItem()) { this.boost({atk: 2, spa: 2}); } }, @@ -4756,7 +4756,7 @@ exports.BattleItems = { type: "Ice" }, onSourceModifyDamage: function (damage, source, target, move) { - if (move.type === 'Ice' && target.runEffectiveness(move) > 0 && !target.volatiles['substitute']) { + if (move.type === 'Ice' && move.typeMod > 0 && !target.volatiles['substitute']) { if (target.eatItem()) { this.debug('-50% reduction'); return this.chainModify(0.5); diff --git a/mods/gennext/abilities.js b/mods/gennext/abilities.js index 68e7d652b6..ad37c4d70d 100644 --- a/mods/gennext/abilities.js +++ b/mods/gennext/abilities.js @@ -256,19 +256,19 @@ exports.BattleAbilities = { }, "solidrock": { inherit: true, - onFoeBasePower: function (basePower, attacker, defender, move) { - if (defender.runEffectiveness(move) > 0) { + onSourceModifyDamage: function (damage, attacker, defender, move) { + if (move.typeMod > 0) { this.add('-message', "The attack was weakened by Solid Rock!"); - return basePower * 1 / 2; + return this.chainModify(0.5); } } }, "filter": { inherit: true, - onFoeBasePower: function (basePower, attacker, defender, move) { - if (defender.runEffectiveness(move) > 0) { + onSourceModifyDamage: function (damage, attacker, defender, move) { + if (move.typeMod > 0) { this.add('-message', "The attack was weakened by Filter!"); - return basePower * 1 / 2; + return this.chainModify(0.5); } } }, From 81be17cd3c85750c06f74c323a2a9a43433d3841 Mon Sep 17 00:00:00 2001 From: Kevin Lau Date: Sat, 11 Apr 2015 00:43:56 -0700 Subject: [PATCH 4/4] Fix Wonder Guard's interaction with Iron Ball/Thousand Arrows --- data/abilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/abilities.js b/data/abilities.js index 65f063c40e..f8cb5c5af7 100644 --- a/data/abilities.js +++ b/data/abilities.js @@ -3155,7 +3155,7 @@ exports.BattleAbilities = { onTryHit: function (target, source, move) { if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle' || move.isFutureMove) return; this.debug('Wonder Guard immunity: ' + move.id); - if (target.runEffectiveness(move) <= 0) { + if ((target.negateImmunity[move.type] === 'IgnoreEffectiveness' && !this.getImmunity(move.type, target)) || target.runEffectiveness(move) <= 0) { this.add('-activate', target, 'ability: Wonder Guard'); return null; }