Compare commits

..

6 Commits

Author SHA1 Message Date
Opaque02
9eb57c0c66
[QoL] IV scanner update to hide the prompt and let you see the stats mid battle on the stats screen (#3285)
* IV scanner PR fix

Updated locales to have updated keys for IV scanner in menu

Updated legacy UI to work with IV scanner changes

Updated code to have player and enemy battle stats for ease of use

Updated logic to make the player side work exactly the same as previously since there should be no HP stat shown for players. Also updated the colours so there's no more grey option for unknown stats

Added HP, updated logic and colours. Need to undo changes to player pokemon to not be as squished because of no HP, and need to see what happens for trainers

Fixing up some git errors

Fixed a bug with double battles not updating the pokemon properly

Updated settings to allow for the ability to skip the IV scanning prompts

Adding functionality to IV scanner to skip prompt and see IVs mid battle

* Merged with latest + fixed bug with IV scanner vs pokemon info container getRootSpeciesId being different

* Updated as per merge comment

* Updating to fix comments

* Updated comments

* Update src/locales/fr/settings.ts

Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr>

* Update src/locales/de/settings.ts

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

* Update src/locales/pt_BR/settings.ts

Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br>

* Update src/locales/ko/settings.ts

Co-authored-by: Enoch <enoch.jwsong@gmail.com>

* Update src/locales/zh_CN/settings.ts

Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>

* Update src/locales/zh_TW/settings.ts

Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>

* Updating text of IV scanner to be gold

* Updated text colour to use one of the existing golds instead of a custom gold

* Japanese locale

* Updating docs

---------

Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr>
Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>
Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br>
Co-authored-by: Enoch <enoch.jwsong@gmail.com>
Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>
2024-08-08 01:04:12 -04:00
flx-sta
7b4e91eb02
[Bug] Fix no wild evolutions (#3426)
* fix wild pokemon not evolving anymore

* add test to cover defaults for wildDelay
2024-08-08 00:46:53 -04:00
Adrian T.
d0629830a8
[Dev] Add mockHitCheck util (#3421)
* add mockHitCheck helper

* update docs
2024-08-07 23:18:37 -04:00
Adrian T.
9ff8685752
[Dev] Update changeTurnOrder name to mockTurnOrder (naming convention) (#3422)
* change changeTurnOrder to mockTurnOrder

* update docs
2024-08-07 23:15:45 -04:00
Lugiad
0bd32b7a76
[Localization] French - Some missing translations, typos and rephrasing. (#3423)
* Update modifier-type.ts

* Update tutorial.ts

* Update modifier-type.ts

* Update modifier-type.ts
2024-08-07 23:14:00 -04:00
Yonmaru40
c70cf87199
[Localization] Zh_cn adding translation 8/8 (#3425)
* Update achv.ts

* Update dialogue.ts

* Update filter-bar.ts

* Update trainers.ts

* Update starter-select-ui-handler.ts
2024-08-07 23:06:57 -04:00
50 changed files with 508 additions and 322 deletions

View File

@ -176,6 +176,27 @@
"w": 12, "w": 12,
"h": 6 "h": 6
} }
},
{
"filename": "HP",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 9,
"h": 8
},
"spriteSourceSize": {
"x": 1,
"y": 2,
"w": 8,
"h": 6
},
"frame": {
"x": 112,
"y": 0,
"w": 8,
"h": 6
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 451 B

View File

@ -281,6 +281,27 @@
"w": 9, "w": 9,
"h": 8 "h": 8
} }
},
{
"filename": "empty",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 1,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 1,
"h": 8
},
"frame": {
"x": 117,
"y": 0,
"w": 1,
"h": 8
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 435 B

View File

@ -176,6 +176,27 @@
"w": 13, "w": 13,
"h": 7 "h": 7
} }
},
{
"filename": "HP",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 9,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 1,
"w": 9,
"h": 7
},
"frame": {
"x": 120,
"y": 0,
"w": 9,
"h": 7
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 496 B

View File

@ -281,6 +281,27 @@
"w": 9, "w": 9,
"h": 8 "h": 8
} }
},
{
"filename": "empty",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 1,
"h": 8
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 1,
"h": 8
},
"frame": {
"x": 117,
"y": 0,
"w": 1,
"h": 8
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 499 B

View File

@ -122,6 +122,7 @@ export default class BattleScene extends SceneBase {
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1"; public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
public enableMoveInfo: boolean = true; public enableMoveInfo: boolean = true;
public enableRetries: boolean = false; public enableRetries: boolean = false;
public hideIvs: boolean = false;
/** /**
* Determines the condition for a notification should be shown for Candy Upgrades * Determines the condition for a notification should be shown for Candy Upgrades
* - 0 = 'Off' * - 0 = 'Off'

View File

@ -8,7 +8,8 @@ export enum BattleStat {
SPD, SPD,
ACC, ACC,
EVA, EVA,
RAND RAND,
HP
} }
export function getBattleStatName(stat: BattleStat) { export function getBattleStatName(stat: BattleStat) {
@ -27,6 +28,8 @@ export function getBattleStatName(stat: BattleStat) {
return i18next.t("pokemonInfo:Stat.ACC"); return i18next.t("pokemonInfo:Stat.ACC");
case BattleStat.EVA: case BattleStat.EVA:
return i18next.t("pokemonInfo:Stat.EVA"); return i18next.t("pokemonInfo:Stat.EVA");
case BattleStat.HP:
return i18next.t("pokemonInfo:Stat.HPStat");
default: default:
return "???"; return "???";
} }

View File

@ -64,21 +64,21 @@ export class SpeciesFormEvolution {
public level: integer; public level: integer;
public item: EvolutionItem | null; public item: EvolutionItem | null;
public condition: SpeciesEvolutionCondition | null; public condition: SpeciesEvolutionCondition | null;
public wildDelay: SpeciesWildEvolutionDelay | null; public wildDelay: SpeciesWildEvolutionDelay;
constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay | null) { constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) {
this.speciesId = speciesId; this.speciesId = speciesId;
this.preFormKey = preFormKey; this.preFormKey = preFormKey;
this.evoFormKey = evoFormKey; this.evoFormKey = evoFormKey;
this.level = level; this.level = level;
this.item = item || EvolutionItem.NONE; this.item = item || EvolutionItem.NONE;
this.condition = condition; this.condition = condition;
this.wildDelay = wildDelay!; // TODO: is this bang correct? this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE;
} }
} }
export class SpeciesEvolution extends SpeciesFormEvolution { export class SpeciesEvolution extends SpeciesFormEvolution {
constructor(speciesId: Species, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay | null) { constructor(speciesId: Species, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) {
super(speciesId, null, null, level, item, condition, wildDelay); super(speciesId, null, null, level, item, condition, wildDelay);
} }
} }
@ -400,8 +400,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LINOONE, 20, null, null) new SpeciesEvolution(Species.LINOONE, 20, null, null)
], ],
[Species.WURMPLE]: [ [Species.WURMPLE]: [
new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)),
new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.SILCOON]: [ [Species.SILCOON]: [
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null) new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
@ -945,7 +945,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
], ],
[Species.SALANDIT]: [ [Species.SALANDIT]: [
new SpeciesEvolution(Species.SALAZZLE, 33, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE), null) new SpeciesEvolution(Species.SALAZZLE, 33, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE))
], ],
[Species.STUFFUL]: [ [Species.STUFFUL]: [
new SpeciesEvolution(Species.BEWEAR, 27, null, null) new SpeciesEvolution(Species.BEWEAR, 27, null, null)
@ -969,8 +969,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.COSMOEM, 43, null, null) new SpeciesEvolution(Species.COSMOEM, 43, null, null)
], ],
[Species.COSMOEM]: [ [Species.COSMOEM]: [
new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)),
new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.MELTAN]: [ [Species.MELTAN]: [
new SpeciesEvolution(Species.MELMETAL, 48, null, null) new SpeciesEvolution(Species.MELMETAL, 48, null, null)
@ -1406,9 +1406,9 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ROCKRUFF]: [ [Species.ROCKRUFF]: [
new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0)), null), new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))),
new SpeciesFormEvolution(Species.LYCANROC, "", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1), null), new SpeciesFormEvolution(Species.LYCANROC, "", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)),
new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)), null) new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)))
], ],
[Species.STEENEE]: [ [Species.STEENEE]: [
new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG)

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Gesehenen Dialog überspringen", "skipSeenDialogues": "Gesehenen Dialog überspringen",
"battleStyle": "Kampfstil", "battleStyle": "Kampfstil",
"enableRetries": "Erneut versuchen aktivieren", "enableRetries": "Erneut versuchen aktivieren",
"hideIvs": "IS-Scanner verstecken",
"tutorials": "Tutorials", "tutorials": "Tutorials",
"touchControls": "Touch Steuerung", "touchControls": "Touch Steuerung",
"vibrations": "Vibration", "vibrations": "Vibration",

View File

@ -15,7 +15,8 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPD": "Speed", "SPD": "Speed",
"SPDshortened": "Spd", "SPDshortened": "Spd",
"ACC": "Accuracy", "ACC": "Accuracy",
"EVA": "Evasiveness" "EVA": "Evasiveness",
"HPStat": "HP"
}, },
Type: { Type: {

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Skip Seen Dialogues", "skipSeenDialogues": "Skip Seen Dialogues",
"battleStyle": "Battle Style", "battleStyle": "Battle Style",
"enableRetries": "Enable Retries", "enableRetries": "Enable Retries",
"hideIvs": "Hide IV scanner",
"tutorials": "Tutorials", "tutorials": "Tutorials",
"touchControls": "Touch Controls", "touchControls": "Touch Controls",
"vibrations": "Vibrations", "vibrations": "Vibrations",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Skip Seen Dialogues", "skipSeenDialogues": "Skip Seen Dialogues",
"battleStyle": "Battle Style", "battleStyle": "Battle Style",
"enableRetries": "Enable Retries", "enableRetries": "Enable Retries",
"hideIvs": "Hide IV scanner",
"tutorials": "Tutorials", "tutorials": "Tutorials",
"touchControls": "Touch Controls", "touchControls": "Touch Controls",
"vibrations": "Vibrations", "vibrations": "Vibrations",

View File

@ -24,7 +24,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
} }
}, },
"PokemonReviveModifierType": { "PokemonReviveModifierType": {
description: "Réanime un Pokémon et restaure {{restorePercent}}% de ses PV.", description: "Ranime un Pokémon et restaure {{restorePercent}}% de ses PV.",
}, },
"PokemonStatusHealModifierType": { "PokemonStatusHealModifierType": {
description: "Soigne tous les problèmes de statut dun Pokémon.", description: "Soigne tous les problèmes de statut dun Pokémon.",
@ -64,13 +64,13 @@ export const modifierType: ModifierTypeTranslationEntries = {
description: "Fait monter toute léquipe de {{levels}} niveau·x.", description: "Fait monter toute léquipe de {{levels}} niveau·x.",
}, },
"PokemonBaseStatBoosterModifierType": { "PokemonBaseStatBoosterModifierType": {
description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.", description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.",
}, },
"AllPokemonFullHpRestoreModifierType": { "AllPokemonFullHpRestoreModifierType": {
description: "Restaure tous les PV de toute l'équipe.", description: "Restaure tous les PV de toute léquipe.",
}, },
"AllPokemonFullReviveModifierType": { "AllPokemonFullReviveModifierType": {
description: "Réanime et restaure tous les PV de tous les Pokémon K.O.", description: "Ranime et restaure tous les PV de tous les Pokémon K.O. .",
}, },
"MoneyRewardModifierType": { "MoneyRewardModifierType": {
description: "Octroie une {{moneyMultiplier}} somme dargent.\n({{moneyAmount}} ₽)", description: "Octroie une {{moneyMultiplier}} somme dargent.\n({{moneyAmount}} ₽)",
@ -151,9 +151,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"SACRED_ASH": { name: "Cendre Sacrée" }, "SACRED_ASH": { name: "Cendre Sacrée" },
"REVIVER_SEED": { name: "Résugraine", description: "Réanime et restaure la moitié des PV de son porteur sil est mis K.O. par une capacité directe." }, "REVIVER_SEED": { name: "Résugraine", description: "Ranime et restaure la moitié des PV de son porteur sil est mis K.O. par une capacité directe." },
"WHITE_HERB": { name: "White Herb", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, "WHITE_HERB": { name: "Herbe Blanche", description: "Restaure toute stat ayant subi une baisse en combat." },
"ETHER": { name: "Huile" }, "ETHER": { name: "Huile" },
"MAX_ETHER": { name: "Huile Max" }, "MAX_ETHER": { name: "Huile Max" },
@ -184,12 +184,12 @@ export const modifierType: ModifierTypeTranslationEntries = {
"SOOTHE_BELL": { name: "Grelot Zen" }, "SOOTHE_BELL": { name: "Grelot Zen" },
"SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente le taux de critiques du porteur." }, "SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente dun cran le taux de critiques du porteur." },
"LEEK": { name: "Poireau", description: "Objet à faire tenir à Canarticho. Un poireau très long et solide qui augmente son taux de critiques." }, "LEEK": { name: "Poireau", description: "À faire tenir à Canarticho ou Palarticho. Un poireau très long et solide qui augmente de 2 crans le taux de critiques." },
"EVIOLITE": { name: "Évoluroc", description: "Un étrange concentré dévolution qui augmente la Défense et la Défense Spéciale dun Pokémon pouvant évoluer." }, "EVIOLITE": { name: "Évoluroc", description: "Augmente de 50% la Défense et Déf. Spé. si le porteur peut évoluer, 25% aux fusions dont une moitié le peut encore." },
"SOUL_DEW": { name: "Rosée Âme", description: "Augmente de 10% linfluence de la nature dun Pokémon sur ses statistiques (cumulatif)." }, "SOUL_DEW": { name: "Rosée Âme", description: "Augmente de 10% linfluence de la nature dun Pokémon sur ses statistiques. Effet cumulatif." },
"NUGGET": { name: "Pépite" }, "NUGGET": { name: "Pépite" },
"BIG_NUGGET": { name: "Maxi Pépite" }, "BIG_NUGGET": { name: "Maxi Pépite" },
@ -199,7 +199,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
"GOLDEN_PUNCH": { name: "Poing Doré", description: "50% des dégâts infligés sont convertis en argent." }, "GOLDEN_PUNCH": { name: "Poing Doré", description: "50% des dégâts infligés sont convertis en argent." },
"COIN_CASE": { name: "Boite Jetons", description: "Tous les 10 combats, recevez 10% de votre argent en intérêts." }, "COIN_CASE": { name: "Boite Jetons", description: "Tous les 10 combats, recevez 10% de votre argent en intérêts." },
"LOCK_CAPSULE": { name: "Poké Écrin", description: "Permet de verrouiller des objets rares si vous relancez les objets proposés." }, "LOCK_CAPSULE": { name: "Poké Écrin", description: "Permet de conserver la rareté des objets si vous relancez les objets proposés." },
"GRIP_CLAW": { name: "Accro Griffe" }, "GRIP_CLAW": { name: "Accro Griffe" },
"WIDE_LENS": { name: "Loupe" }, "WIDE_LENS": { name: "Loupe" },
@ -220,8 +220,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Restes", description: "Soigne à chaque tour 1/16 des PV max dun Pokémon." }, "LEFTOVERS": { name: "Restes", description: "Soigne à chaque tour 1/16 des PV max dun Pokémon." },
"SHELL_BELL": { name: "Grelot Coque", description: "Soigne son porteur avec 1/8 des dégâts quil inflige à un Pokémon." }, "SHELL_BELL": { name: "Grelot Coque", description: "Soigne son porteur avec 1/8 des dégâts quil inflige à un Pokémon." },
"TOXIC_ORB": { name: "Orbe Toxique", description: "Un orbe bizarre qui empoisonne gravement son porteur durant le combat." }, "TOXIC_ORB": { name: "Orbe Toxique", description: "Empoisonne gravement son porteur à la fin du tour sil na pas déjà de problème de statut." },
"FLAME_ORB": { name: "Orbe Flamme", description: "Un orbe bizarre qui brule son porteur durant le combat." }, "FLAME_ORB": { name: "Orbe Flamme", description: "Brule son porteur à la fin du tour sil na pas déjà de problème de statut." },
"BATON": { name: "Bâton", description: "Permet de transmettre les effets en cas de changement de Pokémon. Ignore les pièges." }, "BATON": { name: "Bâton", description: "Permet de transmettre les effets en cas de changement de Pokémon. Ignore les pièges." },
@ -242,21 +242,21 @@ export const modifierType: ModifierTypeTranslationEntries = {
"ENEMY_ATTACK_POISON_CHANCE": { name: "Jeton Poison" }, "ENEMY_ATTACK_POISON_CHANCE": { name: "Jeton Poison" },
"ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Jeton Paralysie" }, "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Jeton Paralysie" },
"ENEMY_ATTACK_BURN_CHANCE": { name: "Jeton Brulure" }, "ENEMY_ATTACK_BURN_CHANCE": { name: "Jeton Brulure" },
"ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Jeton Total Soin", description: "Ajoute 2.5% de chances à chaque tour de se soigner dun problème de statut." }, "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Jeton Total Soin", description: "Ajoute 2,5% de chances à chaque tour de se soigner dun problème de statut." },
"ENEMY_ENDURE_CHANCE": { name: "Jeton Ténacité" }, "ENEMY_ENDURE_CHANCE": { name: "Jeton Ténacité" },
"ENEMY_FUSED_CHANCE": { name: "Jeton Fusion", description: "Ajoute 1% de chances quun Pokémon sauvage soit une fusion." }, "ENEMY_FUSED_CHANCE": { name: "Jeton Fusion", description: "Ajoute 1% de chances quun Pokémon sauvage soit une fusion." },
}, },
SpeciesBoosterItem: { SpeciesBoosterItem: {
"LIGHT_BALL": { name: "Balle Lumière", description: "Objet à faire tenir à Pikachu. Un orbe énigmatique qui augmente son Attaque et son Attaque Spéciale." }, "LIGHT_BALL": { name: "Balle Lumière", description: "À faire tenir à Pikachu. Un orbe énigmatique qui double son Attaque et son Atq. Spé. ." },
"THICK_CLUB": { name: "Masse Os", description: "Objet à faire tenir à Osselait ou Ossatueur. Un os dur qui augmente leur Attaque." }, "THICK_CLUB": { name: "Masse Os", description: "À faire tenir à Osselait ou Ossatueur. Un os dur qui double leur Attaque." },
"METAL_POWDER": { name: "Poudre Métal", description: "Objet à faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, augmente sa Défense." }, "METAL_POWDER": { name: "Poudre Métal", description: "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Défense." },
"QUICK_POWDER": { name: "Poudre Vite", description: "Objet à faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, augmente sa Vitesse." } "QUICK_POWDER": { name: "Poudre Vite", description: "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Vitesse." }
}, },
TempBattleStatBoosterItem: { TempBattleStatBoosterItem: {
"x_attack": "Attaque +", "x_attack": "Attaque +",
"x_defense": "Défense +", "x_defense": "Défense +",
"x_sp_atk": "Atq. Spé. +", "x_sp_atk": "Atq. Spé. +",
"x_sp_def": "Déf. Spé. +", "x_sp_def": "Déf. Spé. +",
"x_speed": "Vitesse +", "x_speed": "Vitesse +",
"x_accuracy": "Précision +", "x_accuracy": "Précision +",
"dire_hit": "Muscle +", "dire_hit": "Muscle +",
@ -265,8 +265,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
TempBattleStatBoosterStatName: { TempBattleStatBoosterStatName: {
"ATK": "Attaque", "ATK": "Attaque",
"DEF": "Défense", "DEF": "Défense",
"SPATK": "Atq. Spé.", "SPATK": "Atq. Spé.",
"SPDEF": "Déf. Spé.", "SPDEF": "Déf. Spé.",
"SPD": "Vitesse", "SPD": "Vitesse",
"ACC": "Précision", "ACC": "Précision",
"CRIT": "Taux de critique", "CRIT": "Taux de critique",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Passer dialogues connus", "skipSeenDialogues": "Passer dialogues connus",
"battleStyle": "Style de combat", "battleStyle": "Style de combat",
"enableRetries": "Activer les réessais", "enableRetries": "Activer les réessais",
"hideIvs": "Masquer Scanner dIV",
"tutorials": "Tutoriels", "tutorials": "Tutoriels",
"touchControls": "Contrôles tactiles", "touchControls": "Contrôles tactiles",
"vibrations": "Vibrations", "vibrations": "Vibrations",

View File

@ -34,9 +34,9 @@ export const tutorial: SimpleTranslationEntries = {
"selectItem": `Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre quun. "selectItem": `Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre quun.
$Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents. $Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents.
$La plupart des effets des objets non-consommables se cumuleront de diverses manières. $La plupart des effets des objets non-consommables se cumuleront de diverses manières.
$Certains objets apparaîtront sils peuvent être utilisés, comme les objets dévolution. $Certains objets apparaitront sils peuvent être utilisés, comme les objets dévolution.
$Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant loption de transfert. $Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant loption de transfert.
$Loption de transfert apparaît en bas à droite dès que vous avez obtenu un objet à faire tenir. $Loption de transfert apparait en bas à droite dès que vous avez obtenu un objet à faire tenir.
$Vous pouvez acheter des consommables avec de largent.\nPlus vous progressez, plus le choix sera varié. $Vous pouvez acheter des consommables avec de largent.\nPlus vous progressez, plus le choix sera varié.
$Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.`, $Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.`,

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Skip Seen Dialogues", "skipSeenDialogues": "Skip Seen Dialogues",
"battleStyle": "Battle Style", "battleStyle": "Battle Style",
"enableRetries": "Enable Retries", "enableRetries": "Enable Retries",
"hideIvs": "Hide IV scanner",
"tutorials": "Tutorials", "tutorials": "Tutorials",
"touchControls": "Touch Controls", "touchControls": "Touch Controls",
"vibrations": "Vibrations", "vibrations": "Vibrations",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Skip Seen Dialogues", "skipSeenDialogues": "Skip Seen Dialogues",
"battleStyle": "試合のルール", "battleStyle": "試合のルール",
"enableRetries": "リトライを有効にする", "enableRetries": "リトライを有効にする",
"hideIvs": "Hide IV scanner",
"tutorials": "チュートリアル", "tutorials": "チュートリアル",
"touchControls": "タッチ操作", "touchControls": "タッチ操作",
"vibrations": "振動", "vibrations": "振動",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "본 대화 생략", "skipSeenDialogues": "본 대화 생략",
"battleStyle": "시합 룰", "battleStyle": "시합 룰",
"enableRetries": "재도전 허용", "enableRetries": "재도전 허용",
"hideIvs": "개체값탐지기 효과 끄기",
"tutorials": "튜토리얼", "tutorials": "튜토리얼",
"touchControls": "터치 컨트롤", "touchControls": "터치 컨트롤",
"vibrations": "진동", "vibrations": "진동",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Pular Diálogos Vistos", "skipSeenDialogues": "Pular Diálogos Vistos",
"battleStyle": "Estilo de Batalha", "battleStyle": "Estilo de Batalha",
"enableRetries": "Habilitar Novas Tentativas", "enableRetries": "Habilitar Novas Tentativas",
"hideIvs": "Esconder scanner de IV",
"tutorials": "Tutorial", "tutorials": "Tutorial",
"touchControls": "Controles de Toque", "touchControls": "Controles de Toque",
"vibrations": "Vibração", "vibrations": "Vibração",

View File

@ -170,8 +170,8 @@ export const PGMachv: AchievementTranslationEntries = {
description: "在经典模式中通关游戏", description: "在经典模式中通关游戏",
}, },
"UNEVOLVED_CLASSIC_VICTORY": { "UNEVOLVED_CLASSIC_VICTORY": {
name: "Bring Your Child To Work Day", name: "带孩子来上班",
description: "Beat the game in Classic Mode with at least one unevolved party member." description: "通关经典模式时队伍中至少有一名未进化的宝可梦"
}, },
"MONO_GEN_ONE": { "MONO_GEN_ONE": {

View File

@ -384,199 +384,199 @@ export const PGMdialogue: DialogueTranslationEntries = {
}, },
"archer": { "archer": {
"encounter": { "encounter": {
1: "Before you go any further, let's see how you far against us, Team Rocket!", 1: "在你继续前进之前,\n让我看看你要如何和对付火箭队。",
2: "I have received reports that your skills are not insignificant. Let's see if they are true.", 2: "我收到报告说你的实力与众不同,\n就让我来看看这是否属实吧。",
3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." 3: "我是阿波罗,火箭对的干部。\n我不会对组织的敌人手软。"
}, },
"victory": { "victory": {
1: "What a blunder!", 1: "大失误……",
2: "With my current skills, I was not up to the task after all.", 2: "以我现在的实力,无法胜任我的任务……",
3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." 3: "原……谅我,坂木。\n我竟被一名训练师打败了。."
}, },
}, },
"ariana": { "ariana": {
"encounter": { "encounter": {
1: `Hold it right there! We can't someone on the loose." 1: `站住!我们可不能放过你!"
$It's harmful to Team Rocket's pride, you see.`, $这会损伤火箭对的名誉`,
2: `I don't know or care if what I'm doing is right or wrong... 2: `我不知道也不想知道我的所作所为正确与否…
$I just put my faith in Giovanni and do as I am told`, $我只要遵从坂木老大的指令就可以了`,
3: "Your trip ends here. I'm going to take you down!" 3: "你的旅途到此为止了,我会把你狠狠扳倒!"
}, },
"victory": { "victory": {
1: `Tch, you really are strong. It's too bad. 1: `切,你好强,可恶。
$If you were to join Team Rocket, you could become an Executive.`, $如果你加入火箭队`,
2: "I... I'm shattered...", 2: "好……好崩溃……",
3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" 3: "嗯啊啊!这不可能!我使出全力还是输了!"
}, },
}, },
"proton": { "proton": {
"encounter": { "encounter": {
1: "What do you want? If you interrupt our work, don't expect any mercy!", 1: "你想干什么?如果你要妨碍我们的事业,我可不会手下留情。",
2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket 2: `你在这干什么?别人总说我是火箭队里最残忍和恐怖的人
$I strongly urge you not to interfere with our business!`, $我强烈推荐你别来碍我们的事`,
3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" 3: "我是兰斯,火箭队的干部。就让来扫除你对我们的阻挠。"
}, },
"victory": { "victory": {
1: "The fortress came down!", 1: "我的防线崩溃了……",
2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", 2: "你虽然这次赢了,但是这只是让火箭队的怒火继续燃烧!",
3: "I am defeated… But I will not forget this!" 3: "我输了…但是我不会忘记的。"
}, },
}, },
"petrel": { "petrel": {
"encounter": { "encounter": {
1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. 1: `哇哈哈哈,我们一直在等你。我?你不知道我是谁?是我,坂木啊。
$The majestic Giovanni himself! Wahahaha! Huh? I don't sound anything like Giovanni? $伟大的坂木大人本人
$I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, $我连看起来都不像`,
2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", 2: "我是拉姆达,火箭队的干部。我不会允许你干涉我们的计划!",
3: "Rocket Executive Petrel will deal with this intruder!" 3: "火箭队干部拉姆达来会会这个入侵者!"
}, },
"victory": { "victory": {
1: "OK, OK. I'll tell you where he is.", 1: "好好好,我会说他在哪的",
2: "I… I couldn't do a thing… Giovanni, please forgive me…", 2: "我……我什么也做不了……坂木,请原谅我……",
3: "No, I can't let this affect me. I have to inform the others…" 3: "不,我不能慌了神,必须通知其他人…"
}, },
}, },
"tabitha": { "tabitha": {
"encounter": { "encounter": {
1: "Hehehe! So you've come all the way here! But you're too late!", 1: "呵呵呵!原来你都一路来到这里了!但你来晚了!",
2: `Hehehe... Got here already, did you? We underestimated you! But this is it! 2: `呵呵呵……你终于来了?我们小瞧你了,没不过事!
$I'm a cut above the Grunts you've seen so far. I'm not stalling for time. $我比你见过的所有队员都要厉害
$I'm going to pulverize you!`, $我会把你碾碎`,
3: "I'm going to give you a little taste of pain! Resign yourself to it!" 3: "我要让你尝尝痛苦的滋味!认命吧!"
}, },
"victory": { "victory": {
1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! 1: `呵呵呵!虽然你打败了我,但你根本没机会打败老大!
$If you get lost now, you won't have to face a sound whipping!`, $如果你现在输了`,
2: "Hehehe... So, I lost, too...", 2: "呵呵呵……所以,我也输了……",
3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." 3: "啊哈!怎么会这样?像我这样的干部\n竟然输给了一个随处可见的训练师……"
}, },
}, },
"courtney": { "courtney": {
"encounter": { "encounter": {
1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", 1: "那个东西……你所拥有的那个东西……\n那就是……那就是我们熔岩队所寻找的东西……",
2: "... Well then...Deleting...", 2: "……那么……删除记忆……",
3: "...Ha. ...Analyzing... ...Hah♪" 3: "……哈……分析中……啊哈♪"
}, },
"victory": { "victory": {
1: "... ...Change...the world.", 1: "……改变……世界。",
2: `As anticipated. Unanticipated. You. Target lock...completed. 2: `如预期。出乎意料。目标锁定…锁定你……完成。
$Commencing...experiment. You. Forever. Aha... `, $开始`,
3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" 3: "……又来了?出乎意料……我就知道。你……很有趣!……啊哈哈!♪"
}, },
}, },
"shelly": { "shelly": {
"encounter": { "encounter": {
1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? 1: `啊哈哈哈哈!你要插手海洋队的事?
$You're either absolutely fearless, simply ignorant, or both! $你要么是绝对无畏
$You're so cute, you're disgusting! I'll put you down`, $你太可爱了`,
2: "What's this? Who's this spoiled brat?", 2: "怎么回事?这个小鬼头是谁?",
3: "Cool your jets. Be patient. I'll crush you shortly." 3: "冷静点,耐心点。我很快就会把你击溃。"
}, },
"victory": { "victory": {
1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. 1: `啊哈哈哈哈!我们意外地被人干扰了!我们别无选择。
$We'll have to pull out. But this isn't the last you'll see of Team Aqua! $不得不撤退了
$We have other plans! Don't you forget it!`, $我们还有其他计划`,
2: "Ahhh?! Did I go too easy on you?!", 2: "啊?!我是不是对你太温柔了?!",
3: `Uh. Are you telling me you've upped your game even more during the fight? 3: `呃…难道在对战中你也一刻不停地在变强吗?
$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight $你真是个前途光明的小鬼\n我和我的宝可梦已经没有任何力量去战斗了
$Go on Go and be destroyed by Archie.` $继续吧`
}, },
}, },
"matt": { "matt": {
"encounter": { "encounter": {
1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", 1: "嚯!哈哈哈!怎么,你是不是脑子不正常了?\n看看你像个幕下力士",
2: "Oho! You! You're that funny kid!", 2: "“哦吼!你!你真是个有趣的孩子!",
3: "What are you doing here? Did you follow us?" 3: "你在这里干什么?你跟踪我们了吗?"
}, },
"victory": { "victory": {
1: "All right then, until the Boss has time for you, I'll be your opponent!", 1: "好吧,在老大有时间对付你之前,我来成为你的对手!",
2: `I can feel it! I can feel it, all right! The strength coming offa you! 2: `我能感觉到!我感觉到了,没错!你身上散发出的力量!
$More! I still want more! But looks like we're outta time...`, $更多`,
3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" 3: "真有趣!我就知道你会让我尽兴的!\n我期待有一天再次面对你"
}, },
}, },
"mars": { "mars": {
"encounter": { "encounter": {
1: "I'm Mars, one of Team Galactic's top Commanders.", 1: "我是伙星,银河队的顶级干部之一。",
2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", 2: "银河队对未来的愿景坚定不移。\n反对者将被无情地粉碎",
3: "Feeling nervous? You should be!" 3: "“紧张吗?你是该感到紧张了!"
}, },
"victory": { "victory": {
1: "This can't be happening! How did I lose?!", 1: "这不可能!我怎么会输?!",
2: "You have some skill, I'll give you that.", 2: "你很有本事,我承认。",
3: "Defeated... This was a costly mistake." 3: "输了……犯了一个代价高昂的大错。"
} }
}, },
"jupiter": { "jupiter": {
"encounter": { "encounter": {
1: "Jupiter, Commander of Team Galactic, at your service.", 1: "岁星,银河队干部,为您效劳。",
2: "Resistance is futile. Team Galactic will prevail!", 2: "抵抗是徒劳的。银河队必将获胜!",
3: "You're trembling... scared already?" 3: "你在发抖啊……已经害怕了吗?"
}, },
"victory": { "victory": {
1: "No way... I lost?!", 1: "不会吧……我输了?!",
2: "Impressive, you've got guts!", 2: "厉害,你胆子真大!",
3: "Losing like this... How embarrassing." 3: "输成这样……真丢人。"
} }
}, },
"saturn": { "saturn": {
"encounter": { "encounter": {
1: "I am Saturn, Commander of Team Galactic.", 1: "我是镇星,银河队的干部。",
2: "Our mission is absolute. Any hindrance will be obliterated!", 2: "我们的使命是绝对的,任何阻碍都将被消灭!",
3: "Is that fear I see in your eyes?" 3: "我从你的眼中看到的是恐惧吗?"
}, },
"victory": { "victory": {
1: "Impossible... Defeated by you?!", 1: "不可能……被你打败了?!",
2: "You have proven yourself a worthy adversary.", 2: "你证明了自己是一个值得尊敬的对手。",
3: "Bestowed in defeat... This is unacceptable." 3: "失败的苦涩……难以接受……。"
}}, }},
"zinzolin": { "zinzolin": {
"encounter": { "encounter": {
1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", 1: "你可能会对等离子队构成威胁,所以我们现在就消灭你!",
2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", 2: "哦,天哪……我没想到要在这么冷的天气里战斗!",
3: "You're an impressive Trainer to have made it this far. But it ends here." 3: "能走到今天这一步,你真是个了不起的训练师。\n但一切到此结束。"
}, },
"victory": { "victory": {
1: "Ghetsis... I have failed you...", 1: "魁奇思大人……我让你失望了……",
2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", 2: "好冷,我不仅发抖,还要遭罪。",
3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." 3: "哼。你比我想象的要聪明,但还不够。"
} }
}, },
"rood": { "rood": {
"encounter": { "encounter": {
1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", 1: "你对等离子队是个威胁。我们现在不能让你离开这里!",
2: "Oh, this icy wind... I never thought I'd have to fight here!", 2: "哦,这寒风……我从没想过我必须在这里战斗!",
3: "You are a remarkable Trainer to have made it this far. But this is where it ends." 3: "能走到今天这一步,你是一位了不起的训练师,但这就是你的结局了。"
}, },
"victory": { "victory": {
1: "Ghetsis... I have failed my mission...", 1: "魁奇思大人……我的任务失败了",
2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", 2: "寒风刺骨。我瑟瑟发抖。我痛苦不堪。",
3: "Hm. You are a talented Trainer, but unfortunately not talented enough." 3: "嗯,你是很有才。但是要打败等离子队还不够……!"
} }
}, },
"xerosic": { "xerosic": {
"encounter": { "encounter": {
1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", 1: "啊哈哈!我很乐意。\n来吧小训练师让我们看看你有什么本事",
2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", 2: "嗯……你比看上去更强大。\n我想知道你体内有多少能量。",
3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" 3: "我一直在等你!我需要对你做一点研究!\n来吧我们开始吧"
}, },
"victory": { "victory": {
1: "Ah, you're quite strong. Oh yes—very strong, indeed.", 1: "啊,你好强大啊……嗯……确实非常强大。",
2: "Ding-ding-ding! You did it! To the victor go the spoils!", 2: "叮叮叮!你成功了!\n战利品归胜利者",
3: "Wonderful! Amazing! You have tremendous skill and bravery!" 3: "太棒了!太神奇了!\n你的技巧和勇气都无与伦比"
} }
}, },
"bryony": { "bryony": {
"encounter": { "encounter": {
1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", 1: "我是芭菈,能与你一战是我的荣幸。\n让我看看你的实力。",
2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", 2: "令人印象深刻……你比你看上去的还要强大。\n让我们看看你真正的实力。",
3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" 3: "我预料到了你的到来。\n是时候进行一个小实验了我们开始吧"
}, },
"victory": { "victory": {
1: "You're quite strong. Oh yes—very strong, indeed.", 1: "你很强大。哦,嗯嗯!确实非常强大",
2: "Ding-ding-ding! You've done well. Victory is yours.", 2: "叮叮叮!你做得很好。胜利属于你。",
3: "Wonderful! Remarkable! Your skill and bravery are commendable." 3: "太棒了!了不起!你的技巧和勇气值得称赞。"
} }
}, },
"rocket_grunt": { "rocket_grunt": {

View File

@ -13,18 +13,18 @@ export const filterBar: SimpleTranslationEntries = {
"passive": "被动", "passive": "被动",
"passiveUnlocked": "被动解锁", "passiveUnlocked": "被动解锁",
"passiveLocked": "被动未解锁", "passiveLocked": "被动未解锁",
"costReduction": "Cost Reduction", "costReduction": "费用降低",
"costReductionUnlocked": "Cost Reduction Unlocked", "costReductionUnlocked": "已降费",
"costReductionLocked": "Cost Reduction Locked", "costReductionLocked": "未降费",
"ribbon": "缎带", "ribbon": "缎带",
"hasWon": "有缎带", "hasWon": "有缎带",
"hasNotWon": "无缎带", "hasNotWon": "无缎带",
"hiddenAbility": "Hidden Ability", "hiddenAbility": "梦特",
"hasHiddenAbility": "Hidden Ability - Yes", "hasHiddenAbility": "有梦特",
"noHiddenAbility": "Hidden Ability - No", "noHiddenAbility": "无梦特",
"pokerus": "Pokerus", "pokerus": "病毒",
"hasPokerus": "Pokerus - Yes", "hasPokerus": "有病毒",
"noPokerus": "Pokerus - No", "noPokerus": "无病毒",
"sortByNumber": "编号", "sortByNumber": "编号",
"sortByCost": "费用", "sortByCost": "费用",
"sortByCandies": "糖果", "sortByCandies": "糖果",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "跳过已读对话", "skipSeenDialogues": "跳过已读对话",
"battleStyle": "对战模式", "battleStyle": "对战模式",
"enableRetries": "允许重试", "enableRetries": "允许重试",
"hideIvs": "禁用个体值探测器信息",
"tutorials": "教程", "tutorials": "教程",
"touchControls": "触摸操作", "touchControls": "触摸操作",
"vibrations": "手柄震动", "vibrations": "手柄震动",

View File

@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam": "使用这些宝可梦开始游戏吗?", "confirmStartTeam": "使用这些宝可梦开始游戏吗?",
"confirmExit": "Do you want to exit?", "confirmExit": "确定要退出吗?",
"invalidParty": "初始队伍不可用!", "invalidParty": "初始队伍不可用!",
"gen1": "I", "gen1": "I",
"gen2": "II", "gen2": "II",

View File

@ -20,18 +20,18 @@ export const titles: SimpleTranslationEntries = {
"plasma_boss": "等离子队老大", "plasma_boss": "等离子队老大",
"flare_boss": "闪焰队老大", "flare_boss": "闪焰队老大",
"rocket_admin": "Team Rocket Admin", "rocket_admin": "火箭队干部",
"rocket_admin_female": "Team Rocket Admin", "rocket_admin_female": "火箭队干部",
"magma_admin": "Team Magma Admin", "magma_admin": "熔岩队干部",
"magma_admin_female": "Team Magma Admin", "magma_admin_female": "熔岩队干部",
"aqua_admin": "Team Aqua Admin", "aqua_admin": "海洋队干部",
"aqua_admin_female": "Team Aqua Admin", "aqua_admin_female": "海洋队干部",
"galactic_commander": "Team Galactic Commander", "galactic_commander": "银河队干部",
"galactic_commander_female": "Team Galactic Commander", "galactic_commander_female": "银河队干部",
"plasma_sage": "Team Plasma Sage", "plasma_sage": "等离子队贤人",
"plasma_admin": "Team Plasma Admin", "plasma_admin": "等离子队干部",
"flare_admin": "Team Flare Admin", "flare_admin": "闪焰队干部",
"flare_admin_female": "Team Flare Admin", "flare_admin_female": "闪焰队干部",
// Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc.
} as const; } as const;
@ -342,21 +342,21 @@ export const trainerNames: SimpleTranslationEntries = {
"rival_female": "艾薇", "rival_female": "艾薇",
// Evil Team Admins // Evil Team Admins
"archer": "Archer", "archer": "阿波罗",
"ariana": "Ariana", "ariana": "雅典娜",
"proton": "Proton", "proton": "兰斯",
"petrel": "Petrel", "petrel": "拉姆达",
"tabitha": "Tabitha", "tabitha": "火村",
"courtney": "Courtney", "courtney": "火雁",
"shelly": "Shelly", "shelly": "阿泉",
"matt": "Matt", "matt": "阿潮",
"mars": "Mars", "mars": "伙星",
"jupiter": "Jupiter", "jupiter": "碎星",
"saturn": "Saturn", "saturn": "镇星",
"zinzolin": "Zinzolin", "zinzolin": "维奥",
"rood": "Rood", "rood": "罗德",
"xerosic": "Xerosic", "xerosic": "库瑟洛斯奇",
"bryony": "Bryony", "bryony": "芭菈",
// ---- 组织老大 Bosses ---- // ---- 组织老大 Bosses ----
"maxie": "赤焰松", "maxie": "赤焰松",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "跳過已讀對話", "skipSeenDialogues": "跳過已讀對話",
"battleStyle": "對戰模式", "battleStyle": "對戰模式",
"enableRetries": "允許重試", "enableRetries": "允許重試",
"hideIvs": "禁用個體值探測器信息",
"tutorials": "教程", "tutorials": "教程",
"touchControls": "觸摸操作", "touchControls": "觸摸操作",
"vibrations": "手柄震動", "vibrations": "手柄震動",

View File

@ -50,7 +50,7 @@ import { GameMode, GameModes, getGameMode } from "./game-mode";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
import i18next from "./plugins/i18n"; import i18next from "./plugins/i18n";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { TextStyle, addTextObject } from "./ui/text"; import { TextStyle, addTextObject, getTextColor } from "./ui/text";
import { Type } from "./data/type"; import { Type } from "./data/type";
import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene"; import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
@ -5599,19 +5599,45 @@ export class ScanIvsPhase extends PokemonPhase {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { let enemyIvs: number[] = [];
this.scene.ui.setMode(Mode.CONFIRM, () => { let statsContainer: Phaser.GameObjects.Sprite[] = [];
this.scene.ui.setMode(Mode.MESSAGE); let statsContainerLabels: Phaser.GameObjects.Sprite[] = [];
this.scene.ui.clearText(); const enemyField = this.scene.getEnemyField();
new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible
this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end()); for (let e = 0; e < enemyField.length; e++) {
enemyIvs = enemyField[e].ivs;
const currentIvs = this.scene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists
const ivsToShow = this.scene.ui.getMessageHandler().getTopIvs(enemyIvs, this.shownIvs);
statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[];
statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0);
for (let s = 0; s < statsContainerLabels.length; s++) {
const ivStat = Stat[statsContainerLabels[s].frame.name];
if (enemyIvs[ivStat] > currentIvs[ivStat] && ivsToShow.indexOf(Number(ivStat)) >= 0) {
const hexColour = enemyIvs[ivStat] === 31 ? getTextColor(TextStyle.PERFECT_IV, false, uiTheme) : getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme);
const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color;
statsContainerLabels[s].setTint(hexTextColour);
}
statsContainerLabels[s].setVisible(true);
}
}
if (!this.scene.hideIvs) {
this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => {
this.scene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.clearText();
new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => {
this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end());
});
}, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.clearText();
this.end();
}); });
}, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.clearText();
this.end();
}); });
}); } else {
this.end();
}
} }
} }

View File

@ -79,6 +79,7 @@ export const SettingKeys = {
Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES",
Battle_Style: "BATTLE_STYLE", Battle_Style: "BATTLE_STYLE",
Enable_Retries: "ENABLE_RETRIES", Enable_Retries: "ENABLE_RETRIES",
Hide_IVs: "HIDE_IVS",
Tutorials: "TUTORIALS", Tutorials: "TUTORIALS",
Touch_Controls: "TOUCH_CONTROLS", Touch_Controls: "TOUCH_CONTROLS",
Vibration: "VIBRATION", Vibration: "VIBRATION",
@ -250,6 +251,13 @@ export const Setting: Array<Setting> = [
default: 0, default: 0,
type: SettingType.GENERAL type: SettingType.GENERAL
}, },
{
key: SettingKeys.Hide_IVs,
label: i18next.t("settings:hideIvs"),
options: OFF_ON,
default: 0,
type: SettingType.GENERAL
},
{ {
key: SettingKeys.Tutorials, key: SettingKeys.Tutorials,
label: i18next.t("settings:tutorials"), label: i18next.t("settings:tutorials"),
@ -618,6 +626,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
case SettingKeys.Enable_Retries: case SettingKeys.Enable_Retries:
scene.enableRetries = Setting[index].options[value].value === "On"; scene.enableRetries = Setting[index].options[value].value === "On";
break; break;
case SettingKeys.Hide_IVs:
scene.hideIvs = Setting[index].options[value].value === "On";
break;
case SettingKeys.Skip_Seen_Dialogues: case SettingKeys.Skip_Seen_Dialogues:
scene.skipSeenDialogues = Setting[index].options[value].value === "On"; scene.skipSeenDialogues = Setting[index].options[value].value === "On";
break; break;

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
describe("Abilities - Hustle", () => { describe("Abilities - Hustle", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -44,8 +44,7 @@ describe("Abilities - Hustle", () => {
vi.spyOn(pikachu, "getBattleStat"); vi.spyOn(pikachu, "getBattleStat");
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5);

View File

@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js";
import { Type } from "#app/data/type.js"; import { Type } from "#app/data/type.js";
import { Weather, WeatherType } from "#app/data/weather.js"; import { Weather, WeatherType } from "#app/data/weather.js";
import { PlayerPokemon } from "#app/field/pokemon.js"; import { PlayerPokemon } from "#app/field/pokemon.js";
import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; import { TurnEndPhase } from "#app/phases.js";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
@ -12,7 +12,7 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -192,8 +192,7 @@ describe("Abilities - Protean", () => {
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;

View File

@ -1,8 +1,8 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import Phaser from "phaser"; import Phaser from "phaser";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TurnEndPhase, MoveEffectPhase } from "#app/phases"; import { TurnEndPhase } from "#app/phases";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag";
@ -11,7 +11,7 @@ import { Abilities } from "#enums/abilities";
import { WeatherType } from "#app/data/weather.js"; import { WeatherType } from "#app/data/weather.js";
import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
const TIMEOUT = 20 * 1000; // 20 sec timeout const TIMEOUT = 20 * 1000; // 20 sec timeout
@ -258,8 +258,7 @@ describe("Abilities - Magic Guard", () => {
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -7,10 +7,10 @@ import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -129,10 +129,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to(BerryPhase, false);
@ -175,9 +172,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to(DamagePhase, false);
@ -373,9 +368,7 @@ describe("Abilities - Parental Bond", () => {
const enemyStartingHp = enemyPokemon.hp; const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -404,9 +397,7 @@ describe("Abilities - Parental Bond", () => {
const enemyStartingHp = enemyPokemon.hp; const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -432,9 +423,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -462,9 +451,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -494,9 +481,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);
@ -523,9 +508,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to(MoveEffectPhase);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
@ -549,9 +532,7 @@ describe("Abilities - Parental Bond", () => {
expect(enemyPokemon).not.toBe(undefined); expect(enemyPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP));
await mockHitCheck(game, true);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to(DamagePhase);

View File

@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js";
import { Type } from "#app/data/type.js"; import { Type } from "#app/data/type.js";
import { Weather, WeatherType } from "#app/data/weather.js"; import { Weather, WeatherType } from "#app/data/weather.js";
import { PlayerPokemon } from "#app/field/pokemon.js"; import { PlayerPokemon } from "#app/field/pokemon.js";
import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; import { TurnEndPhase } from "#app/phases.js";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
@ -12,7 +12,7 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -192,8 +192,7 @@ describe("Abilities - Protean", () => {
expect(leadPokemon).not.toBe(undefined); expect(leadPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;

View File

@ -11,7 +11,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { changeTurnOrder } from "../utils/testUtils"; import { mockTurnOrder } from "../utils/testUtils";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
@ -57,7 +57,7 @@ describe("Abilities - Serene Grace", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
// Check chance of Air Slash without Serene Grace // Check chance of Air Slash without Serene Grace
@ -90,7 +90,7 @@ describe("Abilities - Serene Grace", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
// Check chance of Air Slash with Serene Grace // Check chance of Air Slash with Serene Grace

View File

@ -11,7 +11,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { changeTurnOrder } from "../utils/testUtils"; import { mockTurnOrder } from "../utils/testUtils";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
@ -58,7 +58,7 @@ describe("Abilities - Sheer Force", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase; const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
@ -97,7 +97,7 @@ describe("Abilities - Sheer Force", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase; const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
@ -136,7 +136,7 @@ describe("Abilities - Sheer Force", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase; const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
@ -177,7 +177,7 @@ describe("Abilities - Sheer Force", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase; const phase = game.scene.getCurrentPhase() as MoveEffectPhase;

View File

@ -12,7 +12,7 @@ import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import { changeTurnOrder } from "../utils/testUtils"; import { mockTurnOrder } from "../utils/testUtils";
describe("Abilities - Shield Dust", () => { describe("Abilities - Shield Dust", () => {
@ -58,7 +58,7 @@ describe("Abilities - Shield Dust", () => {
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
// Shield Dust negates secondary effect // Shield Dust negates secondary effect

View File

@ -1,14 +1,14 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import Phaser from "phaser"; import Phaser from "phaser";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { CommandPhase, MoveEffectPhase, MovePhase, TurnEndPhase } from "#app/phases"; import { CommandPhase, MovePhase, TurnEndPhase } from "#app/phases";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { Abilities } from "#app/enums/abilities.js"; import { Abilities } from "#app/enums/abilities.js";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils";
describe("Abilities - Sweet Veil", () => { describe("Abilities - Sweet Veil", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -80,13 +80,11 @@ describe("Abilities - Sweet Veil", () => {
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
// First pokemon move // First pokemon move
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
// Second pokemon move // Second pokemon move
await game.phaseInterceptor.to(MovePhase, false); await game.phaseInterceptor.to(MovePhase, false);
await game.phaseInterceptor.to(MoveEffectPhase, false); await mockHitCheck(game, true);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true);

View File

@ -12,7 +12,7 @@ import { Species } from "#enums/species";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import { changeTurnOrder } from "../utils/testUtils"; import { mockTurnOrder } from "../utils/testUtils";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -58,7 +58,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse); const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to(DamagePhase, false);
// await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false); // await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false);
const damagePhase = game.scene.getCurrentPhase() as DamagePhase; const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
@ -86,7 +86,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse); const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to(QuietFormChangePhase); await game.phaseInterceptor.to(QuietFormChangePhase);
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to(TurnInitPhase, false);
expect(game.scene.getParty()[0].hp).not.toBe(100); expect(game.scene.getParty()[0].hp).not.toBe(100);
@ -111,7 +111,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse); const movePosition = getMovePosition(game.scene, 0, moveToUse);
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
}); });
await changeTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to(DamagePhase, false);
// await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false); // await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false);
const damagePhase = game.scene.getCurrentPhase() as DamagePhase; const damagePhase = game.scene.getCurrentPhase() as DamagePhase;

View File

@ -1,4 +1,4 @@
import { pokemonEvolutions } from "#app/data/pokemon-evolutions.js"; import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions.js";
import { Abilities } from "#app/enums/abilities.js"; import { Abilities } from "#app/enums/abilities.js";
import { Species } from "#app/enums/species.js"; import { Species } from "#app/enums/species.js";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
@ -83,4 +83,10 @@ describe("Evolution", () => {
expect(ninjask.abilityIndex).toBe(2); expect(ninjask.abilityIndex).toBe(2);
expect(shedinja.abilityIndex).toBe(1); expect(shedinja.abilityIndex).toBe(1);
}, TIMEOUT); }, TIMEOUT);
it("should set wild delay to NONE by default", () => {
const speciesFormEvo = new SpeciesFormEvolution(Species.ABRA, null, null, 1000, null, null);
expect(speciesFormEvo.wildDelay).toBe(SpeciesWildEvolutionDelay.NONE);
});
}); });

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phase from "phaser"; import Phase from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { changeTurnOrder } from "#test/utils/testUtils"; import { mockTurnOrder } from "#test/utils/testUtils";
describe("Items - Leek", () => { describe("Items - Leek", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -44,7 +44,7 @@ describe("Items - Leek", () => {
game.doAttack(0); game.doAttack(0);
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to(MoveEffectPhase);

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Phase from "phaser"; import Phase from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { changeTurnOrder } from "#test/utils/testUtils"; import { mockTurnOrder } from "#test/utils/testUtils";
describe("Items - Scope Lens", () => { describe("Items - Scope Lens", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -43,7 +43,7 @@ describe("Items - Scope Lens", () => {
]); ]);
game.doAttack(0); game.doAttack(0);
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to(MoveEffectPhase);

View File

@ -8,7 +8,7 @@ import { allMoves } from "#app/data/move";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { changeTurnOrder } from "#test/utils/testUtils"; import { mockTurnOrder } from "#test/utils/testUtils";
describe("Moves - Fusion Flare and Fusion Bolt", () => { describe("Moves - Fusion Flare and Fusion Bolt", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -56,7 +56,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.ENEMY); game.doSelectTarget(BattlerIndex.ENEMY);
// Force user party to act before enemy party // Force user party to act before enemy party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
@ -82,7 +82,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.ENEMY); game.doSelectTarget(BattlerIndex.ENEMY);
// Force user party to act before enemy party // Force user party to act before enemy party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
@ -108,7 +108,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(0); game.doSelectTarget(0);
// Force first enemy to act (and fail) in between party // Force first enemy to act (and fail) in between party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
@ -140,7 +140,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.ENEMY); game.doSelectTarget(BattlerIndex.ENEMY);
// Force first enemy to act in between party // Force first enemy to act in between party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
@ -170,7 +170,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.PLAYER); game.doSelectTarget(BattlerIndex.PLAYER);
// Force user party to act before enemy party // Force user party to act before enemy party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
@ -222,7 +222,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.ENEMY); game.doSelectTarget(BattlerIndex.ENEMY);
// Force first enemy to act in between party // Force first enemy to act in between party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
@ -284,7 +284,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.PLAYER); game.doSelectTarget(BattlerIndex.PLAYER);
// Force first enemy to act in between party // Force first enemy to act in between party
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to(MoveEffectPhase, false);
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);

View File

@ -6,7 +6,7 @@ import { MoveResult } from "#app/field/pokemon.js";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { changeTurnOrder, SPLASH_ONLY } from "#test/utils/testUtils"; import { mockTurnOrder, SPLASH_ONLY } from "#test/utils/testUtils";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -75,7 +75,7 @@ describe("Moves - Gastro Acid", () => {
game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER)); game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER));
// Force player to be slower to enable Core Enforcer to proc its suppression effect // Force player to be slower to enable Core Enforcer to proc its suppression effect
await changeTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnInitPhase"); await game.phaseInterceptor.to("TurnInitPhase");

View File

@ -3,7 +3,7 @@ import i18next, { type ParseKeys } from "i18next";
import { vi } from "vitest"; import { vi } from "vitest";
import GameManager from "./gameManager"; import GameManager from "./gameManager";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import { TurnStartPhase } from "#app/phases.js"; import { MoveEffectPhase, TurnStartPhase } from "#app/phases.js";
/** Ready to use array of Moves.SPLASH x4 */ /** Ready to use array of Moves.SPLASH x4 */
export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]; export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH];
@ -40,16 +40,33 @@ export function removeEnemyHeldItems(game: GameManager): void {
} }
/** /**
* Used to modify the turn order. Will advance the turn to {@link TurnStartPhase} * Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder}
* @param game The {@link GameManager} instance * Used to modify the turn order.
* @param order The turn order to set * @param {GameManager} game The GameManager instance
* @param {BattlerIndex[]} order The turn order to set
* @example * @example
* ```ts * ```ts
* await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2]); * await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2]);
* ``` * ```
*/ */
export async function changeTurnOrder(game: GameManager, order: BattlerIndex[]): Promise<void> { export async function mockTurnOrder(game: GameManager, order: BattlerIndex[]): Promise<void> {
await game.phaseInterceptor.to(TurnStartPhase, false); await game.phaseInterceptor.to(TurnStartPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order);
} }
/**
* Intercepts `MoveEffectPhase` and mocks the hitCheck's return value {@linkcode MoveEffectPhase.hitCheck}.
* Used to force a move to either hit or miss.
* Note that this uses `mockReturnValue()`, meaning it will also apply to a
* succeeding `MoveEffectPhase` immediately following the first one
* (in the case of a multi-target move)
*
* @param {GameManager} game The GameManager instance
* @param shouldHit Whether the move should hit
*/
export async function mockHitCheck(game: GameManager, shouldHit: boolean): Promise<void> {
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(shouldHit);
}

