From 597abdcd414745fa71aeea5989ca47b939c51b0f Mon Sep 17 00:00:00 2001 From: Korwai Date: Fri, 3 May 2024 12:58:03 -0700 Subject: [PATCH] Implement "Emergency Exit" and "Wimp Out" --- src/data/ability.ts | 63 +++++++++++++++++++++++++++++++++++++++++--- src/field/pokemon.ts | 7 +++-- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 2ac7d6be1..53ec9ebd2 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1,8 +1,8 @@ -import Pokemon, { HitResult, PokemonMove } from "../field/pokemon"; +import Pokemon, { EnemyPokemon, HitResult, PlayerPokemon, PokemonMove } from "../field/pokemon"; import { Type } from "./type"; import * as Utils from "../utils"; import { BattleStat, getBattleStatName } from "./battle-stat"; -import { PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; +import { BattleEndPhase, CheckSwitchPhase, NewBattlePhase, PokemonHealPhase, ReturnPhase, ShowAbilityPhase, StatChangePhase, SwitchPhase, SwitchSummonPhase } from "../phases"; import { getPokemonMessage } from "../messages"; import { Weather, WeatherType } from "./weather"; import { BattlerTag } from "./battler-tags"; @@ -438,12 +438,62 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } } +export class PostDamageAbAttr extends AbAttr { + applyPostDamage(pokemon: Pokemon, initialPokemonHpRatio: number, passive: boolean): boolean | Promise { + return false; + } +} + export class PostDefendAbAttr extends AbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise { return false; } } +type HpThresholdCondition = (postMovePokemon: Pokemon, initialPokemonHpRatio: integer) => boolean; + +export class PostDamageForcedSwitchAbAttr extends PostDamageAbAttr { + private condition: HpThresholdCondition; + + constructor(condition?: HpThresholdCondition) { + super(); + + this.condition = condition; + } + + applyPostDamage(pokemon: Pokemon, initialPokemonHpRatio: number, passive: boolean): Promise { + return new Promise(resolve => { + if (this.condition(pokemon, initialPokemonHpRatio) && pokemon.isPlayer()) { + // Player's pokemon + applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, pokemon); + pokemon.scene.unshiftPhase(new SwitchPhase(pokemon.scene, pokemon.getFieldIndex(), true, true)); + resolve(true); + } else if (this.condition(pokemon, initialPokemonHpRatio) && !pokemon.hasTrainer()) { + // Wild pokemon + pokemon.hideInfo().then(() => pokemon.destroy()); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' fled!'), null, true, 500); + pokemon.scene.pushPhase(new BattleEndPhase(pokemon.scene)); + pokemon.scene.pushPhase(new NewBattlePhase(pokemon.scene)); + resolve(true); + } else if (this.condition(pokemon, initialPokemonHpRatio) && pokemon.hasTrainer()) { + // Enemy trainer pokemon + pokemon.updateInfo(); + pokemon.scene.pushPhase(new SwitchSummonPhase(pokemon.scene, pokemon.getFieldIndex(), pokemon.scene.currentBattle.trainer.getNextSummonIndex((pokemon as EnemyPokemon).trainerSlot), false, false, false)); + + pokemon.resetTurnData(); + pokemon.resetSummonData(); + pokemon.hideInfo(); + pokemon.setVisible(false); + pokemon.scene.field.remove(pokemon); + + resolve(true); + } else { + resolve(false); + } + }); + } +} + export class PostDefendDisguiseAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { @@ -2385,6 +2435,11 @@ export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefe return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); } +export function applyPostDamageAbAttrs(attrType: { new(...args: any[]): PostDamageAbAttr }, + pokemon: Pokemon, initialPokemonHpRatio: integer, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDamage(pokemon, initialPokemonHpRatio, passive), args); +} + export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr }, pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args); @@ -3031,9 +3086,9 @@ export function initAbilities() { new Ability(Abilities.STAMINA, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1), new Ability(Abilities.WIMP_OUT, 7) - .unimplemented(), + .attr(PostDamageForcedSwitchAbAttr, (postMovePokemon, initialPokemonHpRatio) => initialPokemonHpRatio > 0.5 && postMovePokemon.getHpRatio() <= 0.5), new Ability(Abilities.EMERGENCY_EXIT, 7) - .unimplemented(), + .attr(PostDamageForcedSwitchAbAttr, (postMovePokemon, initialPokemonHpRatio) => initialPokemonHpRatio > 0.5 && postMovePokemon.getHpRatio() <= 0.5), new Ability(Abilities.WATER_COMPACTION, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, 7) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 55036e190..0aab01892 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PostDamageForcedSwitchAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDamageAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -1223,7 +1223,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const move = battlerMove.getMove(); let damage = new Utils.NumberHolder(0); const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField(); - + const initialPokemonHpRatio: integer = this.getHpRatio(); + const variableCategory = new Utils.IntegerHolder(move.category); applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory); const moveCategory = variableCategory.value as MoveCategory; @@ -1442,6 +1443,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + applyPostDamageAbAttrs(PostDamageForcedSwitchAbAttr, this, initialPokemonHpRatio); + if (damage) this.scene.clearPhaseQueueSplice(); }