Implement "Emergency Exit" and "Wimp Out"

pull/384/head
Korwai 2024-05-03 12:58:03 -07:00
parent 56b24c70b6
commit 597abdcd41
2 changed files with 64 additions and 6 deletions

View File

@ -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 { Type } from "./type";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattleStat, getBattleStatName } from "./battle-stat"; 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 { getPokemonMessage } from "../messages";
import { Weather, WeatherType } from "./weather"; import { Weather, WeatherType } from "./weather";
import { BattlerTag } from "./battler-tags"; 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<boolean> {
return false;
}
}
export class PostDefendAbAttr extends AbAttr { export class PostDefendAbAttr extends AbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
return false; 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<boolean> {
return new Promise<boolean>(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 { export class PostDefendDisguiseAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { 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<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); return applyAbAttrsInternal<PostDefendAbAttr>(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<void> {
return applyAbAttrsInternal<PostDamageAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDamage(pokemon, initialPokemonHpRatio, passive), args);
}
export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr }, export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr },
pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise<void> { pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<BattleStatMultiplierAbAttr>(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args); return applyAbAttrsInternal<BattleStatMultiplierAbAttr>(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args);
@ -3031,9 +3086,9 @@ export function initAbilities() {
new Ability(Abilities.STAMINA, 7) new Ability(Abilities.STAMINA, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1),
new Ability(Abilities.WIMP_OUT, 7) new Ability(Abilities.WIMP_OUT, 7)
.unimplemented(), .attr(PostDamageForcedSwitchAbAttr, (postMovePokemon, initialPokemonHpRatio) => initialPokemonHpRatio > 0.5 && postMovePokemon.getHpRatio() <= 0.5),
new Ability(Abilities.EMERGENCY_EXIT, 7) new Ability(Abilities.EMERGENCY_EXIT, 7)
.unimplemented(), .attr(PostDamageForcedSwitchAbAttr, (postMovePokemon, initialPokemonHpRatio) => initialPokemonHpRatio > 0.5 && postMovePokemon.getHpRatio() <= 0.5),
new Ability(Abilities.WATER_COMPACTION, 7) new Ability(Abilities.WATER_COMPACTION, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2),
new Ability(Abilities.MERCILESS, 7) new Ability(Abilities.MERCILESS, 7)

View File

@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag';
import { ArenaTagType } from "../data/enums/arena-tag-type"; import { ArenaTagType } from "../data/enums/arena-tag-type";
import { Biome } from "../data/enums/biome"; 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 { Abilities } from "#app/data/enums/abilities";
import PokemonData from '../system/pokemon-data'; import PokemonData from '../system/pokemon-data';
import Battle, { BattlerIndex } from '../battle'; import Battle, { BattlerIndex } from '../battle';
@ -1223,6 +1223,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const move = battlerMove.getMove(); const move = battlerMove.getMove();
let damage = new Utils.NumberHolder(0); let damage = new Utils.NumberHolder(0);
const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField(); const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField();
const initialPokemonHpRatio: integer = this.getHpRatio();
const variableCategory = new Utils.IntegerHolder(move.category); const variableCategory = new Utils.IntegerHolder(move.category);
applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory); applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory);
@ -1442,6 +1443,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
applyPostDamageAbAttrs(PostDamageForcedSwitchAbAttr, this, initialPokemonHpRatio);
if (damage) if (damage)
this.scene.clearPhaseQueueSplice(); this.scene.clearPhaseQueueSplice();
} }