View File

@ -12,8 +12,6 @@ import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme"; import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next"; import i18next from "i18next";
const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ];
export default class BattleInfo extends Phaser.GameObjects.Container { export default class BattleInfo extends Phaser.GameObjects.Container {
private baseY: number; private baseY: number;
@ -70,6 +68,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
public flyoutMenu?: BattleFlyout; public flyoutMenu?: BattleFlyout;
private battleStatOrder: BattleStat[];
private battleStatOrderPlayer = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD];
private battleStatOrderEnemy = [BattleStat.HP, BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD];
constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) { constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) {
super(scene, x, y); super(scene, x, y);
this.baseY = y; this.baseY = y;
@ -222,20 +224,44 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.statValuesContainer = this.scene.add.container(0, 0); this.statValuesContainer = this.scene.add.container(0, 0);
this.statsContainer.add(this.statValuesContainer); this.statsContainer.add(this.statValuesContainer);
battleStatOrder.map((s, i) => { // this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
const statX = i > 1 ? this.statNumbers[i - 2].x + this.statNumbers[i - 2].width + 4 : -this.statsBox.width + 8; // since the player won't have HP to show, it doesn't need to change from the current version
const statY = -this.statsBox.height / 2 + 4 + (i < battleStatOrder.length - 1 ? (i % 2 ? 10 : 0) : 5); const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5;
const paddingX = this.player ? 4 : 2;
const statOverflow = this.player ? 1 : 0;
this.battleStatOrder = this.player ? this.battleStatOrderPlayer : this.battleStatOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
this.battleStatOrder.map((s, i) => {
// we do a check for i > statOverflow to see when the stat labels go onto the next column
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
const statX = i > statOverflow ? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX : startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
let statY: number; // this will be the y-axis placement for the labels
if (this.battleStatOrder[i] === BattleStat.SPD || this.battleStatOrder[i] === BattleStat.HP) {
statY = baseY + 5;
} else {
statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
}
const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", BattleStat[s]); const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", BattleStat[s]);
statLabel.setName("icon_stat_label_" + i.toString()); statLabel.setName("icon_stat_label_" + i.toString());
statLabel.setOrigin(0, 0); statLabel.setOrigin(0, 0);
statLabels.push(statLabel); statLabels.push(statLabel);
this.statValuesContainer.add(statLabel); this.statValuesContainer.add(statLabel);
const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", "3"); const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.battleStatOrder[i] !== BattleStat.HP ? "3" : "empty");
statNumber.setName("icon_stat_number_" + i.toString()); statNumber.setName("icon_stat_number_" + i.toString());
statNumber.setOrigin(0, 0); statNumber.setOrigin(0, 0);
this.statNumbers.push(statNumber); this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber); this.statValuesContainer.add(statNumber);
if (this.battleStatOrder[i] === BattleStat.HP) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
}); });
if (!this.player) { if (!this.player) {
@ -274,6 +300,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
} }
} }
getStatsValueContainer(): Phaser.GameObjects.Container {
return this.statValuesContainer;
}
initInfo(pokemon: Pokemon) { initInfo(pokemon: Pokemon) {
this.updateNameText(pokemon); this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth; const nameTextWidth = this.nameText.displayWidth;
@ -403,7 +433,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.statValuesContainer.setPosition(8, 7); this.statValuesContainer.setPosition(8, 7);
} }
const battleStats = battleStatOrder.map(() => 0); const battleStats = this.battleStatOrder.map(() => 0);
this.lastBattleStats = battleStats.join(""); this.lastBattleStats = battleStats.join("");
this.updateBattleStats(battleStats); this.updateBattleStats(battleStats);
@ -622,7 +652,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
const battleStats = pokemon.summonData const battleStats = pokemon.summonData
? pokemon.summonData.battleStats ? pokemon.summonData.battleStats
: battleStatOrder.map(() => 0); : this.battleStatOrder.map(() => 0);
const battleStatsStr = battleStats.join(""); const battleStatsStr = battleStats.join("");
if (this.lastBattleStats !== battleStatsStr) { if (this.lastBattleStats !== battleStatsStr) {
@ -740,8 +770,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
} }
updateBattleStats(battleStats: integer[]): void { updateBattleStats(battleStats: integer[]): void {
battleStatOrder.map((s, i) => { this.battleStatOrder.map((s, i) => {
this.statNumbers[i].setFrame(battleStats[s].toString()); if (s !== BattleStat.HP) {
this.statNumbers[i].setFrame(battleStats[s].toString());
}
}); });
} }

