Skip to content

Commit

Permalink
Fix GSC Toxic interaction with Baton Pass/Heal Bell (#5226)
Browse files Browse the repository at this point in the history
  • Loading branch information
scheibo authored and Marty-D committed Mar 5, 2019
1 parent 69f9c29 commit 6f82443
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 40 deletions.
33 changes: 3 additions & 30 deletions data/mods/gen1/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ let BattleStatuses = {
},
onAfterMoveSelfPriority: 2,
onAfterMoveSelf(pokemon) {
let toxicCounter = 1;
if (pokemon.volatiles['residualdmg']) {
pokemon.volatiles['residualdmg'].counter++;
toxicCounter = pokemon.volatiles['residualdmg'].counter;
}
let toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1;
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * toxicCounter, pokemon);
},
onSwitchIn(pokemon) {
Expand Down Expand Up @@ -118,39 +114,16 @@ let BattleStatuses = {
},
onAfterMoveSelfPriority: 2,
onAfterMoveSelf(pokemon) {
let toxicCounter = 1;
if (pokemon.volatiles['residualdmg']) {
pokemon.volatiles['residualdmg'].counter++;
toxicCounter = pokemon.volatiles['residualdmg'].counter;
}
let toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1;
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * toxicCounter, pokemon);
},
onAfterSwitchInSelf(pokemon) {
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1));
},
},
tox: {
name: 'tox',
id: 'tox',
num: 0,
effectType: 'Status',
onStart(target) {
this.add('-status', target, 'tox');
if (!target.volatiles['residualdmg']) target.addVolatile('residualdmg');
target.volatiles['residualdmg'].counter = 0;
},
inherit: true,
onAfterMoveSelfPriority: 2,
onAfterMoveSelf(pokemon) {
pokemon.volatiles['residualdmg'].counter++;
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * pokemon.volatiles['residualdmg'].counter, pokemon, pokemon);
},
onSwitchIn(pokemon) {
// Regular poison status and damage after a switchout -> switchin.
pokemon.setStatus('psn');
},
onAfterSwitchInSelf(pokemon) {
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1));
},
},
confusion: {
name: 'confusion',
Expand Down
45 changes: 35 additions & 10 deletions data/mods/gen2/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ let BattleStatuses = {
},
onAfterMoveSelfPriority: 3,
onAfterMoveSelf(pokemon) {
this.damage(pokemon.maxhp / 8);
residualdmg(this, pokemon);
},
onAfterSwitchInSelf(pokemon) {
this.damage(pokemon.maxhp / 8);
residualdmg(this, pokemon);
},
},
par: {
Expand Down Expand Up @@ -89,10 +89,10 @@ let BattleStatuses = {
},
onAfterMoveSelfPriority: 3,
onAfterMoveSelf(pokemon) {
this.damage(pokemon.maxhp / 8);
residualdmg(this, pokemon);
},
onAfterSwitchInSelf(pokemon) {
this.damage(pokemon.maxhp / 8);
residualdmg(this, pokemon);
},
},
tox: {
Expand All @@ -102,18 +102,15 @@ let BattleStatuses = {
effectType: 'Status',
onStart(target) {
this.add('-status', target, 'tox');
this.effectData.stage = 0;
if (!target.volatiles['residualdmg']) target.addVolatile('residualdmg');
target.volatiles['residualdmg'].counter = 0;
},
onAfterMoveSelfPriority: 3,
onAfterMoveSelf(pokemon) {
if (this.effectData.stage < 15) {
this.effectData.stage++;
}
this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
this.damage(this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * pokemon.volatiles['residualdmg'].counter, pokemon, pokemon);
},
onSwitchIn(pokemon) {
// Regular poison status and damage after a switchout -> switchin.
this.effectData.stage = 0;
pokemon.setStatus('psn');
},
onAfterSwitchInSelf(pokemon) {
Expand Down Expand Up @@ -226,6 +223,34 @@ let BattleStatuses = {
this.effectData.duration = 2;
},
},
residualdmg: {
name: 'residualdmg',
id: 'residualdmg',
num: 0,
onStart(target) {
target.volatiles['residualdmg'].counter = 0;
},
onAfterMoveSelfPriority: 100,
onAfterMoveSelf(pokemon) {
if (['brn', 'psn', 'tox'].includes(pokemon.status)) pokemon.volatiles['residualdmg'].counter++;
},
onAfterSwitchInSelf(pokemon) {
if (['brn', 'psn', 'tox'].includes(pokemon.status)) pokemon.volatiles['residualdmg'].counter++;
},
},
};

/**
* @param {Battle} battle
* @param {Pokemon} pokemon
*/
function residualdmg(battle, pokemon) {
if (pokemon.volatiles['residualdmg']) {
battle.damage(battle.clampIntRange(Math.floor(pokemon.maxhp / 16) * pokemon.volatiles['residualdmg'].counter, 1), pokemon);
battle.hint('In GSC, Toxic\'s counter is retained through Baton Pass/Heal Bell and applies to PSN/BRN.', true);
} else {
battle.damage(battle.clampIntRange(Math.floor(pokemon.maxhp / 8), 1), pokemon);
}
}

exports.BattleStatuses = BattleStatuses;
20 changes: 20 additions & 0 deletions sim/battle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class Battle extends Dex.ModdedDex {
firstStaleWarned?: boolean;
staleWarned?: boolean;
activeTurns?: number;
hints: Set<string>;

constructor(options: BattleOptions) {
let format = Dex.getFormat(options.formatid, true);
Expand Down Expand Up @@ -157,6 +158,7 @@ export class Battle extends Dex.ModdedDex {
// bound function for faster speedSort
// (so speedSort doesn't need to bind before use)
this.comparePriority = this.comparePriority.bind(this);
this.hints = new Set();

const inputOptions: {formatid: string, seed: PRNGSeed, rated?: string | true} = {formatid: options.formatid, seed: this.prng.seed};
if (this.rated) inputOptions.rated = this.rated;
Expand Down Expand Up @@ -2987,6 +2989,24 @@ export class Battle extends Dex.ModdedDex {
return false;
}

hint(hint: string, once?: boolean, sideid?: 'p1' | 'p2') {
if (this.hints.has(hint)) return;

if (sideid) {
for (const line of [false, this.sides[0], this.sides[1], true]) {
if (line === true || (line && line.id === sideid)) {
this.add('-hint', hint);
} else {
this.log.push('');
}
}
} else {
this.add('-hint', hint);
}

if (once) this.hints.add(hint);
}

add(...parts: (string | number | boolean | ((side: Side | boolean) => string) | AnyObject | null | undefined)[]) {
if (!parts.some(part => typeof part === 'function')) {
this.log.push(`|${parts.join('|')}`);
Expand Down
73 changes: 73 additions & 0 deletions test/simulator/misc/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,76 @@ describe('Toxic Poison [Gen 1]', function () {
assert.strictEqual(pokemon.maxhp - pokemon.hp, Math.floor(pokemon.maxhp / 16) * 6);
});
});


describe('Toxic Poison [Gen 2]', function () {
afterEach(function () {
battle.destroy();
});

it('should not affect Leech Seed damage counter', function () {
battle = common.gen(2).createBattle([
[{species: 'Venusaur', moves: ['toxic', 'leechseed']}],
[{species: 'Chansey', moves: ['splash']}],
]);
battle.makeChoices('move toxic', 'move splash');
let pokemon = battle.p2.active[0];
assert.strictEqual(pokemon.maxhp - pokemon.hp, Math.floor(pokemon.maxhp / 16));
battle.makeChoices('move leechseed', 'move splash');
// (1/16) + (2/16) + (1/8) = (5/16)
assert.strictEqual(pokemon.maxhp - pokemon.hp, Math.floor(pokemon.maxhp / 16) * 5);
});

it('should pass the damage counter to Pokemon with Baton Pass', function () {
battle = common.gen(2).createBattle([
[{species: 'Smeargle', moves: ['toxic', 'sacredfire', 'splash']}],
[
{species: 'Chansey', moves: ['splash']},
{species: 'Celebi', moves: ['batonpass', 'splash']},
],
]);
battle.resetRNG(); // Guarantee Sacred Fire burns
battle.makeChoices('move sacredfire', 'move splash');
let pokemon = battle.p2.active[0];
battle.resetRNG(); // Guarantee Toxic hits.
battle.makeChoices('move toxic', 'switch 2');
battle.makeChoices('move splash', 'move splash');
battle.makeChoices('move splash', 'move splash');
battle.makeChoices('move splash', 'move batonpass');
battle.makeChoices('pass', 'switch 2');
let hp = pokemon.hp;
battle.makeChoices('move splash', 'move splash');
assert.strictEqual(hp - pokemon.hp, Math.floor(pokemon.maxhp / 16) * 4);

// Only hint about this once per battle, not every turn.
assert.strictEqual(battle.log.filter(m => m.startsWith('|-hint')).length, 1);

// Damage counter should be removed on regular switch out
battle.makeChoices('move splash', 'switch 2');
hp = pokemon.hp;
battle.makeChoices('move splash', 'switch 2');
assert.strictEqual(hp - pokemon.hp, Math.floor(pokemon.maxhp / 8));
});

it('should not have its damage counter affected by Heal Bell', function () {
battle = common.gen(2).createBattle([
[{species: 'Smeargle', moves: ['toxic', 'sacredfire', 'splash']}],
[{species: 'Chansey', moves: ['splash', 'healbell']}],
]);
battle.makeChoices('move toxic', 'move splash');
let pokemon = battle.p2.active[0];
battle.makeChoices('move splash', 'move healbell');
battle.resetRNG(); // Guarantee Sacred Fire burns
battle.makeChoices('move sacredfire', 'move splash');
let hp = pokemon.hp;
battle.makeChoices('move splash', 'move splash');
assert.strictEqual(hp - pokemon.hp, Math.floor(pokemon.maxhp / 16) * 3);
hp = pokemon.hp;

battle.makeChoices('move splash', 'move healbell');
battle.resetRNG(); // Guarantee Toxic hits
battle.makeChoices('move toxic', 'move splash');
// Toxic counter should be reset by a successful Toxic
assert.strictEqual(hp - pokemon.hp, Math.floor(pokemon.maxhp / 16));
});
});

0 comments on commit 6f82443

Please sign in to comment.