Compare commits

..

No commits in common. "b1d4037a57c01d794a49c0f0a5d299918e153ac6" and "1487d7f51cb359d741a5ad3b3d5184ef0506a8f3" have entirely different histories.

57 changed files with 598 additions and 927 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -2,35 +2,79 @@
"1": {
"5a4d37": "1c1e76",
"766348": "323aa5",
"9d8361": "4059bd",
"c5b4aa": "d3e6e6",
"37332b": "104139",
"9e8360": "4059bd",
"f18725": "2e8c19",
"f18625": "2e8c19",
"bb6f2a": "2f7410",
"0f0f0f": "0f0f0f",
"575243": "18734a",
"b8702d": "2f7410",
"777462": "199e46",
"afa668": "f9fba2",
"a28c88": "c1d8db",
"585243": "18734a",
"4f4531": "b29c3e",
"4d4530": "b29c3e",
"aea56b": "f9fba2",
"afa667": "f9fba2",
"a28e85": "c1d8db",
"a28c8c": "c1d8db",
"b64c95": "dedb64",
"f2f2f2": "e8eab5",
"b45492": "dedb64",
"b05f8f": "dedb64",
"776348": "323aa5",
"f28625": "2e8c19",
"4e4530": "b29c3e",
"ba6e29": "2f7410"
"bc6e28": "2f7410",
"bb6e27": "2f7410",
"4d4430": "b29c3e",
"b0a766": "f9fba2",
"a18f8d": "c1d8db",
"a28f86": "c1d8db",
"b74a94": "dedb64",
"9e8461": "4059bd",
"777362": "199e46",
"ba6d27": "2f7410",
"afa567": "f9fba2",
"a18f85": "c1d8db"
},
"2": {
"5a4d37": "333e5f",
"766348": "8c9fbf",
"9d8361": "dbedec",
"c5b4aa": "39cfbc",
"37332b": "261031",
"9e8360": "dbedec",
"f18725": "4baecd",
"f18625": "4baecd",
"bb6f2a": "4792bd",
"0f0f0f": "0f0f0f",
"575243": "5e2d72",
"b8702d": "4792bd",
"777462": "8358a1",
"afa668": "c9dbac",
"a28e88": "52b0b0",
"585243": "5e2d72",
"4f4531": "534b8c",
"4d4530": "534b8c",
"aea56b": "c9dbac",
"afa667": "c9dbac",
"a28e85": "52b0b0",
"a28c8c": "52b0b0",
"b64c95": "b56c3e",
"f2f2f2": "d9c951",
"b45492": "b56c3e",
"b05f8f": "b56c3e",
"776348": "8c9fbf",
"f28625": "4baecd",
"4e4530": "534b8c",
"ba6e29": "4792bd"
"bc6e28": "4792bd",
"bb6e27": "4792bd",
"4d4430": "534b8c",
"b0a766": "c9dbac",
"a18f8d": "52b0b0",
"a28f86": "52b0b0",
"b74a94": "b56c3e",
"9e8461": "dbedec",
"777362": "8358a1",
"ba6d27": "4792bd",
"afa567": "c9dbac",
"a18f85": "52b0b0"
}
}
}

View File

@ -1100,7 +1100,7 @@ export default class BattleScene extends SceneBase {
} else if (trainerConfigs[trainerType].hasDouble) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
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
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)) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
newDouble = !Utils.randSeedInt(doubleChance.value);
} else if (newBattleType === BattleType.TRAINER) {
newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
@ -1518,7 +1518,7 @@ export default class BattleScene extends SceneBase {
return;
}
const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money);
this.moneyText.setText(i18next.t("battleScene:moneyOwned", { formattedMoney }));
this.moneyText.setText(`${formattedMoney}`);
this.fieldUI.moveAbove(this.moneyText, this.luckText);
if (forceVisible) {
this.moneyText.setVisible(true);
@ -2136,7 +2136,7 @@ export default class BattleScene extends SceneBase {
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority);
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority);
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, movePhase.move.getMove(), movePriority);
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value);
if (lowerPriorityPhase) {
this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase);

File diff suppressed because it is too large Load Diff

View File

