[Bug] Fix strong winds calculation (#2423)

* Fix strong winds calculation

* add unit test

* Add strong winds effect message

* Update src/locales/de/weather.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

* Update src/locales/es/weather.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

* Update src/locales/fr/weather.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

* Update src/locales/it/weather.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

* Update src/locales/ko/weather.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

---------

Co-authored-by: torranx <torranicles@gmail.com>
Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>
This commit is contained in:
Tempoanon 2024-07-05 14:59:53 -04:00 committed by GitHub
parent 160a5ce5aa
commit 0f510996f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 105 additions and 3 deletions

View File

@ -1162,7 +1162,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier;
}
getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false): TypeDamageMultiplier {
/**
* Calculates the type effectiveness multiplier for an attack type
* @param moveType Type of the move
* @param source the Pokemon using the move
* @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks)
* @param simulated tag to only apply the strong winds effect message when the move is used
* @returns a multiplier for the type effectiveness
*/
getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true): TypeDamageMultiplier {
if (moveType === Type.STELLAR) {
return this.isTerastallized() ? 2 : 1;
}
@ -1183,8 +1191,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
// Handle strong winds lowering effectiveness of types super effective against pure flying
if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && multiplier >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
multiplier /= 2;
if (!simulated) {
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
}
}
if (!!this.summonData?.tags.find((tag) => tag instanceof TypeImmuneTag && tag.immuneType === moveType)) {
@ -1739,7 +1750,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const cancelled = new Utils.BooleanHolder(false);
const typeless = move.hasAttr(TypelessAttr);
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
? this.getAttackTypeEffectiveness(move.type, source)
? this.getAttackTypeEffectiveness(move.type, source, false, false)
: 1);
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
if (typeless) {

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "Alle Flug-Pokémon werden von rätselhaften Luftströmungen geschützt!",
"strongWindsLapseMessage": "Die rätselhafte Luftströmung hält an.",
"strongWindsEffectMessage": "Rätselhafte Luftströmungen haben den Angriff abgeschwächt!",
"strongWindsClearMessage": "Die rätselhafte Luftströmung hat sich wieder geleget.",
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "A heavy wind began!",
"strongWindsLapseMessage": "The wind blows intensely.",
"strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "The heavy wind stopped."
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "¡Comenzó un fuerte viento!",
"strongWindsLapseMessage": "El viento sopla intensamente.",
"strongWindsEffectMessage": "¡Las misteriosas turbulencias atenúan el ataque!",
"strongWindsClearMessage": "El fuerte viento cesó."
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "Un vent mystérieux se lève !",
"strongWindsLapseMessage": "Le vent mystérieux souffle violemment !",
"strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit lattaque!",
"strongWindsClearMessage": "Le vent mystérieux sest dissipé…"
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "È apparsa una corrente d'aria misteriosa!",
"strongWindsLapseMessage": "La corrente d'aria soffia intensamente.",
"strongWindsEffectMessage": "La corrente misteriosa indebolisce lattacco!",
"strongWindsClearMessage": "La corrente d'aria è cessata."
};

View File

@ -41,5 +41,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "수수께끼의 난기류가\n비행포켓몬을 지킨다!",
"strongWindsLapseMessage": "수수께끼의 난기류가 강렬하게 불고 있다",
"strongWindsEffectMessage": "수수께끼의 난기류가 공격을 약하게 만들었다!",
"strongWindsClearMessage": "수수께끼의 난기류가 멈췄다!" // 임의번역
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "Ventos fortes apareceram!",
"strongWindsLapseMessage": "Os ventos fortes continuam.",
"strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "Os ventos fortes diminuíram.",
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "吹起了神秘的乱流!",
"strongWindsLapseMessage": "神秘的乱流势头不减。",
"strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "神秘的乱流停止了。"
};

View File

@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = {
"strongWindsStartMessage": "吹起了神秘的亂流!",
"strongWindsLapseMessage": "神秘的亂流勢頭不減。",
"strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "神秘的亂流停止了。"
};

View File

@ -0,0 +1,82 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import { Species } from "#enums/species";
import {
TurnStartPhase,
} from "#app/phases";
import { Moves } from "#enums/moves";
import { getMovePosition } from "#app/test/utils/gameManagerUtils";
import { Abilities } from "#enums/abilities";
import { allMoves } from "#app/data/move.js";
describe("Weather - Strong Winds", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(10);
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.TAILLOW);
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.DELTA_STREAM);
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.THUNDERBOLT, Moves.ICE_BEAM, Moves.ROCK_SLIDE]);
});
it("electric type move is not very effective on Rayquaza", async () => {
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RAYQUAZA);
await game.startBattle([Species.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon();
const enemy = game.scene.getEnemyPokemon();
game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT));
await game.phaseInterceptor.to(TurnStartPhase);
expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(0.5);
});
it("electric type move is neutral for flying type pokemon", async () => {
await game.startBattle([Species.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon();
const enemy = game.scene.getEnemyPokemon();
game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT));
await game.phaseInterceptor.to(TurnStartPhase);
expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(1);
});
it("ice type move is neutral for flying type pokemon", async () => {
await game.startBattle([Species.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon();
const enemy = game.scene.getEnemyPokemon();
game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM));
await game.phaseInterceptor.to(TurnStartPhase);
expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ICE_BEAM].type, pikachu)).toBe(1);
});
it("rock type move is neutral for flying type pokemon", async () => {
await game.startBattle([Species.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon();
const enemy = game.scene.getEnemyPokemon();
game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE));
await game.phaseInterceptor.to(TurnStartPhase);
expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ROCK_SLIDE].type, pikachu)).toBe(1);
});
});