Implement Magic Guard ability

This commit is contained in:
Flashfyre 2024-04-02 15:14:07 -04:00
parent b9b7afb3ee
commit f95f3ff30e
5 changed files with 92 additions and 36 deletions

View File

@ -977,9 +977,16 @@ export class BlockCritAbAttr extends AbAttr {
}
}
export class BlockNonDirectDamageAbAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
return true;
}
}
export class BlockOneHitKOAbAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = false;
cancelled.value = true;
return true;
}
}
@ -2200,7 +2207,8 @@ export function initAbilities() {
new Ability(Abilities.QUICK_FEET, "Quick Feet (N)", "Boosts the Speed stat if the Pokémon has a status condition.", 4),
new Ability(Abilities.NORMALIZE, "Normalize (N)", "All the Pokémon's moves become Normal type. The power of those moves is boosted a little.", 4),
new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits when attacking.", 4),
new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "The Pokémon only takes damage from attacks.", 4),
new Ability(Abilities.MAGIC_GUARD, "Magic Guard", "The Pokémon only takes damage from attacks.", 4)
.attr(BlockNonDirectDamageAbAttr),
new Ability(Abilities.NO_GUARD, "No Guard (N)", "The Pokémon employs no-guard tactics to ensure incoming and outgoing attacks always land.", 4),
new Ability(Abilities.STALL, "Stall (N)", "The Pokémon moves after all other Pokémon do.", 4),
new Ability(Abilities.TECHNICIAN, "Technician", "Powers up the Pokémon's weaker moves.", 4)

View File

@ -9,6 +9,7 @@ import { StatusEffect } from "./status-effect";
import { BattlerIndex } from "../battle";
import { Moves } from "./enums/moves";
import { ArenaTagType } from "./enums/arena-tag-type";
import { BlockNonDirectDamageAbAttr, applyAbAttrs } from "./ability";
export enum ArenaTagSide {
BOTH,
@ -172,12 +173,17 @@ class SpikesTag extends ArenaTrapTag {
activateTrap(pokemon: Pokemon): boolean {
if (pokemon.isGrounded()) {
const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
pokemon.damageAndUpdate(damage, HitResult.OTHER);
return true;
if (!cancelled.value) {
const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
pokemon.damageAndUpdate(damage, HitResult.OTHER);
return true;
}
}
return false;
@ -293,6 +299,12 @@ class StealthRockTag extends ArenaTrapTag {
}
activateTrap(pokemon: Pokemon): boolean {
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (cancelled.value)
return false;
const damageHpRatio = this.getDamageHpRatio(pokemon);
if (damageHpRatio) {

View File

@ -8,7 +8,7 @@ import * as Utils from "../utils";
import { Moves } from "./enums/moves";
import { ChargeAttr, MoveFlags, allMoves } from "./move";
import { Type } from "./type";
import { Abilities, FlinchEffectAbAttr, applyAbAttrs } from "./ability";
import { Abilities, BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, applyAbAttrs } from "./ability";
import { BattlerTagType } from "./enums/battler-tag-type";
import { TerrainType } from "./terrain";
import { WeatherType } from "./weather";
@ -280,10 +280,15 @@ export class SeedTag extends BattlerTag {
if (ret) {
const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex);
if (source) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true));
if (!cancelled.value) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true));
}
}
}
@ -319,7 +324,11 @@ export class NightmareTag extends BattlerTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a Nightmare!'));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4));
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value)
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4));
}
return ret;
@ -506,7 +515,11 @@ export abstract class DamagingTrapTag extends TrappedTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby ${this.getMoveName()}!`));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, this.commonAnim));
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8))
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value)
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8))
}
return ret;
@ -924,10 +937,15 @@ export class SaltCuredTag extends BattlerTag {
if (ret) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE));
const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER);
pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1));
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by ${this.getMoveName()}!`));
if (!cancelled.value) {
const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER);
pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1));
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by ${this.getMoveName()}!`));
}
}
return ret;

View File

@ -12,7 +12,7 @@ import * as Utils from "../utils";
import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { Abilities, ProtectAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs } from "./ability";
import { Abilities, ProtectAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr } from "./ability";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import { BattlerIndex } from "../battle";
import { Stat } from "./pokemon-stat";
@ -634,6 +634,10 @@ export class RecoilAttr extends MoveEffectAttr {
if (!recoilDamage)
return false;
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (cancelled.value)
return false;
user.damageAndUpdate(recoilDamage, HitResult.OTHER, false, true);
user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!'));
@ -1825,6 +1829,11 @@ export class MissEffectAttr extends MoveAttr {
}
const halveHpMissEffectFunc = (user: Pokemon, move: Move) => {
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (cancelled.value)
return false;
const damage = user.damage(Math.floor(user.getMaxHp() / 2));
if (damage)
user.scene.damageNumberHandler.add(user, damage, HitResult.OTHER);

View File

@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type";
import { Abilities, CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "./data/ability";
import { Abilities, CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -2551,6 +2551,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
const cancelled = new Utils.BooleanHolder(false);
applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (cancelled.value)
return;
@ -2620,24 +2621,32 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
const pokemon = this.getPokemon();
if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) {
pokemon.status.incrementTurn();
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect)));
let damage: integer = 0;
switch (pokemon.status.effect) {
case StatusEffect.POISON:
damage = Math.max(pokemon.getMaxHp() >> 3, 1);
break;
case StatusEffect.TOXIC:
damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1);
break;
case StatusEffect.BURN:
damage = Math.max(pokemon.getMaxHp() >> 4, 1);
break;
}
if (damage) {
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage));
pokemon.updateInfo();
}
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
console.log(cancelled.value)
if (!cancelled.value) {
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect)));
let damage: integer = 0;
switch (pokemon.status.effect) {
case StatusEffect.POISON:
damage = Math.max(pokemon.getMaxHp() >> 3, 1);
break;
case StatusEffect.TOXIC:
damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1);
break;
case StatusEffect.BURN:
damage = Math.max(pokemon.getMaxHp() >> 4, 1);
break;
}
if (damage) {
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage));
pokemon.updateInfo();
}
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
} else
this.end();
} else
this.end();
}