[Enhancement] Add support for simulated calls to Abilities (#3529)
* Adds simulated tag support to all abilities
* Fix compiler errors in ability.ts
Most things are still on fire 😢
* Fix errors left over after merge
* Another pass through simulated ability call logic
* Fix leftover errors from merge resolution
* Another gh pages issue :pikamad:
* Simulated call fixes based on Kev's feedback
* RIP phases.ts
---------
Co-authored-by: Xavion3 <xavion333@gmail.com>
This commit is contained in:
parent
1487d7f51c
commit
051d38e0a2
|
@ -1100,7 +1100,7 @@ export default class BattleScene extends SceneBase {
|
||||||
} else if (trainerConfigs[trainerType].hasDouble) {
|
} else if (trainerConfigs[trainerType].hasDouble) {
|
||||||
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||||
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
|
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
|
||||||
doubleTrainer = !Utils.randSeedInt(doubleChance.value);
|
doubleTrainer = !Utils.randSeedInt(doubleChance.value);
|
||||||
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
|
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
|
||||||
if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) {
|
if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) {
|
||||||
|
@ -1116,7 +1116,7 @@ export default class BattleScene extends SceneBase {
|
||||||
if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) {
|
if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) {
|
||||||
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||||
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
|
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
|
||||||
newDouble = !Utils.randSeedInt(doubleChance.value);
|
newDouble = !Utils.randSeedInt(doubleChance.value);
|
||||||
} else if (newBattleType === BattleType.TRAINER) {
|
} else if (newBattleType === BattleType.TRAINER) {
|
||||||
newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
|
newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
|
||||||
|
@ -2136,7 +2136,7 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
|
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
|
||||||
const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority);
|
const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority);
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, movePhase.move.getMove(), movePriority);
|
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority);
|
||||||
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value);
|
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value);
|
||||||
if (lowerPriorityPhase) {
|
if (lowerPriorityPhase) {
|
||||||
this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase);
|
this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -269,7 +269,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
||||||
if (effectPhase instanceof MoveEffectPhase) {
|
if (effectPhase instanceof MoveEffectPhase) {
|
||||||
const attacker = effectPhase.getUserPokemon()!;
|
const attacker = effectPhase.getUserPokemon()!;
|
||||||
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
|
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, priority);
|
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
|
||||||
}
|
}
|
||||||
return priority.value > 0;
|
return priority.value > 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,25 +36,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new Utils.NumberHolder(0.25);
|
const threshold = new Utils.NumberHolder(0.25);
|
||||||
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
|
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6;
|
return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6;
|
||||||
};
|
};
|
||||||
case BerryType.LANSAT:
|
case BerryType.LANSAT:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new Utils.NumberHolder(0.25);
|
const threshold = new Utils.NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
|
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
||||||
};
|
};
|
||||||
case BerryType.STARF:
|
case BerryType.STARF:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new Utils.NumberHolder(0.25);
|
const threshold = new Utils.NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
|
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25;
|
return pokemon.getHpRatio() < 0.25;
|
||||||
};
|
};
|
||||||
case BerryType.LEPPA:
|
case BerryType.LEPPA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new Utils.NumberHolder(0.25);
|
const threshold = new Utils.NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
|
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
||||||
return !!pokemon.getMoveset().find(m => !m?.getPpRatio());
|
return !!pokemon.getMoveset().find(m => !m?.getPpRatio());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||||
pokemon.battleData.berriesEaten.push(berryType);
|
pokemon.battleData.berriesEaten.push(berryType);
|
||||||
}
|
}
|
||||||
const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4));
|
const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4));
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, hpHealed);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
|
||||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
||||||
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
|
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
|
||||||
};
|
};
|
||||||
|
@ -97,7 +97,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||||
}
|
}
|
||||||
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||||
const statLevels = new Utils.NumberHolder(1);
|
const statLevels = new Utils.NumberHolder(1);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels);
|
||||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value));
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value));
|
||||||
};
|
};
|
||||||
case BerryType.LANSAT:
|
case BerryType.LANSAT:
|
||||||
|
@ -113,7 +113,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||||
pokemon.battleData.berriesEaten.push(berryType);
|
pokemon.battleData.berriesEaten.push(berryType);
|
||||||
}
|
}
|
||||||
const statLevels = new Utils.NumberHolder(2);
|
const statLevels = new Utils.NumberHolder(2);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels);
|
||||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value));
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value));
|
||||||
};
|
};
|
||||||
case BerryType.LEPPA:
|
case BerryType.LEPPA:
|
||||||
|
|
|
@ -590,7 +590,7 @@ export default class Move implements Localizable {
|
||||||
case MoveFlags.IGNORE_ABILITIES:
|
case MoveFlags.IGNORE_ABILITIES:
|
||||||
if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) {
|
if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) {
|
||||||
const abilityEffectsIgnored = new Utils.BooleanHolder(false);
|
const abilityEffectsIgnored = new Utils.BooleanHolder(false);
|
||||||
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, this);
|
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this);
|
||||||
if (abilityEffectsIgnored.value) {
|
if (abilityEffectsIgnored.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -686,11 +686,11 @@ export default class Move implements Localizable {
|
||||||
* @param target {@linkcode Pokemon} The Pokémon being targeted by the move.
|
* @param target {@linkcode Pokemon} The Pokémon being targeted by the move.
|
||||||
* @returns The calculated accuracy of the move.
|
* @returns The calculated accuracy of the move.
|
||||||
*/
|
*/
|
||||||
calculateBattleAccuracy(user: Pokemon, target: Pokemon) {
|
calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) {
|
||||||
const moveAccuracy = new Utils.NumberHolder(this.accuracy);
|
const moveAccuracy = new Utils.NumberHolder(this.accuracy);
|
||||||
|
|
||||||
applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy);
|
applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy);
|
||||||
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, moveAccuracy);
|
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy);
|
||||||
|
|
||||||
if (moveAccuracy.value === -1) {
|
if (moveAccuracy.value === -1) {
|
||||||
return moveAccuracy.value;
|
return moveAccuracy.value;
|
||||||
|
@ -724,7 +724,7 @@ export default class Move implements Localizable {
|
||||||
* @param target {@linkcode Pokemon} The Pokémon being targeted by the move.
|
* @param target {@linkcode Pokemon} The Pokémon being targeted by the move.
|
||||||
* @returns The calculated power of the move.
|
* @returns The calculated power of the move.
|
||||||
*/
|
*/
|
||||||
calculateBattlePower(source: Pokemon, target: Pokemon): number {
|
calculateBattlePower(source: Pokemon, target: Pokemon, simulated: boolean = false): number {
|
||||||
if (this.category === MoveCategory.STATUS) {
|
if (this.category === MoveCategory.STATUS) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -732,17 +732,17 @@ export default class Move implements Localizable {
|
||||||
const power = new Utils.NumberHolder(this.power);
|
const power = new Utils.NumberHolder(this.power);
|
||||||
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
|
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
|
||||||
|
|
||||||
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, typeChangeMovePowerMultiplier);
|
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, simulated, typeChangeMovePowerMultiplier);
|
||||||
|
|
||||||
const sourceTeraType = source.getTeraType();
|
const sourceTeraType = source.getTeraType();
|
||||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !source.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !source.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||||
power.value = 60;
|
power.value = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, power);
|
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power);
|
||||||
|
|
||||||
if (source.getAlly()) {
|
if (source.getAlly()) {
|
||||||
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, power);
|
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldAuras = new Set(
|
const fieldAuras = new Set(
|
||||||
|
@ -752,11 +752,11 @@ export default class Move implements Localizable {
|
||||||
);
|
);
|
||||||
for (const aura of fieldAuras) {
|
for (const aura of fieldAuras) {
|
||||||
// The only relevant values are `move` and the `power` holder
|
// The only relevant values are `move` and the `power` holder
|
||||||
aura.applyPreAttack(null, null, null, this, [power]);
|
aura.applyPreAttack(null, null, simulated, null, this, [power]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField();
|
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField();
|
||||||
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, power));
|
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power));
|
||||||
|
|
||||||
power.value *= typeChangeMovePowerMultiplier.value;
|
power.value *= typeChangeMovePowerMultiplier.value;
|
||||||
|
|
||||||
|
@ -984,9 +984,9 @@ export class MoveEffectAttr extends MoveAttr {
|
||||||
*/
|
*/
|
||||||
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer {
|
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer {
|
||||||
const moveChance = new Utils.NumberHolder(move.chance);
|
const moveChance = new Utils.NumberHolder(move.chance);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, moveChance, move, target, selfEffect, showAbility);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility);
|
||||||
if (!selfEffect) {
|
if (!selfEffect) {
|
||||||
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, moveChance);
|
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance);
|
||||||
}
|
}
|
||||||
return moveChance.value;
|
return moveChance.value;
|
||||||
}
|
}
|
||||||
|
@ -1883,7 +1883,7 @@ export class MultiHitAttr extends MoveAttr {
|
||||||
{
|
{
|
||||||
const rand = user.randSeedInt(16);
|
const rand = user.randSeedInt(16);
|
||||||
const hitValue = new Utils.IntegerHolder(rand);
|
const hitValue = new Utils.IntegerHolder(rand);
|
||||||
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
|
applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue);
|
||||||
if (hitValue.value >= 10) {
|
if (hitValue.value >= 10) {
|
||||||
return 2;
|
return 2;
|
||||||
} else if (hitValue.value >= 4) {
|
} else if (hitValue.value >= 4) {
|
||||||
|
@ -1954,7 +1954,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
|
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||||
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
|
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
|
||||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect);
|
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class Terrain {
|
||||||
case TerrainType.PSYCHIC:
|
case TerrainType.PSYCHIC:
|
||||||
if (!move.hasAttr(ProtectAttr)) {
|
if (!move.hasAttr(ProtectAttr)) {
|
||||||
const priority = new Utils.IntegerHolder(move.priority);
|
const priority = new Utils.IntegerHolder(move.priority);
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, move, priority);
|
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, false, move, priority);
|
||||||
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
||||||
return priority.value > 0 && user.getOpponents().some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded());
|
return priority.value > 0 && user.getOpponents().some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded());
|
||||||
}
|
}
|
||||||
|
|
|
@ -694,7 +694,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel);
|
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, false, statLevel);
|
||||||
if (move) {
|
if (move) {
|
||||||
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel);
|
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel);
|
||||||
}
|
}
|
||||||
|
@ -1122,10 +1122,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const suppressed = new Utils.BooleanHolder(false);
|
const suppressed = new Utils.BooleanHolder(false);
|
||||||
this.scene.getField(true).filter(p => p !== this).map(p => {
|
this.scene.getField(true).filter(p => p !== this).map(p => {
|
||||||
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
|
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
|
||||||
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability]));
|
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ability]));
|
||||||
}
|
}
|
||||||
if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) {
|
if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) {
|
||||||
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, suppressed, [ability]));
|
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ability]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (suppressed.value) {
|
if (suppressed.value) {
|
||||||
|
@ -1177,7 +1177,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
getWeight(): number {
|
getWeight(): number {
|
||||||
const weight = new Utils.NumberHolder(this.species.weight);
|
const weight = new Utils.NumberHolder(this.species.weight);
|
||||||
// This will trigger the ability overlay so only call this function when necessary
|
// This will trigger the ability overlay so only call this function when necessary
|
||||||
applyAbAttrs(WeightMultiplierAbAttr, this, null, weight);
|
applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight);
|
||||||
return weight.value;
|
return weight.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,10 +1237,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||||
if (!typeless && !ignoreAbility) {
|
if (!typeless && !ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
|
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier);
|
||||||
}
|
}
|
||||||
if (!cancelled.value && !ignoreAbility) {
|
if (!cancelled.value && !ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
|
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier;
|
return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier;
|
||||||
|
@ -1281,7 +1281,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
if (source) {
|
if (source) {
|
||||||
const ignoreImmunity = new Utils.BooleanHolder(false);
|
const ignoreImmunity = new Utils.BooleanHolder(false);
|
||||||
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
|
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
|
||||||
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, moveType, defType);
|
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, false, moveType, defType);
|
||||||
}
|
}
|
||||||
if (ignoreImmunity.value) {
|
if (ignoreImmunity.value) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1922,9 +1922,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]);
|
const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]);
|
||||||
const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]);
|
const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]);
|
||||||
|
|
||||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel);
|
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, false, userAccuracyLevel);
|
||||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, targetEvasionLevel);
|
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, false, targetEvasionLevel);
|
||||||
applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, targetEvasionLevel);
|
applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, false, targetEvasionLevel);
|
||||||
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel);
|
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel);
|
||||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel);
|
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel);
|
||||||
|
|
||||||
|
@ -1939,7 +1939,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
: 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6));
|
: 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, sourceMove);
|
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, false, sourceMove);
|
||||||
|
|
||||||
const evasionMultiplier = new Utils.NumberHolder(1);
|
const evasionMultiplier = new Utils.NumberHolder(1);
|
||||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier);
|
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier);
|
||||||
|
@ -1949,6 +1949,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return accuracyMultiplier.value;
|
return accuracyMultiplier.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the results of a move to this pokemon
|
||||||
|
* @param {Pokemon} source The pokemon using the move
|
||||||
|
* @param {PokemonMove} battlerMove The move being used
|
||||||
|
* @returns {HitResult} The result of the attack
|
||||||
|
*/
|
||||||
apply(source: Pokemon, move: Move): HitResult {
|
apply(source: Pokemon, move: Move): HitResult {
|
||||||
let result: HitResult;
|
let result: HitResult;
|
||||||
const damage = new Utils.NumberHolder(0);
|
const damage = new Utils.NumberHolder(0);
|
||||||
|
@ -1984,12 +1990,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const sourceTeraType = source.getTeraType();
|
const sourceTeraType = source.getTeraType();
|
||||||
|
|
||||||
if (!typeless) {
|
if (!typeless) {
|
||||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
|
||||||
applyMoveAttrs(NeutralDamageAgainstFlyingTypeMultiplierAttr, source, this, move, typeMultiplier);
|
applyMoveAttrs(NeutralDamageAgainstFlyingTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||||
}
|
}
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
|
||||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
|
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
|
@ -2012,7 +2018,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const critOnly = new Utils.BooleanHolder(false);
|
const critOnly = new Utils.BooleanHolder(false);
|
||||||
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
|
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||||
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
|
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
|
||||||
applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move);
|
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
|
||||||
if (critOnly.value || critAlways) {
|
if (critOnly.value || critAlways) {
|
||||||
isCritical = true;
|
isCritical = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2022,7 +2028,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||||
const bonusCrit = new Utils.BooleanHolder(false);
|
const bonusCrit = new Utils.BooleanHolder(false);
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
|
if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
|
||||||
if (bonusCrit.value) {
|
if (bonusCrit.value) {
|
||||||
critLevel.value += 1;
|
critLevel.value += 1;
|
||||||
}
|
}
|
||||||
|
@ -2040,7 +2046,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
if (isCritical) {
|
if (isCritical) {
|
||||||
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||||
const blockCrit = new Utils.BooleanHolder(false);
|
const blockCrit = new Utils.BooleanHolder(false);
|
||||||
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
|
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
|
||||||
if (noCritTag || blockCrit.value) {
|
if (noCritTag || blockCrit.value) {
|
||||||
isCritical = false;
|
isCritical = false;
|
||||||
}
|
}
|
||||||
|
@ -2048,7 +2054,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical));
|
const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical));
|
||||||
const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
|
const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
|
||||||
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
|
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
|
||||||
applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier);
|
applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier);
|
||||||
const screenMultiplier = new Utils.NumberHolder(1);
|
const screenMultiplier = new Utils.NumberHolder(1);
|
||||||
if (!isCritical) {
|
if (!isCritical) {
|
||||||
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
|
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
|
||||||
|
@ -2063,7 +2069,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
stabMultiplier.value += 0.5;
|
stabMultiplier.value += 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
|
applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier);
|
||||||
|
|
||||||
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
|
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
|
||||||
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
|
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
|
||||||
|
@ -2081,7 +2087,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
numTargets = effectPhase.getTargets().length;
|
numTargets = effectPhase.getTargets().length;
|
||||||
}
|
}
|
||||||
const twoStrikeMultiplier = new Utils.NumberHolder(1);
|
const twoStrikeMultiplier = new Utils.NumberHolder(1);
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
|
||||||
|
|
||||||
if (!isTypeImmune) {
|
if (!isTypeImmune) {
|
||||||
const levelMultiplier = (2 * source.level / 5 + 2);
|
const levelMultiplier = (2 * source.level / 5 + 2);
|
||||||
|
@ -2100,14 +2106,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
|
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
|
||||||
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
|
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
|
||||||
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
||||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled);
|
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false);
|
||||||
if (!burnDamageReductionCancelled.value) {
|
if (!burnDamageReductionCancelled.value) {
|
||||||
damage.value = Math.floor(damage.value / 2);
|
damage.value = Math.floor(damage.value / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, damage);
|
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
||||||
|
@ -2165,7 +2171,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, damage);
|
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
||||||
|
@ -2178,7 +2184,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
if (damage.value) {
|
if (damage.value) {
|
||||||
if (this.isFullHp()) {
|
if (this.isFullHp()) {
|
||||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, damage);
|
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
||||||
} else if (!this.isPlayer() && damage.value >= this.hp) {
|
} else if (!this.isPlayer() && damage.value >= this.hp) {
|
||||||
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
|
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
|
||||||
}
|
}
|
||||||
|
@ -2245,11 +2251,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
break;
|
break;
|
||||||
case MoveCategory.STATUS:
|
case MoveCategory.STATUS:
|
||||||
if (!typeless) {
|
if (!typeless) {
|
||||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
|
||||||
}
|
}
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
|
||||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
|
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier));
|
||||||
}
|
}
|
||||||
if (!typeMultiplier.value) {
|
if (!typeMultiplier.value) {
|
||||||
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
|
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
|
||||||
|
@ -2341,6 +2347,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!));
|
return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAddTag(tagType: BattlerTagType): boolean {
|
||||||
|
if (this.getTag(tagType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stubTag = new BattlerTag(tagType, 0, 0);
|
||||||
|
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, stubTag, cancelled, true);
|
||||||
|
|
||||||
|
const userField = this.getAlliedField();
|
||||||
|
userField.forEach(pokemon => applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, stubTag, cancelled, true));
|
||||||
|
|
||||||
|
return !cancelled.value;
|
||||||
|
}
|
||||||
|
|
||||||
addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean {
|
addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean {
|
||||||
const existingTag = this.getTag(tagType);
|
const existingTag = this.getTag(tagType);
|
||||||
if (existingTag) {
|
if (existingTag) {
|
||||||
|
@ -2727,7 +2749,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
|
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
|
||||||
const cancelImmunity = new Utils.BooleanHolder(false);
|
const cancelImmunity = new Utils.BooleanHolder(false);
|
||||||
if (sourcePokemon) {
|
if (sourcePokemon) {
|
||||||
applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, effect, defType);
|
applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType);
|
||||||
if (cancelImmunity.value) {
|
if (cancelImmunity.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2799,7 +2821,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
if (effect === StatusEffect.SLEEP) {
|
if (effect === StatusEffect.SLEEP) {
|
||||||
statusCureTurn = new Utils.IntegerHolder(this.randSeedIntRange(2, 4));
|
statusCureTurn = new Utils.IntegerHolder(this.randSeedIntRange(2, 4));
|
||||||
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn);
|
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, false, effect, statusCureTurn);
|
||||||
|
|
||||||
this.setFrameRate(4);
|
this.setFrameRate(4);
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class AttemptRunPhase extends PokemonPhase {
|
||||||
const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length;
|
const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length;
|
||||||
|
|
||||||
const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256);
|
const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256);
|
||||||
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance);
|
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
|
||||||
|
|
||||||
if (playerPokemon.randSeedInt(256) < escapeChance.value) {
|
if (playerPokemon.randSeedInt(256) < escapeChance.value) {
|
||||||
this.scene.playSound("flee");
|
this.scene.playSound("flee");
|
||||||
|
|
|
@ -189,7 +189,7 @@ export class CommandPhase extends FieldPhase {
|
||||||
const batonPass = isSwitch && args[0] as boolean;
|
const batonPass = isSwitch && args[0] as boolean;
|
||||||
const trappedAbMessages: string[] = [];
|
const trappedAbMessages: string[] = [];
|
||||||
if (!batonPass) {
|
if (!batonPass) {
|
||||||
enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, true, trappedAbMessages));
|
enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true));
|
||||||
}
|
}
|
||||||
if (batonPass || (!trapTag && !trapped.value)) {
|
if (batonPass || (!trapTag && !trapped.value)) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class EncounterPhase extends BattlePhase {
|
||||||
battle.enemyParty[e].ivs = new Array(6).fill(31);
|
battle.enemyParty[e].ivs = new Array(6).fill(31);
|
||||||
}
|
}
|
||||||
this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
|
this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
|
||||||
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]);
|
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export class EnemyCommandPhase extends FieldPhase {
|
||||||
|
|
||||||
const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
|
const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
|
||||||
const trapped = new Utils.BooleanHolder(false);
|
const trapped = new Utils.BooleanHolder(false);
|
||||||
opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, true, []));
|
opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, [], true));
|
||||||
if (!trapTag && !trapped.value) {
|
if (!trapTag && !trapped.value) {
|
||||||
const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true);
|
const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true);
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
||||||
// If Parental Bond is applicable, double the hit count
|
// If Parental Bond is applicable, double the hit count
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0));
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new Utils.IntegerHolder(0));
|
||||||
// If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user
|
// If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user
|
||||||
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
||||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
||||||
|
|
|
@ -90,7 +90,7 @@ export class MovePhase extends BattlePhase {
|
||||||
: null;
|
: null;
|
||||||
if (moveTarget) {
|
if (moveTarget) {
|
||||||
const oldTarget = moveTarget.value;
|
const oldTarget = moveTarget.value;
|
||||||
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
|
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget));
|
||||||
this.pokemon.getOpponents().forEach(p => {
|
this.pokemon.getOpponents().forEach(p => {
|
||||||
const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag;
|
const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag;
|
||||||
if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) {
|
if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||||
break;
|
break;
|
||||||
case StatusEffect.BURN:
|
case StatusEffect.BURN:
|
||||||
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
||||||
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage);
|
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, false, damage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (damage.value) {
|
if (damage.value) {
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class StatChangePhase extends PokemonPhase {
|
||||||
const levels = new Utils.IntegerHolder(this.levels);
|
const levels = new Utils.IntegerHolder(this.levels);
|
||||||
|
|
||||||
if (!this.ignoreAbilities) {
|
if (!this.ignoreAbilities) {
|
||||||
applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, levels);
|
applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, false, levels);
|
||||||
}
|
}
|
||||||
|
|
||||||
const battleStats = this.getPokemon().summonData.battleStats;
|
const battleStats = this.getPokemon().summonData.battleStats;
|
||||||
|
@ -90,7 +90,7 @@ export class StatChangePhase extends PokemonPhase {
|
||||||
|
|
||||||
if (levels.value > 0 && this.canBeCopied) {
|
if (levels.value > 0 && this.canBeCopied) {
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponents()) {
|
||||||
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value);
|
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, false, this.stats, levels.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
this.scene.getField(true).filter(p => p.summonData).map(p => {
|
this.scene.getField(true).filter(p => p.summonData).map(p => {
|
||||||
const bypassSpeed = new Utils.BooleanHolder(false);
|
const bypassSpeed = new Utils.BooleanHolder(false);
|
||||||
const canCheckHeldItems = new Utils.BooleanHolder(true);
|
const canCheckHeldItems = new Utils.BooleanHolder(true);
|
||||||
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed);
|
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed);
|
||||||
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems);
|
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems);
|
||||||
if (canCheckHeldItems.value) {
|
if (canCheckHeldItems.value) {
|
||||||
this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
|
this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here?
|
applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here?
|
||||||
applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here?
|
applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here?
|
||||||
|
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here?
|
applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); //TODO: is the bang correct here?
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here?
|
applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); //TODO: is the bang correct here?
|
||||||
|
|
||||||
if (aPriority.value !== bPriority.value) {
|
if (aPriority.value !== bPriority.value) {
|
||||||
const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value);
|
const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value);
|
||||||
|
|
|
@ -52,7 +52,7 @@ describe("Abilities - Sand Veil", () => {
|
||||||
|
|
||||||
const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0];
|
const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0];
|
||||||
vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation(
|
vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation(
|
||||||
(pokemon, passive, battleStat, statValue, args) => {
|
(pokemon, passive, simulated, battleStat, statValue, args) => {
|
||||||
if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
||||||
statValue.value *= -1; // will make all attacks miss
|
statValue.value *= -1; // will make all attacks miss
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -67,7 +67,7 @@ describe("Abilities - Serene Grace", () => {
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
|
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
expect(chance.value).toBe(30);
|
expect(chance.value).toBe(30);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
@ -99,7 +99,7 @@ describe("Abilities - Serene Grace", () => {
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
expect(chance.value).toBe(60);
|
expect(chance.value).toBe(60);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
|
@ -69,8 +69,8 @@ describe("Abilities - Sheer Force", () => {
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new Utils.IntegerHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(0);
|
expect(chance.value).toBe(0);
|
||||||
expect(power.value).toBe(move.power * 5461/4096);
|
expect(power.value).toBe(move.power * 5461/4096);
|
||||||
|
@ -108,8 +108,8 @@ describe("Abilities - Sheer Force", () => {
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new Utils.IntegerHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
expect(chance.value).toBe(-1);
|
||||||
expect(power.value).toBe(move.power);
|
expect(power.value).toBe(move.power);
|
||||||
|
@ -147,8 +147,8 @@ describe("Abilities - Sheer Force", () => {
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new Utils.IntegerHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
expect(chance.value).toBe(-1);
|
||||||
expect(power.value).toBe(move.power);
|
expect(power.value).toBe(move.power);
|
||||||
|
@ -191,8 +191,8 @@ describe("Abilities - Sheer Force", () => {
|
||||||
const target = phase.getTarget()!;
|
const target = phase.getTarget()!;
|
||||||
const opponentType = target.getTypes()[0];
|
const opponentType = target.getTypes()[0];
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, false, power);
|
||||||
applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move));
|
applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move));
|
||||||
|
|
||||||
expect(chance.value).toBe(0);
|
expect(chance.value).toBe(0);
|
||||||
|
|
|
@ -67,8 +67,8 @@ describe("Abilities - Shield Dust", () => {
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
||||||
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance);
|
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null, null, false, chance);
|
||||||
expect(chance.value).toBe(0);
|
expect(chance.value).toBe(0);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
Loading…
Reference in New Issue