mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-29 02:06:07 +00:00
[Bug] Fix some damage formulas processed with ceil instead of floor (#3557)
* fix damage calculations. add test code * define toIntValue function to replace every repeatitive min floor function. * revert unnecessary minimum boundary * update function name `toIntValue` -> `toDmgValue`. update comments. * add missing updates for changing function name * Update src/utils.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * remove redundant comment * update import code for test with phase --------- Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
This commit is contained in:
parent
61d659d8bb
commit
b1d4037a57
@ -301,7 +301,7 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * this.damageMultiplier);
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -390,7 +390,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr {
|
||||
if (!pokemon.isFullHp() && !simulated) {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -904,8 +904,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
|
||||
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2049,7 +2049,7 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr {
|
||||
if (target?.isActive(true)) {
|
||||
if (!simulated) {
|
||||
target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / this.healRatio), 1), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2440,7 +2440,7 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
||||
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
if (!pokemon.isFullHp()) {
|
||||
if (!simulated) {
|
||||
const healAmount = Math.floor(pokemon.getMaxHp() * 0.33);
|
||||
const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33);
|
||||
pokemon.heal(healAmount);
|
||||
pokemon.updateInfo();
|
||||
}
|
||||
@ -3074,7 +3074,7 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
if (!simulated) {
|
||||
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3101,7 +3101,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
||||
if (!simulated) {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }));
|
||||
pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER);
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -3181,7 +3181,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
|
||||
const scene = pokemon.scene;
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3350,7 +3350,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr {
|
||||
const scene = pokemon.scene;
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -3402,7 +3402,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
||||
for (const opp of pokemon.getOpponents()) {
|
||||
if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
|
||||
if (!simulated) {
|
||||
opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER);
|
||||
opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER);
|
||||
pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)}));
|
||||
}
|
||||
hadEffect = true;
|
||||
@ -3604,7 +3604,7 @@ export class ReduceBurnDamageAbAttr extends AbAttr {
|
||||
* @returns `true`
|
||||
*/
|
||||
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor((args[0] as Utils.NumberHolder).value * this.multiplier), 1);
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.multiplier);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3649,7 +3649,7 @@ export class HealFromBerryUseAbAttr extends AbAttr {
|
||||
new PokemonHealPhase(
|
||||
pokemon.scene,
|
||||
pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() * this.healPercent), 1),
|
||||
Utils.toDmgValue(pokemon.getMaxHp() * this.healPercent),
|
||||
i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }),
|
||||
true
|
||||
)
|
||||
@ -3840,8 +3840,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
||||
return false;
|
||||
}
|
||||
if (!simulated) {
|
||||
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3922,7 +3922,7 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr {
|
||||
|
||||
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (args[0] === this.statusEffect) {
|
||||
(args[1] as Utils.IntegerHolder).value = Math.floor((args[1] as Utils.IntegerHolder).value / 2);
|
||||
(args[1] as Utils.IntegerHolder).value = Utils.toDmgValue((args[1] as Utils.IntegerHolder).value / 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5211,7 +5211,7 @@ export function initAbilities() {
|
||||
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
|
||||
.attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE,
|
||||
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
|
||||
(pokemon) => Math.floor(pokemon.getMaxHp() / 8))
|
||||
(pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8))
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.bypassFaint()
|
||||
.ignorable(),
|
||||
|
@ -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 = Math.max(Math.floor(user.getMaxHp() / 2), 1);
|
||||
this.healHp = Utils.toDmgValue(user.getMaxHp() / 2);
|
||||
} 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 = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
|
||||
const damage = Utils.toDmgValue(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 = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
|
||||
const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||
pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||
pokemon.damageAndUpdate(damage, HitResult.OTHER);
|
||||
if (pokemon.turnData) {
|
||||
|
@ -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 = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
|
||||
const damage = Utils.toDmgValue(((((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(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
|
||||
const damage = pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8));
|
||||
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(Math.ceil(pokemon.getMaxHp() / 4));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4));
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,7 +714,7 @@ export class IngrainTag extends TrappedTag {
|
||||
new PokemonHealPhase(
|
||||
pokemon.scene,
|
||||
pokemon.getBattlerIndex(),
|
||||
Math.floor(pokemon.getMaxHp() / 16),
|
||||
Utils.toDmgValue(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(),
|
||||
Math.floor(pokemon.getMaxHp() / 16),
|
||||
Utils.toDmgValue(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(Math.ceil(pokemon.getMaxHp() / 8));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(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(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.damageAndUpdate(Utils.toDmgValue(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(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8));
|
||||
|
||||
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(Math.max(Math.floor(pokemon.getMaxHp() / 4), 1));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4));
|
||||
pokemon.scene.queueMessage(i18next.t("battlerTags:cursedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
if (pokemon.battleData) {
|
||||
pokemon.battleData.berriesEaten.push(berryType);
|
||||
}
|
||||
const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4));
|
||||
const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4));
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
|
||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
||||
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
|
||||
|
@ -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 = Math.max(Math.floor(target.hp / 2), 1);
|
||||
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||
|
||||
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 = Math.floor(Math.max(damage * this.multiplier, 1));
|
||||
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1234,7 +1234,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
|
||||
}
|
||||
|
||||
getDamage(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1);
|
||||
return Utils.toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1293,8 +1293,9 @@ export class RecoilAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio),
|
||||
user.turnData.damageDealt ? 1 : 0);
|
||||
const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio;
|
||||
const minValue = user.turnData.damageDealt ? 1 : 0;
|
||||
const recoilDamage = Utils.toDmgValue(damageValue, minValue);
|
||||
if (!recoilDamage) {
|
||||
return false;
|
||||
}
|
||||
@ -1415,7 +1416,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(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
|
||||
user.scene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", {pokemonName: getPokemonNameWithAffix(user)})); // Queue recoil message
|
||||
}
|
||||
return true;
|
||||
@ -1466,7 +1467,7 @@ export class HealAttr extends MoveEffectAttr {
|
||||
*/
|
||||
addHealPhase(target: Pokemon, healRatio: number) {
|
||||
target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(),
|
||||
Math.max(Math.floor(target.getMaxHp() * healRatio), 1), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim));
|
||||
Utils.toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim));
|
||||
}
|
||||
|
||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||
@ -1750,7 +1751,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 = Math.max(Math.floor(user.turnData.currDamageDealt * this.healRatio), 1);
|
||||
healAmount = Utils.toDmgValue(user.turnData.currDamageDealt * this.healRatio);
|
||||
message = i18next.t("battle:regainHealth", {pokemonName: getPokemonNameWithAffix(user)});
|
||||
}
|
||||
if (reverseDrain) {
|
||||
@ -2710,7 +2711,7 @@ export class CutHpStatBoostAttr extends StatChangeAttr {
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
user.damageAndUpdate(Math.floor(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
|
||||
user.updateInfo().then(() => {
|
||||
const ret = super.apply(user, target, move, args);
|
||||
if (this.messageCallback) {
|
||||
@ -3190,7 +3191,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 = Math.max(Math.floor(150 * user.getHpRatio()), 1);
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(150 * user.getHpRatio());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3218,7 +3219,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr {
|
||||
* @returns true
|
||||
*/
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1);
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.maxBasePower * target.getHpRatio());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3412,7 +3413,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(),
|
||||
Math.max(Math.floor(target.getMaxHp() / 4), 1), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true));
|
||||
Utils.toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -4232,9 +4233,9 @@ const crashDamageFunc = (user: Pokemon, move: Move) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true);
|
||||
user.scene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", {pokemonName: getPokemonNameWithAffix(user)}));
|
||||
user.turnData.damageTaken += Math.floor(user.getMaxHp() / 2);
|
||||
user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2);
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -4944,7 +4945,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(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp()));
|
||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||
user.scene.queueMessage(`${getPokemonNameWithAffix(pokemon)} was revived!`,0,true);
|
||||
|
||||
if (user.scene.currentBattle.double && user.scene.getEnemyParty().length > 1) {
|
||||
|
@ -2092,7 +2092,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (!isTypeImmune) {
|
||||
const levelMultiplier = (2 * source.level / 5 + 2);
|
||||
const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100);
|
||||
damage.value = Math.ceil((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
|
||||
damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
|
||||
* stabMultiplier.value
|
||||
* typeMultiplier.value
|
||||
* arenaAttackTypeMultiplier.value
|
||||
@ -2108,7 +2108,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false);
|
||||
if (!burnDamageReductionCancelled.value) {
|
||||
damage.value = Math.floor(damage.value / 2);
|
||||
damage.value = Utils.toDmgValue(damage.value / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2129,7 +2129,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 = Math.floor(damage.value / 2);
|
||||
damage.value = Utils.toDmgValue(damage.value / 2);
|
||||
}
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
@ -3455,7 +3455,7 @@ export class PlayerPokemon extends Pokemon {
|
||||
|
||||
pokemon.resetTurnData();
|
||||
pokemon.resetStatus();
|
||||
pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp()));
|
||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||
this.scene.queueMessage(`${pokemon.name} was revived!`,0,true);
|
||||
|
||||
if (this.scene.currentBattle.double && this.scene.getParty().length > 1) {
|
||||
@ -4382,7 +4382,7 @@ export class PokemonMove {
|
||||
}
|
||||
|
||||
getMovePp(): integer {
|
||||
return this.getMove().pp + this.ppUp * Math.max(Math.floor(this.getMove().pp / 5), 1);
|
||||
return this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5);
|
||||
}
|
||||
|
||||
getPpRatio(): number {
|
||||
|
@ -1160,7 +1160,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
|
||||
if (!pokemon.isFullHp()) {
|
||||
const scene = pokemon.scene;
|
||||
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, 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(),
|
||||
Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
|
||||
Utils.toDmgValue(pokemon.turnData.damageDealt / 8) * this.stackCount, 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(),
|
||||
Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
|
||||
Utils.toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
|
||||
|
||||
pokemon.resetStatus(true, false, true);
|
||||
return true;
|
||||
|
@ -6,6 +6,7 @@ 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";
|
||||
@ -47,7 +48,7 @@ describe("Abilities - Disguise", () => {
|
||||
|
||||
const mimikyu = game.scene.getEnemyPokemon()!;
|
||||
const maxHp = mimikyu.getMaxHp();
|
||||
const disguiseDamage = Math.floor(maxHp / 8);
|
||||
const disguiseDamage = toDmgValue(maxHp / 8);
|
||||
|
||||
expect(mimikyu.formIndex).toBe(disguisedForm);
|
||||
|
||||
@ -80,7 +81,7 @@ describe("Abilities - Disguise", () => {
|
||||
|
||||
const mimikyu = game.scene.getEnemyPokemon()!;
|
||||
const maxHp = mimikyu.getMaxHp();
|
||||
const disguiseDamage = Math.floor(maxHp / 8);
|
||||
const disguiseDamage = toDmgValue(maxHp / 8);
|
||||
|
||||
expect(mimikyu.formIndex).toBe(disguisedForm);
|
||||
|
||||
@ -121,7 +122,7 @@ describe("Abilities - Disguise", () => {
|
||||
|
||||
const mimikyu = game.scene.getPlayerPokemon()!;
|
||||
const maxHp = mimikyu.getMaxHp();
|
||||
const disguiseDamage = Math.floor(maxHp / 8);
|
||||
const disguiseDamage = toDmgValue(maxHp / 8);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
|
||||
|
@ -8,6 +8,7 @@ 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;
|
||||
@ -72,6 +73,6 @@ describe("Abilities - Heatproof", () => {
|
||||
await game.toNextTurn();
|
||||
|
||||
// Normal burn damage is /16
|
||||
expect(enemy.hp).toBe(enemy.getMaxHp() - Math.floor(enemy.getMaxHp() / 32));
|
||||
expect(enemy.hp).toBe(enemy.getMaxHp() - toDmgValue(enemy.getMaxHp() / 32));
|
||||
});
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ 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;
|
||||
|
||||
@ -73,7 +74,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(secondStrikeDamage).toBe(Math.ceil(0.25 * firstStrikeDamage));
|
||||
expect(secondStrikeDamage).toBe(toDmgValue(0.25 * firstStrikeDamage));
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -303,7 +304,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
// This test will time out if the user faints
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(leadPokemon.hp).toBe(Math.floor(leadPokemon.getMaxHp()/2));
|
||||
expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp()/2));
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
|
71
src/test/battle/damage_calculation.test.ts
Normal file
71
src/test/battle/damage_calculation.test.ts
Normal file
@ -0,0 +1,71 @@
|
||||
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);
|
||||
});
|
||||
});
|
@ -6,6 +6,7 @@ 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
|
||||
@ -44,7 +45,7 @@ describe("Moves - BELLY DRUM", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
@ -59,7 +60,7 @@ describe("Moves - BELLY DRUM", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
// Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = -3;
|
||||
@ -95,7 +96,7 @@ describe("Moves - BELLY DRUM", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
leadPokemon.hp = hpLost - PREDAMAGE;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));
|
||||
|
@ -7,6 +7,7 @@ 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 */
|
||||
@ -45,7 +46,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
@ -64,7 +65,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
//Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
@ -113,7 +114,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
leadPokemon.hp = hpLost - PREDAMAGE;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));
|
||||
|
@ -7,6 +7,7 @@ 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 */
|
||||
@ -45,7 +46,7 @@ describe("Moves - FILLET AWAY", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
@ -62,7 +63,7 @@ describe("Moves - FILLET AWAY", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
//Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
@ -103,7 +104,7 @@ describe("Moves - FILLET AWAY", () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO);
|
||||
leadPokemon.hp = hpLost - PREDAMAGE;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));
|
||||
|
@ -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).toBe(4);
|
||||
expect(hpLost).toBeLessThan(4);
|
||||
}, 20000);
|
||||
});
|
||||
|
14
src/utils.ts
14
src/utils.ts
@ -560,3 +560,17 @@ 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user