pokerogue/src/phases/switch-summon-phase.ts
innerthunder a6a61b2984
[Move] Implement Shed Tail (#4382)
* Implement Shed Tail

* Fix leftover batonPass reference in docs

* Fix ChillyReceptionAttr

* oops

* Remove unneeded default arg in ReturnPhase

* Fix imports per Kev's suggestions

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Docs and Shed Tail on-add message

* Remove mixin attribute

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Update battler-tags.json

* Fix indents

* More nit fixes

* Make Switch[Summon]Phase params readonly

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Lugiad <adrien.grivel@hotmail.fr>
Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
2024-09-25 17:17:46 -07:00

198 lines
8.3 KiB
TypeScript

import BattleScene from "#app/battle-scene";
import { applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr } from "#app/data/ability";
import { allMoves, ForceSwitchOutAttr } from "#app/data/move";
import { getPokeballTintColor } from "#app/data/pokeball";
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
import { TrainerSlot } from "#app/data/trainer-config";
import Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { SwitchEffectTransferModifier } from "#app/modifier/modifier";
import { Command } from "#app/ui/command-ui-handler";
import i18next from "i18next";
import { PostSummonPhase } from "./post-summon-phase";
import { SummonPhase } from "./summon-phase";
import { SubstituteTag } from "#app/data/battler-tags";
import { SwitchType } from "#enums/switch-type";
export class SwitchSummonPhase extends SummonPhase {
private readonly switchType: SwitchType;
private readonly slotIndex: integer;
private readonly doReturn: boolean;
private lastPokemon: Pokemon;
/**
* Constructor for creating a new SwitchSummonPhase
* @param scene {@linkcode BattleScene} the scene the phase is associated with
* @param switchType the type of switch behavior
* @param fieldIndex integer representing position on the battle field
* @param slotIndex integer for the index of pokemon (in party of 6) to switch into
* @param doReturn boolean whether to render "comeback" dialogue
* @param player boolean if the switch is from the player
*/
constructor(scene: BattleScene, switchType: SwitchType, fieldIndex: integer, slotIndex: integer, doReturn: boolean, player?: boolean) {
super(scene, fieldIndex, player !== undefined ? player : true);
this.switchType = switchType;
this.slotIndex = slotIndex;
this.doReturn = doReturn;
}
start(): void {
super.start();
}
preSummon(): void {
if (!this.player) {
if (this.slotIndex === -1) {
//@ts-ignore
this.slotIndex = this.scene.currentBattle.trainer?.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); // TODO: what would be the default trainer-slot fallback?
}
if (this.slotIndex > -1) {
this.showEnemyTrainer(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER);
this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty());
}
}
if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) {
if (this.player) {
return this.switchAndSummon();
} else {
this.scene.time.delayedCall(750, () => this.switchAndSummon());
return;
}
}
const pokemon = this.getPokemon();
if (this.switchType === SwitchType.SWITCH) {
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
const substitute = pokemon.getTag(SubstituteTag);
if (substitute) {
this.scene.tweens.add({
targets: substitute.sprite,
duration: 250,
scale: substitute.sprite.scale * 0.5,
ease: "Sine.easeIn",
onComplete: () => substitute.sprite.destroy()
});
}
}
this.scene.ui.showText(this.player ?
i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) :
i18next.t("battle:trainerComeBack", {
trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER),
pokemonName: pokemon.getNameToRender()
})
);
this.scene.playSound("se/pb_rel");
pokemon.hideInfo();
pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn");
this.scene.tweens.add({
targets: pokemon,
duration: 250,
ease: "Sine.easeIn",
scale: 0.5,
onComplete: () => {
pokemon.leaveField(this.switchType === SwitchType.SWITCH, false);
this.scene.time.delayedCall(750, () => this.switchAndSummon());
}
});
}
switchAndSummon() {
const party = this.player ? this.getParty() : this.scene.getEnemyParty();
const switchedInPokemon = party[this.slotIndex];
this.lastPokemon = this.getPokemon();
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
if (this.switchType === SwitchType.BATON_PASS && switchedInPokemon) {
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id));
if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) {
const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier
&& (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier;
if (batonPassModifier && !this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) {
this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedInPokemon, false);
}
}
}
if (switchedInPokemon) {
party[this.slotIndex] = this.lastPokemon;
party[this.fieldIndex] = switchedInPokemon;
const showTextAndSummon = () => {
this.scene.ui.showText(this.player ?
i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedInPokemon) }) :
i18next.t("battle:trainerGo", {
trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER),
pokemonName: this.getPokemon().getNameToRender()
})
);
/**
* If this switch is passing a Substitute, make the switched Pokemon match the returned Pokemon's state as it left.
* Otherwise, clear any persisting tags on the returned Pokemon.
*/
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
const substitute = this.lastPokemon.getTag(SubstituteTag);
if (substitute) {
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
switchedInPokemon.setAlpha(0.5);
}
} else {
switchedInPokemon.resetBattleData();
switchedInPokemon.resetSummonData();
}
this.summon();
};
if (this.player) {
showTextAndSummon();
} else {
this.scene.time.delayedCall(1500, () => {
this.hideEnemyTrainer();
this.scene.pbTrayEnemy.hide();
showTextAndSummon();
});
}
} else {
this.end();
}
}
onEnd(): void {
super.onEnd();
const pokemon = this.getPokemon();
const moveId = this.lastPokemon?.scene.currentBattle.lastMove;
const lastUsedMove = moveId ? allMoves[moveId] : undefined;
const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command;
const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted();
// Compensate for turn spent summoning
// Or compensate for force switch move if switched out pokemon is not fainted
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) {
pokemon.battleSummonData.turnCount--;
}
if (this.switchType === SwitchType.BATON_PASS && pokemon) {
pokemon.transferSummon(this.lastPokemon);
} else if (this.switchType === SwitchType.SHED_TAIL && pokemon) {
const subTag = this.lastPokemon.getTag(SubstituteTag);
if (subTag) {
pokemon.summonData.tags.push(subTag);
}
}
this.lastPokemon?.resetSummonData();
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
// Reverts to weather-based forms when weather suppressors (Cloud Nine/Air Lock) are switched out
this.scene.arena.triggerWeatherBasedFormChanges();
}
queuePostSummon(): void {
this.scene.unshiftPhase(new PostSummonPhase(this.scene, this.getPokemon().getBattlerIndex()));
}
}