Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Gen 4 interaction between U-turn/Substitute/Intimidate #5222

Merged
merged 9 commits into from
Mar 6, 2019
32 changes: 32 additions & 0 deletions data/mods/gen4/abilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,38 @@ let BattleAbilities = {
inherit: true,
rating: 2.5,
},
"intimidate": {
inherit: true,
onStart(pokemon) {
let activated = false;
for (const target of pokemon.side.foe.active) {
if (target && this.isAdjacent(target, pokemon) &&
!(target.volatiles['substitute'] ||
target.volatiles['substitutebroken'] && target.volatiles['substitutebroken'].move === 'uturn')) {
activated = true;
break;
}
}

if (!activated) {
this.hint('In Gen 4, Intimidate does not activate if every target has a Substitute (or the Substitute was just broken by U-turn).', false, pokemon.side.id);
scheibo marked this conversation as resolved.
Show resolved Hide resolved
return;
}
this.add('-ability', pokemon, 'Intimidate', 'boost');

for (const target of pokemon.side.foe.active) {
if (!target || !this.isAdjacent(target, pokemon)) continue;

if (target.volatiles['substitute']) {
this.add('-immune', target);
} else if (target.volatiles['substitutebroken'] && target.volatiles['substitutebroken'].move === 'uturn') {
this.hint('In Gen 4, if U-turn breaks Substitute the incoming Intimidate does nothing.');
} else {
this.boost({atk: -1}, target, pokemon);
}
}
},
},
"leafguard": {
inherit: true,
desc: "If Sunny Day is active, this Pokemon cannot gain a major status condition, but can use Rest normally.",
Expand Down
49 changes: 49 additions & 0 deletions data/mods/gen4/moves.js
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,55 @@ let BattleMovedex = {
inherit: true,
desc: "If the target lost HP, the user takes recoil damage equal to 1/4 the HP lost by the target, rounded down, but not less than 1 HP.",
},
substitute: {
inherit: true,
effect: {
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectData.hp = Math.floor(target.maxhp / 4);
delete target.volatiles['partiallytrapped'];
},
onTryPrimaryHitPriority: -1,
onTryPrimaryHit(target, source, move) {
if (target === source || move.flags['authentic']) {
return;
}
let damage = this.getDamage(source, target, move);
if (!damage && damage !== 0) {
this.add('-fail', source);
this.attrLastMove('[still]');
return null;
}
damage = this.runEvent('SubDamage', target, source, move, damage);
if (!damage) {
return damage;
}
if (damage > target.volatiles['substitute'].hp) {
damage = /** @type {number} */ (target.volatiles['substitute'].hp);
}
target.volatiles['substitute'].hp -= damage;
source.lastDamage = damage;
if (target.volatiles['substitute'].hp <= 0) {
target.removeVolatile('substitute');
target.addVolatile('substitutebroken');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three lines are the only ones that changed - shame to have to copy the rest of effect from Gen 5.

if (target.volatiles['substitutebroken']) target.volatiles['substitutebroken'].move = move.id;
} else {
this.add('-activate', target, 'Substitute', '[damage]');
}
if (move.recoil && damage) {
this.damage(this.calcRecoilDamage(damage, move), source, target, 'recoil');
}
if (move.drain) {
this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain');
}
this.runEvent('AfterSubDamage', target, source, move, damage);
return 0; // hit
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
},
suckerpunch: {
inherit: true,
desc: "Fails if the target did not select a physical or special attack for use this turn, or if the target moves before the user.",
Expand Down
3 changes: 3 additions & 0 deletions data/mods/gen4/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ let BattleStatuses = {
return false;
},
},
substitutebroken: {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.... Not super keen on having added a new volatile....

noCopy: true,
},
trapped: {
inherit: true,
noCopy: false,
Expand Down
5 changes: 5 additions & 0 deletions sim/battle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2755,6 +2755,11 @@ export class Battle extends Dex.ModdedDex {
action.pokemon.abilityOrder = this.abilityOrder++;
this.singleEvent('Start', action.pokemon.getItem(), action.pokemon.itemData, action.pokemon);
}
if (this.gen === 4) {
for (const foeActive of action.pokemon.side.foe.active) {
foeActive.removeVolatile('substitutebroken');
}
}
delete action.pokemon.draggedIn;
break;
case 'runPrimal':
Expand Down
25 changes: 25 additions & 0 deletions test/simulator/abilities/intimidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ describe('Intimidate', function () {
assert.statStage(battle.p1.active[0], 'atk', 0);
});

it('should not activate if U-turn breaks the Substitute in Gen 4', function () {
battle = common.gen(4).createBattle({gameType: 'doubles'});
battle.join('p1', 'Guest 1', 1, [
{species: "Gengar", level: 1, item: 'leftovers', ability: 'levitate', moves: ['substitute']},
{species: "Suicune", level: 1, item: 'leftovers', ability: 'pressure', moves: ['substitute']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Gliscor", item: 'laggingtail', ability: 'sandveil', moves: ['uturn']},
{species: "Scizor", item: 'laggingtail', ability: 'technician', moves: ['batonpass']},
{species: "Gyarados", item: 'leftovers', ability: 'intimidate', moves: ['splash']},
{species: "Salamence", item: 'leftovers', ability: 'intimidate', moves: ['splash']},
]);
battle.makeChoices('move substitute, move substitute', 'move uturn, move batonpass');
battle.makeChoices('pass, pass', 'switch 3, pass');

const activate = '|-ability|p2a: Gyarados|Intimidate|boost';
assert.strictEqual(battle.log.filter(m => m === activate).length, 0);
assert.statStage(battle.p1.active[0], 'atk', 0);
assert.statStage(battle.p1.active[1], 'atk', 0);

battle.makeChoices('pass, pass', 'pass, switch 4');
assert.statStage(battle.p1.active[0], 'atk', -1);
assert.statStage(battle.p1.active[1], 'atk', 0);
});

it('should affect adjacent foes only', function () {
battle = common.createBattle({gameType: 'triples'});
const p1 = battle.join('p1', 'Guest 1', 1, [
Expand Down