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,
"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,
"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,
"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,
"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 enableMoveInfo: boolean = true;
public enableRetries: boolean = false;
public hideIvs: boolean = false;
/**
* Determines the condition for a notification should be shown for Candy Upgrades
* - 0 = 'Off'

View File

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

View File

@ -64,21 +64,21 @@ export class SpeciesFormEvolution {
public level: integer;
public item: EvolutionItem | 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.preFormKey = preFormKey;
this.evoFormKey = evoFormKey;
this.level = level;
this.item = item || EvolutionItem.NONE;
this.condition = condition;
this.wildDelay = wildDelay!; // TODO: is this bang correct?
this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE;
}
}
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);
}
}
@ -400,8 +400,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LINOONE, 20, null, null)
],
[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.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), 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))
],
[Species.SILCOON]: [
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
@ -945,7 +945,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
],
[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]: [
new SpeciesEvolution(Species.BEWEAR, 27, null, null)
@ -969,8 +969,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.COSMOEM, 43, null, null)
],
[Species.COSMOEM]: [
new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null),
new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), 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))
],
[Species.MELTAN]: [
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)
],
[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, "", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1), 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)), 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)),
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]: [
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",
"battleStyle": "Kampfstil",
"enableRetries": "Erneut versuchen aktivieren",
"hideIvs": "IS-Scanner verstecken",
"tutorials": "Tutorials",
"touchControls": "Touch Steuerung",
"vibrations": "Vibration",

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
}
},
"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": {
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.",
},
"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": {
description: "Restaure tous les PV de toute l'équipe.",
description: "Restaure tous les PV de toute léquipe.",
},
"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": {
description: "Octroie une {{moneyMultiplier}} somme dargent.\n({{moneyAmount}} ₽)",
@ -151,9 +151,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"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" },
"MAX_ETHER": { name: "Huile Max" },
@ -184,12 +184,12 @@ export const modifierType: ModifierTypeTranslationEntries = {
"SOOTHE_BELL": { name: "Grelot Zen" },
"SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente 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." },
"SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente dun cran le taux de critiques du porteur." },
"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" },
"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." },
"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" },
"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." },
"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." },
"FLAME_ORB": { name: "Orbe Flamme", description: "Un orbe bizarre qui brule 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: "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." },
@ -242,21 +242,21 @@ export const modifierType: ModifierTypeTranslationEntries = {
"ENEMY_ATTACK_POISON_CHANCE": { name: "Jeton Poison" },
"ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Jeton Paralysie" },
"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_FUSED_CHANCE": { name: "Jeton Fusion", description: "Ajoute 1% de chances quun Pokémon sauvage soit une fusion." },
},
SpeciesBoosterItem: {
"LIGHT_BALL": { name: "Balle Lumière", description: "Objet à faire tenir à Pikachu. Un orbe énigmatique qui augmente son Attaque et son Attaque Spéciale." },
"THICK_CLUB": { name: "Masse Os", description: "Objet à faire tenir à Osselait ou Ossatueur. Un os dur qui augmente 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." },
"QUICK_POWDER": { name: "Poudre Vite", description: "Objet à faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, augmente sa Vitesse." }
"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: "À faire tenir à Osselait ou Ossatueur. Un os dur qui double leur Attaque." },
"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: "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Vitesse." }
},
TempBattleStatBoosterItem: {
"x_attack": "Attaque +",
"x_defense": "Défense +",
"x_sp_atk": "Atq. Spé. +",
"x_sp_def": "Déf. Spé. +",
"x_sp_atk": "Atq. Spé. +",
"x_sp_def": "Déf. Spé. +",
"x_speed": "Vitesse +",
"x_accuracy": "Précision +",
"dire_hit": "Muscle +",
@ -265,8 +265,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
TempBattleStatBoosterStatName: {
"ATK": "Attaque",
"DEF": "Défense",
"SPATK": "Atq. Spé.",
"SPDEF": "Déf. Spé.",
"SPATK": "Atq. Spé.",
"SPDEF": "Déf. Spé.",
"SPD": "Vitesse",
"ACC": "Précision",
"CRIT": "Taux de critique",

View File

@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = {
"skipSeenDialogues": "Passer dialogues connus",
"battleStyle": "Style de combat",
"enableRetries": "Activer les réessais",
"hideIvs": "Masquer Scanner dIV",
"tutorials": "Tutoriels",
"touchControls": "Contrôles tactiles",
"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.
$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.
$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.
$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é.
$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",
"battleStyle": "Battle Style",
"enableRetries": "Enable Retries",
"hideIvs": "Hide IV scanner",
"tutorials": "Tutorials",
"touchControls": "Touch Controls",
"vibrations": "Vibrations",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ import { GameMode, GameModes, getGameMode } from "./game-mode";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
import i18next from "./plugins/i18n";
import Overrides from "#app/overrides";
import { TextStyle, addTextObject } from "./ui/text";
import { TextStyle, addTextObject, getTextColor } from "./ui/text";
import { Type } from "./data/type";
import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene";
import { Abilities } from "#enums/abilities";
@ -5599,19 +5599,45 @@ export class ScanIvsPhase extends PokemonPhase {
const pokemon = this.getPokemon();
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());
let enemyIvs: number[] = [];
let statsContainer: Phaser.GameObjects.Sprite[] = [];
let statsContainerLabels: Phaser.GameObjects.Sprite[] = [];
const enemyField = this.scene.getEnemyField();
const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible
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",
Battle_Style: "BATTLE_STYLE",
Enable_Retries: "ENABLE_RETRIES",
Hide_IVs: "HIDE_IVS",
Tutorials: "TUTORIALS",
Touch_Controls: "TOUCH_CONTROLS",
Vibration: "VIBRATION",
@ -250,6 +251,13 @@ export const Setting: Array<Setting> = [
default: 0,
type: SettingType.GENERAL
},
{
key: SettingKeys.Hide_IVs,
label: i18next.t("settings:hideIvs"),
options: OFF_ON,
default: 0,
type: SettingType.GENERAL
},
{
key: SettingKeys.Tutorials,
label: i18next.t("settings:tutorials"),
@ -618,6 +626,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
case SettingKeys.Enable_Retries:
scene.enableRetries = Setting[index].options[value].value === "On";
break;
case SettingKeys.Hide_IVs:
scene.hideIvs = Setting[index].options[value].value === "On";
break;
case SettingKeys.Skip_Seen_Dialogues:
scene.skipSeenDialogues = Setting[index].options[value].value === "On";
break;

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
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", () => {
let phaserGame: Phaser.Game;
@ -44,8 +44,7 @@ describe("Abilities - Hustle", () => {
vi.spyOn(pikachu, "getBattleStat");
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
await mockHitCheck(game, true);
await game.phaseInterceptor.to(DamagePhase);
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 { Weather, WeatherType } from "#app/data/weather.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 { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome";
@ -12,7 +12,7 @@ import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "#test/utils/gameManager";
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;
@ -192,8 +192,7 @@ describe("Abilities - Protean", () => {
expect(leadPokemon).not.toBe(undefined);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await mockHitCheck(game, false);
await game.phaseInterceptor.to(TurnEndPhase);
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 GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species";
import { TurnEndPhase, MoveEffectPhase } from "#app/phases";
import { TurnEndPhase } from "#app/phases";
import { Moves } from "#enums/moves";
import { ArenaTagType } from "#enums/arena-tag-type";
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 { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
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
@ -258,8 +258,7 @@ describe("Abilities - Magic Guard", () => {
const leadPokemon = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK));
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false);
await mockHitCheck(game, false);
await game.phaseInterceptor.to(TurnEndPhase);

View File

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

View File

@ -11,7 +11,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
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";
@ -57,7 +57,7 @@ describe("Abilities - Serene Grace", () => {
(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);
// 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);
});
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false);
// 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 Phaser from "phaser";
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";
@ -58,7 +58,7 @@ describe("Abilities - Sheer Force", () => {
(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);
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);
});
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false);
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);
});
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false);
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);
});
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false);
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;

View File

@ -12,7 +12,7 @@ import { Species } from "#enums/species";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { BattlerIndex } from "#app/battle.js";
import { changeTurnOrder } from "../utils/testUtils";
import { mockTurnOrder } from "../utils/testUtils";
describe("Abilities - Shield Dust", () => {
@ -58,7 +58,7 @@ describe("Abilities - Shield Dust", () => {
(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);
// 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 GameManager from "#test/utils/gameManager";
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 { getMovePosition } from "#test/utils/gameManagerUtils";
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { Abilities } from "#app/enums/abilities.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", () => {
let phaserGame: Phaser.Game;
@ -80,13 +80,11 @@ describe("Abilities - Sweet Veil", () => {
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
// First pokemon move
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
await mockHitCheck(game, true);
// Second pokemon move
await game.phaseInterceptor.to(MovePhase, false);
await game.phaseInterceptor.to(MoveEffectPhase, false);
vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true);
await mockHitCheck(game, 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 { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
import { BattlerIndex } from "#app/battle.js";
import { changeTurnOrder } from "../utils/testUtils";
import { mockTurnOrder } from "../utils/testUtils";
const TIMEOUT = 20 * 1000;
@ -58,7 +58,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(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.runFrom(DamagePhase).to(DamagePhase, false);
const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
@ -86,7 +86,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(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(TurnInitPhase, false);
expect(game.scene.getParty()[0].hp).not.toBe(100);
@ -111,7 +111,7 @@ describe("Abilities - ZEN MODE", () => {
const movePosition = getMovePosition(game.scene, 0, moveToUse);
(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.runFrom(DamagePhase).to(DamagePhase, false);
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 { Species } from "#app/enums/species.js";
import GameManager from "#test/utils/gameManager";
@ -83,4 +83,10 @@ describe("Evolution", () => {
expect(ninjask.abilityIndex).toBe(2);
expect(shedinja.abilityIndex).toBe(1);
}, 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 Phase from "phaser";
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", () => {
let phaserGame: Phaser.Game;
@ -44,7 +44,7 @@ describe("Items - Leek", () => {
game.doAttack(0);
await changeTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase);

View File

@ -8,7 +8,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phase from "phaser";
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", () => {
let phaserGame: Phaser.Game;
@ -43,7 +43,7 @@ describe("Items - Scope Lens", () => {
]);
game.doAttack(0);
await changeTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(MoveEffectPhase);

View File

@ -8,7 +8,7 @@ import { allMoves } from "#app/data/move";
import { BattlerIndex } from "#app/battle";
import { Species } from "#enums/species";
import { Moves } from "#enums/moves";
import { changeTurnOrder } from "#test/utils/testUtils";
import { mockTurnOrder } from "#test/utils/testUtils";
describe("Moves - Fusion Flare and Fusion Bolt", () => {
let phaserGame: Phaser.Game;
@ -56,7 +56,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
game.doSelectTarget(BattlerIndex.ENEMY);
// 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);
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);
// 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);
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);
// 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);
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);
// 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);
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);
// 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);
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);
// 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);
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);
// 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);
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 GameManager from "#test/utils/gameManager";
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;
@ -75,7 +75,7 @@ describe("Moves - Gastro Acid", () => {
game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER));
// 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");

View File

@ -3,7 +3,7 @@ import i18next, { type ParseKeys } from "i18next";
import { vi } from "vitest";
import GameManager from "./gameManager";
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 */
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}
* @param game The {@link GameManager} instance
* @param order The turn order to set
* Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder}
* Used to modify the turn order.
* @param {GameManager} game The GameManager instance
* @param {BattlerIndex[]} order The turn order to set
* @example
* ```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);
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 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 {
private baseY: number;
@ -70,6 +68,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
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) {
super(scene, x, 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.statsContainer.add(this.statValuesContainer);
battleStatOrder.map((s, i) => {
const statX = i > 1 ? this.statNumbers[i - 2].x + this.statNumbers[i - 2].width + 4 : -this.statsBox.width + 8;
const statY = -this.statsBox.height / 2 + 4 + (i < battleStatOrder.length - 1 ? (i % 2 ? 10 : 0) : 5);
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
// since the player won't have HP to show, it doesn't need to change from the current version
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]);
statLabel.setName("icon_stat_label_" + i.toString());
statLabel.setOrigin(0, 0);
statLabels.push(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.setOrigin(0, 0);
this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber);
if (this.battleStatOrder[i] === BattleStat.HP) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
});
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) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
@ -403,7 +433,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.statValuesContainer.setPosition(8, 7);
}
const battleStats = battleStatOrder.map(() => 0);
const battleStats = this.battleStatOrder.map(() => 0);
this.lastBattleStats = battleStats.join("");
this.updateBattleStats(battleStats);
@ -622,7 +652,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
const battleStats = pokemon.summonData
? pokemon.summonData.battleStats
: battleStatOrder.map(() => 0);
: this.battleStatOrder.map(() => 0);
const battleStatsStr = battleStats.join("");
if (this.lastBattleStats !== battleStatsStr) {
@ -740,8 +770,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
}
updateBattleStats(battleStats: integer[]): void {
battleStatOrder.map((s, i) => {
this.statNumbers[i].setFrame(battleStats[s].toString());
this.battleStatOrder.map((s, i) => {
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(() => {
let levelUpStatsValuesText = "";
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;
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;
}
const shownStats = this.getTopIvs(ivs, shownIvsCount);
for (const s of stats) {
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 {
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 uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible
// Function to wrap text in color based on comparison
const coloredText = (text: string, isBetter: boolean) => {
const textStyle: TextStyle = isBetter ? TextStyle.SUMMARY_GREEN : TextStyle.SUMMARY;
const coloredText = (text: string, isBetter: boolean, ivValue) => {
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);
return `[color=${color}][shadow=${getTextColor(textStyle, true, uiTheme)}]${text}[/shadow][/color]`;
};
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) {
return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv]);
return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv], value);
}
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) {
return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv]);
return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv], value);
}
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 {

View File

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