Added Super Fang fix to Multi Lens using MATH

Yay math
This commit is contained in:
Bertie690 2024-11-18 19:40:53 -05:00
parent 33d8db73ef
commit 95bbd45cf1
2 changed files with 101 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import BattleScene, { AnySound } from "#app/battle-scene";
import { Variant, VariantSet, variantColorCache } from "#app/data/variant"; import { Variant, VariantSet, variantColorCache } from "#app/data/variant";
import { variantData } from "#app/data/variant"; import { variantData } from "#app/data/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget, CombinedPledgeStabBoostAttr, VariableMoveTypeChartAttr } from "#app/data/move"; import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget, CombinedPledgeStabBoostAttr, VariableMoveTypeChartAttr, TargetHalfHpDamageAttr } from "#app/data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { default as PokemonSpecies, PokemonSpeciesForm, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER, getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER, getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
import { starterPassiveAbilities } from "#app/data/balance/passives"; import { starterPassiveAbilities } from "#app/data/balance/passives";
@ -2618,10 +2618,34 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}; };
} }
// If the attack deals fixed damaged, return a result with that much damage // If the attack deals fixed damage, return a result with that much damage
const fixedDamage = new Utils.IntegerHolder(0); const fixedDamage = new Utils.NumberHolder(0);
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (fixedDamage.value) { if (fixedDamage.value) {
const lensCount = source.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0;
// Apply damage fixing for hp cutting moves on multi lens hits (NOT PARENTAL BOND)
if (move.hasAttr(TargetHalfHpDamageAttr) &&
(source.turnData.hitCount === source.turnData.hitsLeft ||
source.turnData.hitCount - source.turnData.hitsLeft !== lensCount + 1)) {
// Do some unholy math to make the moves' damage values add up to 50%
// Values obtained courtesy of WolframAlpha and Desmos Graphing Calculator
// (https://www.desmos.com/calculator/wdngrksdfz)
let damageMulti = 0;
// NOTE: If multi lens ever gets updated (again) this switch case will NEED to be updated alongside it!
switch (lensCount) {
case 1:
damageMulti = 0.558481559888;
break;
case 2:
damageMulti = 0.60875846088;
break;
case 3:
damageMulti = 0.636414338985;
break;
}
fixedDamage.value = this.hp * damageMulti;
}
const multiLensMultiplier = new Utils.NumberHolder(1); const multiLensMultiplier = new Utils.NumberHolder(1);
source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier); source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier);
fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value); fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value);

View File

@ -135,4 +135,78 @@ describe("Items - Multi Lens", () => {
expect(damageResults[0]).toBe(Math.floor(playerPokemon.level * 0.75)); expect(damageResults[0]).toBe(Math.floor(playerPokemon.level * 0.75));
expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25)); expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25));
}); });
it("should result in correct damage for hp% attacks with 1 lens", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }])
.moveset(Moves.SUPER_FANG)
.ability(Abilities.COMPOUND_EYES)
.enemyLevel(100000)
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SUPER_FANG);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEndPhase", true);
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 10); // unrealistically high level of precision
});
it("should result in correct damage for hp% attacks with 2 lenses", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }])
.moveset(Moves.SUPER_FANG)
.ability(Abilities.COMPOUND_EYES)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(100000)
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SUPER_FANG);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEndPhase", true);
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 8); // unrealistically high level of precision
});
it("should result in correct damage for hp% attacks with 3 lenses", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 3 }])
.moveset(Moves.SUPER_FANG)
.ability(Abilities.COMPOUND_EYES)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(100000)
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SUPER_FANG);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEndPhase", true);
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 8);
});
it("should result in correct damage for hp% attacks with 3 lenses + Parental Bond", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 3 },
{ name: "WIDE_LENS", count: 2 }]) // ensures move always hits
.moveset(Moves.SUPER_FANG)
.ability(Abilities.PARENTAL_BOND)
.enemyMoveset(Moves.SPLASH)
.enemyLevel(100000)
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SUPER_FANG);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEndPhase", true);
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.25, 8);
});
}); });