@ -269,7 +269,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
if (effectPhase instanceof MoveEffectPhase) {
const attacker = effectPhase.getUserPokemon()!;
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, priority);
}
return priority.value > 0;
};
@ -427,7 +427,7 @@ class WishTag extends ArenaTag {
if (user) {
this.battlerIndex = user.getBattlerIndex();
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
this.healHp = Utils.toDmgValue(user.getMaxHp() / 2);
this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1);
} else {
console.warn("Failed to get source for WishTag onAdd");
}
@ -585,7 +585,7 @@ class SpikesTag extends ArenaTrapTag {
if (!cancelled.value) {
const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio);
const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER);
@ -745,7 +745,7 @@ class StealthRockTag extends ArenaTrapTag {
const damageHpRatio = this.getDamageHpRatio(pokemon);
if (damageHpRatio) {
const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio);
const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) {

View File

@ -347,7 +347,7 @@ export class ConfusedTag extends BattlerTag {
if (pokemon.randSeedInt(3) === 0) {
const atk = pokemon.getBattleStat(Stat.ATK);
const def = pokemon.getBattleStat(Stat.DEF);
const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
pokemon.damageAndUpdate(damage);
pokemon.battleData.hitCount++;
@ -524,7 +524,7 @@ export class SeedTag extends BattlerTag {
if (!cancelled.value) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const damage = pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8));
const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(),
!reverseDrain ? damage : damage * -1,
@ -570,7 +570,7 @@ export class NightmareTag extends BattlerTag {
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value) {
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4));
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4));
}
}
@ -714,7 +714,7 @@ export class IngrainTag extends TrappedTag {
new PokemonHealPhase(
pokemon.scene,
pokemon.getBattlerIndex(),
Utils.toDmgValue(pokemon.getMaxHp() / 16),
Math.floor(pokemon.getMaxHp() / 16),
i18next.t("battlerTags:ingrainLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }),
true
)
@ -777,7 +777,7 @@ export class AquaRingTag extends BattlerTag {
new PokemonHealPhase(
pokemon.scene,
pokemon.getBattlerIndex(),
Utils.toDmgValue(pokemon.getMaxHp() / 16),
Math.floor(pokemon.getMaxHp() / 16),
i18next.t("battlerTags:aquaRingLapse", {
moveName: this.getMoveName(),
pokemonName: getPokemonNameWithAffix(pokemon)
@ -883,7 +883,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value) {
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8));
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8));
}
}
@ -1067,7 +1067,7 @@ export class ContactDamageProtectedTag extends ProtectedTag {
if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) {
const attacker = effectPhase.getPokemon();
if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
}
}
}
@ -1541,7 +1541,7 @@ export class SaltCuredTag extends BattlerTag {
if (!cancelled.value) {
const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER);
pokemon.damageAndUpdate(Utils.toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8));
pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1));
pokemon.scene.queueMessage(
i18next.t("battlerTags:saltCuredLapse", {
@ -1587,7 +1587,7 @@ export class CursedTag extends BattlerTag {
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value) {
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4));
pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 4), 1));
pokemon.scene.queueMessage(i18next.t("battlerTags:cursedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
}
}

View File

@ -36,25 +36,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6;
};
case BerryType.LANSAT:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
};
case BerryType.STARF:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
return pokemon.getHpRatio() < 0.25;
};
case BerryType.LEPPA:
return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
return !!pokemon.getMoveset().find(m => !m?.getPpRatio());
};
}
@ -70,8 +70,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType);
}
const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, hpHealed);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
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 statLevels = new Utils.NumberHolder(1);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value));
};
case BerryType.LANSAT:
@ -113,7 +113,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
pokemon.battleData.berriesEaten.push(berryType);
}
const statLevels = new Utils.NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value));
};
case BerryType.LEPPA:

View File

