refactored code as requested
pls tell me this isn't too much tech debt now (also RIP unnecessarily convoluted cube roots, you will be missed)
This commit is contained in:
parent
2d21a5510b
commit
e582f561bb
|
@ -9,7 +9,7 @@ import { Constructor, NumberHolder } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
||||||
import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPostItemLostAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ChangeMovePriorityAbAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, InfiltratorAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, PostDamageForceSwitchAbAttr, PostItemLostAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability";
|
import { AddSecondStrikeAbAttr, allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPostItemLostAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ChangeMovePriorityAbAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, InfiltratorAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, PostDamageForceSwitchAbAttr, PostItemLostAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability";
|
||||||
import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier";
|
import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier";
|
||||||
import { BattlerIndex, BattleType } from "../battle";
|
import { BattlerIndex, BattleType } from "../battle";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
|
@ -1385,12 +1385,33 @@ export class UserHpDamageAttr extends FixedDamageAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TargetHalfHpDamageAttr extends FixedDamageAttr {
|
export class TargetHalfHpDamageAttr extends FixedDamageAttr {
|
||||||
|
private targetHp: number; // the final amount of hp we want the target to have
|
||||||
|
private hpAfterFirstAttack: number | null;
|
||||||
constructor() {
|
constructor() {
|
||||||
super(0);
|
super(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2);
|
// first, determine if the hit is coming from multi lens or not
|
||||||
|
const lensCount = user.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0;
|
||||||
|
if (lensCount <= 0) {
|
||||||
|
// no multi lenses; we can just halve the target's hp and call it a day
|
||||||
|
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out what hit # we're on
|
||||||
|
if (user.turnData.hitCount === user.turnData.hitsLeft) {
|
||||||
|
// first hit of move; apply damage as normal and update targetHp accordingly
|
||||||
|
this.targetHp = target.hp * (0.5 ** (user.hasAbilityWithAttr(AddSecondStrikeAbAttr) ? 2 : 1));
|
||||||
|
this.hpAfterFirstAttack = null;
|
||||||
|
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||||
|
} else {
|
||||||
|
// all subsequent hits split the damage evenly among themselves
|
||||||
|
this.hpAfterFirstAttack ??= Utils.toDmgValue(target.hp); // nullish coalescing assignment go brrr
|
||||||
|
const totalHits = user.turnData.hitCount - 1;
|
||||||
|
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue((this.hpAfterFirstAttack - this.targetHp) / totalHits);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2622,34 +2622,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const fixedDamage = new Utils.NumberHolder(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;
|
// Don't apply the multi lens damage penalty for subsequent hits of a hp cutting move
|
||||||
// Apply damage fixing for hp cutting moves on multi lens hits (NOT PARENTAL BOND)
|
// those get handled separately in move.ts
|
||||||
if (lensCount > 0
|
if (!(move.hasAttr(TargetHalfHpDamageAttr)
|
||||||
&& move.hasAttr(TargetHalfHpDamageAttr)
|
&& source.turnData.hitCount !== source.turnData.hitsLeft)) {
|
||||||
&& (source.turnData.hitCount === source.turnData.hitsLeft
|
const multiLensMultiplier = new Utils.NumberHolder(1);
|
||||||
|| source.turnData.hitCount - source.turnData.hitsLeft !== lensCount + 1)) {
|
source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier);
|
||||||
// Do some unholy math to make the moves' damage values add up to 50%
|
fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value);
|
||||||
// Values obtained courtesy of WolframAlpha and Desmos Graphing Calculator
|
|
||||||
// (https://www.desmos.com/calculator/wdngrksdfz)
|
|
||||||
let damageMulti = 1;
|
|
||||||
// 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;
|
|
||||||
default:
|
|
||||||
damageMulti = 0.5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixedDamage.value = this.hp * damageMulti;
|
|
||||||
}
|
}
|
||||||
const multiLensMultiplier = new Utils.NumberHolder(1);
|
|
||||||
source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier);
|
|
||||||
fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
|
|
|
@ -140,7 +140,7 @@ describe("Items - Multi Lens", () => {
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }])
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }])
|
||||||
.moveset(Moves.SUPER_FANG)
|
.moveset(Moves.SUPER_FANG)
|
||||||
.ability(Abilities.COMPOUND_EYES)
|
.ability(Abilities.COMPOUND_EYES)
|
||||||
.enemyLevel(100000)
|
.enemyLevel(1000)
|
||||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||||
|
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
@ -150,7 +150,7 @@ describe("Items - Multi Lens", () => {
|
||||||
game.move.select(Moves.SUPER_FANG);
|
game.move.select(Moves.SUPER_FANG);
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 10); // unrealistically high level of precision
|
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should result in correct damage for hp% attacks with 2 lenses", async () => {
|
it("should result in correct damage for hp% attacks with 2 lenses", async () => {
|
||||||
|
@ -158,7 +158,7 @@ describe("Items - Multi Lens", () => {
|
||||||
.moveset(Moves.SUPER_FANG)
|
.moveset(Moves.SUPER_FANG)
|
||||||
.ability(Abilities.COMPOUND_EYES)
|
.ability(Abilities.COMPOUND_EYES)
|
||||||
.enemyMoveset(Moves.SPLASH)
|
.enemyMoveset(Moves.SPLASH)
|
||||||
.enemyLevel(100000)
|
.enemyLevel(1000)
|
||||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||||
|
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
@ -168,15 +168,15 @@ describe("Items - Multi Lens", () => {
|
||||||
game.move.select(Moves.SUPER_FANG);
|
game.move.select(Moves.SUPER_FANG);
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 8); // unrealistically high level of precision
|
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5);
|
||||||
});
|
});
|
||||||
it("should result in correct damage for hp% attacks with 2 lenses + Parental Bond", async () => {
|
it("should result in correct damage for hp% attacks with 2 lenses + Parental Bond", async () => {
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 },
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }])
|
||||||
{ name: "WIDE_LENS", count: 2 }]) // ensures move always hits
|
|
||||||
.moveset(Moves.SUPER_FANG)
|
.moveset(Moves.SUPER_FANG)
|
||||||
.ability(Abilities.PARENTAL_BOND)
|
.ability(Abilities.PARENTAL_BOND)
|
||||||
|
.passiveAbility(Abilities.COMPOUND_EYES)
|
||||||
.enemyMoveset(Moves.SPLASH)
|
.enemyMoveset(Moves.SPLASH)
|
||||||
.enemyLevel(100000)
|
.enemyLevel(1000)
|
||||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||||
|
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
@ -186,6 +186,6 @@ describe("Items - Multi Lens", () => {
|
||||||
game.move.select(Moves.SUPER_FANG);
|
game.move.select(Moves.SUPER_FANG);
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.25, 8);
|
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.25, 5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue