Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Move] Fully implement Freeze Dry #4840

Merged
merged 18 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions src/data/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4971,16 +4971,42 @@ export class NeutralDamageAgainstFlyingTypeMultiplierAttr extends VariableMoveTy
}
}

export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr {
/**
* This class forces Freeze-Dry to be super effective against Water Type.
* It considers if target is Mono or Dual Type and calculates the new Multiplier accordingly.
* @see {@linkcode apply}
*/
export class FreezeDryAttr extends VariableMoveTypeMultiplierAttr {
geeilhan marked this conversation as resolved.
Show resolved Hide resolved
/**
* If the target is Mono Type (Water only) then a 2x Multiplier is always forced.
* If target is Dual Type (containing Water) then only a 2x Multiplier is forced for the Water Type.
*
* Additionally Freeze-Dry's effectiveness against water is always forced during {@linkcode InverseBattleChallenge}.
* The multiplier is recalculated for the non-Water Type in case of Dual Type targets containing Water Type.
*
* @param user The {@linkcode Pokemon} applying the move
* @param target The {@linkcode Pokemon} targeted by the move
* @param move The move used by the user
* @param args `[0]` a {@linkcode Utils.NumberHolder | NumberHolder} containing a type effectiveness multiplier
* @returns `true` if super effectiveness on water type is forced; `false` otherwise
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const multiplier = args[0] as Utils.NumberHolder;
if (target.isOfType(Type.WATER)) {
const effectivenessAgainstWater = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, Type.WATER));
applyChallenges(user.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, effectivenessAgainstWater);
if (effectivenessAgainstWater.value !== 0) {
multiplier.value *= 2 / effectivenessAgainstWater.value;
geeilhan marked this conversation as resolved.
Show resolved Hide resolved
if (target.isOfType(Type.WATER) && multiplier.value !== 0) {
const multipleTypes = (target.getTypes().length > 1);

if (multipleTypes) {
const nonWaterType = target.getTypes().filter(type => type !== Type.WATER)[0];
const effectivenessAgainstTarget = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, nonWaterType));
geeilhan marked this conversation as resolved.
Show resolved Hide resolved

applyChallenges(user.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, effectivenessAgainstTarget);

multiplier.value = effectivenessAgainstTarget.value * 2;
return true;
}

multiplier.value = 2;
return true;
}

return false;
Expand Down Expand Up @@ -9422,7 +9448,7 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
.attr(StatusEffectAttr, StatusEffect.FREEZE)
.attr(WaterSuperEffectTypeMultiplierAttr)
.attr(FreezeDryAttr)
.edgeCase(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell.
new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
.soundBased()
Expand Down
131 changes: 127 additions & 4 deletions src/test/moves/freeze_dry.test.ts
geeilhan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BattlerIndex } from "#app/battle";
import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { Challenges } from "#enums/challenges";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
Expand Down Expand Up @@ -97,8 +98,7 @@ describe("Moves - Freeze-Dry", () => {
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
});

// enable if this is ever fixed (lol)
it.todo("should deal 2x damage to water types under Normalize", async () => {
it("should deal 2x damage to water type under Normalize", async () => {
game.override.ability(Abilities.NORMALIZE);
await game.classicMode.startBattle();

Expand All @@ -112,8 +112,23 @@ describe("Moves - Freeze-Dry", () => {
expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2);
});

// enable once Electrify is implemented (and the interaction is fixed, as above)
it.todo("should deal 2x damage to water types under Electrify", async () => {
it("should deal 0.25x damage to rock/steel type under Normalize", async () => {
game.override
.ability(Abilities.NORMALIZE)
.enemySpecies(Species.SHIELDON);
await game.classicMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase");

expect(enemy.getMoveEffectiveness).toHaveReturnedWith(0.25);
});

it("should deal 2x damage to water type under Electrify", async () => {
game.override.enemyMoveset([ Moves.ELECTRIFY ]);
await game.classicMode.startBattle();

Expand All @@ -126,4 +141,112 @@ describe("Moves - Freeze-Dry", () => {

expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2);
});

it("should deal 4x damage to water/flying type under Electrify", async () => {
geeilhan marked this conversation as resolved.
Show resolved Hide resolved
game.override
.enemyMoveset([ Moves.ELECTRIFY ])
.enemySpecies(Species.GYARADOS);
await game.classicMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase");

expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4);
});

it("should deal 0.25x damage to Grass/Dragon type under Electrify", async () => {
game.override
.enemyMoveset([ Moves.ELECTRIFY ])
.enemySpecies(Species.FLAPPLE);
await game.classicMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase");

expect(enemy.getMoveEffectiveness).toHaveReturnedWith(0.25);
});

it("should deal 2x damage to Water type during inverse battle", async () => {
geeilhan marked this conversation as resolved.
Show resolved Hide resolved
game.override
.moveset([ Moves.FREEZE_DRY ])
.enemySpecies(Species.MAGIKARP);
game.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);


await game.challengeMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase");

expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
});

it("should deal 2x damage to Water type during inverse battle under Normalize", async () => {
game.override
.moveset([ Moves.FREEZE_DRY ])
.ability(Abilities.NORMALIZE)
.enemySpecies(Species.MAGIKARP);
game.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);

await game.challengeMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase");

expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
});

it("should deal 2x damage to Water type during inverse battle under Electrify", async () => {
game.override
.moveset([ Moves.FREEZE_DRY ])
.enemySpecies(Species.MAGIKARP)
.enemyMoveset([ Moves.ELECTRIFY ]);
game.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);

await game.challengeMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("MoveEffectPhase");

expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
});

it("should deal 1x damage to water/flying type during inverse battle under Electrify", async () => {
game.override
.enemyMoveset([ Moves.ELECTRIFY ])
.enemySpecies(Species.GYARADOS);

game.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);

await game.challengeMode.startBattle();

const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");

game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase");

expect(enemy.getMoveEffectiveness).toHaveReturnedWith(1);
});
});
Loading