From 18a6b7ffebee71f06e0c524a46a8a7e2772e731c Mon Sep 17 00:00:00 2001 From: Alvin Zou Date: Sat, 4 May 2024 22:54:19 -0700 Subject: [PATCH] Update move.ts --- src/data/move.ts | 142 +++++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index afad81d4807..e60ccfa8814 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -201,7 +201,7 @@ export default class Move implements Localizable { return this; } - + partial(): this { this.nameAppend += ' (P)'; return this; @@ -468,7 +468,7 @@ export class MoveEffectAttr extends MoveAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { - return this.canApply(user, target, move, args); + return this.canApply(user, target, move, args); } } @@ -587,16 +587,16 @@ export class MatchHpAttr extends FixedDamageAttr { super(0); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value = target.hp - user.hp; return true; - } - + } + getCondition(): MoveConditionFunc { return (user, target, move) => user.hp <= target.hp; } - + // TODO /*getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { return 0; @@ -669,7 +669,7 @@ export class SurviveDamageAttr extends ModifiedDamageAttr { getCondition(): MoveConditionFunc { return (user, target, move) => target.hp > 1; } - + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { return target.hp > 1 ? 0 : -20; } @@ -707,7 +707,7 @@ export class RecoilAttr extends MoveEffectAttr { applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (cancelled.value) return false; - + user.damageAndUpdate(recoilDamage, HitResult.OTHER, false, true, true); user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!')); user.turnData.damageTaken += recoilDamage; @@ -1096,7 +1096,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } return statusAfflictResult; } - + return false; } @@ -1217,7 +1217,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr { pokemon.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect))); pokemon.resetStatus(); pokemon.updateInfo(); - + return true; } @@ -1246,7 +1246,7 @@ export class BypassSleepAttr extends MoveAttr { export class WeatherChangeAttr extends MoveEffectAttr { private weatherType: WeatherType; - + constructor(weatherType: WeatherType) { super(); @@ -1264,7 +1264,7 @@ export class WeatherChangeAttr extends MoveEffectAttr { export class ClearWeatherAttr extends MoveEffectAttr { private weatherType: WeatherType; - + constructor(weatherType: WeatherType) { super(); @@ -1281,7 +1281,7 @@ export class ClearWeatherAttr extends MoveEffectAttr { export class TerrainChangeAttr extends MoveEffectAttr { private terrainType: TerrainType; - + constructor(terrainType: TerrainType) { super(); @@ -1318,7 +1318,7 @@ export class OneHitKOAttr extends MoveAttr { return false; (args[0] as Utils.BooleanHolder).value = true; - + return true; } @@ -1558,7 +1558,7 @@ export class AcupressureStatChangeAttr extends MoveEffectAttr { return true; } return false; - } + } } export class GrowthStatChangeAttr extends StatChangeAttr { @@ -1686,7 +1686,7 @@ export class HpSplitAttr extends MoveEffectAttr { return resolve(false); const infoUpdates = []; - + const hpValue = Math.floor((target.hp + user.hp) / 2); if (user.hp < hpValue) { const healing = user.heal(hpValue - user.hp); @@ -1843,7 +1843,7 @@ export class BattleStatRatioPowerAttr extends VariablePowerAttr { // Gen 6+ always have 1 base power power.value = 1; return true; - } + } let bp = Math.floor(Math.min(150, 25 * target.getBattleStat(this.stat) / userSpeed + 1)); power.value = bp; return true; @@ -1899,7 +1899,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { if (!userWeight || userWeight === 0) return false; - + const relativeWeight = (targetWeight / userWeight) * 100; switch (true) { @@ -1990,7 +1990,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr { const magnitudePowers = [ 10, 30, 50, 70, 90, 100, 110, 150 ]; let rand: integer; - + user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), user.scene.currentBattle.turn << 6, user.scene.waveSeed); let m = 0; @@ -2054,7 +2054,7 @@ export class HitCountPowerAttr extends VariablePowerAttr { } } -export class StatChangeCountPowerAttr extends VariablePowerAttr { +export class UserStatChangeCountPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const positiveStats: number = user.summonData.battleStats.reduce((total, stat) => stat > 0 && stat ? total + stat : total, 0); @@ -2064,6 +2064,16 @@ export class StatChangeCountPowerAttr extends VariablePowerAttr { } } +export class OppStatChangeCountPowerAttr extends VariablePowerAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const positiveStats: number = target.summonData.battleStats.reduce((total, stat) => stat > 0 && stat ? total + stat : total, 0); + + (args[0] as Utils.NumberHolder).value += Math.min(140, positiveStats * 20); + + return true; + } +} + export class PresentPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -2090,9 +2100,9 @@ export class KnockOffPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if(target.getHeldItems().length > 0){ (args[0] as Utils.NumberHolder).value *= 1.5; - return true; + return true; } - + return false; } } @@ -2429,7 +2439,7 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { +(user.ivs[Stat.SPD] & 1) * 8 +(user.ivs[Stat.SPATK] & 1) * 16 +(user.ivs[Stat.SPDEF] & 1) * 32) * 15/63); - + type.value = [ Type.FIGHTING, Type.FLYING, Type.POISON, Type.GROUND, Type.ROCK, Type.BUG, Type.GHOST, Type.STEEL, @@ -2527,11 +2537,11 @@ const crashDamageFunc = (user: Pokemon, move: Move) => { applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (cancelled.value) return false; - + user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true); user.scene.queueMessage(getPokemonMessage(user, ' kept going\nand crashed!')); user.turnData.damageTaken += Math.floor(user.getMaxHp() / 2); - + return true; }; @@ -2552,23 +2562,23 @@ export class DisableMoveAttr extends MoveEffectAttr { turnMove = moveQueue.shift(); if (turnMove.virtual) continue; - + const moveIndex = target.getMoveset().findIndex(m => m.moveId === turnMove.move); if (moveIndex === -1) return false; - + const disabledMove = target.getMoveset()[moveIndex]; target.summonData.disabledMove = disabledMove.moveId; target.summonData.disabledTurns = 4; user.scene.queueMessage(getPokemonMessage(target, `'s ${disabledMove.getName()}\nwas disabled!`)); - + return true; } - + return false; } - + getCondition(): MoveConditionFunc { return (user, target, move) => { if (target.summonData.disabledMove || target.isMax()) @@ -2580,7 +2590,7 @@ export class DisableMoveAttr extends MoveEffectAttr { turnMove = moveQueue.shift(); if (turnMove.virtual) continue; - + const move = target.getMoveset().find(m => m.moveId === turnMove.move); if (!move) continue; @@ -2719,7 +2729,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { } export class CurseAttr extends MoveEffectAttr { - + apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { if (user.getTypes(true).includes(Type.GHOST)) { if (target.getTag(BattlerTagType.CURSED)) { @@ -2754,7 +2764,7 @@ export class LapseBattlerTagAttr extends MoveEffectAttr { for (let tagType of this.tagTypes) (this.selfTarget ? user : target).lapseTag(tagType); - + return true; } } @@ -2774,7 +2784,7 @@ export class RemoveBattlerTagAttr extends MoveEffectAttr { for (let tagType of this.tagTypes) (this.selfTarget ? user : target).removeTag(tagType); - + return true; } } @@ -2977,13 +2987,13 @@ export class RemoveScreensAttr extends MoveEffectAttr { export class ForceSwitchOutAttr extends MoveEffectAttr { private user: boolean; private batonPass: boolean; - + constructor(user?: boolean, batonPass?: boolean) { super(false, MoveEffectTrigger.POST_APPLY, true); this.user = !!user; this.batonPass = !!batonPass; } - + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { @@ -2997,7 +3007,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // Move the switch out logic inside the conditional block // This ensures that the switch out only happens when the conditions are met const switchOutTarget = this.user ? user : target; - if (switchOutTarget instanceof PlayerPokemon) { + if (switchOutTarget instanceof PlayerPokemon) { if (switchOutTarget.hp) { applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, switchOutTarget); (switchOutTarget as PlayerPokemon).switchOut(this.batonPass, true).then(() => resolve(true)); @@ -3013,30 +3023,30 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { switchOutTarget.setVisible(false); switchOutTarget.scene.field.remove(switchOutTarget); user.scene.triggerPokemonFormChange(switchOutTarget, SpeciesFormChangeActiveTrigger, true); - + if (switchOutTarget.hp) user.scene.unshiftPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot), false, this.batonPass, false)); } - else { + else { // Switch out logic for everything else switchOutTarget.setVisible(false); - + if (switchOutTarget.hp) { switchOutTarget.hideInfo().then(() => switchOutTarget.destroy()); switchOutTarget.scene.field.remove(switchOutTarget); user.scene.queueMessage(getPokemonMessage(switchOutTarget, ' fled!'), null, true, 500); } - + if (!switchOutTarget.getAlly()?.isActive(true)) { user.scene.clearEnemyHeldItemModifiers(); - + if (switchOutTarget.hp) { user.scene.pushPhase(new BattleEndPhase(user.scene)); user.scene.pushPhase(new NewBattlePhase(user.scene)); } } } - + resolve(true); }); } @@ -3217,7 +3227,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { return false; const firstMoveType = target.getMoveset()[0].getMove().type - + user.summonData.types = [ firstMoveType ]; user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto to the ${Utils.toReadableString(Type[firstMoveType])} type!`)); @@ -3259,8 +3269,8 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { selectTargets = [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; break; } - } - const targets = selectTargets; + } + const targets = selectTargets; user.getMoveQueue().push({ move: move.moveId, targets: targets, ignorePP: true }); user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true)); return true; @@ -3275,7 +3285,7 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr { return new Promise(resolve => { const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL) && !allMoves[m].name.endsWith(' (N)')); const moveId = moveIds[user.randSeedInt(moveIds.length)]; - + const moveTargets = getMoveTargets(user, moveId); if (!moveTargets.targets.length) { resolve(false); @@ -3428,7 +3438,7 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { moveId = Moves.TRI_ATTACK; break; } - + user.getMoveQueue().push({ move: moveId, targets: [target.getBattlerIndex()], ignorePP: true }); user.scene.unshiftPhase(new MovePhase(user.scene, user, [target.getBattlerIndex()], new PokemonMove(moveId, 0, 0, true), true)); initMoveAnim(user.scene, moveId).then(() => { @@ -3595,16 +3605,16 @@ export class SketchAttr extends MoveEffectAttr { return (user, target, move) => { if (!targetMoveCopiableCondition(user, target, move)) return false; - + const targetMoves = target.getMoveHistory().filter(m => !m.virtual); if (!targetMoves.length) return false; - + const sketchableMove = targetMoves[0]; - + if (user.getMoveset().find(m => m.moveId === sketchableMove.move)) return false; - + return true; }; } @@ -3651,7 +3661,7 @@ export class AbilityCopyAttr extends MoveEffectAttr { user.summonData.ability = target.getAbility().id; user.scene.queueMessage(getPokemonMessage(user, ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`)); - + if (this.copyToPartner && user.scene.currentBattle?.double && user.getAlly().hp) { user.getAlly().summonData.ability = target.getAbility().id; user.getAlly().scene.queueMessage(getPokemonMessage(user.getAlly(), ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`)); @@ -3900,7 +3910,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { const moveTarget = allMoves[move].getAttrs(VariableTargetAttr).length ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : []; const opponents = user.getOpponents(); - + let set: Pokemon[] = []; let multiple = false; @@ -4568,7 +4578,7 @@ export function initMoves() { .partial(), new AttackMove(Moves.RAPID_SPIN, Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2) .attr(StatChangeAttr, BattleStat.SPD, 1, true) - .attr(RemoveBattlerTagAttr, [ + .attr(RemoveBattlerTagAttr, [ BattlerTagType.BIND, BattlerTagType.WRAP, BattlerTagType.FIRE_SPIN, @@ -4959,7 +4969,7 @@ export function initMoves() { new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, 100, 0, 4) .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1), new AttackMove(Moves.ASSURANCE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => target.turnData.damageTaken > 0 ? 2 : 1), new StatusMove(Moves.EMBARGO, Type.DARK, 100, 15, -1, 0, 4) @@ -5004,9 +5014,9 @@ export function initMoves() { .unimplemented(), new StatusMove(Moves.GUARD_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 4) .unimplemented(), - new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) - .unimplemented(), - new AttackMove(Moves.LAST_RESORT, Type.NORMAL, MoveCategory.PHYSICAL, 140, 100, 5, -1, 0, 4) + new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 5, -1, 0, 4) + .attr(OppStatChangeCountPowerAttr), + new AttackMove(Moves.LAST_RESORT, Type.NORMAL, MoveCategory.PHYSICAL, 60, 100, 5, -1, 0, 4) .attr(LastResortAttr), new StatusMove(Moves.WORRY_SEED, Type.GRASS, 100, 10, -1, 0, 4) .attr(AbilityChangeAttr, Abilities.INSOMNIA), @@ -5021,7 +5031,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) .attr(AddBattlerTagAttr, BattlerTagType.MAGNET_RISEN, true, true) - .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && + .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && !user.getTag(BattlerTagType.IGNORE_FLYING) && !user.getTag(BattlerTagType.INGRAIN) && !user.getTag(BattlerTagType.MAGNET_RISEN)) .unimplemented(), @@ -5305,7 +5315,7 @@ export function initMoves() { new AttackMove(Moves.CLEAR_SMOG, Type.POISON, MoveCategory.SPECIAL, 50, -1, 15, -1, 0, 5) .attr(ResetStatsAttr), new AttackMove(Moves.STORED_POWER, Type.PSYCHIC, MoveCategory.SPECIAL, 20, 100, 10, -1, 0, 5) - .attr(StatChangeCountPowerAttr), + .attr(UserStatChangeCountPowerAttr), new StatusMove(Moves.QUICK_GUARD, Type.FIGHTING, -1, 15, -1, 3, 5) .target(MoveTarget.USER_SIDE) .unimplemented(), @@ -5328,7 +5338,7 @@ export function initMoves() { new AttackMove(Moves.SKY_DROP, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) .attr(ChargeAttr, ChargeAnim.SKY_DROP_CHARGING, 'took {TARGET}\ninto the sky!', BattlerTagType.FLYING) // TODO: Add 2nd turn message .condition(failOnGravityCondition) - .ignoresVirtual(), + .ignoresVirtual(), new SelfStatusMove(Moves.SHIFT_GEAR, Type.STEEL, -1, 10, -1, 0, 5) .attr(StatChangeAttr, BattleStat.ATK, 1, true) .attr(StatChangeAttr, BattleStat.SPD, 2, true), @@ -5389,7 +5399,7 @@ export function initMoves() { .attr(HitHealAttr) .triageMove(), new AttackMove(Moves.SACRED_SWORD, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 5) - .attr(IgnoreOpponentStatChangesAttr) + .attr(IgnoreOpponentStatChangesAttr) .slicingMove(), new AttackMove(Moves.RAZOR_SHELL, Type.WATER, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 5) .attr(StatChangeAttr, BattleStat.DEF, -1) @@ -5778,7 +5788,7 @@ export function initMoves() { new AttackMove(Moves.FIRE_LASH, Type.FIRE, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) .attr(StatChangeAttr, BattleStat.DEF, -1), new AttackMove(Moves.POWER_TRIP, Type.DARK, MoveCategory.PHYSICAL, 20, 100, 10, -1, 0, 7) - .attr(StatChangeCountPowerAttr), + .attr(UserStatChangeCountPowerAttr), new AttackMove(Moves.BURN_UP, Type.FIRE, MoveCategory.SPECIAL, 130, 100, 5, -1, 0, 7) .condition((user) => { const userTypes = user.getTypes(true); @@ -6446,7 +6456,7 @@ export function initMoves() { .attr(StatChangeAttr, BattleStat.SPATK, -1, true, null, true, true) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.PSYBLADE, Type.PSYCHIC, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) .slicingMove(), new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) .partial(), @@ -6584,4 +6594,4 @@ export function initMoves() { new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) .attr(StatusEffectAttr, StatusEffect.TOXIC) ); -} +}