mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Linked: Fix bugs with Encore, Stalwart, Propeller Tail, and Snipe Shot
This commit is contained in:
parent
bf612bf9ee
commit
6545c74486
|
|
@ -279,8 +279,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
// @ts-expect-error modded
|
||||
const linkedMoves: [ActiveMove, ActiveMove] = target.getLinkedMoves(true);
|
||||
const moveSlot = target.getMoveData(move.id);
|
||||
const hasLinkedMove = linkedMoves.some(x => x.id === move.id);
|
||||
if (hasLinkedMove && linkedMoves.every(m => !!m.flags['failencore'])) {
|
||||
const isLinkedMove = linkedMoves.some(x => x.id === move.id);
|
||||
if (isLinkedMove && linkedMoves.every(m => !!m.flags['failencore'])) {
|
||||
// both moves cannot be encored
|
||||
delete target.volatiles['encore'];
|
||||
return false;
|
||||
|
|
@ -292,7 +292,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
this.effectState.timesActivated = {};
|
||||
this.effectState.move = move.id;
|
||||
this.add('-start', target, 'Encore');
|
||||
if (hasLinkedMove) {
|
||||
if (isLinkedMove) {
|
||||
this.effectState.move = linkedMoves;
|
||||
}
|
||||
if (!this.queue.willMove(target)) {
|
||||
|
|
@ -313,7 +313,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
this.queue.cancelAction(pokemon);
|
||||
if (move.id !== this.effectState.move) return this.effectState.move;
|
||||
} else {
|
||||
// Locked into a link
|
||||
// Locked into a link
|
||||
switch (this.effectState.timesActivated[this.turn]) {
|
||||
case 1: {
|
||||
if (this.effectState.move[0] !== move.id) return this.effectState.move[0];
|
||||
|
|
@ -327,22 +327,13 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
},
|
||||
onResidualOrder: 13,
|
||||
onResidual(target) {
|
||||
// early termination if you run out of PP
|
||||
const lastMove = target.m.lastMoveAbsolute;
|
||||
const moveSlot = target.getMoveData(lastMove);
|
||||
if (!moveSlot) {
|
||||
target.removeVolatile('encore');
|
||||
return; // no last move
|
||||
}
|
||||
|
||||
// @ts-expect-error modded
|
||||
if (target.hasLinkedMove(lastMove)) {
|
||||
// TODO: Check instead whether the last executed move was linked
|
||||
if (target.moveSlots[0].pp <= 0 || target.moveSlots[1].pp <= 0) {
|
||||
if (Array.isArray(this.effectState.move)) {
|
||||
if (this.effectState.move.map(move => target.getMoveData(move)).some(moveSlot => !moveSlot || moveSlot.pp <= 0)) {
|
||||
target.removeVolatile('encore');
|
||||
}
|
||||
} else {
|
||||
if (moveSlot.pp <= 0) {
|
||||
const moveSlot = target.getMoveData(this.effectState.move);
|
||||
if (!moveSlot || moveSlot.pp <= 0) {
|
||||
target.removeVolatile('encore');
|
||||
}
|
||||
}
|
||||
|
|
@ -351,19 +342,20 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
this.add('-end', target, 'Encore');
|
||||
},
|
||||
onDisableMove(pokemon) {
|
||||
if (!this.effectState.move) return;
|
||||
if (Array.isArray(this.effectState.move)) {
|
||||
if (this.effectState.move.every(move => !pokemon.hasMove(move))) return;
|
||||
for (const moveSlot of pokemon.moveSlots) {
|
||||
if (moveSlot.id !== this.effectState.move[0] && moveSlot.id !== this.effectState.move[1]) {
|
||||
if (!this.effectState.move.map(move => move.id).includes(moveSlot.id)) {
|
||||
pokemon.disableMove(moveSlot.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) {
|
||||
return;
|
||||
}
|
||||
for (const moveSlot of pokemon.moveSlots) {
|
||||
if (moveSlot.id !== this.effectState.move) {
|
||||
pokemon.disableMove(moveSlot.id);
|
||||
} else {
|
||||
if (!pokemon.hasMove(this.effectState.move)) return;
|
||||
for (const moveSlot of pokemon.moveSlots) {
|
||||
if (moveSlot.id !== this.effectState.move) {
|
||||
pokemon.disableMove(moveSlot.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -300,6 +300,51 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
|
||||
return false;
|
||||
},
|
||||
getTarget(pokemon, move, targetLoc, originalTarget) {
|
||||
move = this.dex.moves.get(move);
|
||||
|
||||
// Delete tracksTarget stuff because it's useless in Linked anyway
|
||||
|
||||
// banning Dragon Darts from directly targeting itself is done in side.ts, but
|
||||
// Dragon Darts can target itself if Ally Switch is used afterwards
|
||||
if (move.smartTarget) {
|
||||
const curTarget = pokemon.getAtLoc(targetLoc);
|
||||
return curTarget && !curTarget.fainted ? curTarget : this.getRandomTarget(pokemon, move);
|
||||
}
|
||||
|
||||
// Fails if the target is the user and the move can't target its own position
|
||||
const selfLoc = pokemon.getLocOf(pokemon);
|
||||
if (
|
||||
['adjacentAlly', 'any', 'normal'].includes(move.target) && targetLoc === selfLoc &&
|
||||
!pokemon.volatiles['twoturnmove'] && !pokemon.volatiles['iceball'] && !pokemon.volatiles['rollout']
|
||||
) {
|
||||
return move.flags['futuremove'] ? pokemon : null;
|
||||
}
|
||||
if (move.target !== 'randomNormal' && this.validTargetLoc(targetLoc, pokemon, move.target)) {
|
||||
const target = pokemon.getAtLoc(targetLoc);
|
||||
if (target?.fainted) {
|
||||
if (this.gameType === 'freeforall') {
|
||||
// Target is a fainted opponent in a free-for-all battle; attack shouldn't retarget
|
||||
return target;
|
||||
}
|
||||
if (target.isAlly(pokemon)) {
|
||||
if (move.target === 'adjacentAllyOrSelf' && this.gen !== 5) {
|
||||
return pokemon;
|
||||
}
|
||||
// Target is a fainted ally: attack shouldn't retarget
|
||||
return target;
|
||||
}
|
||||
}
|
||||
if (target && !target.fainted) {
|
||||
// Target is unfainted: use selected target location
|
||||
return target;
|
||||
}
|
||||
|
||||
// Chosen target not valid,
|
||||
// retarget randomly with getRandomTarget
|
||||
}
|
||||
return this.getRandomTarget(pokemon, move);
|
||||
},
|
||||
actions: {
|
||||
runMove(moveOrMoveName, pokemon, targetLoc, options) {
|
||||
pokemon.activeMoveActions++;
|
||||
|
|
@ -546,6 +591,14 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
targetLoc: action.targetLoc,
|
||||
});
|
||||
}
|
||||
if (linkedOtherMove.priorityChargeCallback) {
|
||||
this.addChoice({
|
||||
choice: 'priorityChargeMove',
|
||||
pokemon: action.pokemon,
|
||||
move: linkedOtherMove,
|
||||
targetLoc: action.targetLoc,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (['switch', 'instaswitch'].includes(action.choice)) {
|
||||
|
|
@ -573,6 +626,67 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
},
|
||||
},
|
||||
pokemon: {
|
||||
clearVolatile(includeSwitchFlags = true) {
|
||||
this.boosts = {
|
||||
atk: 0,
|
||||
def: 0,
|
||||
spa: 0,
|
||||
spd: 0,
|
||||
spe: 0,
|
||||
accuracy: 0,
|
||||
evasion: 0,
|
||||
};
|
||||
|
||||
if (this.battle.gen === 1 && this.baseMoves.includes('mimic' as ID) && !this.transformed) {
|
||||
const moveslot = this.baseMoves.indexOf('mimic' as ID);
|
||||
const mimicPP = this.moveSlots[moveslot] ? this.moveSlots[moveslot].pp : 16;
|
||||
this.moveSlots = this.baseMoveSlots.slice();
|
||||
this.moveSlots[moveslot].pp = mimicPP;
|
||||
} else {
|
||||
this.moveSlots = this.baseMoveSlots.slice();
|
||||
}
|
||||
|
||||
this.transformed = false;
|
||||
this.ability = this.baseAbility;
|
||||
this.hpType = this.baseHpType;
|
||||
this.hpPower = this.baseHpPower;
|
||||
if (this.canTerastallize === false) this.canTerastallize = this.teraType;
|
||||
for (const i in this.volatiles) {
|
||||
if (this.volatiles[i].linkedStatus) {
|
||||
this.removeLinkedVolatiles(this.volatiles[i].linkedStatus, this.volatiles[i].linkedPokemon);
|
||||
}
|
||||
}
|
||||
if (this.species.name === 'Eternatus-Eternamax' && this.volatiles['dynamax']) {
|
||||
this.volatiles = { dynamax: this.volatiles['dynamax'] };
|
||||
} else {
|
||||
this.volatiles = {};
|
||||
}
|
||||
if (includeSwitchFlags) {
|
||||
this.switchFlag = false;
|
||||
this.forceSwitchFlag = false;
|
||||
}
|
||||
|
||||
this.m.lastMoveAbsolute = null;
|
||||
this.lastMove = null;
|
||||
if (this.battle.gen === 2) this.lastMoveEncore = null;
|
||||
this.lastMoveUsed = null;
|
||||
this.moveThisTurn = '';
|
||||
this.moveLastTurnResult = undefined;
|
||||
this.moveThisTurnResult = undefined;
|
||||
|
||||
this.lastDamage = 0;
|
||||
this.attackedBy = [];
|
||||
this.hurtThisTurn = null;
|
||||
this.newlySwitched = true;
|
||||
this.beingCalledBack = false;
|
||||
|
||||
this.volatileStaleness = undefined;
|
||||
|
||||
delete this.abilityState.started;
|
||||
delete this.itemState.started;
|
||||
|
||||
this.setSpecies(this.baseSpecies);
|
||||
},
|
||||
moveUsed(move, targetLoc) {
|
||||
if (!this.moveThisTurn) this.m.lastMoveAbsolute = move;
|
||||
this.lastMove = move;
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ interface ModdedBattlePokemon {
|
|||
lostItemForDelibird?: Item | null;
|
||||
boostBy?: (this: Pokemon, boost: SparseBoostsTable) => boolean | number;
|
||||
clearBoosts?: (this: Pokemon) => void;
|
||||
clearVolatile?: (this: Pokemon, includeSwitchFlags?: boolean) => void;
|
||||
calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
|
||||
cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
|
||||
deductPP?: (
|
||||
|
|
@ -383,6 +384,7 @@ interface ModdedBattleScriptsData extends Partial<BattleScriptsData> {
|
|||
checkWin?: (this: Battle, faintQueue?: Battle['faintQueue'][0]) => true | undefined;
|
||||
fieldEvent?: (this: Battle, eventid: string, targets?: Pokemon[]) => void;
|
||||
getAllActive?: (this: Battle, includeFainted?: boolean, includeCommanding?: boolean) => Pokemon[];
|
||||
getTarget?: (this: Battle, pokemon: Pokemon, move: string | Move, targetLoc: number, originalTarget?: Pokemon) => Pokemon | null;
|
||||
}
|
||||
|
||||
type TypeInfo = import('./dex-data').TypeInfo;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user