Implement Zero to hero (#1131)
* Implemented Zero-To-Hero * Zero to Hero documentation and form reset on summon * Zero to Hero: form reset on biome/trainer * Updated documentation on PreSwitchOutFormChangeAbAttr apply * Faint bypass on canApplyAbility * revert zygarde * zero to hero post-merge
This commit is contained in:
parent
4b8c9c87c3
commit
89f58961cb
|
@ -17,7 +17,7 @@ import { Moves } from "./data/enums/moves";
|
|||
import { allMoves } from "./data/move";
|
||||
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue } from "./modifier/modifier-type";
|
||||
import AbilityBar from "./ui/ability-bar";
|
||||
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, applyAbAttrs } from "./data/ability";
|
||||
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
|
||||
import { allAbilities } from "./data/ability";
|
||||
import Battle, { BattleType, FixedBattleConfig, fixedBattles } from "./battle";
|
||||
import { GameMode, GameModes, gameModes } from "./game-mode";
|
||||
|
@ -979,6 +979,7 @@ export default class BattleScene extends SceneBase {
|
|||
if (pokemon) {
|
||||
if (resetArenaState) {
|
||||
pokemon.resetBattleData();
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon, true);
|
||||
}
|
||||
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
|
||||
}
|
||||
|
|
|
@ -1753,6 +1753,39 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute for form changes that occur on switching out
|
||||
* @extends PreSwitchOutAbAttr
|
||||
* @see {@linkcode applyPreSwitchOut}
|
||||
*/
|
||||
export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr {
|
||||
private formFunc: (p: Pokemon) => integer;
|
||||
|
||||
constructor(formFunc: ((p: Pokemon) => integer)) {
|
||||
super();
|
||||
|
||||
this.formFunc = formFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* On switch out, trigger the form change to the one defined in the ability
|
||||
* @param pokemon The pokemon switching out and changing form {@linkcode Pokemon}
|
||||
* @param passive N/A
|
||||
* @param args N/A
|
||||
* @returns true if the form change was successful
|
||||
*/
|
||||
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
const formIndex = this.formFunc(pokemon);
|
||||
if (formIndex !== pokemon.formIndex) {
|
||||
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class PreStatChangeAbAttr extends AbAttr {
|
||||
applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
|
@ -3038,7 +3071,7 @@ export class MoneyAbAttr extends PostBattleAbAttr {
|
|||
function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any[]): TAttr },
|
||||
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, args: any[], isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false, passive: boolean = false): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (!pokemon.canApplyAbility(passive)) {
|
||||
if (!pokemon.canApplyAbility(passive, args[0])) {
|
||||
if (!passive) {
|
||||
return applyAbAttrsInternal(attrType, pokemon, applyFunc, args, isAsync, showAbilityInstant, quiet, true).then(() => resolve());
|
||||
} else {
|
||||
|
@ -3712,7 +3745,7 @@ export function initAbilities() {
|
|||
.attr(PostDefendContactDamageAbAttr, 8)
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.ZEN_MODE, 5)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
@ -3810,7 +3843,7 @@ export function initAbilities() {
|
|||
new Ability(Abilities.MERCILESS, 7)
|
||||
.attr(ConditionalCritAbAttr, (user, target, move) => target.status?.effect === StatusEffect.TOXIC || target.status?.effect === StatusEffect.POISON),
|
||||
new Ability(Abilities.SHIELDS_DOWN, 7)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
@ -3843,7 +3876,7 @@ export function initAbilities() {
|
|||
new Ability(Abilities.SURGE_SURFER, 7)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2),
|
||||
new Ability(Abilities.SCHOOLING, 7)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
@ -3853,7 +3886,7 @@ export function initAbilities() {
|
|||
new Ability(Abilities.DISGUISE, 7)
|
||||
.attr(PreDefendMovePowerToOneAbAttr, (target, user, move) => target.formIndex === 0 && target.getAttackTypeEffectiveness(move.type, user) > 0)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1)
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PostDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1)
|
||||
.attr(PreDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1)
|
||||
.attr(PostDefendDisguiseAbAttr)
|
||||
|
@ -3865,13 +3898,14 @@ export function initAbilities() {
|
|||
.ignorable()
|
||||
.partial(),
|
||||
new Ability(Abilities.BATTLE_BOND, 7)
|
||||
.attr(PostVictoryFormChangeAbAttr, p => p.getFormKey() ? 2 : 1)
|
||||
.attr(PostVictoryFormChangeAbAttr, () => 2)
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoFusionAbilityAbAttr),
|
||||
new Ability(Abilities.POWER_CONSTRUCT, 7) // TODO: 10% Power Construct Zygarde isn't accounted for yet. If changed, update Zygarde's getSpeciesFormIndex entry accordingly
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2)
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 2)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 || p.getFormKey() === "complete" ? 4 : 2)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
@ -4103,7 +4137,8 @@ export function initAbilities() {
|
|||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.unimplemented(),
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PreSwitchOutFormChangeAbAttr, () => 1),
|
||||
new Ability(Abilities.COMMANDER, 9)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
|
@ -4186,22 +4221,26 @@ export function initAbilities() {
|
|||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPDEF, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.ATK, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.DEF, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(),
|
||||
new Ability(Abilities.TERA_SHIFT, 9)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
|
|
|
@ -570,6 +570,10 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
new SpeciesFormChange(Species.GRENINJA, "battle-bond", "ash", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.GRENINJA, "ash", "battle-bond", new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.PALAFIN] : [
|
||||
new SpeciesFormChange(Species.PALAFIN, "zero", "hero", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.PALAFIN, "hero", "zero", new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.AEGISLASH]: [
|
||||
new SpeciesFormChange(Species.AEGISLASH, "blade", "shield", new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))),
|
||||
new SpeciesFormChange(Species.AEGISLASH, "shield", "blade", new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))),
|
||||
|
|
|
@ -962,7 +962,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* @param {boolean} passive If true, check if passive can be applied instead of non-passive
|
||||
* @returns {Ability} The passive ability of the pokemon
|
||||
*/
|
||||
canApplyAbility(passive: boolean = false): boolean {
|
||||
canApplyAbility(passive: boolean = false, forceBypass: boolean = false): boolean {
|
||||
if (passive && !this.hasPassive()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -990,7 +990,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
||||
return (this.hp || ability.isBypassFaint || forceBypass) && !ability.conditions.find(condition => !condition(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, 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, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "./data/ability";
|
||||
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "./data/ability";
|
||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||
import { getBiomeKey } from "./field/arena";
|
||||
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
|
||||
|
@ -987,18 +987,14 @@ export class EncounterPhase extends BattlePhase {
|
|||
if (!this.loaded) {
|
||||
const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted());
|
||||
|
||||
if (availablePartyMembers[0].isOnField()) {
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, availablePartyMembers[0]);
|
||||
} else {
|
||||
if (!availablePartyMembers[0].isOnField()) {
|
||||
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()) {
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, availablePartyMembers[1]);
|
||||
} else {
|
||||
if (!availablePartyMembers[1].isOnField()) {
|
||||
this.scene.pushPhase(new SummonPhase(this.scene, 1));
|
||||
}
|
||||
}
|
||||
|
@ -1475,8 +1471,6 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
|
||||
}
|
||||
|
||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, pokemon);
|
||||
|
||||
this.scene.ui.showText(this.player ?
|
||||
i18next.t("battle:playerComeBack", { pokemonName: pokemon.name }) :
|
||||
i18next.t("battle:trainerComeBack", {
|
||||
|
@ -1505,6 +1499,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||
const party = this.player ? this.getParty() : this.scene.getEnemyParty();
|
||||
const switchedPokemon = party[this.slotIndex];
|
||||
this.lastPokemon = this.getPokemon();
|
||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
|
||||
if (this.batonPass && switchedPokemon) {
|
||||
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedPokemon.id));
|
||||
if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedPokemon.id)) {
|
||||
|
|
Loading…
Reference in New Issue