This commit is contained in:
André Bastos Dias 2026-06-02 14:06:54 +01:00 committed by GitHub
commit f22a514bbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 192 additions and 10 deletions

View File

@ -3192,7 +3192,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
num: 253,
},
pickpocket: {
onAfterMoveSecondary(target, source, move) {
onAfterMoveSecondaryLast(target, source, move) {
if (source && source !== target && move?.flags['contact']) {
if (target.item || target.switchFlag || target.forceSwitchFlag || source.switchFlag === true) {
return;

View File

@ -552,5 +552,172 @@ export const Scripts: ModdedBattleScriptsData = {
return damage;
},
// Sheer Force shouldnt suppress Pickpocket
useMoveInner(moveOrMoveName, pokemon, options) {
let target = options?.target;
let sourceEffect = options?.sourceEffect;
const zMove = options?.zMove;
const maxMove = options?.maxMove;
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
if (sourceEffect && ['instruct', 'custapberry'].includes(sourceEffect.id)) sourceEffect = null;
let move = this.dex.getActiveMove(moveOrMoveName);
pokemon.lastMoveUsed = move;
if (move.id === 'weatherball' && zMove) {
// Z-Weather Ball only changes types if it's used directly,
// not if it's called by Z-Sleep Talk or something.
this.battle.singleEvent('ModifyType', move, null, pokemon, target, move, move);
if (move.type !== 'Normal') sourceEffect = move;
}
if (zMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isZ)) {
move = this.getActiveZMove(move, pokemon);
}
if (maxMove && move.category !== 'Status') {
// Max move outcome is dependent on the move type after type modifications from ability and the move itself
this.battle.singleEvent('ModifyType', move, null, pokemon, target, move, move);
this.battle.runEvent('ModifyType', pokemon, target, move, move);
}
if (maxMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isMax)) {
move = this.getActiveMaxMove(move, pokemon);
}
if (this.battle.activeMove) {
move.priority = this.battle.activeMove.priority;
if (!move.hasBounced) move.pranksterBoosted = this.battle.activeMove.pranksterBoosted;
}
const baseTarget = move.target;
let targetRelayVar = { target };
targetRelayVar = this.battle.runEvent('ModifyTarget', pokemon, target, move, targetRelayVar, true);
if (targetRelayVar.target !== undefined) target = targetRelayVar.target;
if (target === undefined) target = this.battle.getRandomTarget(pokemon, move);
if (move.target === 'self' || move.target === 'allies') {
target = pokemon;
}
if (sourceEffect) {
move.sourceEffect = sourceEffect.id;
move.ignoreAbility = (sourceEffect as ActiveMove).ignoreAbility;
}
let moveResult = false;
this.battle.setActiveMove(move, pokemon, target);
this.battle.singleEvent('ModifyType', move, null, pokemon, target, move, move);
this.battle.singleEvent('ModifyMove', move, null, pokemon, target, move, move);
if (baseTarget !== move.target) {
// Target changed in ModifyMove, so we must adjust it here
// Adjust before the next event so the correct target is passed to the
// event
target = this.battle.getRandomTarget(pokemon, move);
}
move = this.battle.runEvent('ModifyType', pokemon, target, move, move);
move = this.battle.runEvent('ModifyMove', pokemon, target, move, move);
if (baseTarget !== move.target) {
// Adjust again
target = this.battle.getRandomTarget(pokemon, move);
}
if (!move || pokemon.fainted) {
return false;
}
let attrs = '';
let movename = move.name;
if (move.id === 'hiddenpower') movename = 'Hidden Power';
if (sourceEffect) attrs += `|[from] ${sourceEffect.fullname}`;
if (zMove && move.isZ === true) {
attrs = `|[anim]${movename}${attrs}`;
movename = `Z-${movename}`;
}
this.battle.addMove('move', pokemon, movename, `${target}${attrs}`);
if (zMove) this.runZPower(move, pokemon);
if (!target) {
this.battle.attrLastMove('[notarget]');
this.battle.add(this.battle.gen >= 5 ? '-fail' : '-notarget', pokemon);
return false;
}
const { targets, pressureTargets } = pokemon.getMoveTargets(move, target);
if (targets.length) {
target = targets[targets.length - 1]; // in case of redirection
}
const callerMoveForPressure = sourceEffect && (sourceEffect as ActiveMove).pp ? sourceEffect as ActiveMove : null;
if (!sourceEffect || callerMoveForPressure || sourceEffect.id === 'pursuit') {
let extraPP = 0;
for (const source of pressureTargets) {
const ppDrop = this.battle.runEvent('DeductPP', source, pokemon, move);
if (ppDrop !== true) {
extraPP += ppDrop || 0;
}
}
if (extraPP > 0) {
pokemon.deductPP(callerMoveForPressure || moveOrMoveName, extraPP);
}
}
let tryMoveResult = this.battle.singleEvent('TryMove', move, null, pokemon, target, move);
if (tryMoveResult) {
tryMoveResult = this.battle.runEvent('TryMove', pokemon, target, move);
}
if (!tryMoveResult) {
move.mindBlownRecoil = false;
return tryMoveResult;
}
this.battle.singleEvent('UseMoveMessage', move, null, pokemon, target, move);
if (move.ignoreImmunity === undefined) {
move.ignoreImmunity = (move.category === 'Status');
}
if (this.battle.gen !== 4 && move.selfdestruct === 'always') {
this.battle.faint(pokemon, pokemon, move);
}
let damage: number | false | undefined | '' = false;
if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') {
damage = this.tryMoveHit(targets, pokemon, move);
if (damage === this.battle.NOT_FAIL) pokemon.moveThisTurnResult = null;
if (damage || damage === 0 || damage === undefined) moveResult = true;
} else {
if (!targets.length) {
this.battle.attrLastMove('[notarget]');
this.battle.add(this.battle.gen >= 5 ? '-fail' : '-notarget', pokemon);
return false;
}
if (this.battle.gen === 4 && move.selfdestruct === 'always') {
this.battle.faint(pokemon, pokemon, move);
}
moveResult = this.trySpreadMoveHit(targets, pokemon, move);
}
if (move.selfBoost && moveResult) this.moveHit(pokemon, pokemon, move, move.selfBoost, false, true);
if (!pokemon.hp) {
this.battle.faint(pokemon, pokemon, move);
}
if (!moveResult) {
this.battle.singleEvent('MoveFail', move, null, target, pokemon, move);
return false;
}
if (!(move.hasSheerForce && pokemon.hasAbility('sheerforce')) && !move.flags['futuremove']) {
const originalHp = pokemon.hp;
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
if (pokemon && pokemon !== target && move.category !== 'Status') {
if (pokemon.hp <= pokemon.maxhp / 2 && originalHp > pokemon.maxhp / 2) {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
}
for (const curTarget of targets) {
this.battle.singleEvent('AfterMoveSecondaryLast', move, null, curTarget, pokemon, move);
this.battle.runEvent('AfterMoveSecondaryLast', curTarget, pokemon, move);
}
return true;
},
},
};

View File

@ -9425,11 +9425,8 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
pp: 15,
priority: 0,
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
onAfterHit(target, source) {
this.field.clearTerrain();
},
onAfterSubDamage(damage, target, source) {
if (source.hp) {
onAfterMoveSecondaryLast(target, source) {
if (source.hp && !source.forceSwitchFlag) {
this.field.clearTerrain();
}
},

View File

@ -537,6 +537,10 @@ export class BattleActions {
this.battle.runEvent('EmergencyExit', pokemon, pokemon);
}
}
for (const curTarget of targets) {
this.battle.singleEvent('AfterMoveSecondaryLast', move, null, curTarget, pokemon, move);
this.battle.runEvent('AfterMoveSecondaryLast', curTarget, pokemon, move);
}
}
return true;
@ -1747,7 +1751,7 @@ export class BattleActions {
}
// weather modifier
baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
// crit - not a modifier
const isCrit = target.getMoveHitData(move).crit;

View File

@ -24,6 +24,7 @@ export interface EventMethods {
onAfterTakeItem?: (this: Battle, item: Item, pokemon: Pokemon) => void;
onAfterBoost?: (this: Battle, boost: SparseBoostsTable, target: Pokemon, source: Pokemon, effect: Effect) => void;
onAfterFaint?: (this: Battle, length: number, target: Pokemon, source: Pokemon, effect: Effect) => void;
onAfterMoveSecondaryLast?: MoveEventMethods['onAfterMoveSecondaryLast'];
onAfterMoveSecondarySelf?: MoveEventMethods['onAfterMoveSecondarySelf'];
onAfterMoveSecondary?: MoveEventMethods['onAfterMoveSecondary'];
onAfterMove?: MoveEventMethods['onAfterMove'];

View File

@ -108,6 +108,7 @@ export interface MoveEventMethods {
onAfterHit?: CommonHandlers['VoidSourceMove'];
onAfterSubDamage?: (this: Battle, damage: number, target: Pokemon, source: Pokemon, move: ActiveMove) => void;
onAfterMoveSecondaryLast?: CommonHandlers['VoidMove'];
onAfterMoveSecondarySelf?: CommonHandlers['VoidSourceMove'];
onAfterMoveSecondary?: CommonHandlers['VoidMove'];
onAfterMove?: CommonHandlers['VoidSourceMove'];

View File

@ -46,7 +46,7 @@ describe('Pickpocket', () => {
assert.holdsItem(battle.p2.active[0]);
});
it.skip(`should steal items back and forth when hit by a Magician user`, () => {
it(`should steal items back and forth when hit by a Magician user`, () => {
battle = common.createBattle([[
{ species: 'Weavile', ability: 'pickpocket', item: 'cheriberry', moves: ['agility'] },
], [

View File

@ -21,7 +21,19 @@ describe(`Ice Spinner`, () => {
assert.false(battle.field.isTerrain('psychicterrain'));
});
it.skip(`should not remove Terrains if the user faints from Life Orb`, () => {
it(`should remove Terrains if target has a substitute`, () => {
battle = common.createBattle([[
{ species: 'wynaut', moves: ['sleeptalk', 'icespinner'] },
], [
{ species: 'registeel', ability: 'psychicsurge', moves: ['substitute', 'sleeptalk'] },
]]);
battle.makeChoices('move sleeptalk', 'move substitute');
battle.makeChoices('move ice spinner', 'move sleeptalk');
assert.false(battle.field.isTerrain('psychicterrain'));
});
it(`should not remove Terrains if the user faints from Life Orb`, () => {
battle = common.createBattle([[
{ species: 'shedinja', item: 'lifeorb', moves: ['icespinner'] },
{ species: 'wynaut', moves: ['sleeptalk'] },
@ -45,7 +57,7 @@ describe(`Ice Spinner`, () => {
assert(battle.field.isTerrain('psychicterrain'));
});
it.skip(`should not remove Terrains if the user is forced out via Red Card`, () => {
it(`should not remove Terrains if the user is forced out via Red Card`, () => {
battle = common.createBattle([[
{ species: 'shedinja', moves: ['icespinner'] },
{ species: 'wynaut', moves: ['sleeptalk'] },