@ -590,7 +590,7 @@ export default class Move implements Localizable {
case MoveFlags.IGNORE_ABILITIES:
if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) {
const abilityEffectsIgnored = new Utils.BooleanHolder(false);
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this);
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, this);
if (abilityEffectsIgnored.value) {
return true;
}
@ -686,11 +686,11 @@ export default class Move implements Localizable {
* @param target {@linkcode Pokemon} The Pokémon being targeted by the move.
* @returns The calculated accuracy of the move.
*/
calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) {
calculateBattleAccuracy(user: Pokemon, target: Pokemon) {
const moveAccuracy = new Utils.NumberHolder(this.accuracy);
applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy);
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy);
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, moveAccuracy);
if (moveAccuracy.value === -1) {
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.
* @returns The calculated power of the move.
*/
calculateBattlePower(source: Pokemon, target: Pokemon, simulated: boolean = false): number {
calculateBattlePower(source: Pokemon, target: Pokemon): number {
if (this.category === MoveCategory.STATUS) {
return -1;
}
@ -732,17 +732,17 @@ export default class Move implements Localizable {
const power = new Utils.NumberHolder(this.power);
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, simulated, typeChangeMovePowerMultiplier);
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, typeChangeMovePowerMultiplier);
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)) {
power.value = 60;
}
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power);
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, power);
if (source.getAlly()) {
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power);
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, power);
}
const fieldAuras = new Set(
@ -752,11 +752,11 @@ export default class Move implements Localizable {
);
for (const aura of fieldAuras) {
// The only relevant values are `move` and the `power` holder
aura.applyPreAttack(null, null, simulated, null, this, [power]);
aura.applyPreAttack(null, null, null, this, [power]);
}
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField();
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power));
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, power));
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 {
const moveChance = new Utils.NumberHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, moveChance, move, target, selfEffect, showAbility);
if (!selfEffect) {
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance);
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, moveChance);
}
return moveChance.value;
}
@ -1162,7 +1162,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr {
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2);
(args[0] as Utils.IntegerHolder).value = Math.max(Math.floor(target.hp / 2), 1);
return true;
}
@ -1208,7 +1208,7 @@ export class CounterDamageAttr extends FixedDamageAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: integer, ar: AttackMoveResult) => total + ar.damage, 0);
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier);
(args[0] as Utils.IntegerHolder).value = Math.floor(Math.max(damage * this.multiplier, 1));
return true;
}
@ -1234,7 +1234,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
}
getDamage(user: Pokemon, target: Pokemon, move: Move): number {
return Utils.toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01));
return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1);
}
}
@ -1293,9 +1293,8 @@ export class RecoilAttr extends MoveEffectAttr {
return false;
}
const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio;
const minValue = user.turnData.damageDealt ? 1 : 0;
const recoilDamage = Utils.toDmgValue(damageValue, minValue);
const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio),
user.turnData.damageDealt ? 1 : 0);
if (!recoilDamage) {
return false;
}
@ -1416,7 +1415,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
// Check to see if the Pokemon has an ability that blocks non-direct damage
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (!cancelled.value) {
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
user.scene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", {pokemonName: getPokemonNameWithAffix(user)})); // Queue recoil message
}
return true;
@ -1467,7 +1466,7 @@ export class HealAttr extends MoveEffectAttr {
*/
addHealPhase(target: Pokemon, healRatio: number) {
target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(),
Utils.toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim));
Math.max(Math.floor(target.getMaxHp() * healRatio), 1), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim));
}
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
@ -1751,7 +1750,7 @@ export class HitHealAttr extends MoveEffectAttr {
message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)});
} else {
// Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc.
healAmount = Utils.toDmgValue(user.turnData.currDamageDealt * this.healRatio);
healAmount = Math.max(Math.floor(user.turnData.currDamageDealt * this.healRatio), 1);
message = i18next.t("battle:regainHealth", {pokemonName: getPokemonNameWithAffix(user)});
}
if (reverseDrain) {
@ -1884,7 +1883,7 @@ export class MultiHitAttr extends MoveAttr {
{
const rand = user.randSeedInt(16);
const hitValue = new Utils.IntegerHolder(rand);
applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue);
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
if (hitValue.value >= 10) {
return 2;
} else if (hitValue.value >= 4) {
@ -1955,7 +1954,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
}
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect);
return true;
}
}
@ -2711,7 +2710,7 @@ export class CutHpStatBoostAttr extends StatChangeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => {
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
user.damageAndUpdate(Math.floor(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
user.updateInfo().then(() => {
const ret = super.apply(user, target, move, args);
if (this.messageCallback) {
@ -3191,7 +3190,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr {
export class HpPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(150 * user.getHpRatio());
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(150 * user.getHpRatio()), 1);
return true;
}
@ -3219,7 +3218,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr {
* @returns true
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.maxBasePower * target.getHpRatio());
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1);
return true;
}
@ -3413,7 +3412,7 @@ export class PresentPowerAttr extends VariablePowerAttr {
// If this move is multi-hit, disable all other hits
user.stopMultiHit();
target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(),
Utils.toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true));
Math.max(Math.floor(target.getMaxHp() / 4), 1), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true));
}
return true;
@ -4233,9 +4232,9 @@ const crashDamageFunc = (user: Pokemon, move: Move) => {
return false;
}
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true);
user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true);
user.scene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", {pokemonName: getPokemonNameWithAffix(user)}));
user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2);
user.turnData.damageTaken += Math.floor(user.getMaxHp() / 2);
return true;
};
@ -4945,7 +4944,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)];
const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id);
pokemon.resetStatus();
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp()));
user.scene.queueMessage(`${getPokemonNameWithAffix(pokemon)} was revived!`,0,true);
if (user.scene.currentBattle.double && user.scene.getEnemyParty().length > 1) {

View File

@ -59,7 +59,7 @@ export class Terrain {
case TerrainType.PSYCHIC:
if (!move.hasAttr(ProtectAttr)) {
const priority = new Utils.IntegerHolder(move.priority);
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, false, move, priority);
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, move, priority);
// 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());
}

