diff --git a/src/data/move.ts b/src/data/move.ts index 650725b311b..473e2e51f41 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2836,7 +2836,7 @@ export class SwapStatStagesAttr extends MoveEffectAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any []): boolean { if (super.apply(user, target, move, args)) { - for (const s of BATTLE_STATS) { + for (const s of this.stats) { const temp = user.getStatStage(s); user.setStatStage(s, target.getStatStage(s)); target.setStatStage(s, temp); diff --git a/src/locales/fr/challenges.json b/src/locales/fr/challenges.json index a83ec2e0be4..86a21881a50 100644 --- a/src/locales/fr/challenges.json +++ b/src/locales/fr/challenges.json @@ -1,6 +1,7 @@ { "title": "Paramètres du Challenge", "illegalEvolution": "{{pokemon}} est devenu\ninéligible pour ce challenge !", + "noneSelected": "Aucun sélectionné", "singleGeneration": { "name": "Monogénération", "desc": "Vous ne pouvez choisir que des Pokémon de {{gen}} génération.", @@ -33,4 +34,4 @@ "value.0": "Non", "value.1": "Oui" } -} \ No newline at end of file +} diff --git a/src/locales/fr/tutorial.json b/src/locales/fr/tutorial.json index f15a7c7c6d4..7936987457f 100644 --- a/src/locales/fr/tutorial.json +++ b/src/locales/fr/tutorial.json @@ -2,9 +2,9 @@ "intro": "Bienvenue dans PokéRogue, un fangame axé sur les combats Pokémon avec des éléments roguelite !\n$Ce jeu n’est pas monétisé et nous ne prétendons à la propriété d’aucun élément sous copyright utilisé.\n$Bien qu’en développement permanent, PokéRogue reste entièrement jouable.\n$Tout signalement de bugs et d’erreurs quelconques passe par le serveur Discord.\n$Si le jeu est lent, vérifiez que l’Accélération Matérielle est activée dans les paramètres du navigateur.", "accessMenu": "Accédez au menu avec M ou Échap lors de l’attente d’une\naction.\n$Il contient les paramètres et diverses fonctionnalités.", "menu": "Vous pouvez accéder aux paramètres depuis ce menu.\n$Vous pouvez entre autres y changer la vitesse du jeu ou le style de fenêtre…\n$Mais également des tonnes d’autres fonctionnalités, jetez-y un œil !", - "starterSelect": "Choisissez vos starters depuis cet écran avec Z ou Espace.\nIls formeront votre équipe de départ.\n$Chacun possède une valeur. Votre équipe peut avoir jusqu’à 6 membres, sans dépasser un cout de 10.\n$Vous pouvez aussi choisir le sexe, le talent et la forme en\nfonction des variants déjà capturés ou éclos.\n$Les IV d’un starter sont les meilleurs de tous ceux de son espèce déjà possédés. Obtenez-en plusieurs !", + "starterSelect": "Choisissez vos starters depuis cet écran avec Z ou Espace.\nIls formeront votre équipe de départ.\n$Chacun possède une valeur. Votre équipe peut avoir\njusqu’à 6 membres, sans dépasser un cout de 10.\n$Vous pouvez aussi choisir le sexe, le talent et la forme en\nfonction des variants déjà capturés ou éclos.\n$Les IV d’un starter sont les meilleurs de tous ceux de\nson espèce déjà possédés. Obtenez-en plusieurs !", "pokerus": "Chaque jour, 3 starters tirés aléatoirement ont un contour violet.\n$Si un starter que vous possédez l’a, essayez de l’ajouter à votre équipe. Vérifiez bien son résumé !", - "statChange": "Les changements de stats persistent à travers les combats tant que le Pokémon n’est pas rappelé.\n$Vos Pokémon sont rappelés avant un combat de Dresseur et avant d’entrer dans un nouveau biome.\n$Vous pouvez voir en combat les changements de stats d’un Pokémon en maintenant C ou Maj.\n$Vous pouvez également voir les capacités de l’adversaire en maintenant V.\n$Seules les capacités que le Pokémon a utilisées dans ce combat sont consultables.", - "selectItem": "Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre qu’un.\n$Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents.\n$La plupart des effets des objets non-consommables se cumuleront de diverses manières.\n$Certains objets n’apparaitront que s’ils ont une utilité immédiate, comme les objets d’évolution.\n$Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant l’option de transfert.\n$L’option de transfert apparait en bas à droite dès qu’un Pokémon de l’équipe porte un objet.\n$Vous pouvez acheter des consommables avec de l’argent.\nPlus vous progressez, plus le choix sera large.\n$Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.", + "statChange": "Les changements de stats persistent à travers\nles combats tant que le Pokémon n’est pas rappelé.\n$Vos Pokémon sont rappelés avant un combat de\nDresseur et avant d’entrer dans un nouveau biome.\n$Vous pouvez voir en combat les changements de stats\nd’un Pokémon en maintenant C ou Maj.\n$Vous pouvez également voir les capacités de l’adversaire\nen maintenant V.\n$Seules les capacités que le Pokémon a utilisées dans\nce combat sont consultables.", + "selectItem": "Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre qu’un.\n$Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents.\n$La plupart des effets des objets non-consommables se cumuleront de diverses manières.\n$Certains objets n’apparaitront que s’ils ont une utilité immédiate, comme les objets d’évolution.\n$Vous pouvez aussi transférer des objets tenus entre\nPokémon en utilisant l’option de transfert.\n$L’option de transfert apparait en bas à droite dès\nqu’un Pokémon de l’équipe porte un objet.\n$Vous pouvez acheter des consommables avec de\nl’argent. Plus vous progressez, plus le choix sera large.\n$Choisir un des objets gratuits déclenchera le prochain\ncombat, donc faites bien tous vos achats avant.", "eggGacha": "Depuis cet écran, vous pouvez utiliser vos coupons\npour recevoir Œufs de Pokémon au hasard.\n$Les Œufs éclosent après avoir remporté un certain nombre de combats. Plus ils sont rares, plus ils mettent de temps.\n$Les Pokémon éclos ne rejoindront pas votre équipe, mais seront ajoutés à vos starters.\n$Les Pokémon issus d’Œufs ont généralement de meilleurs IV que les Pokémon sauvages.\n$Certains Pokémon ne peuvent être obtenus que dans des Œufs.\n$Il y a 3 différentes machines à actionner avec différents\nbonus, prenez celle qui vous convient le mieux !" } diff --git a/src/test/moves/guard_swap.test.ts b/src/test/moves/guard_swap.test.ts index a27afaaa7ba..0c24f69c32c 100644 --- a/src/test/moves/guard_swap.test.ts +++ b/src/test/moves/guard_swap.test.ts @@ -1,16 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; import GameManager from "#app/test/utils/gameManager"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { Stat } from "#enums/stat"; +import { Stat, BATTLE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Guard Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; + const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -27,37 +28,42 @@ describe("Moves - Guard Swap", () => { game.override .battleType("single") .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset([Moves.SHELL_SMASH]) - .enemySpecies(Species.MEW) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) .enemyLevel(200) .moveset([ Moves.GUARD_SWAP ]) .ability(Abilities.NONE); }); - it("should swap the user's DEF AND SPDEF stat stages with the target's", async () => { - await game.startBattle([ + it("should swap the user's DEF and SPDEF stat stages with the target's", async () => { + await game.classicMode.startBattle([ Species.INDEEDEE ]); - // Should start with no stat stages const player = game.scene.getPlayerPokemon()!; - // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + game.move.select(Moves.GUARD_SWAP); await game.phaseInterceptor.to(MoveEndPhase); - expect(player.getStatStage(Stat.DEF)).toBe(0); - expect(player.getStatStage(Stat.SPDEF)).toBe(0); - expect(enemy.getStatStage(Stat.DEF)).toBe(-1); - expect(enemy.getStatStage(Stat.SPDEF)).toBe(-1); + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } await game.phaseInterceptor.to(TurnEndPhase); - expect(player.getStatStage(Stat.DEF)).toBe(-1); - expect(player.getStatStage(Stat.SPDEF)).toBe(-1); - expect(enemy.getStatStage(Stat.DEF)).toBe(0); - expect(enemy.getStatStage(Stat.SPDEF)).toBe(0); - }, 20000); + for (const s of BATTLE_STATS) { + if (s === Stat.DEF || s === Stat.SPDEF) { + expect(player.getStatStage(s)).toBe(1); + expect(enemy.getStatStage(s)).toBe(0); + } else { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + } + }, TIMEOUT); }); diff --git a/src/test/moves/heart_swap.test.ts b/src/test/moves/heart_swap.test.ts new file mode 100644 index 00000000000..f658641d46f --- /dev/null +++ b/src/test/moves/heart_swap.test.ts @@ -0,0 +1,64 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { BATTLE_STATS } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; + +describe("Moves - Heart Swap", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) + .enemyLevel(200) + .moveset([ Moves.HEART_SWAP ]) + .ability(Abilities.NONE); + }); + + it("should swap all of the user's stat stages with the target's", async () => { + await game.classicMode.startBattle([ + Species.MANAPHY + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + + game.move.select(Moves.HEART_SWAP); + + await game.phaseInterceptor.to(MoveEndPhase); + + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + + await game.phaseInterceptor.to(TurnEndPhase); + + for (const s of BATTLE_STATS) { + expect(enemy.getStatStage(s)).toBe(0); + expect(player.getStatStage(s)).toBe(1); + } + }, TIMEOUT); +}); diff --git a/src/test/moves/power_swap.test.ts b/src/test/moves/power_swap.test.ts index a3d4bfca19a..92cd786c050 100644 --- a/src/test/moves/power_swap.test.ts +++ b/src/test/moves/power_swap.test.ts @@ -1,16 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; import GameManager from "#app/test/utils/gameManager"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { Stat } from "#enums/stat"; +import { Stat, BATTLE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Power Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; + const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -27,36 +28,42 @@ describe("Moves - Power Swap", () => { game.override .battleType("single") .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset([Moves.SHELL_SMASH]) - .enemySpecies(Species.MEW) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) .enemyLevel(200) .moveset([ Moves.POWER_SWAP ]) .ability(Abilities.NONE); }); - it("should swap the user's ATK AND SPATK stat stages with the target's", async () => { - await game.startBattle([ + it("should swap the user's ATK and SPATK stat stages with the target's", async () => { + await game.classicMode.startBattle([ Species.INDEEDEE ]); - // Should start with no stat stages const player = game.scene.getPlayerPokemon()!; - // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + game.move.select(Moves.POWER_SWAP); await game.phaseInterceptor.to(MoveEndPhase); - expect(player.getStatStage(Stat.ATK)).toBe(0); - expect(player.getStatStage(Stat.SPATK)).toBe(0); - expect(enemy.getStatStage(Stat.ATK)).toBe(2); - expect(enemy.getStatStage(Stat.SPATK)).toBe(2); + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } await game.phaseInterceptor.to(TurnEndPhase); - expect(player.getStatStage(Stat.ATK)).toBe(2); - expect(player.getStatStage(Stat.SPATK)).toBe(2); - expect(enemy.getStatStage(Stat.ATK)).toBe(0); - expect(enemy.getStatStage(Stat.SPATK)).toBe(0); - }, 20000); + for (const s of BATTLE_STATS) { + if (s === Stat.ATK || s === Stat.SPATK) { + expect(player.getStatStage(s)).toBe(1); + expect(enemy.getStatStage(s)).toBe(0); + } else { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + } + }, TIMEOUT); });