From 1f50ebdae0d256c32a9962da1952378287a99f09 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Fri, 8 Aug 2025 19:19:00 -0400 Subject: [PATCH] Cleaned up flying press/etc moves; removed `VariableMoveTypeMultiplierAttr` --- src/data/abilities/ability.ts | 4 +- src/data/moves/move.ts | 131 ++++++++++++++++------------------ src/field/pokemon.ts | 1 - 3 files changed, 63 insertions(+), 73 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index f5fd9b19f72..9512c3feaad 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -730,9 +730,7 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { override canApply(params: TypeMultiplierAbAttrParams): boolean { const { move } = params; return ( - move.category !== MoveCategory.STATUS && - !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") && - super.canApply(params) + move.category !== MoveCategory.STATUS && !move.hasAttr("VariableMoveTypeChartAttr") && super.canApply(params) ); } } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 067bd05c2ae..a9b044fc857 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -67,7 +67,7 @@ import { StatusEffect } from "#enums/status-effect"; import { SwitchType } from "#enums/switch-type"; import { WeatherType } from "#enums/weather-type"; import { MoveUsedEvent } from "#events/battle-scene"; -import type { EnemyPokemon, Pokemon } from "#field/pokemon"; +import { EnemyPokemon, Pokemon } from "#field/pokemon"; import { AttackTypeBoosterModifier, BerryModifier, @@ -5349,48 +5349,60 @@ export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr { } } -export class VariableMoveTypeMultiplierAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - return false; - } -} - -export class NeutralDamageAgainstFlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!target.getTag(BattlerTagType.IGNORE_FLYING)) { - const multiplier = args[0] as NumberHolder; - //When a flying type is hit, the first hit is always 1x multiplier. - if (target.isOfType(PokemonType.FLYING)) { - multiplier.value = 1; - } - return true; - } - - return false; - } -} - -export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { +/** + * Attribute for moves which have a custom type chart interaction. + */ +export class VariableMoveTypeChartAttr extends MoveAttr { /** - * Checks to see if the Target is Ice-Type or not. If so, the move will have no effect. - * @param user n/a - * @param target The {@linkcode Pokemon} targeted by the move - * @param move n/a - * @param args `[0]` a {@linkcode NumberHolder | NumberHolder} containing a type effectiveness multiplier - * @returns `true` if this Ice-type immunity applies; `false` otherwise + * @param user - The {@linkcode Pokemon} using the move + * @param target - The {@linkcode Pokemon} targeted by the move + * @param move - The {@linkcode Move} with this attribute + * @param args - + * `[0]`: A {@linkcode NumberHolder} holding the type effectiveness + * `[1]`: The target's entire defensive type profile. + * @returns `true` if application of the attribute succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as NumberHolder; - if (target.isOfType(PokemonType.ICE)) { - multiplier.value = 0; + apply(user: Pokemon, target: Pokemon, move: Move, args: [multiplier: NumberHolder, types: PokemonType[]]): boolean { + return false; + } +} + +/** + * Attribute to implement {@linkcode MoveId.FREEZE_DRY}'s guaranteed water type super effectiveness. + */ +export class FreezeDryAttr extends VariableMoveTypeChartAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder, PokemonType[]]): boolean { + const [multiplier, types] = args; + + if (types.includes(PokemonType.WATER)) { + multiplier.value = Math.max(multiplier.value, 2); return true; } return false; } } -export class FlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { +/** + * Attribute used by {@linkcode MoveId.THOUSAND_ARROWS} to cause it to deal a fixed 1x damage + * against all ungrounded flying types. + */ +export class NeutralDamageAgainstFlyingTypeAttr extends VariableMoveTypeChartAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: [multiplier: NumberHolder, types: PokemonType[]]): boolean { + const [multiplier, types] = args; + if (target.isGrounded() || !types.includes(PokemonType.FLYING)) { + return false; + } + multiplier.value = 1; + + return true; + } +} + +/** + * Attribute used by {@linkcode MoveId.FLYING_PRESS} to add the Flying Type to its type effectiveness. + */ +export class FlyingTypeMultiplierAttr extends VariableMoveTypeChartAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: [multiplier: NumberHolder, types: PokemonType[]]): boolean { const multiplier = args[0] as NumberHolder; multiplier.value *= target.getAttackTypeEffectiveness(PokemonType.FLYING, user); return true; @@ -5398,39 +5410,19 @@ export class FlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { } /** - * Attribute for moves which have a custom type chart interaction. + * Attribute used by {@linkcode MoveId.SHEER_COLD} to implement its Gen VII+ ice ineffectiveness. */ -export class VariableMoveTypeChartAttr extends MoveAttr { - /** - * @param user {@linkcode Pokemon} using the move - * @param target {@linkcode Pokemon} target of the move - * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode NumberHolder} holding the type effectiveness - * @param args [1] A single defensive type of the target - * - * @returns true if application of the attribute succeeds - */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { +export class IceNoEffectTypeAttr extends VariableMoveTypeChartAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: [multiplier: NumberHolder, types: PokemonType[]]): boolean { + const [multiplier, types] = args; + if (types.includes(PokemonType.ICE)) { + multiplier.value = 0; + return true; + } return false; } } -/** - * This class forces Freeze-Dry to be super effective against Water Type. - */ -export class FreezeDryAttr extends VariableMoveTypeChartAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as NumberHolder; - const defType = args[1] as PokemonType; - - if (defType === PokemonType.WATER) { - multiplier.value = 2; - return true; - } else { - return false; - } - } -} export class OneHitKOAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -5916,8 +5908,8 @@ export class ProtectAttr extends AddBattlerTagAttr { for (const turnMove of user.getLastXMoves(-1).slice()) { if ( // Quick & Wide guard increment the Protect counter without using it for fail chance - !(allMoves[turnMove.move].hasAttr("ProtectAttr") || - [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || + !(allMoves[turnMove.move].hasAttr("ProtectAttr") || + [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || turnMove.result !== MoveResult.SUCCESS ) { break; @@ -8146,7 +8138,9 @@ export class UpperHandCondition extends MoveCondition { } } -export class HitsSameTypeAttr extends VariableMoveTypeMultiplierAttr { +// TODO: Does this need to extend from this? +// The only reason it might is to show ineffectiveness text but w/e +export class HitsSameTypeAttr extends VariableMoveTypeChartAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const multiplier = args[0] as NumberHolder; if (!user.getTypes(true).some(type => target.getTypes(true).includes(type))) { @@ -8417,8 +8411,7 @@ const MoveAttrs = Object.freeze({ TeraStarstormTypeAttr, MatchUserTypeAttr, CombinedPledgeTypeAttr, - VariableMoveTypeMultiplierAttr, - NeutralDamageAgainstFlyingTypeMultiplierAttr, + NeutralDamageAgainstFlyingTypeAttr, IceNoEffectTypeAttr, FlyingTypeMultiplierAttr, VariableMoveTypeChartAttr, @@ -10454,7 +10447,7 @@ export function initMoves() { .attr(HitHealAttr, 0.75) .triageMove(), new AttackMove(MoveId.THOUSAND_ARROWS, PokemonType.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) - .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) + .attr(NeutralDamageAgainstFlyingTypeAttr) .attr(FallDownAttr) .attr(HitsTagAttr, BattlerTagType.FLYING) .attr(HitsTagAttr, BattlerTagType.FLOATING) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fe85e92772c..d8860352c1b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2400,7 +2400,6 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { : 1, ); - applyMoveAttrs("VariableMoveTypeMultiplierAttr", source, this, move, typeMultiplier); if (this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t))) { typeMultiplier.value = 0; }