From 887a4176ab4e153b3cc901a05bb1def7c624db78 Mon Sep 17 00:00:00 2001 From: Jaime Date: Fri, 5 Apr 2024 17:21:17 +0200 Subject: [PATCH] Implemented moves Reflect, Light Screen and Aurora Veil --- src/data/arena-tag.ts | 77 +++++++++++++++++++++++++++++++- src/data/enums/arena-tag-type.ts | 5 ++- src/data/move.ts | 11 +++-- src/field/pokemon.ts | 8 +++- 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 9bbdc1136..74ba36e1f 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1,7 +1,7 @@ import { Arena } from "../field/arena"; import { Type } from "./type"; import * as Utils from "../utils"; -import { allMoves } from "./move"; +import { MoveCategory, allMoves } from "./move"; import { getPokemonMessage } from "../messages"; import Pokemon, { HitResult, PokemonMove } from "../field/pokemon"; import { MoveEffectPhase } from "../phases"; @@ -76,6 +76,75 @@ export class MistTag extends ArenaTag { } } +export class WeakenMoveScreenTag extends ArenaTag { + constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) { + super(tagType, turnCount, sourceMove, sourceId); + } + + apply(arena: Arena, args: any[]): boolean { + if ((args[1] as boolean)) { + (args[2] as Utils.NumberHolder).value = 2732/4096; + } else { + (args[2] as Utils.NumberHolder).value = 0.5; + } + return true; + } +} + +class ReflectTag extends WeakenMoveScreenTag { + constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side); + } + + apply(arena: Arena, args: any[]): boolean { + if ((args[0] as MoveCategory) === MoveCategory.PHYSICAL) { + if ((args[1] as boolean)) { + (args[2] as Utils.NumberHolder).value = 2732/4096; + } else { + (args[2] as Utils.NumberHolder).value = 0.5; + } + return true; + } + return false; + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(`Reflect reduced the damage of physical moves${this.side === ArenaTagSide.PLAYER ? '\non your side' : this.side === ArenaTagSide.ENEMY ? '\non the foe\'s side' : ''}.`); + } +} + +class LightScreenTag extends WeakenMoveScreenTag { + constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side); + } + + apply(arena: Arena, args: any[]): boolean { + if ((args[0] as MoveCategory) === MoveCategory.SPECIAL) { + if ((args[1] as boolean)) { + (args[2] as Utils.NumberHolder).value = 2732/4096; + } else { + (args[2] as Utils.NumberHolder).value = 0.5; + } + return true; + } + return false; + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(`Light Screen reduced the damage of special moves${this.side === ArenaTagSide.PLAYER ? '\non your side' : this.side === ArenaTagSide.ENEMY ? '\non the foe\'s side' : ''}.`); + } +} + +class AuroraVeilTag extends WeakenMoveScreenTag { + constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side); + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(`Aurora Veil reduced the damage of moves${this.side === ArenaTagSide.PLAYER ? '\non your side' : this.side === ArenaTagSide.ENEMY ? '\non the foe\'s side' : ''}.`); + } +} + export class WeakenMoveTypeTag extends ArenaTag { private weakenedType: Type; @@ -378,5 +447,11 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new TrickRoomTag(turnCount, sourceId); case ArenaTagType.GRAVITY: return new GravityTag(turnCount); + case ArenaTagType.REFLECT: + return new ReflectTag(turnCount, sourceId, side); + case ArenaTagType.LIGHT_SCREEN: + return new LightScreenTag(turnCount, sourceId, side); + case ArenaTagType.AURORA_VEIL: + return new AuroraVeilTag(turnCount, sourceId, side); } } diff --git a/src/data/enums/arena-tag-type.ts b/src/data/enums/arena-tag-type.ts index 90c5b1dc5..ab6d28caa 100644 --- a/src/data/enums/arena-tag-type.ts +++ b/src/data/enums/arena-tag-type.ts @@ -10,5 +10,8 @@ export enum ArenaTagType { DOOM_DESIRE = "DOOM_DESIRE", STEALTH_ROCK = "STEALTH_ROCK", TRICK_ROOM = "TRICK_ROOM", - GRAVITY = "GRAVITY" + GRAVITY = "GRAVITY", + REFLECT = "REFLECT", + LIGHT_SCREEN = "LIGHT_SCREEN", + AURORA_VEIL = "AURORA_VEIL" } diff --git a/src/data/move.ts b/src/data/move.ts index 8ac38c5ff..e17ece481 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -3071,11 +3071,13 @@ export function initMoves() { .attr(StatChangeAttr, BattleStat.DEF, 1, true), new SelfStatusMove(Moves.BARRIER, "Barrier", Type.PSYCHIC, -1, 20, "The user throws up a sturdy wall that sharply raises its Defense stat.", -1, 0, 1) .attr(StatChangeAttr, BattleStat.DEF, 2, true), - new StatusMove(Moves.LIGHT_SCREEN, "Light Screen (N)", Type.PSYCHIC, -1, 30, "A wondrous wall of light is put up to reduce damage from special attacks for five turns.", -1, 0, 1) + new StatusMove(Moves.LIGHT_SCREEN, "Light Screen", Type.PSYCHIC, -1, 30, "A wondrous wall of light is put up to reduce damage from special attacks for five turns.", -1, 0, 1) + .attr(AddArenaTagAttr, ArenaTagType.LIGHT_SCREEN, 5, true) .target(MoveTarget.USER_SIDE), new StatusMove(Moves.HAZE, "Haze (N)", Type.ICE, -1, 30, "The user creates a haze that eliminates every stat change among all the Pokémon engaged in battle.", -1, 0, 1) .target(MoveTarget.BOTH_SIDES), - new StatusMove(Moves.REFLECT, "Reflect (N)", Type.PSYCHIC, -1, 20, "A wondrous wall of light is put up to reduce damage from physical attacks for five turns.", -1, 0, 1) + new StatusMove(Moves.REFLECT, "Reflect", Type.PSYCHIC, -1, 20, "A wondrous wall of light is put up to reduce damage from physical attacks for five turns.", -1, 0, 1) + .attr(AddArenaTagAttr, ArenaTagType.REFLECT, 5, true) .target(MoveTarget.USER_SIDE), new SelfStatusMove(Moves.FOCUS_ENERGY, "Focus Energy", Type.NORMAL, -1, 30, "The user takes a deep breath and focuses so that critical hits land more easily.", -1, 0, 1) .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, true), @@ -4415,8 +4417,9 @@ export function initMoves() { new AttackMove(Moves.DRAGON_HAMMER, "Dragon Hammer", Type.DRAGON, MoveCategory.PHYSICAL, 90, 100, 15, "The user uses its body like a hammer to attack the target and inflict damage.", -1, 0, 7), new AttackMove(Moves.BRUTAL_SWING, "Brutal Swing", Type.DARK, MoveCategory.PHYSICAL, 60, 100, 20, "The user swings its body around violently to inflict damage on everything in its vicinity.", -1, 0, 7) .target(MoveTarget.ALL_NEAR_OTHERS), - new StatusMove(Moves.AURORA_VEIL, "Aurora Veil (N)", Type.ICE, -1, 20, "This move reduces damage from physical and special moves for five turns. This can be used only in a hailstorm.", -1, 0, 7) - .condition((user, target, move) => user.scene.arena.weather?.weatherType === WeatherType.HAIL) + new StatusMove(Moves.AURORA_VEIL, "Aurora Veil", Type.ICE, -1, 20, "This move reduces damage from physical and special moves for five turns. This can be used only in a hailstorm.", -1, 0, 7) + .condition((user, target, move) => user.scene.arena.weather?.weatherType === WeatherType.HAIL && !user.scene.arena.weather?.isEffectSuppressed(user.scene)) + .attr(AddArenaTagAttr, ArenaTagType.AURORA_VEIL, 5, true) .target(MoveTarget.USER_SIDE), /* Unused */ new AttackMove(Moves.SINISTER_ARROW_RAID, "Sinister Arrow Raid (P)", Type.GHOST, MoveCategory.PHYSICAL, 180, -1, 1, "The user, Decidueye, creates countless arrows using its Z-Power and shoots the target with full force.", -1, 0, 7) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 732439c7a..c02f44cdb 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -22,7 +22,7 @@ import { BattlerTagType } from "../data/enums/battler-tag-type"; import { Species } from '../data/enums/species'; import { WeatherType } from '../data/weather'; import { TempBattleStat } from '../data/temp-battle-stat'; -import { WeakenMoveTypeTag } from '../data/arena-tag'; +import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; @@ -1133,6 +1133,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = isCritical ? 2 : 1; + const screenMultiplier = new Utils.NumberHolder(1); + if (!isCritical) { + this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier); + } const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier) === 0; const sourceTypes = source.getTypes(); const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type); @@ -1151,7 +1155,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); if (!isTypeImmune) { - damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * ((this.scene.randBattleSeedInt(15) + 85) / 100)) * criticalMultiplier; + damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100)) * criticalMultiplier; if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); applyAbAttrs(BypassBurnDamageReductionAbAttr, this, burnDamageReductionCancelled);