From 552bda98400a26698b9c286be264a8751bb91cd5 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 10 Dec 2023 22:29:13 -0500 Subject: [PATCH] Implement some abilities --- src/battle-phases.ts | 2 +- src/data/ability.ts | 83 +++++++++++++++++++++++++++----------------- src/data/move.ts | 15 ++++++-- src/pokemon.ts | 9 ++++- 4 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/battle-phases.ts b/src/battle-phases.ts index f60cb81bb..21999050f 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -2785,7 +2785,7 @@ export class PokemonHealPhase extends CommonAnimPhase { this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); const healAmount = new Utils.NumberHolder(this.hpHealed * hpRestoreMultiplier.value); pokemon.heal(healAmount.value); - this.scene.validateAchvs(HealAchv, healAmount) + this.scene.validateAchvs(HealAchv, healAmount); pokemon.updateInfo().then(() => super.end()); } else if (this.showFullHpMessage) this.message = getPokemonMessage(pokemon, `'s\nHP is full!`); diff --git a/src/data/ability.ts b/src/data/ability.ts index ee896d2b1..d70d2273a 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -291,7 +291,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { } applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { - if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance && !pokemon.status) { + if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && Utils.randInt(100) < this.chance && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)]; pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, attacker.getBattlerIndex(), effect)); } @@ -314,7 +314,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { } applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { - if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) + if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && Utils.randInt(100) < this.chance) return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id); return false; @@ -637,6 +637,8 @@ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { export class BlockCritAbAttr extends AbAttr { } +export class IgnoreContactAbAttr extends AbAttr { } + export class PreWeatherEffectAbAttr extends AbAttr { applyPreWeatherEffect(pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { return false; @@ -681,19 +683,6 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { } } -export class PostTurnAbAttr extends AbAttr { - applyPostTurn(pokemon: Pokemon, args: any[]) { - return false; - } -} - -export class PostTurnSpeedBoostAbAttr extends PostTurnAbAttr { - applyPostTurn(pokemon: Pokemon, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.SPD ], 1)); - return true; - } -} - function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition { return (pokemon: Pokemon) => { if (pokemon.scene.arena.weather?.isEffectSuppressed(pokemon.scene)) @@ -703,19 +692,6 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition { }; } -export class PostTurnHealAbAttr extends PostTurnAbAttr { - applyPostTurn(pokemon: Pokemon, args: any[]): boolean { - if (pokemon.getHpRatio() < 1) { - const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); - return true; - } - - return false; - } -} - export class PostWeatherLapseAbAttr extends AbAttr { protected weatherTypes: WeatherType[]; @@ -777,6 +753,32 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { } } +export class PostTurnAbAttr extends AbAttr { + applyPostTurn(pokemon: Pokemon, args: any[]) { + return false; + } +} + +export class PostTurnSpeedBoostAbAttr extends PostTurnAbAttr { + applyPostTurn(pokemon: Pokemon, args: any[]): boolean { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.SPD ], 1)); + return true; + } +} + +export class PostTurnHealAbAttr extends PostTurnAbAttr { + applyPostTurn(pokemon: Pokemon, args: any[]): boolean { + if (pokemon.getHpRatio() < 1) { + const scene = pokemon.scene; + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); + return true; + } + + return false; + } +} + export class StatChangeMultiplierAbAttr extends AbAttr { private multiplier: integer; @@ -810,6 +812,22 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } } +export class WeightMultiplierAbAttr extends AbAttr { + private multiplier: integer; + + constructor(multiplier: integer) { + super(true); + + this.multiplier = multiplier; + } + + apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean { + (args[0] as Utils.NumberHolder).value *= this.multiplier; + + return true; + } +} + export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): void { if (!pokemon.canApplyAbility()) return; @@ -1712,8 +1730,10 @@ export function initAbilities() { new Ability(Abilities.HEALER, "Healer (N)", "Sometimes heals an ally's status condition.", 5), new Ability(Abilities.FRIEND_GUARD, "Friend Guard (N)", "Reduces damage done to allies.", 5), new Ability(Abilities.WEAK_ARMOR, "Weak Armor (N)", "Physical attacks to the Pokémon lower its Defense\nstat but sharply raise its Speed stat.", 5), - new Ability(Abilities.HEAVY_METAL, "Heavy Metal (N)", "Doubles the Pokémon's weight.", 5), - new Ability(Abilities.LIGHT_METAL, "Light Metal (N)", "Halves the Pokémon's weight.", 5), + new Ability(Abilities.HEAVY_METAL, "Heavy Metal", "Doubles the Pokémon's weight.", 5) + .attr(WeightMultiplierAbAttr, 2), + new Ability(Abilities.LIGHT_METAL, "Light Metal", "Halves the Pokémon's weight.", 5) + .attr(WeightMultiplierAbAttr, 0.5), new Ability(Abilities.MULTISCALE, "Multiscale (N)", "Reduces the amount of damage the Pokémon takes\nwhile its HP is full.", 5), new Ability(Abilities.TOXIC_BOOST, "Toxic Boost (N)", "Powers up physical attacks when the Pokémon\nis poisoned.", 5), new Ability(Abilities.FLARE_BOOST, "Flare Boost (N)", "Powers up special attacks when the Pokémon\nis burned.", 5), @@ -1794,7 +1814,8 @@ export function initAbilities() { new Ability(Abilities.SLUSH_RUSH, "Slush Rush (N)", "Boosts the Pokémon's Speed stat in a hailstorm.", 7) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) .condition(getWeatherCondition(WeatherType.HAIL)), - new Ability(Abilities.LONG_REACH, "Long Reach (N)", "The Pokémon uses its moves without making contact\nwith the target.", 7), + new Ability(Abilities.LONG_REACH, "Long Reach", "The Pokémon uses its moves without making contact\nwith the target.", 7) + .attr(IgnoreContactAbAttr), new Ability(Abilities.LIQUID_VOICE, "Liquid Voice (N)", "All sound-based moves become Water-type moves.", 7), new Ability(Abilities.TRIAGE, "Triage (N)", "Gives priority to a healing move.", 7), new Ability(Abilities.GALVANIZE, "Galvanize (N)", "Normal-type moves become Electric-type moves.\nThe power of those moves is boosted a little.", 7), diff --git a/src/data/move.ts b/src/data/move.ts index 3435a3358..30955036f 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -9,7 +9,7 @@ import { Type } from "./type"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagType, ArenaTrapTag } from "./arena-tag"; -import { Abilities, BlockRecoilDamageAttr, applyAbAttrs } from "./ability"; +import { Abilities, BlockRecoilDamageAttr, IgnoreContactAbAttr, applyAbAttrs } from "./ability"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; import { Stat } from "./pokemon-stat"; @@ -172,6 +172,17 @@ export default class Move { return this; } + checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean { + switch (flag) { + case MoveFlags.MAKES_CONTACT: + if (user.getAbility().hasAttr(IgnoreContactAbAttr)) + return false; + break; + } + + return !!(this.flags & flag); + } + applyConditions(user: Pokemon, target: Pokemon, move: Move): boolean { for (let condition of this.conditions) { if (!condition(user, target, move)) @@ -1933,7 +1944,7 @@ export class WeightPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0] as Utils.NumberHolder; - const targetWeight = target.species.weight; + const targetWeight = target.getWeight(); const weightThresholds = [ 10, 25, 50, 100, 200 ]; let w = 0; diff --git a/src/pokemon.ts b/src/pokemon.ts index c5c7d6eba..e574678f9 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -22,7 +22,7 @@ import { WeatherType } from './data/weather'; import { TempBattleStat } from './data/temp-battle-stat'; import { ArenaTagType, WeakenMoveTypeTag } from './data/arena-tag'; import { Biome } from './data/biome'; -import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, IgnoreOpponentStatChangesAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability'; +import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, IgnoreOpponentStatChangesAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability'; import PokemonData from './system/pokemon-data'; import { BattlerIndex } from './battle'; import { Mode } from './ui/ui'; @@ -582,6 +582,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.hp && !this.getAbility().conditions.find(condition => !condition(this)); } + getWeight(): number { + const weight = new Utils.NumberHolder(this.species.weight); + // This will trigger the ability overlay so only call this function when necessary + applyAbAttrs(WeightMultiplierAbAttr, this, null, weight); + return weight.value; + } + getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier { const types = this.getTypes(); return getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;