View File

@ -694,7 +694,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
break;
}
}
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, false, statLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel);
if (move) {
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);
this.scene.getField(true).filter(p => p !== this).map(p => {
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ability]));
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability]));
}
if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) {
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ability]));
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, suppressed, [ability]));
}
});
if (suppressed.value) {
@ -1177,7 +1177,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
getWeight(): number {
const weight = new Utils.NumberHolder(this.species.weight);
// This will trigger the ability overlay so only call this function when necessary
applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight);
applyAbAttrs(WeightMultiplierAbAttr, this, null, weight);
return weight.value;
}
@ -1237,10 +1237,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const cancelled = new Utils.BooleanHolder(false);
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
if (!typeless && !ignoreAbility) {
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier);
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
}
if (!cancelled.value && !ignoreAbility) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier);
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
}
return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier;
@ -1281,7 +1281,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (source) {
const ignoreImmunity = new Utils.BooleanHolder(false);
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, false, moveType, defType);
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, moveType, defType);
}
if (ignoreImmunity.value) {
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 targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, false, userAccuracyLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, false, targetEvasionLevel);
applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, false, targetEvasionLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, targetEvasionLevel);
applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, targetEvasionLevel);
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel);
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));
}
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, false, sourceMove);
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, sourceMove);
const evasionMultiplier = new Utils.NumberHolder(1);
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier);
@ -1949,12 +1949,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
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 {
let result: HitResult;
const damage = new Utils.NumberHolder(0);
@ -1990,12 +1984,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const sourceTeraType = source.getTeraType();
if (!typeless) {
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
applyMoveAttrs(NeutralDamageAgainstFlyingTypeMultiplierAttr, source, this, move, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier));
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
}
if (cancelled.value) {
@ -2018,7 +2012,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const critOnly = new Utils.BooleanHolder(false);
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move);
if (critOnly.value || critAlways) {
isCritical = true;
} else {
@ -2028,7 +2022,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
const bonusCrit = new Utils.BooleanHolder(false);
//@ts-ignore
if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
if (bonusCrit.value) {
critLevel.value += 1;
}
@ -2046,7 +2040,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (isCritical) {
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
if (noCritTag || blockCrit.value) {
isCritical = false;
}
@ -2054,7 +2048,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 targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier);
applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier);
const screenMultiplier = new Utils.NumberHolder(1);
if (!isCritical) {
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
@ -2069,7 +2063,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
stabMultiplier.value += 0.5;
}
applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier);
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
@ -2087,12 +2081,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
numTargets = effectPhase.getTargets().length;
}
const twoStrikeMultiplier = new Utils.NumberHolder(1);
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
if (!isTypeImmune) {
const levelMultiplier = (2 * source.level / 5 + 2);
const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100);
damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
damage.value = Math.ceil((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
* stabMultiplier.value
* typeMultiplier.value
* arenaAttackTypeMultiplier.value
@ -2106,14 +2100,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled);
if (!burnDamageReductionCancelled.value) {
damage.value = Utils.toDmgValue(damage.value / 2);
damage.value = Math.floor(damage.value / 2);
}
}
}
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage);
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, damage);
/**
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
@ -2129,7 +2123,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && move.type === Type.DRAGON) {
damage.value = Utils.toDmgValue(damage.value / 2);
damage.value = Math.floor(damage.value / 2);
}
const fixedDamage = new Utils.IntegerHolder(0);
@ -2171,7 +2165,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
}
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage);
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, damage);
}
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
@ -2184,7 +2178,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (damage.value) {
if (this.isFullHp()) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, damage);
} else if (!this.isPlayer() && damage.value >= this.hp) {
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
}
@ -2251,11 +2245,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
break;
case MoveCategory.STATUS:
if (!typeless) {
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier));
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
}
if (!typeMultiplier.value) {
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
@ -2347,22 +2341,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
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 {
const existingTag = this.getTag(tagType);
if (existingTag) {
@ -2749,7 +2727,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
const cancelImmunity = new Utils.BooleanHolder(false);
if (sourcePokemon) {
applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType);
applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, effect, defType);
if (cancelImmunity.value) {
return false;
}
@ -2821,7 +2799,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (effect === StatusEffect.SLEEP) {
statusCureTurn = new Utils.IntegerHolder(this.randSeedIntRange(2, 4));
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, false, effect, statusCureTurn);
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn);
this.setFrameRate(4);
@ -3455,7 +3433,7 @@ export class PlayerPokemon extends Pokemon {
pokemon.resetTurnData();
pokemon.resetStatus();
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp()));
this.scene.queueMessage(`${pokemon.name} was revived!`,0,true);
if (this.scene.currentBattle.double && this.scene.getParty().length > 1) {
@ -4382,7 +4360,7 @@ export class PokemonMove {
}
getMovePp(): integer {
return this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5);
return this.getMove().pp + this.ppUp * Math.max(Math.floor(this.getMove().pp / 5), 1);
}
getPpRatio(): number {

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -6,7 +6,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const caESConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,6 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "{{formattedMoney}} ₽"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const deConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -6,7 +6,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const enConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "{{formattedMoney}} ₽"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const esConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "{{formattedMoney}} ₽"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const frConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "{{formattedMoney}} ₽"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const itConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "{{formattedMoney}}円"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -62,7 +61,6 @@ export const jaConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const koConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -4,7 +4,6 @@ import { PGFachv, PGMachv } from "./achv";
import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const ptBrConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const zhCnConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1,5 +0,0 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const battleScene: SimpleTranslationEntries = {
"moneyOwned": "₽{{formattedMoney}}"
} as const;

View File

@ -4,7 +4,6 @@ import { arenaFlyout } from "./arena-flyout";
import { arenaTag } from "./arena-tag";
import { PGFachv, PGMachv } from "./achv";
import { battle } from "./battle";
import { battleScene } from "./battle-scene";
import { battleInfo } from "./battle-info";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { battlerTags } from "./battler-tags";
@ -61,7 +60,6 @@ export const zhTwConfig = {
arenaFlyout: arenaFlyout,
arenaTag: arenaTag,
battle: battle,
battleScene: battleScene,
battleInfo: battleInfo,
battleMessageUiHandler: battleMessageUiHandler,
battlePokemonForm: battlePokemonForm,

View File

@ -1160,7 +1160,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
if (!pokemon.isFullHp()) {
const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Utils.toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
return true;
}
@ -1251,7 +1251,7 @@ export class HitHealModifier extends PokemonHeldItemModifier {
if (pokemon.turnData.damageDealt && !pokemon.isFullHp()) {
const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Utils.toDmgValue(pokemon.turnData.damageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
}
return true;
@ -1386,7 +1386,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
const pokemon = args[0] as Pokemon;
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
Utils.toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
pokemon.resetStatus(true, false, true);
return true;

View File

@ -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 escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256);
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance);
if (playerPokemon.randSeedInt(256) < escapeChance.value) {
this.scene.playSound("flee");

View File

@ -189,7 +189,7 @@ export class CommandPhase extends FieldPhase {
const batonPass = isSwitch && args[0] as boolean;
const trappedAbMessages: string[] = [];
if (!batonPass) {
enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true));
enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, true, trappedAbMessages));
}
if (batonPass || (!trapTag && !trapped.value)) {
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch

View File

@ -67,7 +67,7 @@ export class EncounterPhase extends BattlePhase {
battle.enemyParty[e].ivs = new Array(6).fill(31);
}
this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]);
});
}
}

View File

@ -47,7 +47,7 @@ export class EnemyCommandPhase extends FieldPhase {
const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
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) {
const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true);

View File

@ -74,7 +74,7 @@ export class MoveEffectPhase extends PokemonPhase {
// Assume single target for multi hit
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
// If Parental Bond is applicable, double the hit count
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new Utils.IntegerHolder(0));
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, 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 (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));

View File

@ -90,7 +90,7 @@ export class MovePhase extends BattlePhase {
: null;
if (moveTarget) {
const oldTarget = moveTarget.value;
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget));
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
this.pokemon.getOpponents().forEach(p => {
const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag;
if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) {

View File

@ -34,7 +34,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
break;
case StatusEffect.BURN:
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, false, damage);
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage);
break;
}
if (damage.value) {

View File

@ -68,7 +68,7 @@ export class StatChangePhase extends PokemonPhase {
const levels = new Utils.IntegerHolder(this.levels);
if (!this.ignoreAbilities) {
applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, false, levels);
applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, levels);
}
const battleStats = this.getPokemon().summonData.battleStats;
@ -90,7 +90,7 @@ export class StatChangePhase extends PokemonPhase {
if (levels.value > 0 && this.canBeCopied) {
for (const opponent of pokemon.getOpponents()) {
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, false, this.stats, levels.value);
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value);
}
}

View File

@ -34,8 +34,8 @@ export class TurnStartPhase extends FieldPhase {
this.scene.getField(true).filter(p => p.summonData).map(p => {
const bypassSpeed = new Utils.BooleanHolder(false);
const canCheckHeldItems = new Utils.BooleanHolder(true);
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed);
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems);
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed);
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) {
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() === b)!, null, bMove, bPriority); //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, false, 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() === b)!, null, bMove, bPriority); //TODO: is the bang correct here?
if (aPriority.value !== bPriority.value) {
const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value);

View File

@ -26,7 +26,6 @@ const unicodeRanges = {
kana: "U+3040-30FF",
CJKCommon: "U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-32FF,U+3400-4DBF,U+F900-FAFF,U+FE30-FE4F",
CJKIdeograph: "U+4E00-9FFF",
specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣
};
const rangesByLanguage = {
korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","),
@ -35,15 +34,6 @@ const rangesByLanguage = {
};
const fonts: Array<LoadingFontFaceProperty> = [
// unicode (special character from PokePT)
{
face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }),
},
{
face: new FontFace("pkmnems", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }),
extraOptions: { sizeAdjust: "133%" },
},
// unicode (korean)
{
face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: rangesByLanguage.korean }),
},

View File

@ -6,7 +6,6 @@ import { Species } from "#enums/species";
import { StatusEffect } from "#app/data/status-effect.js";
import { BattleStat } from "#app/data/battle-stat.js";
import { SPLASH_ONLY } from "../utils/testUtils";
import { toDmgValue } from "#app/utils";
import { Mode } from "#app/ui/ui.js";
import { MoveEffectPhase } from "#app/phases/move-effect-phase.js";
import { MoveEndPhase } from "#app/phases/move-end-phase.js";
@ -48,7 +47,7 @@ describe("Abilities - Disguise", () => {
const mimikyu = game.scene.getEnemyPokemon()!;
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
const disguiseDamage = Math.floor(maxHp / 8);
expect(mimikyu.formIndex).toBe(disguisedForm);
@ -81,7 +80,7 @@ describe("Abilities - Disguise", () => {
const mimikyu = game.scene.getEnemyPokemon()!;
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
const disguiseDamage = Math.floor(maxHp / 8);
expect(mimikyu.formIndex).toBe(disguisedForm);
@ -122,7 +121,7 @@ describe("Abilities - Disguise", () => {
const mimikyu = game.scene.getPlayerPokemon()!;
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
const disguiseDamage = Math.floor(maxHp / 8);
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));

View File

@ -8,7 +8,6 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import { StatusEffect } from "#app/enums/status-effect.js";
import { toDmgValue } from "#app/utils";
describe("Abilities - Heatproof", () => {
let phaserGame: Phaser.Game;
@ -73,6 +72,6 @@ describe("Abilities - Heatproof", () => {
await game.toNextTurn();
// Normal burn damage is /16
expect(enemy.hp).toBe(enemy.getMaxHp() - toDmgValue(enemy.getMaxHp() / 32));
expect(enemy.hp).toBe(enemy.getMaxHp() - Math.floor(enemy.getMaxHp() / 32));
});
});

View File

@ -16,7 +16,6 @@ import { DamagePhase } from "#app/phases/damage-phase.js";
import { MoveEffectPhase } from "#app/phases/move-effect-phase.js";
import { MoveEndPhase } from "#app/phases/move-end-phase.js";
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
import { toDmgValue } from "#app/utils";
const TIMEOUT = 20 * 1000;
@ -74,7 +73,7 @@ describe("Abilities - Parental Bond", () => {
const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp;
expect(leadPokemon.turnData.hitCount).toBe(2);
expect(secondStrikeDamage).toBe(toDmgValue(0.25 * firstStrikeDamage));
expect(secondStrikeDamage).toBe(Math.ceil(0.25 * firstStrikeDamage));
}, TIMEOUT
);
@ -304,7 +303,7 @@ describe("Abilities - Parental Bond", () => {
// This test will time out if the user faints
await game.phaseInterceptor.to(BerryPhase, false);
expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp()/2));
expect(leadPokemon.hp).toBe(Math.floor(leadPokemon.getMaxHp()/2));
}, TIMEOUT
);

View File

@ -52,7 +52,7 @@ describe("Abilities - Sand Veil", () => {
const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0];
vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation(
(pokemon, passive, simulated, battleStat, statValue, args) => {
(pokemon, passive, battleStat, statValue, args) => {
if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
statValue.value *= -1; // will make all attacks miss
return true;

View File

@ -67,7 +67,7 @@ describe("Abilities - Serene Grace", () => {
const chance = new Utils.IntegerHolder(move.chance);
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
expect(chance.value).toBe(30);
}, 20000);
@ -99,7 +99,7 @@ describe("Abilities - Serene Grace", () => {
expect(move.id).toBe(Moves.AIR_SLASH);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
expect(chance.value).toBe(60);
}, 20000);

View File

@ -69,8 +69,8 @@ describe("Abilities - Sheer Force", () => {
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
expect(chance.value).toBe(0);
expect(power.value).toBe(move.power * 5461/4096);
@ -108,8 +108,8 @@ describe("Abilities - Sheer Force", () => {
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
expect(chance.value).toBe(-1);
expect(power.value).toBe(move.power);
@ -147,8 +147,8 @@ describe("Abilities - Sheer Force", () => {
const power = new Utils.IntegerHolder(move.power);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power);
expect(chance.value).toBe(-1);
expect(power.value).toBe(move.power);
@ -191,8 +191,8 @@ describe("Abilities - Sheer Force", () => {
const target = phase.getTarget()!;
const opponentType = target.getTypes()[0];
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, false, power);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false);
applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, power);
applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move));
expect(chance.value).toBe(0);

View File

@ -67,8 +67,8 @@ describe("Abilities - Shield Dust", () => {
expect(move.id).toBe(Moves.AIR_SLASH);
const chance = new Utils.IntegerHolder(move.chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null, null, false, chance);
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false);
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance);
expect(chance.value).toBe(0);
}, 20000);

View File

@ -1,71 +0,0 @@
import { DamagePhase } from "#app/phases/damage-phase.js";
import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { ArenaTagType } from "#enums/arena-tag-type";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import { toDmgValue } from "#app/utils";
describe("Round Down and Minimun 1 test in Damage Calculation", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.startingLevel(10);
});
it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => {
game.override.enemySpecies(Species.GASTLY);
game.override.enemyMoveset(SPLASH_ONLY);
game.override.starterSpecies(Species.SHEDINJA);
game.override.moveset([Moves.JUMP_KICK]);
game.override.ability(Abilities.WONDER_GUARD);
await game.startBattle();
const shedinja = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.JUMP_KICK));
await game.phaseInterceptor.to(DamagePhase);
expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1);
});
it("Charizard with odd HP survives Stealth Rock damage twice", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0);
game.override.seed("Charizard Stealth Rock test");
game.override.enemySpecies(Species.CHARIZARD);
game.override.enemyAbility(Abilities.BLAZE);
game.override.starterSpecies(Species.PIKACHU);
game.override.enemyLevel(100);
await game.startBattle();
const charizard = game.scene.getEnemyPokemon()!;
const maxHp = charizard.getMaxHp();
const damage_prediction = toDmgValue(charizard.getMaxHp() / 2);
const currentHp = charizard.hp;
const expectedHP = maxHp - damage_prediction;
expect(currentHp).toBe(expectedHP);
});
});

View File

@ -6,7 +6,6 @@ import { getMovePosition } from "#test/utils/gameManagerUtils";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { BattleStat } from "#app/data/battle-stat";
import { toDmgValue } from "#app/utils";
const TIMEOUT = 20 * 1000;
// RATIO : HP Cost of Move
@ -45,7 +44,7 @@ describe("Moves - BELLY DRUM", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));
await game.phaseInterceptor.to(TurnEndPhase);
@ -60,7 +59,7 @@ describe("Moves - BELLY DRUM", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
// Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6
leadPokemon.summonData.battleStats[BattleStat.ATK] = -3;
@ -96,7 +95,7 @@ describe("Moves - BELLY DRUM", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
leadPokemon.hp = hpLost - PREDAMAGE;
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));

View File

@ -7,7 +7,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { BattleStat } from "#app/data/battle-stat";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import { toDmgValue } from "#app/utils";
const TIMEOUT = 20 * 1000;
/** HP Cost of Move */
@ -46,7 +45,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));
await game.phaseInterceptor.to(TurnEndPhase);
@ -65,7 +64,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
//Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
@ -114,7 +113,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
leadPokemon.hp = hpLost - PREDAMAGE;
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));