View File

@ -196,24 +196,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
this.scene.executeWithSeedOffset(() => { this.scene.executeWithSeedOffset(() => {
let levelUpStatsValuesText = ""; let levelUpStatsValuesText = "";
const stats = Utils.getEnumValues(Stat); const stats = Utils.getEnumValues(Stat);
let shownStats: Stat[] = []; const shownStats = this.getTopIvs(ivs, shownIvsCount);
if (shownIvsCount < 6) {
const statsPool = stats.slice(0);
for (let i = 0; i < shownIvsCount; i++) {
let shownStat: Stat;
let highestIv = -1;
statsPool.map(s => {
if (ivs[s] > highestIv) {
shownStat = s as Stat;
highestIv = ivs[s];
}
});
shownStats.push(shownStat!); // TODO: is the bang correct?
statsPool.splice(statsPool.indexOf(shownStat!), 1); // TODO: is the bang correct?
}
} else {
shownStats = stats;
}
for (const s of stats) { for (const s of stats) {
levelUpStatsValuesText += `${shownStats.indexOf(s) > -1 ? this.getIvDescriptor(ivs[s], s, pokemonId) : "???"}\n`; levelUpStatsValuesText += `${shownStats.indexOf(s) > -1 ? this.getIvDescriptor(ivs[s], s, pokemonId) : "???"}\n`;
} }
@ -229,35 +212,70 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
}); });
} }
getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] {
const stats = Utils.getEnumValues(Stat);
let shownStats: Stat[] = [];
if (shownIvsCount < 6) {
const statsPool = stats.slice(0);
for (let i = 0; i < shownIvsCount; i++) {
let shownStat: Stat | null = null;
let highestIv = -1;
statsPool.map(s => {
if (ivs[s] > highestIv) {
shownStat = s as Stat;
highestIv = ivs[s];
}
});
if (shownStat) {
shownStats.push(shownStat);
statsPool.splice(statsPool.indexOf(shownStat), 1);
}
}
} else {
shownStats = stats;
}
return shownStats;
}
getIvDescriptor(value: integer, typeIv: integer, pokemonId: integer): string { getIvDescriptor(value: integer, typeIv: integer, pokemonId: integer): string {
const starterSpecies = this.scene.getPokemonById(pokemonId)!.species.getRootSpeciesId(true); // TODO: is this bang correct? const starterSpecies = this.scene.getPokemonById(pokemonId)!.species.getRootSpeciesId(); // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists
const starterIvs: number[] = this.scene.gameData.dexData[starterSpecies].ivs; const starterIvs: number[] = this.scene.gameData.dexData[starterSpecies].ivs;
const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible
// Function to wrap text in color based on comparison // Function to wrap text in color based on comparison
const coloredText = (text: string, isBetter: boolean) => { const coloredText = (text: string, isBetter: boolean, ivValue) => {
const textStyle: TextStyle = isBetter ? TextStyle.SUMMARY_GREEN : TextStyle.SUMMARY; let textStyle: TextStyle;
if (isBetter) {
if (ivValue === 31) {
textStyle = TextStyle.PERFECT_IV;
} else {
textStyle = TextStyle.SUMMARY_GREEN;
}
} else {
textStyle = TextStyle.SUMMARY;
}
//const textStyle: TextStyle = isBetter ? TextStyle.SUMMARY_GREEN : TextStyle.SUMMARY;
const color = getTextColor(textStyle, false, uiTheme); const color = getTextColor(textStyle, false, uiTheme);
return `[color=${color}][shadow=${getTextColor(textStyle, true, uiTheme)}]${text}[/shadow][/color]`; return `[color=${color}][shadow=${getTextColor(textStyle, true, uiTheme)}]${text}[/shadow][/color]`;
}; };
if (value > 30) { if (value > 30) {
return coloredText(i18next.t("battleMessageUiHandler:ivBest"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivBest"), value > starterIvs[typeIv], value);
} }
if (value === 30) { if (value === 30) {
return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv], value);
} }
if (value > 20) { if (value > 20) {
return coloredText(i18next.t("battleMessageUiHandler:ivVeryGood"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivVeryGood"), value > starterIvs[typeIv], value);
} }
if (value > 10) { if (value > 10) {
return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv], value);
} }
if (value > 0) { if (value > 0) {
return coloredText(i18next.t("battleMessageUiHandler:ivDecent"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivDecent"), value > starterIvs[typeIv], value);
} }
return coloredText(i18next.t("battleMessageUiHandler:ivNoGood"), value > starterIvs[typeIv]); return coloredText(i18next.t("battleMessageUiHandler:ivNoGood"), value > starterIvs[typeIv], value);
} }
showNameText(name: string): void { showNameText(name: string): void {

View File

@ -37,7 +37,8 @@ export enum TextStyle {
MOVE_PP_NEAR_EMPTY, MOVE_PP_NEAR_EMPTY,
MOVE_PP_EMPTY, MOVE_PP_EMPTY,
SMALLER_WINDOW_ALT, SMALLER_WINDOW_ALT,
BGM_BAR BGM_BAR,
PERFECT_IV
} }
export interface TextStyleOptions { export interface TextStyleOptions {
@ -291,6 +292,7 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui
case TextStyle.SUMMARY_GREEN: case TextStyle.SUMMARY_GREEN:
return !shadow ? "#78c850" : "#306850"; return !shadow ? "#78c850" : "#306850";
case TextStyle.SETTINGS_LABEL: case TextStyle.SETTINGS_LABEL:
case TextStyle.PERFECT_IV:
return !shadow ? "#f8b050" : "#c07800"; return !shadow ? "#f8b050" : "#c07800";
case TextStyle.SETTINGS_SELECTED: case TextStyle.SETTINGS_SELECTED:
return !shadow ? "#f88880" : "#f83018"; return !shadow ? "#f88880" : "#f83018";