Skip to content

Commit

Permalink
[Ability] Heatproof now reduces burn damage by half (#3524)
Browse files Browse the repository at this point in the history
* Heatproof now reduces burn damage by half

* Add tests

* Add comment to test
  • Loading branch information
DayKev authored Aug 13, 2024
1 parent 452fbbb commit 39e7591
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 7 deletions.
25 changes: 25 additions & 0 deletions src/data/ability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value *= 2;
Expand Down Expand Up @@ -4613,6 +4637,7 @@ export function initAbilities() {
.unimplemented(),
new Ability(Abilities.HEATPROOF, 4)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(ReduceBurnDamageAbAttr, 0.5)
.ignorable(),
new Ability(Abilities.SIMPLE, 4)
.attr(StatChangeMultiplierAbAttr, 2)
Expand Down
15 changes: 8 additions & 7 deletions src/phases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Starter } from "./ui/starter-select-ui-handler";
import { Gender } from "./data/gender";
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
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 { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
Expand Down Expand Up @@ -3815,21 +3815,22 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {

if (!cancelled.value) {
this.scene.queueMessage(getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
let damage: integer = 0;
const damage = new Utils.NumberHolder(0);
switch (pokemon.status.effect) {
case StatusEffect.POISON:
damage = Math.max(pokemon.getMaxHp() >> 3, 1);
damage.value = Math.max(pokemon.getMaxHp() >> 3, 1);
break;
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;
case StatusEffect.BURN:
damage = Math.max(pokemon.getMaxHp() >> 4, 1);
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage);
break;
}
if (damage) {
if (damage.value) {
// 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();
}
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
Expand Down
77 changes: 77 additions & 0 deletions src/test/abilities/heatproof.test.ts
Original file line number Diff line number Diff line change
@@ -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));
});
});

0 comments on commit 39e7591

Please sign in to comment.