[Ability] Heatproof now reduces burn damage by half (#3524)
* Heatproof now reduces burn damage by half * Add tests * Add comment to test
This commit is contained in:
parent
452fbbb345
commit
39e7591d3b
|
@ -3428,6 +3428,30 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes Pokemon to take reduced damage from the {@linkcode StatusEffect.BURN | Burn} status
|
||||||
|
* @param multiplier Multiplied with the damage taken
|
||||||
|
*/
|
||||||
|
export class ReduceBurnDamageAbAttr extends AbAttr {
|
||||||
|
constructor(protected multiplier: number) {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the damage reduction
|
||||||
|
* @param pokemon N/A
|
||||||
|
* @param passive N/A
|
||||||
|
* @param cancelled N/A
|
||||||
|
* @param args `[0]` {@linkcode Utils.NumberHolder} The damage value being modified
|
||||||
|
* @returns `true`
|
||||||
|
*/
|
||||||
|
apply(pokemon: Pokemon, passive: 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);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class DoubleBerryEffectAbAttr extends AbAttr {
|
export class DoubleBerryEffectAbAttr extends AbAttr {
|
||||||
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
(args[0] as Utils.NumberHolder).value *= 2;
|
(args[0] as Utils.NumberHolder).value *= 2;
|
||||||
|
@ -4613,6 +4637,7 @@ export function initAbilities() {
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new Ability(Abilities.HEATPROOF, 4)
|
new Ability(Abilities.HEATPROOF, 4)
|
||||||
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
|
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
|
||||||
|
.attr(ReduceBurnDamageAbAttr, 0.5)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.SIMPLE, 4)
|
new Ability(Abilities.SIMPLE, 4)
|
||||||
.attr(StatChangeMultiplierAbAttr, 2)
|
.attr(StatChangeMultiplierAbAttr, 2)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { Starter } from "./ui/starter-select-ui-handler";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
|
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
|
||||||
import { ArenaTagSide, ArenaTrapTag, ConditionalProtectTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
import { ArenaTagSide, ArenaTrapTag, ConditionalProtectTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
||||||
import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, ChangeMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, PreventBypassSpeedChanceAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability";
|
import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, ChangeMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, PreventBypassSpeedChanceAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr, ReduceBurnDamageAbAttr } from "./data/ability";
|
||||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||||
import { getBiomeKey } from "./field/arena";
|
import { getBiomeKey } from "./field/arena";
|
||||||
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
|
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
|
||||||
|
@ -3815,21 +3815,22 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
this.scene.queueMessage(getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
|
this.scene.queueMessage(getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
|
||||||
let damage: integer = 0;
|
const damage = new Utils.NumberHolder(0);
|
||||||
switch (pokemon.status.effect) {
|
switch (pokemon.status.effect) {
|
||||||
case StatusEffect.POISON:
|
case StatusEffect.POISON:
|
||||||
damage = Math.max(pokemon.getMaxHp() >> 3, 1);
|
damage.value = Math.max(pokemon.getMaxHp() >> 3, 1);
|
||||||
break;
|
break;
|
||||||
case StatusEffect.TOXIC:
|
case StatusEffect.TOXIC:
|
||||||
damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1);
|
damage.value = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1);
|
||||||
break;
|
break;
|
||||||
case StatusEffect.BURN:
|
case StatusEffect.BURN:
|
||||||
damage = Math.max(pokemon.getMaxHp() >> 4, 1);
|
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
||||||
|
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (damage) {
|
if (damage.value) {
|
||||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
||||||
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage, false, true));
|
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||||
pokemon.updateInfo();
|
pokemon.updateInfo();
|
||||||
}
|
}
|
||||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
|
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Species } from "#app/enums/species.js";
|
||||||
|
import { TurnEndPhase } from "#app/phases";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
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";
|
||||||
|
|
||||||
|
describe("Abilities - Heatproof", () => {
|
||||||
|
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")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.CHARMANDER)
|
||||||
|
.enemyAbility(Abilities.HEATPROOF)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.starterSpecies(Species.CHANDELURE)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.moveset([Moves.FLAMETHROWER, Moves.SPLASH])
|
||||||
|
.startingLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces Fire type damage by half", async () => {
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
const initialHP = 1000;
|
||||||
|
enemy.hp = initialHP;
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const heatproofDamage = initialHP - enemy.hp;
|
||||||
|
|
||||||
|
enemy.hp = initialHP;
|
||||||
|
game.override.enemyAbility(Abilities.BALL_FETCH);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER));
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const regularDamage = initialHP - enemy.hp;
|
||||||
|
|
||||||
|
expect(heatproofDamage).toBeLessThanOrEqual((regularDamage / 2) + 1);
|
||||||
|
expect(heatproofDamage).toBeGreaterThanOrEqual((regularDamage / 2) - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces Burn damage by half", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyStatusEffect(StatusEffect.BURN)
|
||||||
|
.enemySpecies(Species.ABRA);
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
// Normal burn damage is /16
|
||||||
|
expect(enemy.hp).toBe(enemy.getMaxHp() - Math.floor(enemy.getMaxHp() / 32));
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue