[Ability] Implement Treasures of Ruin Abilities (#1637)

* Implement Treasures of Ruin abilities

* Reformat documentation

---------

Co-authored-by: Temps Ray <temps.ray@gmail.com>
This commit is contained in:
innerthunder 2024-06-11 06:37:10 -07:00 committed by GitHub
parent 1a6b411987
commit 670ea01cb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 105 additions and 10 deletions

View File

@ -12,7 +12,7 @@ import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type"; import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat"; import { Stat, getStatName } from "./pokemon-stat";
import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier";
import { Moves } from "./enums/moves"; import { Moves } from "./enums/moves";
import { TerrainType } from "./terrain"; import { TerrainType } from "./terrain";
@ -1032,6 +1032,52 @@ export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
} }
} }
/**
* Multiplies a BattleStat if the checked Pokemon lacks this ability.
* If this ability cannot stack, a BooleanHolder can be used to prevent this from stacking.
* @see {@link applyFieldBattleStatMultiplierAbAttrs}
* @see {@link applyFieldBattleStat}
* @see {@link Utils.BooleanHolder}
*/
export class FieldMultiplyBattleStatAbAttr extends AbAttr {
private stat: Stat;
private multiplier: number;
private canStack: boolean;
constructor(stat: Stat, multiplier: number, canStack: boolean = false) {
super(false);
this.stat = stat;
this.multiplier = multiplier;
this.canStack = canStack;
}
/**
* applyFieldBattleStat: Tries to multiply a Pokemon's BattleStat
* @param pokemon {@linkcode Pokemon} the Pokemon using this ability
* @param passive {@linkcode boolean} unused
* @param stat {@linkcode Stat} the type of the checked stat
* @param statValue {@linkcode Utils.NumberHolder} the value of the checked stat
* @param checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting
* @param hasApplied {@linkcode Utils.BooleanHolder} whether or not another multiplier has been applied to this stat
* @param args {any[]} unused
* @returns true if this changed the checked stat, false otherwise.
*/
applyFieldBattleStat(pokemon: Pokemon, passive: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean {
if (!this.canStack && hasApplied.value) {
return false;
}
if (this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyBattleStatAbAttr).every(attr => (attr as FieldMultiplyBattleStatAbAttr).stat !== stat)) {
statValue.value *= this.multiplier;
hasApplied.value = true;
return true;
}
return false;
}
}
export class MoveTypeChangeAttr extends PreAttackAbAttr { export class MoveTypeChangeAttr extends PreAttackAbAttr {
private newType: Type; private newType: Type;
private powerMultiplier: number; private powerMultiplier: number;
@ -3573,6 +3619,21 @@ export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[])
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);
} }
/**
* Applies a field Battle Stat multiplier attribute
* @param attrType {@linkcode FieldMultiplyBattleStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
* @param pokemon {@linkcode Pokemon} the Pokemon applying this ability
* @param stat {@linkcode Stat} the type of the checked stat
* @param statValue {@linkcode Utils.NumberHolder} the value of the checked stat
* @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat
* @param hasApplied {@linkcode Utils.BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat
* @param args unused
*/
export function applyFieldBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): FieldMultiplyBattleStatAbAttr },
pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<FieldMultiplyBattleStatAbAttr>(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, stat, statValue, checkedPokemon, hasApplied, args), args);
}
export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr }, export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr },
pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise<void> { pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args);
@ -4596,17 +4657,21 @@ export function initAbilities() {
.ignorable() .ignorable()
.partial(), .partial(),
new Ability(Abilities.VESSEL_OF_RUIN, 9) new Ability(Abilities.VESSEL_OF_RUIN, 9)
.ignorable() .attr(FieldMultiplyBattleStatAbAttr, Stat.SPATK, 0.75)
.unimplemented(), .attr(PostSummonMessageAbAttr, (user) => getPokemonMessage(user, `'s Vessel of Ruin lowered the ${getStatName(Stat.SPATK)}\nof all surrounding Pokémon!`))
.ignorable(),
new Ability(Abilities.SWORD_OF_RUIN, 9) new Ability(Abilities.SWORD_OF_RUIN, 9)
.ignorable() .attr(FieldMultiplyBattleStatAbAttr, Stat.DEF, 0.75)
.unimplemented(), .attr(PostSummonMessageAbAttr, (user) => getPokemonMessage(user, `'s Sword of Ruin lowered the ${getStatName(Stat.DEF)}\nof all surrounding Pokémon!`))
.ignorable(),
new Ability(Abilities.TABLETS_OF_RUIN, 9) new Ability(Abilities.TABLETS_OF_RUIN, 9)
.ignorable() .attr(FieldMultiplyBattleStatAbAttr, Stat.ATK, 0.75)
.unimplemented(), .attr(PostSummonMessageAbAttr, (user) => getPokemonMessage(user, `'s Tablets of Ruin lowered the ${getStatName(Stat.ATK)}\nof all surrounding Pokémon!`))
.ignorable(),
new Ability(Abilities.BEADS_OF_RUIN, 9) new Ability(Abilities.BEADS_OF_RUIN, 9)
.ignorable() .attr(FieldMultiplyBattleStatAbAttr, Stat.SPDEF, 0.75)
.unimplemented(), .attr(PostSummonMessageAbAttr, (user) => getPokemonMessage(user, `'s Beads of Ruin lowered the ${getStatName(Stat.SPDEF)}\nof all surrounding Pokémon!`))
.ignorable(),
new Ability(Abilities.ORICHALCUM_PULSE, 9) new Ability(Abilities.ORICHALCUM_PULSE, 9)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY) .attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY)

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, MoveTypeChangeAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr } from "../data/ability"; import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr } 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 { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
@ -656,6 +656,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), battleStat as integer as TempBattleStat, statLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), battleStat as integer as TempBattleStat, statLevel);
} }
const statValue = new Utils.NumberHolder(this.getStat(stat)); const statValue = new Utils.NumberHolder(this.getStat(stat));
const fieldApplied = new Utils.BooleanHolder(false);
for (const pokemon of this.scene.getField(true)) {
applyFieldBattleStatMultiplierAbAttrs(FieldMultiplyBattleStatAbAttr, pokemon, stat, statValue, this, fieldApplied);
if (fieldApplied.value) {
break;
}
}
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, battleStat, statValue); applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, battleStat, statValue);
let ret = statValue.value * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value)); let ret = statValue.value * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value));
switch (stat) { switch (stat) {
@ -951,6 +958,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return allAbilities[starterPassiveAbilities[starterSpeciesId]]; return allAbilities[starterPassiveAbilities[starterSpeciesId]];
} }
/**
* Gets a list of all instances of a given ability attribute among abilities this pokemon has.
* Accounts for all the various effects which can affect whether an ability will be present or
* in effect, and both passive and non-passive.
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
* @returns {AbAttr[]} A list of all the ability attributes on this ability.
*/
getAbilityAttrs(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): AbAttr[] {
const abilityAttrs: AbAttr[] = [];
if (!canApply || this.canApplyAbility()) {
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType));
}
if (!canApply || this.canApplyAbility(true)) {
abilityAttrs.push(...this.getPassiveAbility().getAttrs(attrType));
}
return abilityAttrs;
}
/** /**
* Checks if a pokemon has a passive either from: * Checks if a pokemon has a passive either from:
* - bought with starter candy * - bought with starter candy