From dbca257a766b3ccae924ce603e827516037327bf Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 30 Mar 2024 00:53:35 -0400 Subject: [PATCH] Add Schooling ability for Wishiwashi and fix Beast Boost --- src/data/ability.ts | 87 ++++++++++++++++++++++++++++++++++++++- src/data/pokemon-forms.ts | 4 ++ src/field/pokemon.ts | 2 +- src/phases.ts | 10 +++-- 4 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index e419803fd..972da7741 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -14,6 +14,7 @@ import { Stat } from "./pokemon-stat"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { Moves } from "./enums/moves"; import { TerrainType } from "./terrain"; +import { SpeciesFormChangeManualTrigger } from "./pokemon-forms"; export class Ability { public id: Abilities; @@ -130,6 +131,30 @@ export class DoubleBattleChanceAbAttr extends AbAttr { } } +export class PostBattleInitAbAttr extends AbAttr { + applyPostBattleInit(pokemon: Pokemon, args: any[]): boolean | Promise { + return false; + } +} + +export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { + private formFunc: (p: Pokemon) => integer; + + constructor(formFunc: ((p: Pokemon) => integer)) { + super(true); + + this.formFunc = formFunc; + } + + applyPostBattleInit(pokemon: Pokemon, args: any[]): boolean { + const formIndex = this.formFunc(pokemon); + if (formIndex !== pokemon.formIndex) + return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + + return false; + } +} + type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: PokemonMove) => boolean; export class PreDefendAbAttr extends AbAttr { @@ -786,6 +811,24 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { } } +export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { + private formFunc: (p: Pokemon) => integer; + + constructor(formFunc: ((p: Pokemon) => integer)) { + super(true); + + this.formFunc = formFunc; + } + + applyPostSummon(pokemon: Pokemon, args: any[]): boolean { + const formIndex = this.formFunc(pokemon); + if (formIndex !== pokemon.formIndex) + return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + + return false; + } +} + export class PostSummonTransformAbAttr extends PostSummonAbAttr { constructor() { super(true); @@ -1174,6 +1217,26 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { } } +export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { + private formFunc: (p: Pokemon) => integer; + + constructor(formFunc: ((p: Pokemon) => integer)) { + super(true); + + this.formFunc = formFunc; + } + + applyPostTurn(pokemon: Pokemon, args: any[]): boolean { + const formIndex = this.formFunc(pokemon); + if (formIndex !== pokemon.formIndex) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + return true; + } + + return false; + } +} + export class StatChangeMultiplierAbAttr extends AbAttr { private multiplier: integer; @@ -1481,6 +1544,11 @@ export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: return applyAbAttrsInternal(attrType, pokemon, attr => attr.apply(pokemon, cancelled, args)); } +export function applyPostBattleInitAbAttrs(attrType: { new(...args: any[]): PostBattleInitAbAttr }, + pokemon: Pokemon, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostBattleInit(pokemon, args)); +} + export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr }, pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; @@ -2337,7 +2405,10 @@ export function initAbilities() { new Ability(Abilities.GALVANIZE, "Galvanize (N)", "Normal-type moves become Electric-type moves. The power of those moves is boosted a little.", 7), new Ability(Abilities.SURGE_SURFER, "Surge Surfer", "Doubles the Pokémon's Speed stat on Electric Terrain.", 7) .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2), - new Ability(Abilities.SCHOOLING, "Schooling (N)", "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.", 7) + new Ability(Abilities.SCHOOLING, "Schooling", "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.", 7) + .attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.25 ? 0 : 1) + .attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.25 ? 0 : 1) + .attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.25 ? 0 : 1) .attr(ProtectAbilityAbAttr), new Ability(Abilities.DISGUISE, "Disguise (N)", "Once per battle, the shroud that covers the Pokémon can protect it from an attack.", 7) .ignorable() @@ -2365,7 +2436,19 @@ export function initAbilities() { new Ability(Abilities.RECEIVER, "Receiver (N)", "The Pokémon copies the Ability of a defeated ally.", 7), new Ability(Abilities.POWER_OF_ALCHEMY, "Power of Alchemy (N)", "The Pokémon copies the Ability of a defeated ally.", 7), new Ability(Abilities.BEAST_BOOST, "Beast Boost", "The Pokémon boosts its most proficient stat each time it knocks out a Pokémon.", 7) - .attr(PostVictoryStatChangeAbAttr, p => Utils.getEnumValues(BattleStat).slice(1, -2).map(s => s as BattleStat).findIndex(bs => p.getStat((bs + 1) as Stat)), 1), + .attr(PostVictoryStatChangeAbAttr, p => { + const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3).map(s => s as BattleStat); + let highestBattleStat = 0; + let highestBattleStatIndex = 0; + battleStats.map((bs: BattleStat, i: integer) => { + const stat = p.getStat(bs + 1); + if (stat > highestBattleStat) { + highestBattleStatIndex = i; + highestBattleStat = stat; + } + }); + return highestBattleStatIndex; + }, 1), new Ability(Abilities.RKS_SYSTEM, "RKS System (N)", "Changes the Pokémon's type to match the memory disc it holds.", 7) .attr(ProtectAbilityAbAttr), new Ability(Abilities.ELECTRIC_SURGE, "Electric Surge", "Turns the ground into Electric Terrain when the Pokémon enters a battle.", 7) diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index be7bafd46..79d8b0be1 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -518,6 +518,10 @@ export const pokemonFormChanges: PokemonFormChanges = { [Species.HOOPA]: [ new SpeciesFormChange(Species.HOOPA, '', 'unbound', new SpeciesFormChangeItemTrigger(FormChangeItem.PRISON_BOTTLE)) ], + [Species.WISHIWASHI]: [ + new SpeciesFormChange(Species.WISHIWASHI, '', 'school', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.WISHIWASHI, 'school', '', new SpeciesFormChangeManualTrigger(), true) + ], [Species.NECROZMA]: [ new SpeciesFormChange(Species.NECROZMA, '', 'dawn-wings', new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER)), new SpeciesFormChange(Species.NECROZMA, '', 'dusk-mane', new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER)) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 84c0250ca..e5f099488 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2514,7 +2514,7 @@ export class EnemyPokemon extends Pokemon { while (segmentIndex - 1 < this.bossSegmentIndex) { let boostedStat = BattleStat.RAND; - const battleStats = Utils.getEnumValues(BattleStat).slice(0, -2); + const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3); const statWeights = new Array().fill(battleStats.length).filter((bs: BattleStat) => this.summonData.battleStats[bs] < 6).map((bs: BattleStat) => this.getStat(bs + 1)); const statThresholds: integer[] = []; let totalWeight = 0; diff --git a/src/phases.ts b/src/phases.ts index 40482b083..86ada84e0 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -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 } 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 } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -811,13 +811,17 @@ export class EncounterPhase extends BattlePhase { if (!this.loaded) { const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()); - if (!availablePartyMembers[0].isOnField()) + if (availablePartyMembers[0].isOnField()) + applyPostBattleInitAbAttrs(PostBattleInitAbAttr, availablePartyMembers[0]); + else this.scene.pushPhase(new SummonPhase(this.scene, 0)); if (this.scene.currentBattle.double) { if (availablePartyMembers.length > 1) { this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, true)); - if (!availablePartyMembers[1].isOnField()) + if (availablePartyMembers[1].isOnField()) + applyPostBattleInitAbAttrs(PostBattleInitAbAttr, availablePartyMembers[1]); + else this.scene.pushPhase(new SummonPhase(this.scene, 1)); } } else {