View File

@ -7,7 +7,6 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { BattleStat } from "#app/data/battle-stat";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import { toDmgValue } from "#app/utils";
const TIMEOUT = 20 * 1000;
/** HP Cost of Move */
@ -46,7 +45,7 @@ describe("Moves - FILLET AWAY", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));
await game.phaseInterceptor.to(TurnEndPhase);
@ -63,7 +62,7 @@ describe("Moves - FILLET AWAY", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
//Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
@ -104,7 +103,7 @@ describe("Moves - FILLET AWAY", () => {
await game.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
leadPokemon.hp = hpLost - PREDAMAGE;
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));

View File

@ -78,6 +78,6 @@ describe("Moves - Tackle", () => {
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
expect(hpLost).toBeGreaterThan(0);
expect(hpLost).toBeLessThan(4);
expect(hpLost).toBe(4);
}, 20000);
});

View File

@ -560,17 +560,3 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole
export function isNullOrUndefined(object: any): boolean {
return null === object || undefined === object;
}
/**
* This function is used in the context of a Pokémon battle game to calculate the actual integer damage value from a float result.
* Many damage calculation formulas involve various parameters and result in float values.
* The actual damage applied to a Pokémon's HP must be an integer.
* This function helps in ensuring that by flooring the float value and enforcing a minimum damage value.
*
* @param value - The float value to convert.
* @param minValue - The minimum integer value to return. Defaults to 1.
* @returns The converted value as an integer.
*/
export function toDmgValue(value: number, minValue: number = 1) {
return Math.max(Math.floor(value), minValue);
}