mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-06-02 22:08:36 -05:00
Merge 3ab30826e7 into 6765d8422d
This commit is contained in:
commit
b868f65e71
|
|
@ -297,13 +297,6 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
|
|||
onLockMove() {
|
||||
return this.effectState.move;
|
||||
},
|
||||
onBeforeTurn(pokemon) {
|
||||
const move = this.dex.moves.get(this.effectState.move);
|
||||
if (move.id) {
|
||||
this.debug('Forcing into ' + move.id);
|
||||
this.queue.changeAction(pokemon, { choice: 'move', moveid: move.id });
|
||||
}
|
||||
},
|
||||
onAfterMove(pokemon) {
|
||||
if (pokemon.volatiles['lockedmove'].time <= 0) {
|
||||
pokemon.removeVolatile('lockedmove');
|
||||
|
|
|
|||
|
|
@ -193,13 +193,6 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
|
|||
onMoveAborted(pokemon) {
|
||||
delete pokemon.volatiles['lockedmove'];
|
||||
},
|
||||
onBeforeTurn(pokemon) {
|
||||
const move = this.dex.moves.get(this.effectState.move);
|
||||
if (move.id) {
|
||||
this.debug('Forcing into ' + move.id);
|
||||
this.queue.changeAction(pokemon, { choice: 'move', moveid: move.id });
|
||||
}
|
||||
},
|
||||
},
|
||||
futuremove: {
|
||||
inherit: true,
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
}
|
||||
this.effectState.move = lockedMove;
|
||||
this.add('-start', target, 'Encore');
|
||||
if (this.effectState.move === 'pursuit') target.addVolatile('pursuit', target);
|
||||
},
|
||||
onResidualOrder: 13,
|
||||
onResidualSubOrder: undefined, // no inherit
|
||||
|
|
@ -459,44 +460,32 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
},
|
||||
pursuit: {
|
||||
inherit: true,
|
||||
beforeTurnCallback(pokemon, target) {
|
||||
if (pokemon.isAlly(target)) return;
|
||||
target.addVolatile('pursuit');
|
||||
const data = target.volatiles['pursuit'];
|
||||
if (!data.sources) {
|
||||
data.sources = [];
|
||||
}
|
||||
data.sources.push(pokemon);
|
||||
},
|
||||
onModifyMove: undefined, // no inherit
|
||||
condition: {
|
||||
inherit: true,
|
||||
onBeforeSwitchOut(pokemon) {
|
||||
onFoeBeforeSwitchOut(pokemon) {
|
||||
const source: Pokemon = this.effectState.source;
|
||||
this.debug('Pursuit start');
|
||||
let alreadyAdded = false;
|
||||
for (const source of this.effectState.sources) {
|
||||
if (source.speed < pokemon.speed || (source.speed === pokemon.speed && this.randomChance(1, 2))) {
|
||||
// Destiny Bond ends if the switch action "outspeeds" the attacker, regardless of host
|
||||
pokemon.removeVolatile('destinybond');
|
||||
}
|
||||
if (!this.queue.cancelMove(source) || !source.hp) continue;
|
||||
if (!alreadyAdded) {
|
||||
this.add('-activate', pokemon, 'move: Pursuit');
|
||||
alreadyAdded = true;
|
||||
}
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source && action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
if (this.effectState.targetLoc !== source.getLocOf(pokemon) ||
|
||||
!source.isAdjacent(pokemon) || !source.hp ||
|
||||
(source.volatiles['encore'] && source.volatiles['encore'].move !== 'pursuit') ||
|
||||
!this.queue.cancelMove(source)) return;
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source && action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.actions.runMove('pursuit', source, source.getLocOf(pokemon));
|
||||
}
|
||||
if (source.speed < pokemon.speed || (source.speed === pokemon.speed && this.randomChance(1, 2))) {
|
||||
// Destiny Bond ends if the switch action "outspeeds" the attacker, regardless of host
|
||||
pokemon.removeVolatile('destinybond');
|
||||
}
|
||||
this.actions.runMove('pursuit', source, source.getLocOf(pokemon));
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -199,6 +199,12 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
|
|||
onStart: undefined, // no inherit
|
||||
onSwitchIn(pokemon) {
|
||||
pokemon.truantTurn = this.turn !== 0;
|
||||
// it is unnecessary to keep a volatile, but it helps with cross-gen implementation
|
||||
if (pokemon.truantTurn) {
|
||||
pokemon.addVolatile('truant');
|
||||
} else {
|
||||
pokemon.removeVolatile('truant');
|
||||
}
|
||||
},
|
||||
onBeforeMove(pokemon) {
|
||||
if (pokemon.truantTurn) {
|
||||
|
|
@ -209,6 +215,11 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
|
|||
onResidualOrder: 27,
|
||||
onResidual(pokemon) {
|
||||
pokemon.truantTurn = !pokemon.truantTurn;
|
||||
if (pokemon.truantTurn) {
|
||||
pokemon.addVolatile('truant');
|
||||
} else {
|
||||
pokemon.removeVolatile('truant');
|
||||
}
|
||||
},
|
||||
},
|
||||
voltabsorb: {
|
||||
|
|
|
|||
|
|
@ -478,15 +478,37 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
pursuit: {
|
||||
inherit: true,
|
||||
beforeTurnCallback(pokemon, target) {
|
||||
if (['frz', 'slp'].includes(pokemon.status) ||
|
||||
(pokemon.hasAbility('truant') && pokemon.truantTurn)) return;
|
||||
if (pokemon.isAlly(target)) return;
|
||||
target.addVolatile('pursuit');
|
||||
const data = target.volatiles['pursuit'];
|
||||
if (!data.sources) {
|
||||
data.sources = [];
|
||||
}
|
||||
data.sources.push(pokemon);
|
||||
pokemon.addVolatile('pursuit');
|
||||
pokemon.volatiles['pursuit'].targetLoc = pokemon.getLocOf(target);
|
||||
},
|
||||
condition: {
|
||||
inherit: true,
|
||||
onFoeBeforeSwitchOut(pokemon) {
|
||||
const source: Pokemon = this.effectState.source;
|
||||
this.debug('Pursuit start');
|
||||
if (this.effectState.targetLoc !== source.getLocOf(pokemon) ||
|
||||
['frz', 'slp'].includes(source.status) || (source.hasAbility('truant') && source.volatiles['truant']) ||
|
||||
!source.isAdjacent(pokemon) || !source.hp ||
|
||||
(source.volatiles['encore'] && source.volatiles['encore'].move !== 'pursuit') ||
|
||||
!this.queue.cancelMove(source)) return;
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source && action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const move = this.dex.getActiveMove('pursuit');
|
||||
source.deductPP(move.id);
|
||||
source.moveUsed(move, pokemon.position);
|
||||
if (this.actions.useMove(move, source, { target: pokemon }) && source.getItem().isChoice) {
|
||||
source.addVolatile('choicelock');
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
recover: {
|
||||
|
|
|
|||
|
|
@ -402,6 +402,16 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
durationCallback() {
|
||||
return this.random(4, 9);
|
||||
},
|
||||
onStart(target, source) {
|
||||
const moveSlot = target.lastMove ? target.getMoveData(target.lastMove.id) : null;
|
||||
if (!target.lastMove || target.lastMove.flags['failencore'] || !moveSlot || moveSlot.pp <= 0) {
|
||||
// it failed
|
||||
return false;
|
||||
}
|
||||
this.effectState.move = target.lastMove.id;
|
||||
this.add('-start', target, 'Encore');
|
||||
if (this.effectState.move === 'pursuit') target.addVolatile('pursuit', target);
|
||||
},
|
||||
onResidualOrder: 10,
|
||||
onResidualSubOrder: 14,
|
||||
},
|
||||
|
|
@ -1036,46 +1046,31 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
},
|
||||
pursuit: {
|
||||
inherit: true,
|
||||
beforeTurnCallback(pokemon) {
|
||||
if (['frz', 'slp'].includes(pokemon.status) ||
|
||||
(pokemon.hasAbility('truant') && pokemon.volatiles['truant'])) return;
|
||||
for (const target of pokemon.foes()) {
|
||||
target.addVolatile('pursuit');
|
||||
const data = target.volatiles['pursuit'];
|
||||
if (!data.sources) {
|
||||
data.sources = [];
|
||||
}
|
||||
data.sources.push(pokemon);
|
||||
}
|
||||
},
|
||||
condition: {
|
||||
inherit: true,
|
||||
onBeforeSwitchOut(pokemon) {
|
||||
onFoeBeforeSwitchOut(pokemon) {
|
||||
const source: Pokemon = this.effectState.source;
|
||||
this.debug('Pursuit start');
|
||||
let alreadyAdded = false;
|
||||
for (const source of this.effectState.sources) {
|
||||
if (!this.queue.cancelMove(source) || !source.hp) continue;
|
||||
if (!alreadyAdded) {
|
||||
this.add('-activate', pokemon, 'move: Pursuit');
|
||||
alreadyAdded = true;
|
||||
}
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source && action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
if (['frz', 'slp'].includes(source.status) || (source.hasAbility('truant') && source.volatiles['truant']) ||
|
||||
!source.isAdjacent(pokemon) || !source.hp ||
|
||||
(source.volatiles['encore'] && source.volatiles['encore'].move !== 'pursuit') ||
|
||||
!this.queue.cancelMove(source)) return;
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source && action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const move = this.dex.getActiveMove('pursuit');
|
||||
source.deductPP(move.id);
|
||||
source.moveUsed(move, pokemon.position);
|
||||
if (this.actions.useMove(move, source, { target: pokemon }) && source.getItem().isChoice) {
|
||||
source.addVolatile('choicelock');
|
||||
}
|
||||
}
|
||||
const move = this.dex.getActiveMove('pursuit');
|
||||
source.deductPP(move.id);
|
||||
source.moveUsed(move, pokemon.position);
|
||||
if (this.actions.useMove(move, source, { target: pokemon }) && source.getItem().isChoice) {
|
||||
source.addVolatile('choicelock');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14390,51 +14390,43 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
|
|||
pp: 20,
|
||||
priority: 0,
|
||||
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
|
||||
beforeTurnCallback(pokemon) {
|
||||
for (const target of pokemon.foes()) {
|
||||
target.addVolatile('pursuit');
|
||||
const data = target.volatiles['pursuit'];
|
||||
if (!data.sources) {
|
||||
data.sources = [];
|
||||
}
|
||||
data.sources.push(pokemon);
|
||||
}
|
||||
beforeTurnCallback(pokemon, target) {
|
||||
pokemon.addVolatile('pursuit');
|
||||
},
|
||||
onModifyMove(move, source, target) {
|
||||
if (target?.beingCalledBack || target?.switchFlag) move.accuracy = true;
|
||||
if (target?.beingCalledBack || target?.switchFlag) {
|
||||
move.accuracy = true;
|
||||
move.tracksTarget = true;
|
||||
}
|
||||
},
|
||||
condition: {
|
||||
duration: 1,
|
||||
onBeforeSwitchOut(pokemon) {
|
||||
onFoeBeforeSwitchOut(pokemon) {
|
||||
const source: Pokemon = this.effectState.source;
|
||||
this.debug('Pursuit start');
|
||||
let alreadyAdded = false;
|
||||
pokemon.removeVolatile('destinybond');
|
||||
for (const source of this.effectState.sources) {
|
||||
if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp) continue;
|
||||
if (!alreadyAdded) {
|
||||
this.add('-activate', pokemon, 'move: Pursuit');
|
||||
alreadyAdded = true;
|
||||
}
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst || source.canTerastallize) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source) {
|
||||
if (action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
} else if (action.choice === 'terastallize') {
|
||||
// Also a "forme" change that happens before moves, though only possible in NatDex
|
||||
this.actions.terastallize(source);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
if (!source.isAdjacent(pokemon) || !source.hp ||
|
||||
(source.volatiles['encore'] && source.volatiles['encore'].move !== 'pursuit') ||
|
||||
!this.queue.cancelMove(source)) return;
|
||||
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
|
||||
// If it is, then Mega Evolve before moving.
|
||||
if (source.canMegaEvo || source.canUltraBurst || source.canTerastallize) {
|
||||
for (const [actionIndex, action] of this.queue.entries()) {
|
||||
if (action.pokemon === source) {
|
||||
if (action.choice === 'megaEvo') {
|
||||
this.actions.runMegaEvo(source);
|
||||
} else if (action.choice === 'terastallize') {
|
||||
// Also a "forme" change that happens before moves, though only possible in NatDex
|
||||
this.actions.terastallize(source);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
this.queue.list.splice(actionIndex, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.actions.runMove('pursuit', source, source.getLocOf(pokemon));
|
||||
}
|
||||
pokemon.removeVolatile('destinybond');
|
||||
this.actions.runMove('pursuit', source, source.getLocOf(pokemon), { sourceEffect: this.effect });
|
||||
},
|
||||
},
|
||||
target: "normal",
|
||||
|
|
|
|||
|
|
@ -832,7 +832,7 @@ export class Pokemon {
|
|||
!(move.id.startsWith('solarb') && ['sunnyday', 'desolateland'].includes(this.effectiveWeather(move))) &&
|
||||
!(move.id === 'electroshot' && ['raindance', 'primordialsea'].includes(this.effectiveWeather(move))) &&
|
||||
!(this.hasItem('powerherb') && move.id !== 'skydrop');
|
||||
if (!isCharging && !(move.id === 'pursuit' && (target.beingCalledBack || target.switchFlag))) {
|
||||
if (!isCharging) {
|
||||
target = this.battle.priorityEvent('RedirectTarget', this, this, move, target);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,6 +190,44 @@ describe(`Pursuit`, () => {
|
|||
assert.fullHP(jolteon);
|
||||
});
|
||||
|
||||
it(`should not activate if Encored into Pursuit`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: "Empoleon", moves: ['tackle', 'pursuit'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: "Deoxys", moves: ['sleeptalk', 'encore'] },
|
||||
{ species: "Infernape", moves: ['sleeptalk', 'uturn'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
]]);
|
||||
const [deoxys, infernape] = battle.p2.active;
|
||||
battle.makeChoices('move pursuit 1, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
deoxys.hp = deoxys.maxhp;
|
||||
battle.makeChoices('move tackle 1, move sleeptalk', 'move encore 1, move uturn 2');
|
||||
assert.fullHP(deoxys);
|
||||
assert.fullHP(infernape);
|
||||
battle.makeChoices('', 'switch 3');
|
||||
assert.false.fullHP(deoxys);
|
||||
});
|
||||
|
||||
it(`should not activate other move if Encored out of Pursuit`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: "Empoleon", moves: ['tackle', 'pursuit'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: "Deoxys", moves: ['sleeptalk', 'encore'] },
|
||||
{ species: "Infernape", moves: ['sleeptalk', 'uturn'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
]]);
|
||||
const [deoxys, infernape] = battle.p2.active;
|
||||
battle.makeChoices('move tackle 1, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
deoxys.hp = deoxys.maxhp;
|
||||
battle.makeChoices('move pursuit 1, move sleeptalk', 'move encore 1, move uturn 2');
|
||||
assert.fullHP(deoxys);
|
||||
assert.fullHP(infernape);
|
||||
battle.makeChoices('', 'switch 3');
|
||||
assert.false.fullHP(deoxys);
|
||||
});
|
||||
|
||||
describe(`[Gen 4]`, () => {
|
||||
it(`should continue the switch`, () => {
|
||||
battle = common.gen(4).createBattle([[
|
||||
|
|
@ -234,6 +272,44 @@ describe(`Pursuit`, () => {
|
|||
battle.makeChoices('move pursuit', 'switch 2');
|
||||
assert.false.fullHP(jolteon);
|
||||
});
|
||||
|
||||
it(`should activate if Encored into Pursuit`, () => {
|
||||
battle = common.gen(4).createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: "Empoleon", moves: ['tackle', 'pursuit'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: "Deoxys", moves: ['sleeptalk', 'encore'] },
|
||||
{ species: "Infernape", moves: ['sleeptalk', 'uturn'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
]]);
|
||||
const [deoxys, infernape] = battle.p2.active;
|
||||
battle.makeChoices('move pursuit 1, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
deoxys.hp = deoxys.maxhp;
|
||||
battle.makeChoices('move tackle 1, move sleeptalk', 'move encore 1, move uturn 2');
|
||||
assert.fullHP(deoxys);
|
||||
assert.false.fullHP(infernape);
|
||||
battle.makeChoices('', 'switch 3');
|
||||
assert.fullHP(deoxys);
|
||||
});
|
||||
|
||||
it(`should not activate other move if Encored out of Pursuit`, () => {
|
||||
battle = common.gen(4).createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: "Empoleon", moves: ['tackle', 'pursuit'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: "Deoxys", moves: ['sleeptalk', 'encore'] },
|
||||
{ species: "Infernape", moves: ['sleeptalk', 'uturn'] },
|
||||
{ species: "Carnivine", moves: ['sleeptalk'] },
|
||||
]]);
|
||||
const [deoxys, infernape] = battle.p2.active;
|
||||
battle.makeChoices('move tackle 1, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
deoxys.hp = deoxys.maxhp;
|
||||
battle.makeChoices('move pursuit 1, move sleeptalk', 'move encore 1, move uturn 2');
|
||||
assert.fullHP(deoxys);
|
||||
assert.fullHP(infernape);
|
||||
battle.makeChoices('', 'switch 3');
|
||||
assert.false.fullHP(deoxys);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`[Gen 3]`, () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user