diff --git a/public/images/pokemon/back/shiny/774.json b/public/images/pokemon/back/shiny/774.json index cabd0eb027b..7643e0ccc87 100644 --- a/public/images/pokemon/back/shiny/774.json +++ b/public/images/pokemon/back/shiny/774.json @@ -1,41 +1,41 @@ -{ - "textures": [ - { - "image": "774.png", - "format": "RGBA8888", - "size": { - "w": 37, - "h": 37 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 37, - "h": 35 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 37, - "h": 35 - }, - "frame": { - "x": 0, - "y": 0, - "w": 37, - "h": 35 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:fa61a53a0684a1d00e0ed5d6737743af:3e6463caf33bbb58cfba4bd9c20890aa:37281ac0aa1e619ef385b889b64064b7$" - } -} +{ + "textures": [ + { + "image": "774.png", + "format": "RGBA8888", + "size": { + "w": 37, + "h": 37 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 37, + "h": 37 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 37, + "h": 37 + }, + "frame": { + "x": 0, + "y": 0, + "w": 37, + "h": 37 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:241dff4083e172e8503b54a7f0210f8d:982b194223ffeef2ba672b3c5979a426:37281ac0aa1e619ef385b889b64064b7$" + } +} diff --git a/public/images/pokemon/back/shiny/774.png b/public/images/pokemon/back/shiny/774.png index 3f91ba876a4..6f93da7a00d 100644 Binary files a/public/images/pokemon/back/shiny/774.png and b/public/images/pokemon/back/shiny/774.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-blue.png b/public/images/pokemon/exp/back/shiny/774-blue.png index dba2d268b0d..bffbc41e81c 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-blue.png and b/public/images/pokemon/exp/back/shiny/774-blue.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-green.png b/public/images/pokemon/exp/back/shiny/774-green.png index 59ae18f321c..f5a85c1ba0f 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-green.png and b/public/images/pokemon/exp/back/shiny/774-green.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-indigo.png b/public/images/pokemon/exp/back/shiny/774-indigo.png index 25984abc9c2..115365f11b8 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-indigo.png and b/public/images/pokemon/exp/back/shiny/774-indigo.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-orange.png b/public/images/pokemon/exp/back/shiny/774-orange.png index a6a13f0ab7d..bf3db830ef8 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-orange.png and b/public/images/pokemon/exp/back/shiny/774-orange.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-red.png b/public/images/pokemon/exp/back/shiny/774-red.png index e3db7f30002..bc3407019f8 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-red.png and b/public/images/pokemon/exp/back/shiny/774-red.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-violet.png b/public/images/pokemon/exp/back/shiny/774-violet.png index 25984abc9c2..68da27cc960 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-violet.png and b/public/images/pokemon/exp/back/shiny/774-violet.png differ diff --git a/public/images/pokemon/exp/back/shiny/774-yellow.png b/public/images/pokemon/exp/back/shiny/774-yellow.png index bdeaccaf3d8..569b836a207 100644 Binary files a/public/images/pokemon/exp/back/shiny/774-yellow.png and b/public/images/pokemon/exp/back/shiny/774-yellow.png differ diff --git a/public/images/pokemon/exp/shiny/774-blue.png b/public/images/pokemon/exp/shiny/774-blue.png index 82b53ea3d81..a6d5fd10f18 100644 Binary files a/public/images/pokemon/exp/shiny/774-blue.png and b/public/images/pokemon/exp/shiny/774-blue.png differ diff --git a/public/images/pokemon/exp/shiny/774-green.png b/public/images/pokemon/exp/shiny/774-green.png index 9ee225dc61c..ed8deff92bd 100644 Binary files a/public/images/pokemon/exp/shiny/774-green.png and b/public/images/pokemon/exp/shiny/774-green.png differ diff --git a/public/images/pokemon/exp/shiny/774-indigo.png b/public/images/pokemon/exp/shiny/774-indigo.png index f378b828450..4c2c954f2e7 100644 Binary files a/public/images/pokemon/exp/shiny/774-indigo.png and b/public/images/pokemon/exp/shiny/774-indigo.png differ diff --git a/public/images/pokemon/exp/shiny/774-orange.png b/public/images/pokemon/exp/shiny/774-orange.png index 54b6eb8eaed..4edcc654418 100644 Binary files a/public/images/pokemon/exp/shiny/774-orange.png and b/public/images/pokemon/exp/shiny/774-orange.png differ diff --git a/public/images/pokemon/exp/shiny/774-red.png b/public/images/pokemon/exp/shiny/774-red.png index 539e8d17913..d04e4468df9 100644 Binary files a/public/images/pokemon/exp/shiny/774-red.png and b/public/images/pokemon/exp/shiny/774-red.png differ diff --git a/public/images/pokemon/exp/shiny/774-violet.png b/public/images/pokemon/exp/shiny/774-violet.png index 840e8dbaeb8..d0721c118a0 100644 Binary files a/public/images/pokemon/exp/shiny/774-violet.png and b/public/images/pokemon/exp/shiny/774-violet.png differ diff --git a/public/images/pokemon/exp/shiny/774-yellow.png b/public/images/pokemon/exp/shiny/774-yellow.png index 64b10fdae1e..ab2f44c185a 100644 Binary files a/public/images/pokemon/exp/shiny/774-yellow.png and b/public/images/pokemon/exp/shiny/774-yellow.png differ diff --git a/public/images/pokemon/shiny/774.json b/public/images/pokemon/shiny/774.json index 11d07b823e0..c61c1791862 100644 --- a/public/images/pokemon/shiny/774.json +++ b/public/images/pokemon/shiny/774.json @@ -1,41 +1,41 @@ -{ - "textures": [ - { - "image": "774.png", - "format": "RGBA8888", - "size": { - "w": 37, - "h": 37 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 37, - "h": 35 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 37, - "h": 35 - }, - "frame": { - "x": 0, - "y": 0, - "w": 37, - "h": 35 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:29265d2eed2689ecb95874383b7e7da7:1339971def468ab6d7c93a72472c7b3d:37281ac0aa1e619ef385b889b64064b7$" - } -} +{ + "textures": [ + { + "image": "774.png", + "format": "RGBA8888", + "size": { + "w": 37, + "h": 37 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 37, + "h": 37 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 37, + "h": 37 + }, + "frame": { + "x": 0, + "y": 0, + "w": 37, + "h": 37 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:566b51540ed595250ead15a4733d98d6:172aa05dcc207383119cd2f2f7977e0e:37281ac0aa1e619ef385b889b64064b7$" + } +} diff --git a/public/images/pokemon/shiny/774.png b/public/images/pokemon/shiny/774.png index 5efd0bc8da1..7d9f5a9cb00 100644 Binary files a/public/images/pokemon/shiny/774.png and b/public/images/pokemon/shiny/774.png differ diff --git a/public/images/ui/bgm_bar.png b/public/images/ui/bgm_bar.png new file mode 100644 index 00000000000..54abe4c5a94 Binary files /dev/null and b/public/images/ui/bgm_bar.png differ diff --git a/public/images/ui/legacy/bgm_bar.png b/public/images/ui/legacy/bgm_bar.png new file mode 100644 index 00000000000..fedeb323fc2 Binary files /dev/null and b/public/images/ui/legacy/bgm_bar.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index ba0720baab2..f20c6503229 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1227,6 +1227,7 @@ export default class BattleScene extends SceneBase { case Species.ZARUDE: case Species.SQUAWKABILLY: case Species.TATSUGIRI: + case Species.GIMMIGHOUL: case Species.PALDEA_TAUROS: return Utils.randSeedInt(species.forms.length); case Species.PIKACHU: diff --git a/src/data/ability.ts b/src/data/ability.ts index d3f871c9c19..f449b33992b 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1296,6 +1296,7 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { const multiplier = args[2] as Utils.NumberHolder; if (this.canApplyPreAttack(move, numTargets)) { + this.showAbility = !!hitCount?.value; if (!!hitCount?.value) { hitCount.value *= 2; } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 96a354bcfef..ec4e00f44d0 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -935,7 +935,9 @@ export class ContactDamageProtectedTag extends ProtectedTag { const effectPhase = pokemon.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { + attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + } } } diff --git a/src/data/move.ts b/src/data/move.ts index ad99e5682f5..677e10e24f3 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2961,9 +2961,29 @@ export class HpPowerAttr extends VariablePowerAttr { } } +/** + * Attribute used for moves whose base power scales with the opponent's HP + * Used for Crush Grip, Wring Out, and Hard Press + * maxBasePower 100 for Hard Press, 120 for others + */ export class OpponentHighHpPowerAttr extends VariablePowerAttr { + maxBasePower: number; + + constructor(maxBasePower: number) { + super(); + this.maxBasePower = maxBasePower; + } + + /** + * Changes the base power of the move to be the target's HP ratio times the maxBasePower with a min value of 1 + * @param user n/a + * @param target the Pokemon being attacked + * @param move n/a + * @param args holds the base power of the move at args[0] + * @returns true + */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(120 * target.getHpRatio()), 1); + (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1); return true; } @@ -6705,7 +6725,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES) .unimplemented(), new AttackMove(Moves.WRING_OUT, Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 4) - .attr(OpponentHighHpPowerAttr) + .attr(OpponentHighHpPowerAttr, 120) .makesContact(), new SelfStatusMove(Moves.POWER_TRICK, Type.PSYCHIC, -1, 10, -1, 0, 4) .unimplemented(), @@ -6929,7 +6949,7 @@ export function initMoves() { .triageMove() .unimplemented(), new AttackMove(Moves.CRUSH_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) - .attr(OpponentHighHpPowerAttr), + .attr(OpponentHighHpPowerAttr, 120), new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4) .attr(TrapAttr, BattlerTagType.MAGMA_STORM), new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4) @@ -8399,8 +8419,8 @@ export function initMoves() { new AttackMove(Moves.TACHYON_CUTTER, Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, -1, 0, 9) .attr(MultiHitAttr, MultiHitType._2) .slicingMove(), - new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) - .attr(OpponentHighHpPowerAttr), + new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) + .attr(OpponentHighHpPowerAttr, 100), new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true) .target(MoveTarget.NEAR_ALLY) diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 1fb1f7db5fd..696cf006ad0 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -1618,7 +1618,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.GIMMIGHOUL]: [ - new SpeciesEvolution(Species.GHOLDENGO, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG), + new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG) ] }; diff --git a/src/data/tms.ts b/src/data/tms.ts index 4616c177eb6..c51a4ede8b5 100644 --- a/src/data/tms.ts +++ b/src/data/tms.ts @@ -38685,6 +38685,8 @@ export const tmSpecies: TmSpecies = { Species.GOLDEEN, Species.SEAKING, Species.SCYTHER, + Species.ELECTABUZZ, + Species.MAGMAR, Species.PINSIR, Species.OMANYTE, Species.OMASTAR, @@ -38692,6 +38694,8 @@ export const tmSpecies: TmSpecies = { Species.KABUTOPS, Species.MEWTWO, Species.MEW, + Species.BAYLEEF, + Species.MEGANIUM, Species.SENTRET, Species.FURRET, Species.LEDYBA, @@ -38708,8 +38712,10 @@ export const tmSpecies: TmSpecies = { Species.SNEASEL, Species.PHANPY, Species.DONPHAN, + Species.ELEKID, Species.TYRANITAR, Species.BLAZIKEN, + Species.SWAMPERT, Species.LOMBRE, Species.LUDICOLO, Species.NUZLEAF, @@ -38740,12 +38746,7 @@ export const tmSpecies: TmSpecies = { Species.CHIMECHO, Species.ABSOL, Species.METAGROSS, - [ - Species.DEOXYS, - "", - "defense", - "speed", - ], + Species.DEOXYS, Species.CHIMCHAR, Species.MONFERNO, Species.INFERNAPE, @@ -38767,6 +38768,8 @@ export const tmSpecies: TmSpecies = { Species.WEAVILE, Species.LICKILICKY, Species.TANGROWTH, + Species.ELECTIVIRE, + Species.MAGMORTAR, Species.LEAFEON, Species.GLISCOR, Species.MAMOSWINE, @@ -38781,6 +38784,8 @@ export const tmSpecies: TmSpecies = { Species.SNIVY, Species.SERVINE, Species.SERPERIOR, + Species.PIGNITE, + Species.EMBOAR, Species.OSHAWOTT, Species.DEWOTT, Species.SAMUROTT, @@ -38837,6 +38842,8 @@ export const tmSpecies: TmSpecies = { Species.ACCELGOR, Species.MIENFOO, Species.MIENSHAO, + Species.GOLETT, + Species.GOLURK, Species.PAWNIARD, Species.BISHARP, Species.VULLABY, @@ -38873,6 +38880,7 @@ export const tmSpecies: TmSpecies = { Species.SALAZZLE, Species.STEENEE, Species.TSAREENA, + Species.COMFEY, Species.ORANGURU, Species.PASSIMIAN, Species.GOLISOPOD, @@ -38945,6 +38953,7 @@ export const tmSpecies: TmSpecies = { Species.GALAR_FARFETCHD, Species.GALAR_ZAPDOS, Species.GALAR_ZIGZAGOON, + Species.GALAR_LINOONE, Species.HISUI_SAMUROTT, Species.HISUI_ZORUA, Species.HISUI_ZOROARK, @@ -61853,6 +61862,7 @@ export const tmSpecies: TmSpecies = { Species.METANG, Species.METAGROSS, Species.JIRACHI, + Species.DEOXYS, Species.BRONZOR, Species.BRONZONG, Species.GALLADE, diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b76717d7d44..0fb74d51304 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1162,7 +1162,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier; } - getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false): TypeDamageMultiplier { + /** + * Calculates the type effectiveness multiplier for an attack type + * @param moveType Type of the move + * @param source the Pokemon using the move + * @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks) + * @param simulated tag to only apply the strong winds effect message when the move is used + * @returns a multiplier for the type effectiveness + */ + getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true): TypeDamageMultiplier { if (moveType === Type.STELLAR) { return this.isTerastallized() ? 2 : 1; } @@ -1183,8 +1191,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; // Handle strong winds lowering effectiveness of types super effective against pure flying - if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && multiplier >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { + if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { multiplier /= 2; + if (!simulated) { + this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage")); + } } if (!!this.summonData?.tags.find((tag) => tag instanceof TypeImmuneTag && tag.immuneType === moveType)) { @@ -1739,7 +1750,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelled = new Utils.BooleanHolder(false); const typeless = move.hasAttr(TypelessAttr); const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType))) - ? this.getAttackTypeEffectiveness(move.type, source) + ? this.getAttackTypeEffectiveness(move.type, source, false, false) : 1); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (typeless) { diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 0a6648a9c57..15cd295d23c 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -78,6 +78,7 @@ export class LoadingScene extends SceneBase { this.loadImage("overlay_exp", "ui"); this.loadImage("icon_owned", "ui"); this.loadImage("ability_bar_left", "ui"); + this.loadImage("bgm_bar", "ui"); this.loadImage("party_exp_bar", "ui"); this.loadImage("achv_bar", "ui"); this.loadImage("achv_bar_2", "ui"); diff --git a/src/locales/de/battle.ts b/src/locales/de/battle.ts index 311e4a1dca2..f6c56f8071a 100644 --- a/src/locales/de/battle.ts +++ b/src/locales/de/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "Es hat keine Wirkung auf {{pokemonName}}…", "hitResultOneHitKO": "Ein K.O.-Treffer!", "attackFailed": "Es ist fehlgeschlagen!", + "attackMissed": "Die Attacke hat {{pokemonNameWithAffix}} verfehlt!", "attackHitsCount": "{{count}}-mal getroffen!", "rewardGain": "Du erhältst\n{{modifierName}}!", "expGain": "{{pokemonName}} erhält\n{{exp}} Erfahrungspunkte!", diff --git a/src/locales/de/bgm-name.ts b/src/locales/de/bgm-name.ts index 71e775c8846..1a6712b8608 100644 --- a/src/locales/de/bgm-name.ts +++ b/src/locales/de/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Musik", + "music": "Musik: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "S2W2 Vs. Kanto Champion", "battle_johto_champion": "S2W2 Vs. Johto Champion", diff --git a/src/locales/de/weather.ts b/src/locales/de/weather.ts index 1132c35c430..305fd7e7827 100644 --- a/src/locales/de/weather.ts +++ b/src/locales/de/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "Alle Flug-Pokémon werden von rätselhaften Luftströmungen geschützt!", "strongWindsLapseMessage": "Die rätselhafte Luftströmung hält an.", + "strongWindsEffectMessage": "Rätselhafte Luftströmungen haben den Angriff abgeschwächt!", "strongWindsClearMessage": "Die rätselhafte Luftströmung hat sich wieder geleget.", }; diff --git a/src/locales/en/battle.ts b/src/locales/en/battle.ts index 23174790b61..2f440e4bfe9 100644 --- a/src/locales/en/battle.ts +++ b/src/locales/en/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "It doesn't affect {{pokemonName}}!", "hitResultOneHitKO": "It's a one-hit KO!", "attackFailed": "But it failed!", + "attackMissed": "{{pokemonNameWithAffix}} avoided the attack!", "attackHitsCount": "Hit {{count}} time(s)!", "rewardGain": "You received\n{{modifierName}}!", "expGain": "{{pokemonName}} gained\n{{exp}} EXP. Points!", diff --git a/src/locales/en/bgm-name.ts b/src/locales/en/bgm-name.ts index ef15c6c6dcb..87d90dabc9d 100644 --- a/src/locales/en/bgm-name.ts +++ b/src/locales/en/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Music", + "music": "Music: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", diff --git a/src/locales/en/weather.ts b/src/locales/en/weather.ts index f50c1ee907e..c7b2963ccd8 100644 --- a/src/locales/en/weather.ts +++ b/src/locales/en/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "A heavy wind began!", "strongWindsLapseMessage": "The wind blows intensely.", + "strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsClearMessage": "The heavy wind stopped." }; diff --git a/src/locales/es/battle.ts b/src/locales/es/battle.ts index f1b7e23e056..d7d3043ac0b 100644 --- a/src/locales/es/battle.ts +++ b/src/locales/es/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "No afecta a {{pokemonName}}!", "hitResultOneHitKO": "¡KO en 1 golpe!", "attackFailed": "¡Pero ha fallado!", + "attackMissed": "¡{{pokemonNameWithAffix}}\nha evitado el ataque!", "attackHitsCount": "N.º de golpes: {{count}}.", "rewardGain": "¡Has obtenido\n{{modifierName}}!", "expGain": "{{pokemonName}} ha ganado\n{{exp}} puntos de experiencia.", diff --git a/src/locales/es/bgm-name.ts b/src/locales/es/bgm-name.ts index cda6fba84f4..a0f7544d608 100644 --- a/src/locales/es/bgm-name.ts +++ b/src/locales/es/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Música", + "music": "Música: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 - ¡Vs Campeón de Kanto!", "battle_johto_champion": "B2W2 - ¡Vs Campeón de Johto!", diff --git a/src/locales/es/weather.ts b/src/locales/es/weather.ts index 5565779a7bd..37f574878dc 100644 --- a/src/locales/es/weather.ts +++ b/src/locales/es/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "¡Comenzó un fuerte viento!", "strongWindsLapseMessage": "El viento sopla intensamente.", + "strongWindsEffectMessage": "¡Las misteriosas turbulencias atenúan el ataque!", "strongWindsClearMessage": "El fuerte viento cesó." }; diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index b17e090c5d3..d5b42b8b2ef 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "Ça n’affecte pas {{pokemonName}}…", "hitResultOneHitKO": "K.O. en un coup !", "attackFailed": "Mais cela échoue !", + "attackMissed": "{{pokemonNameWithAffix}}\névite l’attaque !", "attackHitsCount": "Touché {{count}} fois !", "rewardGain": "Vous recevez\n{{modifierName}} !", "expGain": "{{pokemonName}} gagne\n{{exp}} Points d’Exp !", diff --git a/src/locales/fr/bgm-name.ts b/src/locales/fr/bgm-name.ts index 025f339ee86..15ec145a6ff 100644 --- a/src/locales/fr/bgm-name.ts +++ b/src/locales/fr/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Musique ", + "music": "Musique : ", "missing_entries" : "{{name}}", "battle_kanto_champion": "N2B2 - Vs. Maitre de Kanto", "battle_johto_champion": "N2B2 - Vs. Maitre de Johto", diff --git a/src/locales/fr/party-ui-handler.ts b/src/locales/fr/party-ui-handler.ts index f64859a3a14..b1324f52398 100644 --- a/src/locales/fr/party-ui-handler.ts +++ b/src/locales/fr/party-ui-handler.ts @@ -28,7 +28,7 @@ export const partyUiHandler: SimpleTranslationEntries = { "unspliceConfirmation": "Voulez-vous vraiment séparer {{fusionName}}\nde {{pokemonName}} ? {{fusionName}} sera perdu.", "wasReverted": "{{fusionName}} est redevenu {{pokemonName}}.", "releaseConfirmation": "Voulez-vous relâcher {{pokemonName}} ?", - "releaseInBattle": "Vous ne pouvez pas relâcher Pokémon en combat !", + "releaseInBattle": "Vous ne pouvez pas relâcher un Pokémon en combat !", "selectAMove": "Sélectionnez une capacité.", "changeQuantity": "Sélect. un objet à transférer.\nChangez la quantité avec < et >.", "selectAnotherPokemonToSplice": "Sélectionnez un autre Pokémon à séparer.", @@ -44,7 +44,7 @@ export const partyUiHandler: SimpleTranslationEntries = { "byebye": "Bye-bye, {{pokemonName}} !", "farewell": "Adieu, {{pokemonName}} !", "soLong": "Salut, {{pokemonName}} !", - "thisIsWhereWePart": "C’est là qu’on se sépare, {{pokemonName}}!", + "thisIsWhereWePart": "C’est là qu’on se sépare, {{pokemonName}} !", "illMissYou": "Tu vas me manquer, {{pokemonName}} !", "illNeverForgetYou": "Je ne t’oublierai pas, {{pokemonName}} !", "untilWeMeetAgain": "À la prochaine, {{pokemonName}} !", diff --git a/src/locales/fr/weather.ts b/src/locales/fr/weather.ts index fa6090a3ad5..3df8d0e20c9 100644 --- a/src/locales/fr/weather.ts +++ b/src/locales/fr/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "Un vent mystérieux se lève !", "strongWindsLapseMessage": "Le vent mystérieux souffle violemment !", + "strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit l’attaque !", "strongWindsClearMessage": "Le vent mystérieux s’est dissipé…" }; diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index b32f0a7d7e5..47943c2c0c3 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -5,8 +5,8 @@ export const battle: SimpleTranslationEntries = { "trainerAppeared": "{{trainerName}}\nvuole combattere!", "trainerAppearedDouble": "{{trainerName}}\nvogliono combattere!", "trainerSendOut": "{{trainerName}} manda in campo\n{{pokemonName}}!", - "singleWildAppeared": "Appare {{pokemonName}} selvatico!", - "multiWildAppeared": "Appaiono {{pokemonName1}}\ne {{pokemonName2}} salvatici!", + "singleWildAppeared": "È apparso {{pokemonName}} selvatico!", + "multiWildAppeared": "Sono apparsi {{pokemonName1}}\ne {{pokemonName2}} salvatici!", "playerComeBack": "Rientra, {{pokemonName}}!", "trainerComeBack": "{{trainerName}} ha ritirato {{pokemonName}}!", "playerGo": "Vai! {{pokemonName}}!", @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "Non ha effetto su {{pokemonName}}!", "hitResultOneHitKO": "KO con un colpo!", "attackFailed": "Ma ha fallito!", + "attackMissed": "{{pokemonNameWithAffix}}\nevita l’attacco!", "attackHitsCount": "Colpito {{count}} volta/e!", "rewardGain": "You received\n{{modifierName}}!", "expGain": "{{pokemonName}} ha guadagnato\n{{exp}} Punti Esperienza!", @@ -43,10 +44,10 @@ export const battle: SimpleTranslationEntries = { "moveNotImplemented": "{{moveName}} non è ancora implementata e non può essere selezionata.", "moveNoPP": "Non ci sono PP rimanenti\nper questa mossa!", "moveDisabled": "{{moveName}} è disabilitata!", - "noPokeballForce": "Una forza misteriosa\nimpedisce l'uso dell Poké Ball.", + "noPokeballForce": "Una forza misteriosa\nimpedisce l'uso delle Poké Ball.", "noPokeballTrainer": "Non puoi catturare\nPokémon di altri allenatori!", - "noPokeballMulti": "Puoi lanciare una Poké Ball\nquando rimane un solo Pokémon!", - "noPokeballStrong": "Il Pokémon avversario è troppo forte per essere catturato!\nDevi prima indebolirlo!", + "noPokeballMulti": "Puoi lanciare una Poké Ball\nsolo quando rimane un singolo Pokémon!", + "noPokeballStrong": "Il Pokémon avversario è troppo forte per essere catturato!\nDevi prima indebolirlo.", "noEscapeForce": "Una forza misteriosa\nimpedisce la fuga.", "noEscapeTrainer": "Non puoi sottrarti\nalla lotta con un'allenatore!", "noEscapePokemon": "{{moveName}} di {{pokemonName}}\npreviene la {{escapeVerb}}!", @@ -54,11 +55,11 @@ export const battle: SimpleTranslationEntries = { "runAwayCannotEscape": "Non puoi fuggire!", "escapeVerbSwitch": "cambiando", "escapeVerbFlee": "fuggendo", - "notDisabled": "{{pokemonName}}'s {{moveName}} non è più\ndisabilitata!", + "notDisabled": "{{moveName}} di {{pokemonName}} non è più\ndisabilitata!", "turnEndHpRestore": "{{pokemonName}}'s HP was restored.", "hpIsFull": "{{pokemonName}}'s\nHP is full!", "skipItemQuestion": "Sei sicuro di non voler prendere nessun oggetto?", - "eggHatching": "Oh!", + "eggHatching": "Oh?", "ivScannerUseQuestion": "Vuoi usare lo scanner di IV su {{pokemonName}}?", "stealEatBerry": "{{pokemonName}} stole and ate\n{{targetName}}'s {{berryName}}!", "wildPokemonWithAffix": "{{pokemonName}} selvatico", diff --git a/src/locales/it/bgm-name.ts b/src/locales/it/bgm-name.ts index ef15c6c6dcb..87d90dabc9d 100644 --- a/src/locales/it/bgm-name.ts +++ b/src/locales/it/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Music", + "music": "Music: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", diff --git a/src/locales/it/egg.ts b/src/locales/it/egg.ts index feb148ab990..c51f12065a1 100644 --- a/src/locales/it/egg.ts +++ b/src/locales/it/egg.ts @@ -6,18 +6,18 @@ export const egg: SimpleTranslationEntries = { "greatTier": "Raro", "ultraTier": "Epico", "masterTier": "Leggendario", - "hatchWavesMessageSoon": "Si sentono dei suoni provenienti dall'interno! Si schiuderà presto!", + "hatchWavesMessageSoon": "Si sentono dei rumori provenienti dall'interno. Si schiuderà presto!", "hatchWavesMessageClose": "Sembra muoversi di tanto in tanto. Potrebbe essere prossimo alla schiusa.", - "hatchWavesMessageNotClose": "Cosa uscirà da qui? Non sembra si schiuderà presto.", - "hatchWavesMessageLongTime": "Sembra che questo uovo impiegherà molto tempo per schiudersi.", - "gachaTypeLegendary": "Tasso dei Leggendari Aumentato", - "gachaTypeMove": "Tasso delle Mosse Rare delle Uova Aumentato", - "gachaTypeShiny": "Tasso degli Shiny Aumentato", - "selectMachine": "Seleziona un distributore.", - "notEnoughVouchers": "Non hai abbastanza Biglietti!", - "tooManyEggs": "Hai troppe Uova!", - "pull": "Tiro", - "pulls": "Tiri", + "hatchWavesMessageNotClose": "Cosa uscirà da qui? Pare che non si schiuderà presto.", + "hatchWavesMessageLongTime": "Sembra che questo uovo impiegherà ancora molto tempo per schiudersi.", + "gachaTypeLegendary": "Tasso dei leggendari aumentato", + "gachaTypeMove": "Tasso delle mosse rare da uova aumentato", + "gachaTypeShiny": "Tasso degli shiny aumentato", + "selectMachine": "Seleziona un macchinario.", + "notEnoughVouchers": "Non hai abbastanza biglietti!", + "tooManyEggs": "Hai troppe uova!", + "pull": "Estrazione", + "pulls": "Estrazioni", "sameSpeciesEgg": "{{species}} will hatch from this egg!", "hatchFromTheEgg": "Dall’Uovo è nato {{pokemonName}}!", "eggMoveUnlock": "Egg Move unlocked: {{moveName}}", diff --git a/src/locales/it/menu-ui-handler.ts b/src/locales/it/menu-ui-handler.ts index 7aae9e26f6d..5bd845448aa 100644 --- a/src/locales/it/menu-ui-handler.ts +++ b/src/locales/it/menu-ui-handler.ts @@ -2,23 +2,23 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const menuUiHandler: SimpleTranslationEntries = { "GAME_SETTINGS": "Impostazioni", - "ACHIEVEMENTS": "Trofei", + "ACHIEVEMENTS": "Obiettivi", "STATS": "Statistiche", "VOUCHERS": "Biglietti", - "EGG_LIST": "Lista Uova", - "EGG_GACHA": "Gacha Uova", - "MANAGE_DATA": "Gestisci Dati", + "EGG_LIST": "Lista uova", + "EGG_GACHA": "Macchine uova", + "MANAGE_DATA": "Gestisci dati", "COMMUNITY": "Community", - "SAVE_AND_QUIT": "Salva ed Esci", + "SAVE_AND_QUIT": "Salva ed esci", "LOG_OUT": "Disconnettiti", "slot": "Slot {{slotNumber}}", - "importSession": "Importa Sessione", + "importSession": "Importa sessione", "importSlotSelect": "Seleziona uno slot in cui importare.", - "exportSession": "Esporta Sessione", + "exportSession": "Esporta sessione", "exportSlotSelect": "Seleziona uno slot da cui esportare.", - "importData": "Importa Dati", - "exportData": "Esporta Dati", + "importData": "Importa dati", + "exportData": "Esporta dati", "cancel": "Annulla", - "losingProgressionWarning": "Perderai tutti i progressi dall'inizio della battaglia. Procedere?", + "losingProgressionWarning": "Perderai tutti i progressi dall'inizio della battaglia. Confermi?", "noEggs": "You are not hatching\nany eggs at the moment!" } as const; diff --git a/src/locales/it/modifier-type.ts b/src/locales/it/modifier-type.ts index 2b7147133e1..53cb02232d9 100644 --- a/src/locales/it/modifier-type.ts +++ b/src/locales/it/modifier-type.ts @@ -13,18 +13,18 @@ export const modifierType: ModifierTypeTranslationEntries = { "PokemonHeldItemModifierType": { extra: { "inoperable": "{{pokemonName}} non può prendere\nquesto oggetto!", - "tooMany": "{{pokemonName}} ne ha troppi\ndi questo oggetto!", + "tooMany": "{{pokemonName}} possiede già\nquesto oggetto in abbondanza.", } }, "PokemonHpRestoreModifierType": { description: "Restituisce {{restorePoints}} PS o {{restorePercent}}% PS ad un Pokémon, a seconda del valore più alto.", extra: { "fully": "Restituisce tutti i PS ad un Pokémon.", - "fullyWithStatus": "Restituisce tutti i PS ad un Pokémon e lo cura da ogni stato.", + "fullyWithStatus": "Restituisce tutti i PS ad un Pokémon e lo cura da ogni problema di stato.", } }, "PokemonReviveModifierType": { - description: "Rianima un Pokémon esausto e gli restituisce il {{restorePercent}}% PS.", + description: "Rianima un Pokémon esausto e gli restituisce il {{restorePercent}}% dei PS totali.", }, "PokemonStatusHealModifierType": { description: "Cura tutti i problemi di stato di un Pokémon.", @@ -46,7 +46,7 @@ export const modifierType: ModifierTypeTranslationEntries = { }, "PokemonNatureChangeModifierType": { name: "Menta {{natureName}}.", - description: "Cambia la natura del Pokémon in {{natureName}} e sblocca la natura per il Pokémon iniziale.", + description: "Cambia la natura del Pokémon in {{natureName}} e sblocca la natura nel menu degli starter.", }, "DoubleBattleChanceBoosterModifierType": { description: "Raddoppia la possibilità di imbattersi in doppie battaglie per {{battleCount}} battaglie.", @@ -67,7 +67,7 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Aumenta {{statName}} di base del possessore del 10%.", }, "AllPokemonFullHpRestoreModifierType": { - description: "Recupera il 100% dei PS per tutti i Pokémon.", + description: "Restituisce il 100% dei PS a tutti i Pokémon.", }, "AllPokemonFullReviveModifierType": { description: "Rianima tutti i Pokémon esausti restituendogli tutti i PS.", @@ -75,7 +75,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "MoneyRewardModifierType": { description: "Garantisce una {{moneyMultiplier}} quantità di soldi (₽{{moneyAmount}}).", extra: { - "small": "poca", + "small": "contenuta", "moderate": "moderata", "large": "grande", }, @@ -90,7 +90,7 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Aumenta del 50% il guadagno di amicizia per vittoria.", }, "PokemonMoveAccuracyBoosterModifierType": { - description: "Aumenta l'accuratezza delle mosse di {{accuracyAmount}} (massimo 100).", + description: "Aumenta la precisione delle mosse di {{accuracyAmount}} (massimo 100).", }, "PokemonMultiHitModifierType": { description: "Gli attacchi colpiscono una volta in più al costo di una riduzione di potenza del 60/75/82,5% per mossa.", @@ -117,7 +117,7 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Teracristallizza in {{teraType}} il possessore per massimo 10 battaglie.", }, "ContactHeldItemTransferChanceModifierType": { - description: "Quando si attacca, c'è una probabilità del {{chancePercent}}% che l'oggetto in possesso del nemico venga rubato.", + description: "Quando il possessore attacca, c'è una probabilità del {{chancePercent}}% che l'oggetto in possesso del nemico gli venga rubato.", }, "TurnHeldItemTransferModifierType": { description: "Ogni turno, il possessore acquisisce un oggetto posseduto dal nemico.", @@ -129,72 +129,71 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Aggiunge una probabilità del {{probabilitàPercent}}% di resistere ad un colpo.", }, - "RARE_CANDY": { name: "Caramella Rara" }, - "RARER_CANDY": { name: "Caramella Molto Rara" }, + "RARE_CANDY": { name: "Caramella rara" }, + "RARER_CANDY": { name: "Caramella molto rara" }, - "MEGA_BRACELET": { name: "Megapolsiera", description: "Le Megapietre sono disponibili." }, - "DYNAMAX_BAND": { name: "Polsino Dynamax", description: "I Fungomax sono disponibili." }, - "TERA_ORB": { name: "Terasfera", description: "I Teraliti sono disponibili." }, + "MEGA_BRACELET": { name: "Megapolsiera", description: "Le megapietre diventano disponibili." }, + "DYNAMAX_BAND": { name: "Polsino Dynamax", description: "I fungomax diventano disponibili." }, + "TERA_ORB": { name: "Terasfera", description: "I teraliti diventano disponibili." }, "MAP": { name: "Mappa", description: "Permette di scegliere la propria strada a un bivio." }, "POTION": { name: "Pozione" }, "SUPER_POTION": { name: "Superpozione" }, "HYPER_POTION": { name: "Iperpozione" }, - "MAX_POTION": { name: "Pozione Max" }, - "FULL_RESTORE": { name: "Ricarica Totale" }, + "MAX_POTION": { name: "Pozione max" }, + "FULL_RESTORE": { name: "Ricarica totale" }, "REVIVE": { name: "Revitalizzante" }, - "MAX_REVIVE": { name: "Revitalizzante Max" }, + "MAX_REVIVE": { name: "Revitalizzante max" }, - "FULL_HEAL": { name: "Cura Totale" }, + "FULL_HEAL": { name: "Cura totale" }, - "SACRED_ASH": { name: "Cenere Magica" }, + "SACRED_ASH": { name: "Cenere magica" }, - "REVIVER_SEED": { name: "Revitalseme", description: "Il possessore recupera 1/2 di PS in caso di svenimento." }, + "REVIVER_SEED": { name: "Revitalseme", description: "Il possessore recupera 1/2 di PS in caso di KO." }, "ETHER": { name: "Etere" }, - "MAX_ETHER": { name: "Etere Max" }, + "MAX_ETHER": { name: "Etere max" }, "ELIXIR": { name: "Elisir" }, - "MAX_ELIXIR": { name: "Elisir Max" }, + "MAX_ELIXIR": { name: "Elisir max" }, "PP_UP": { name: "PP-su" }, "PP_MAX": { name: "PP-max" }, - "LURE": { name: "Profumo Invito" }, - "SUPER_LURE": { name: "Profumo Invito Super" }, - "MAX_LURE": { name: "Profumo Invito Max" }, + "LURE": { name: "Esca" }, + "SUPER_LURE": { name: "Super esca" }, + "MAX_LURE": { name: "Esca max" }, - "MEMORY_MUSHROOM": { name: "Fungo della Memoria", description: "Ricorda la mossa dimenticata di un Pokémon." }, + "MEMORY_MUSHROOM": { name: "Fungo della memoria", description: "Permette di insegnare nuovamente una mossa dimenticata ad un Pokémon." }, - "EXP_SHARE": { name: "Condividi Esperienza", description: "Tutti i Pokémon della squadra ricevono il 20% dei Punti Esperienza dalla lotta anche se non vi hanno partecipato." }, - "EXP_BALANCE": { name: "Bilancia Esperienza", description: "Bilancia i Punti Esperienza ricevuti verso i Pokémon del gruppo di livello inferiore." }, + "EXP_SHARE": { name: "Condividi esperienza", description: "Tutti i Pokémon della squadra ricevono il 20% dei Punti Esperienza dalla lotta, anche se non vi hanno partecipato." }, + "EXP_BALANCE": { name: "Bilancia esperienza", description: "Bilancia i Punti Esperienza ricevuti verso i Pokémon della squadra di livello inferiore." }, "OVAL_CHARM": { name: "Ovamuleto", description: "Quando più Pokémon partecipano a una battaglia, ognuno di essi riceve il 10% in più dell'esperienza totale." }, "EXP_CHARM": { name: "Esperienzamuleto" }, - "SUPER_EXP_CHARM": { name: "Esperienzamuleto Super" }, - "GOLDEN_EXP_CHARM": { name: "Esperienzamuleto Oro" }, + "SUPER_EXP_CHARM": { name: "Esperienzamuleto super" }, + "GOLDEN_EXP_CHARM": { name: "Esperienzamuleto dorato" }, - "LUCKY_EGG": { name: "Uovo Fortunato" }, - "GOLDEN_EGG": { name: "Uovo d'Oro" }, + "LUCKY_EGG": { name: "Fortunuovo" }, + "GOLDEN_EGG": { name: "Uovo dorato" }, "SOOTHE_BELL": { name: "Calmanella" }, - "EVIOLITE": { name: "Evolcondensa", description: "Misteriosa materia evolutiva. Aumenta la Difesa e la Difesa Speciale di un Pokémon che può ancora evolversi." }, - "SOUL_DEW": { name: "Cuorugiada", description: "Aumenta del 10% l'influenza della natura di un Pokémon sulle sue statistiche (Aggiuntivo)." }, + "SOUL_DEW": { name: "Cuorugiada", description: "Aumenta del 10% l'influenza della natura di un Pokémon sulle sue statistiche (cumulativo)." }, "NUGGET": { name: "Pepita" }, "BIG_NUGGET": { name: "Granpepita" }, - "RELIC_GOLD": { name: " Dobloantico" }, + "RELIC_GOLD": { name: "Dobloantico" }, "AMULET_COIN": { name: "Monetamuleto", description: "Aumenta le ricompense in denaro del 20%." }, - "GOLDEN_PUNCH": { name: "Pugno Dorato", description: "Garantisce il 50% dei danni inflitti come denaro." }, - "COIN_CASE": { name: " Salvadanaio", description: "Dopo ogni 10° battaglia, riceverete il 10% del vostro denaro in interessi." }, + "GOLDEN_PUNCH": { name: "Pugno dorato", description: "Fornisce il 50% dei danni inflitti sottoforma di denaro." }, + "COIN_CASE": { name: "Salvadanaio", description: "Dopo ogni 10° battaglia, fornisce il 10% del proprio denaro in interessi." }, - "LOCK_CAPSULE": { name: "Capsula Scrigno", description: "Permette di bloccare le rarità degli oggetti quando si fa un reroll degli oggetti." }, + "LOCK_CAPSULE": { name: "Capsula scrigno", description: "Permette di bloccare le rarità degli oggetti quando si fa un reroll (i costi variano in base alle rarità)." }, "GRIP_CLAW": { name: "Presartigli" }, "WIDE_LENS": { name: "Grandelente" }, @@ -202,18 +201,18 @@ export const modifierType: ModifierTypeTranslationEntries = { "MULTI_LENS": { name: "Multilente" }, "HEALING_CHARM": { name: "Curamuleto", description: "Aumenta del 10% l'efficacia delle mosse e degli oggetti che ripristinano i PS (escluse le rianimazioni)." }, - "CANDY_JAR": { name: "Barattolo di caramelle", description: "Aumenta di 1 il numero di livelli aggiunti dalle Caramelle Rare." }, + "CANDY_JAR": { name: "Barattolo di caramelle", description: "Aumenta di 1 il numero di livelli aggiunti dalle caramelle rare." }, - "BERRY_POUCH": { name: "Porta Bacche", description: "Aggiunge il 30% di possibilità che una bacca usata non venga consumata." }, + "BERRY_POUCH": { name: "Porta bacche", description: "Aggiunge il 30% di possibilità che una bacca usata non venga consumata." }, - "FOCUS_BAND": { name: "Bandana", description: "Chi ce l'ha ottiene il 10% di possibilità aggiuntivo di evitare un potenziale KO e rimanere con un solo PS." }, + "FOCUS_BAND": { name: "Bandana", description: "Il possessore ottiene il 10% di possibilità aggiuntivo di evitare un potenziale KO e rimanere con un solo PS." }, - "QUICK_CLAW": { name: "Rapidartigli", description: "Aggiunge una probabilità del 10% di muoversi per primi, indipendentemente dalla velocità (dopo la priorità)." }, + "QUICK_CLAW": { name: "Rapidartigli", description: "Aggiunge una probabilità del 10% di muoversi per primi, indipendentemente dalla velocità (priorità escluse)." }, "KINGS_ROCK": { name: "Roccia di re", description: "Aggiunge il 10% di possibilità che una mossa d'attacco faccia tentennare l'avversario." }, "LEFTOVERS": { name: "Avanzi", description: "Ripristina 1/16 dei PS massimi di un Pokémon ogni turno." }, - "SHELL_BELL": { name: "Conchinella", description: "Guarisce 1/8 del danno inflitto a un Pokémon." }, + "SHELL_BELL": { name: "Conchinella", description: "Cura il possessore di 1/8 del danno inflitto ad un Pokémon." }, "TOXIC_ORB": { name: "Tossicsfera", description: "Sfera bizzarra che iperavvelena chi l’ha con sé in una lotta." }, "FLAME_ORB": { name: "Fiammosfera", description: "Sfera bizzarra che procura una scottatura a chi l’ha con sé in una lotta." }, @@ -223,22 +222,22 @@ export const modifierType: ModifierTypeTranslationEntries = { "SHINY_CHARM": { name: "Cromamuleto", description: "Misterioso amuleto luminoso che aumenta la probabilità di incontrare Pokémon cromatici." }, "ABILITY_CHARM": { name: "Abilitamuleto", description: "Aumenta drasticamente la possibilità che un Pokémon selvatico abbia un'abilità nascosta." }, - "IV_SCANNER": { name: "Scanner IV", description: "Permette di scansionare gli IV dei Pokémon selvatici. Vengono rivelati 2 IV per pila. I migliori IV vengono mostrati per primi." }, + "IV_SCANNER": { name: "Scanner IV", description: "Permette di scansionare gli IV dei Pokémon selvatici. Vengono rivelati 2 IV per ogni scanner. I migliori IV vengono mostrati per primi." }, - "DNA_SPLICERS": { name: " Cuneo DNA" }, + "DNA_SPLICERS": { name: "Cuneo DNA" }, - "MINI_BLACK_HOLE": { name: "Piccolo Buco Nero" }, + "MINI_BLACK_HOLE": { name: "Piccolo buco nero" }, - "GOLDEN_POKEBALL": { name: "Poké Ball Oro", description: "Aggiunge 1 opzione di oggetto extra alla fine di ogni battaglia." }, + "GOLDEN_POKEBALL": { name: "Poké Ball dorata", description: "Aggiunge 1 opzione di oggetto extra alla fine di ogni battaglia." }, - "ENEMY_DAMAGE_BOOSTER": { name: "Gettone del Danno", description: "Aumenta il danno del 5%." }, - "ENEMY_DAMAGE_REDUCTION": { name: "Gettone della Protezione", description: "Riduce i danni ricevuti del 2.5%." }, - "ENEMY_HEAL": { name: "Gettone del Recupero", description: "Cura il 2% dei PS massimi ogni turno." }, - "ENEMY_ATTACK_POISON_CHANCE": { name: "Gettone del Veleno" }, - "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Gettone della Paralisi" }, - "ENEMY_ATTACK_BURN_CHANCE": { name: "Gettone della Bruciatura" }, - "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Gettone Guarigione Completa", description: "Aggiunge una probabilità del 2.5% a ogni turno di curare una condizione di stato." }, - "ENEMY_ENDURE_CHANCE": { name: "Gettone di Resistenza" }, + "ENEMY_DAMAGE_BOOSTER": { name: "Gettone del danno", description: "Aumenta i danni inflitti del 5%." }, + "ENEMY_DAMAGE_REDUCTION": { name: "Gettone della protezione", description: "Riduce i danni ricevuti del 2.5%." }, + "ENEMY_HEAL": { name: "Gettone del recupero", description: "Cura il 2% dei PS massimi ogni turno." }, + "ENEMY_ATTACK_POISON_CHANCE": { name: "Gettone del veleno" }, + "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Gettone della paralisi" }, + "ENEMY_ATTACK_BURN_CHANCE": { name: "Gettone della bruciatura" }, + "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Gettone guarigione completa", description: "Aggiunge una probabilità del 2.5% a ogni turno di guarire da un problema di stato." }, + "ENEMY_ENDURE_CHANCE": { name: "Gettone di resistenza" }, "ENEMY_FUSED_CHANCE": { name: "Gettone della fusione", description: "Aggiunge l'1% di possibilità che un Pokémon selvatico sia una fusione." }, }, SpeciesBoosterItem: { @@ -258,14 +257,14 @@ export const modifierType: ModifierTypeTranslationEntries = { }, TempBattleStatBoosterStatName: { - "ATK": "Attack", - "DEF": "Defense", - "SPATK": "Sp. Atk", - "SPDEF": "Sp. Def", - "SPD": "Speed", - "ACC": "Accuracy", - "CRIT": "Critical Hit Ratio", - "EVA": "Evasiveness", + "ATK": "Attacco", + "DEF": "Difesa", + "SPATK": "Att. Speciale", + "SPDEF": "Dif. Speciale", + "SPD": "Velocità", + "ACC": "Precisione", + "CRIT": "Tasso di brutti colpi", + "EVA": "Elusione", "DEFAULT": "???", }, diff --git a/src/locales/it/weather.ts b/src/locales/it/weather.ts index 2d169421a38..f5d8e3b9397 100644 --- a/src/locales/it/weather.ts +++ b/src/locales/it/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "È apparsa una corrente d'aria misteriosa!", "strongWindsLapseMessage": "La corrente d'aria soffia intensamente.", + "strongWindsEffectMessage": "La corrente misteriosa indebolisce l’attacco!", "strongWindsClearMessage": "La corrente d'aria è cessata." }; diff --git a/src/locales/ko/battle.ts b/src/locales/ko/battle.ts index d055708452b..3032dbda5cc 100644 --- a/src/locales/ko/battle.ts +++ b/src/locales/ko/battle.ts @@ -24,7 +24,8 @@ export const battle: SimpleTranslationEntries = { "hitResultNotVeryEffective": "효과가 별로인 듯하다…", "hitResultNoEffect": "{{pokemonName}}에게는\n효과가 없는 것 같다…", "hitResultOneHitKO": "일격필살!", - "attackFailed": "하지만 실패했다!", + "attackFailed": "그러나 실패하고 말았다!!", + "attackMissed": "{{pokemonNameWithAffix}}에게는\n맞지 않았다!", "attackHitsCount": "{{count}}번 맞았다!", "rewardGain": "{{modifierName}}[[를]] 받았다!", "expGain": "{{pokemonName}}[[는]]\n{{exp}} 경험치를 얻었다!", diff --git a/src/locales/ko/bgm-name.ts b/src/locales/ko/bgm-name.ts index c133a93450e..235d6a3320e 100644 --- a/src/locales/ko/bgm-name.ts +++ b/src/locales/ko/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Music", + "music": "Music: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "BW2 관동 챔피언 배틀", "battle_johto_champion": "BW2 성도 챔피언 배틀", @@ -62,7 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH 버드렉스 배틀", "battle_legendary_birds_galar": "SWSH 가라르 전설의 새 배틀", "battle_legendary_ruinous": "SV 재앙의 보물 배틀", - "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", + "battle_legendary_kor_mir": "SV 에리어 제로 배틀", "battle_legendary_loyal_three": "SV 세벗들 배틀", "battle_legendary_ogerpon": "SV 오거폰 배틀", "battle_legendary_terapagos": "SV 테라파고스 배틀", @@ -74,16 +74,16 @@ export const bgmName: SimpleTranslationEntries = { "battle_wild": "BW 야생 포켓몬 배틀", "battle_wild_strong": "BW 강한 야생 포켓몬 조우 배틀", "end_summit": "불가사의 던전 구조대 DX 천공의 탑 꼭대기", - "battle_rocket_grunt": "HGSS Team Rocket Battle", - "battle_aqua_magma_grunt": "ORAS Team Aqua & Magma Battle", - "battle_galactic_grunt": "BDSP Team Galactic Battle", + "battle_rocket_grunt": "HGSS 로켓단 배틀", + "battle_aqua_magma_grunt": "ORAS 아쿠아단 & 마그마단 배틀", + "battle_galactic_grunt": "BDSP 갤럭시단 배틀", "battle_plasma_grunt": "BW 플라스마단 배틀", - "battle_flare_grunt": "XY Team Flare Battle", - "battle_rocket_boss": "USUM Giovanni Battle", - "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", - "battle_galactic_boss": "BDSP Cyrus Battle", - "battle_plasma_boss": "B2W2 Ghetsis Battle", - "battle_flare_boss": "XY Lysandre Battle", + "battle_flare_grunt": "XY 플레어단 배틀", + "battle_rocket_boss": "USUM 비주기 배틀", + "battle_aqua_magma_boss": "ORAS 아강 & 마적 배틀", + "battle_galactic_boss": "BDSP 태홍 배틀", + "battle_plasma_boss": "B2W2 게치스 배틀", + "battle_flare_boss": "XY 플라드리 배틀", // Biome Music "abyss": "불가사의 던전 하늘의 탐험대 어둠의 화구", diff --git a/src/locales/ko/challenges.ts b/src/locales/ko/challenges.ts index 1f10c4f215a..136417f597b 100644 --- a/src/locales/ko/challenges.ts +++ b/src/locales/ko/challenges.ts @@ -2,7 +2,7 @@ import { TranslationEntries } from "#app/interfaces/locales"; export const challenges: TranslationEntries = { "title": "챌린지 조건 설정", - "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "illegalEvolution": "{{pokemon}}[[는]] 현재의 챌린지에\n부적합한 포켓몬이 되었습니다!", "singleGeneration": { "name": "단일 세대", "desc": "{{gen}}의 포켓몬만 사용할 수 있습니다.", diff --git a/src/locales/ko/menu-ui-handler.ts b/src/locales/ko/menu-ui-handler.ts index 94c3cb2df0c..3bd412bc5ea 100644 --- a/src/locales/ko/menu-ui-handler.ts +++ b/src/locales/ko/menu-ui-handler.ts @@ -20,5 +20,5 @@ export const menuUiHandler: SimpleTranslationEntries = { "exportData": "데이터 내보내기", "cancel": "취소", "losingProgressionWarning": "전투 시작으로부터의 진행 상황을 잃게 됩니다. 계속하시겠습니까?", - "noEggs": "You are not hatching\nany eggs at the moment!" + "noEggs": "부화중인 알이 없습니다!" } as const; diff --git a/src/locales/ko/party-ui-handler.ts b/src/locales/ko/party-ui-handler.ts index 842f10209a9..f0075809345 100644 --- a/src/locales/ko/party-ui-handler.ts +++ b/src/locales/ko/party-ui-handler.ts @@ -35,8 +35,8 @@ export const partyUiHandler: SimpleTranslationEntries = { "cancel": "그만둔다", // Slot TM text - "able": "배울 수 있다", - "notAble": "배울 수 없다", + "able": "배운다!", + "notAble": "배우지 못함", "learned": "알고 있다", // Releasing messages diff --git a/src/locales/ko/weather.ts b/src/locales/ko/weather.ts index 7fbd1eaf20b..9aca57c49d2 100644 --- a/src/locales/ko/weather.ts +++ b/src/locales/ko/weather.ts @@ -41,5 +41,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "수수께끼의 난기류가\n비행포켓몬을 지킨다!", "strongWindsLapseMessage": "수수께끼의 난기류가 강렬하게 불고 있다", + "strongWindsEffectMessage": "수수께끼의 난기류가 공격을 약하게 만들었다!", "strongWindsClearMessage": "수수께끼의 난기류가 멈췄다!" // 임의번역 }; diff --git a/src/locales/pt_BR/battle.ts b/src/locales/pt_BR/battle.ts index fd6f1121be7..98d50802e7b 100644 --- a/src/locales/pt_BR/battle.ts +++ b/src/locales/pt_BR/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "Isso não afeta {{pokemonName}}!", "hitResultOneHitKO": "Foi um nocaute de um golpe!", "attackFailed": "Mas falhou!", + "attackMissed": "{{pokemonNameWithAffix}} desviou do ataque!", "attackHitsCount": "Acertou {{count}} vezes.", "rewardGain": "Você recebeu\n{{modifierName}}!", "expGain": "{{pokemonName}} ganhou\n{{exp}} pontos de experiência.", diff --git a/src/locales/pt_BR/bgm-name.ts b/src/locales/pt_BR/bgm-name.ts index ef15c6c6dcb..87d90dabc9d 100644 --- a/src/locales/pt_BR/bgm-name.ts +++ b/src/locales/pt_BR/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Music", + "music": "Music: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", diff --git a/src/locales/pt_BR/weather.ts b/src/locales/pt_BR/weather.ts index 6aaab6d3cd9..0787b32e416 100644 --- a/src/locales/pt_BR/weather.ts +++ b/src/locales/pt_BR/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "Ventos fortes apareceram!", "strongWindsLapseMessage": "Os ventos fortes continuam.", + "strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsClearMessage": "Os ventos fortes diminuíram.", }; diff --git a/src/locales/zh_CN/battle.ts b/src/locales/zh_CN/battle.ts index 7487a3ec396..de830bdf116 100644 --- a/src/locales/zh_CN/battle.ts +++ b/src/locales/zh_CN/battle.ts @@ -25,6 +25,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "对{{pokemonName}}没有效果!!", "hitResultOneHitKO": "一击必杀!", "attackFailed": "但是失败了!", + "attackMissed": "没有命中{{pokemonNameWithAffix}}!", "attackHitsCount": "击中{{count}}次!", "rewardGain": "你获得了\n{{modifierName}}!", "expGain": "{{pokemonName}}获得了 {{exp}} 点经验值!", diff --git a/src/locales/zh_CN/bgm-name.ts b/src/locales/zh_CN/bgm-name.ts index 70e969d7b5e..b582bc6a2d0 100644 --- a/src/locales/zh_CN/bgm-name.ts +++ b/src/locales/zh_CN/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "BGM", + "music": "BGM: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "黑2白2「决战!关都冠军」", "battle_johto_champion": "黑2白2「决战!城都冠军」", diff --git a/src/locales/zh_CN/party-ui-handler.ts b/src/locales/zh_CN/party-ui-handler.ts index 0348da87b17..4cb2f816b2e 100644 --- a/src/locales/zh_CN/party-ui-handler.ts +++ b/src/locales/zh_CN/party-ui-handler.ts @@ -14,7 +14,7 @@ export const partyUiHandler: SimpleTranslationEntries = { "TRANSFER": "交换", "ALL": "全部道具", "PASS_BATON": "接棒", - "UNPAUSE_EVOLUTION": "接触进化暂停", + "UNPAUSE_EVOLUTION": "解除进化暂停", "REVIVE": "复活", "choosePokemon": "选择一只宝可梦。", diff --git a/src/locales/zh_CN/weather.ts b/src/locales/zh_CN/weather.ts index ad1ecc65007..d280dfccb95 100644 --- a/src/locales/zh_CN/weather.ts +++ b/src/locales/zh_CN/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "吹起了神秘的乱流!", "strongWindsLapseMessage": "神秘的乱流势头不减。", + "strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsClearMessage": "神秘的乱流停止了。" }; diff --git a/src/locales/zh_TW/battle.ts b/src/locales/zh_TW/battle.ts index ccb784e8509..757a8d05764 100644 --- a/src/locales/zh_TW/battle.ts +++ b/src/locales/zh_TW/battle.ts @@ -22,6 +22,7 @@ export const battle: SimpleTranslationEntries = { "hitResultNoEffect": "對 {{pokemonName}} 沒有效果!", "hitResultOneHitKO": "一擊切殺!", "attackFailed": "但是失敗了!", + "attackMissed": "沒有命中{{pokemonNameWithAffix}}!", "attackHitsCount": "擊中 {{count}} 次!", "rewardGain": "You received\n{{modifierName}}!", "expGain": "{{pokemonName}} 獲得了 {{exp}} 經驗值!", diff --git a/src/locales/zh_TW/bgm-name.ts b/src/locales/zh_TW/bgm-name.ts index ef15c6c6dcb..87d90dabc9d 100644 --- a/src/locales/zh_TW/bgm-name.ts +++ b/src/locales/zh_TW/bgm-name.ts @@ -1,7 +1,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const bgmName: SimpleTranslationEntries = { - "music": "Music", + "music": "Music: ", "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", diff --git a/src/locales/zh_TW/weather.ts b/src/locales/zh_TW/weather.ts index 7efdc8af0ad..ae0646ce33d 100644 --- a/src/locales/zh_TW/weather.ts +++ b/src/locales/zh_TW/weather.ts @@ -40,5 +40,6 @@ export const weather: SimpleTranslationEntries = { "strongWindsStartMessage": "吹起了神秘的亂流!", "strongWindsLapseMessage": "神秘的亂流勢頭不減。", + "strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsClearMessage": "神秘的亂流停止了。" }; diff --git a/src/phases.ts b/src/phases.ts index df7314e6285..51391971308 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2894,7 +2894,7 @@ export class MoveEffectPhase extends PokemonPhase { if (!activeTargets.length || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { this.stopMultiHit(); if (activeTargets.length) { - this.scene.queueMessage(getPokemonMessage(user, "'s\nattack missed!")); + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(this.getTarget()) })); moveHistoryEntry.result = MoveResult.MISS; applyMoveAttrs(MissEffectAttr, user, null, move); } else { @@ -2912,7 +2912,7 @@ export class MoveEffectPhase extends PokemonPhase { for (const target of targets) { if (!targetHitChecks[target.getBattlerIndex()]) { this.stopMultiHit(target); - this.scene.queueMessage(getPokemonMessage(user, "'s\nattack missed!")); + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); if (moveHistoryEntry.result === MoveResult.PENDING) { moveHistoryEntry.result = MoveResult.MISS; } @@ -3616,7 +3616,9 @@ export class DamagePhase extends PokemonPhase { super.start(); if (this.damageResult === HitResult.ONE_HIT_KO) { - this.scene.toggleInvert(true); + if (this.scene.moveAnimations) { + this.scene.toggleInvert(true); + } this.scene.time.delayedCall(Utils.fixedInt(1000), () => { this.scene.toggleInvert(false); this.applyDamage(); diff --git a/src/system/achv.ts b/src/system/achv.ts index 511ddd1eb8e..dda6e5c511b 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -316,10 +316,10 @@ export const achvs = { CATCH_LEGENDARY: new Achv("CATCH_LEGENDARY", "", "CATCH_LEGENDARY.description","mb", 100).setSecret(), SEE_SHINY: new Achv("SEE_SHINY", "", "SEE_SHINY.description","pb_gold", 75), SHINY_PARTY: new Achv("SHINY_PARTY", "", "SHINY_PARTY.description","shiny_charm", 100).setSecret(true), - HATCH_MYTHICAL: new Achv("HATCH_MYTHICAL", "", "HATCH_MYTHICAL.description","pair_of_tickets", 75).setSecret(), - HATCH_SUB_LEGENDARY: new Achv("HATCH_SUB_LEGENDARY","", "HATCH_SUB_LEGENDARY.description","mystic_ticket", 100).setSecret(), - HATCH_LEGENDARY: new Achv("HATCH_LEGENDARY","", "HATCH_LEGENDARY.description","mystic_ticket", 125).setSecret(), - HATCH_SHINY: new Achv("HATCH_SHINY","", "HATCH_SHINY.description","golden_mystic_ticket", 100).setSecret(), + HATCH_MYTHICAL: new Achv("HATCH_MYTHICAL", "", "HATCH_MYTHICAL.description","mystery_egg", 75).setSecret(), + HATCH_SUB_LEGENDARY: new Achv("HATCH_SUB_LEGENDARY","", "HATCH_SUB_LEGENDARY.description","oval_stone", 100).setSecret(), + HATCH_LEGENDARY: new Achv("HATCH_LEGENDARY","", "HATCH_LEGENDARY.description","lucky_egg", 125).setSecret(), + HATCH_SHINY: new Achv("HATCH_SHINY","", "HATCH_SHINY.description","golden_egg", 100).setSecret(), HIDDEN_ABILITY: new Achv("HIDDEN_ABILITY","", "HIDDEN_ABILITY.description","ability_charm", 75), PERFECT_IVS: new Achv("PERFECT_IVS","", "PERFECT_IVS.description","blunder_policy", 100), CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY","", "CLASSIC_VICTORY.description","relic_crown", 150), diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts new file mode 100644 index 00000000000..d022d69a772 --- /dev/null +++ b/src/test/arena/weather_strong_winds.test.ts @@ -0,0 +1,82 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import * as overrides from "#app/overrides"; +import { Species } from "#enums/species"; +import { + TurnStartPhase, +} from "#app/phases"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import { allMoves } from "#app/data/move.js"; + +describe("Weather - Strong Winds", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(10); + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.TAILLOW); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.DELTA_STREAM); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.THUNDERBOLT, Moves.ICE_BEAM, Moves.ROCK_SLIDE]); + }); + + it("electric type move is not very effective on Rayquaza", async () => { + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RAYQUAZA); + + await game.startBattle([Species.PIKACHU]); + const pikachu = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + + await game.phaseInterceptor.to(TurnStartPhase); + expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(0.5); + }); + + it("electric type move is neutral for flying type pokemon", async () => { + await game.startBattle([Species.PIKACHU]); + const pikachu = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + + await game.phaseInterceptor.to(TurnStartPhase); + expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(1); + }); + + it("ice type move is neutral for flying type pokemon", async () => { + await game.startBattle([Species.PIKACHU]); + const pikachu = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + + await game.phaseInterceptor.to(TurnStartPhase); + expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ICE_BEAM].type, pikachu)).toBe(1); + }); + + it("rock type move is neutral for flying type pokemon", async () => { + await game.startBattle([Species.PIKACHU]); + const pikachu = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE)); + + await game.phaseInterceptor.to(TurnStartPhase); + expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ROCK_SLIDE].type, pikachu)).toBe(1); + }); +}); diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts new file mode 100644 index 00000000000..46f25f66bcd --- /dev/null +++ b/src/test/battle-stat.spec.ts @@ -0,0 +1,149 @@ +import { + BattleStat, + getBattleStatLevelChangeDescription, + getBattleStatName, +} from "#app/data/battle-stat.js"; +import { describe, expect, it } from "vitest"; +import { arrayOfRange, mockI18next } from "./utils/testUtils"; + +const TEST_BATTLE_STAT = -99 as unknown as BattleStat; +const TEST_POKEMON = "Testmon"; +const TEST_STAT = "Teststat"; + +describe("battle-stat", () => { + describe("getBattleStatName", () => { + it("should return the correct name for each BattleStat", () => { + mockI18next(); + + expect(getBattleStatName(BattleStat.ATK)).toBe("pokemonInfo:Stat.ATK"); + expect(getBattleStatName(BattleStat.DEF)).toBe("pokemonInfo:Stat.DEF"); + expect(getBattleStatName(BattleStat.SPATK)).toBe( + "pokemonInfo:Stat.SPATK" + ); + expect(getBattleStatName(BattleStat.SPDEF)).toBe( + "pokemonInfo:Stat.SPDEF" + ); + expect(getBattleStatName(BattleStat.SPD)).toBe("pokemonInfo:Stat.SPD"); + expect(getBattleStatName(BattleStat.ACC)).toBe("pokemonInfo:Stat.ACC"); + expect(getBattleStatName(BattleStat.EVA)).toBe("pokemonInfo:Stat.EVA"); + }); + + it("should fall back to ??? for an unknown BattleStat", () => { + expect(getBattleStatName(TEST_BATTLE_STAT)).toBe("???"); + }); + }); + + describe("getBattleStatLevelChangeDescription", () => { + it("should return battle:statRose for +1", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 1, + true + ); + + expect(message).toBe("battle:statRose"); + }); + + it("should return battle:statSharplyRose for +2", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 2, + true + ); + + expect(message).toBe("battle:statSharplyRose"); + }); + + it("should return battle:statRoseDrastically for +3 to +6", () => { + mockI18next(); + + arrayOfRange(3, 6).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + true + ); + + expect(message).toBe("battle:statRoseDrastically"); + }); + }); + + it("should return battle:statWontGoAnyHigher for 7 or higher", () => { + mockI18next(); + + arrayOfRange(7, 10).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + true + ); + + expect(message).toBe("battle:statWontGoAnyHigher"); + }); + }); + + it("should return battle:statFell for -1", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 1, + false + ); + + expect(message).toBe("battle:statFell"); + }); + + it("should return battle:statHarshlyFell for -2", () => { + mockI18next(); + + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + 2, + false + ); + + expect(message).toBe("battle:statHarshlyFell"); + }); + + it("should return battle:statSeverelyFell for -3 to -6", () => { + mockI18next(); + + arrayOfRange(3, 6).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + false + ); + + expect(message).toBe("battle:statSeverelyFell"); + }); + }); + + it("should return battle:statWontGoAnyLower for -7 or lower", () => { + mockI18next(); + + arrayOfRange(7, 10).forEach((n) => { + const message = getBattleStatLevelChangeDescription( + TEST_POKEMON, + TEST_STAT, + n, + false + ); + + expect(message).toBe("battle:statWontGoAnyLower"); + }); + }); + }); +}); diff --git a/src/test/lokalisation/status-effect.test.ts b/src/test/lokalisation/status-effect.test.ts index b4267ea7b8b..ad33a59a675 100644 --- a/src/test/lokalisation/status-effect.test.ts +++ b/src/test/lokalisation/status-effect.test.ts @@ -7,9 +7,9 @@ import { getStatusEffectObtainText, getStatusEffectOverlapText, } from "#app/data/status-effect"; -import i18next, { ParseKeys } from "i18next"; +import i18next from "i18next"; +import { mockI18next } from "../utils/testUtils"; -const tMock = (key: ParseKeys) => key; const pokemonName = "PKM"; const sourceText = "SOURCE"; @@ -22,7 +22,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.NONE; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.obtain"); @@ -32,7 +32,7 @@ describe("status-effect", () => { }); it("should return the source-obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName, sourceText); expect(text).toBe("statusEffect:none.obtainSource"); @@ -42,25 +42,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.activation"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.overlap"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:none.heal"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:none.description"); }); @@ -70,7 +70,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.POISON; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.obtain"); @@ -80,25 +80,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:poison.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:poison.overlap"); }); @@ -108,7 +108,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.TOXIC; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.obtain"); @@ -118,25 +118,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:toxic.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:toxic.overlap"); }); @@ -146,7 +146,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.PARALYSIS; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.obtain"); @@ -156,25 +156,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:paralysis.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:paralysis.overlap"); }); @@ -184,7 +184,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.SLEEP; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.obtain"); @@ -194,25 +194,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:sleep.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:sleep.overlap"); }); @@ -222,7 +222,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.FREEZE; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.obtain"); @@ -232,25 +232,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:freeze.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:freeze.overlap"); }); @@ -260,7 +260,7 @@ describe("status-effect", () => { const statusEffect = StatusEffect.BURN; it("should return the obtain text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectObtainText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.obtain"); @@ -270,25 +270,25 @@ describe("status-effect", () => { }); it("should return the activation text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectActivationText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.activation"); }); it("should return the descriptor", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectDescriptor(statusEffect); expect(text).toBe("statusEffect:burn.description"); }); it("should return the heal text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectHealText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.heal"); }); it("should return the overlap text", () => { - vi.spyOn(i18next, "t").mockImplementation(tMock); + mockI18next(); const text = getStatusEffectOverlapText(statusEffect, pokemonName); expect(text).toBe("statusEffect:burn.overlap"); }); diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts new file mode 100644 index 00000000000..c6f071a699f --- /dev/null +++ b/src/test/moves/hard_press.test.ts @@ -0,0 +1,83 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import * as overrides from "#app/overrides"; +import { Species } from "#enums/species"; +import { + MoveEffectPhase, +} from "#app/phases"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import { NumberHolder } from "#app/utils.js"; +import Move from "#app/data/move.js"; +import Pokemon from "#app/field/pokemon.js"; +import { allMoves, OpponentHighHpPowerAttr } from "#app/data/move.js"; + +describe("Moves - Hard Press", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.HARD_PRESS]); + }); + + it("power varies between 1 and 100, and is greater the more HP the target has", async () => { + await game.startBattle([Species.GRAVELER]); + const moveToBeUsed = allMoves[Moves.HARD_PRESS]; + + game.doAttack(getMovePosition(game.scene, 0, moveToBeUsed)); + await game.phaseInterceptor.to(MoveEffectPhase); + + const enemy = game.scene.getEnemyPokemon(); + const movePower = getMockedMovePower(enemy, game.scene.getPlayerPokemon(), moveToBeUsed); + const moveMaxBasePower = getMoveMaxBasePower(moveToBeUsed); + + expect(movePower).toBe(moveMaxBasePower * enemy.getHpRatio()); + }); +}); + +/** + * Calculates the mocked move power based on the attributes of the move and the opponent's high HP. + * + * @param defender - The defending Pokémon. + * @param attacker - The attacking Pokémon. + * @param move - The move being used. + * @returns The calculated move power. + */ +const getMockedMovePower = (defender: Pokemon, attacker: Pokemon, move: Move) => { + const powerHolder = new NumberHolder(move.power); + + if (move.hasAttr(OpponentHighHpPowerAttr)) { + const attr = move.getAttrs(OpponentHighHpPowerAttr); + attr[0].apply(attacker, defender, move, [ powerHolder ]); + } + + return powerHolder.value; +}; + +/** + * Retrieves the maximum base power of a move based on its attributes. + * + * @param move - The move which maximum base power is being retrieved. + * @returns The maximum base power of the move. + */ +const getMoveMaxBasePower = (move: Move) => { + const attr = move.getAttrs(OpponentHighHpPowerAttr); + + return (attr[0] as OpponentHighHpPowerAttr)["maxBasePower"]; +}; diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts new file mode 100644 index 00000000000..336e5bccd26 --- /dev/null +++ b/src/test/ui/transfer-item.test.ts @@ -0,0 +1,100 @@ +import { BerryType } from "#app/enums/berry-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { Button } from "#app/enums/buttons"; +import * as overrides from "#app/overrides"; +import { + BattleEndPhase, + SelectModifierPhase +} from "#app/phases"; +import GameManager from "#app/test/utils/gameManager"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; +import { Mode } from "#app/ui/ui"; +import Phaser from "phaser"; +import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { getMovePosition } from "../utils/gameManagerUtils"; + + +describe("UI - Transfer Items", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); + vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(1); + vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([ + { name: "BERRY", count: 1, type: BerryType.SITRUS }, + { name: "BERRY", count: 2, type: BerryType.APICOT }, + { name: "BERRY", count: 2, type: BerryType.LUM }, + ]); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.DRAGON_CLAW]); + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH]); + + await game.startBattle([Species.RAYQUAZA, Species.RAYQUAZA, Species.RAYQUAZA]); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_CLAW)); + + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); + + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + handler.setCursor(1); + handler.processInput(Button.ACTION); + + game.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER); + }); + + await game.phaseInterceptor.to(BattleEndPhase); + }); + + it("check red tint for held item limit in transfer menu", async () => { + game.onNextPrompt("SelectModifierPhase", Mode.PARTY, () => { + expect(game.scene.ui.getHandler()).toBeInstanceOf(PartyUiHandler); + + const handler = game.scene.ui.getHandler() as PartyUiHandler; + handler.processInput(Button.ACTION); + + expect(handler.optionsContainer.list.some((option) => (option as BBCodeText).text?.includes("Sitrus Berry"))).toBe(true); + expect(handler.optionsContainer.list.some((option) => (option as BBCodeText).text?.includes("Apicot Berry (2)"))).toBe(true); + expect(handler.optionsContainer.list.some((option) => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text))).toBe(true); + + game.phaseInterceptor.unlock(); + }); + + await game.phaseInterceptor.to(SelectModifierPhase); + }, 20000); + + it("check transfer option for pokemon to transfer to", async () => { + game.onNextPrompt("SelectModifierPhase", Mode.PARTY, () => { + expect(game.scene.ui.getHandler()).toBeInstanceOf(PartyUiHandler); + + const handler = game.scene.ui.getHandler() as PartyUiHandler; + handler.processInput(Button.ACTION); // select Pokemon + handler.processInput(Button.ACTION); // select held item (Sitrus Berry) + + handler.setCursor(1); // move to other Pokemon + handler.processInput(Button.ACTION); // select Pokemon + + expect(handler.optionsContainer.list.some((option) => (option as BBCodeText).text?.includes("Transfer"))).toBe(true); + + game.phaseInterceptor.unlock(); + }); + + await game.phaseInterceptor.to(SelectModifierPhase); + }, 20000); +}); diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts new file mode 100644 index 00000000000..b922fc9c61c --- /dev/null +++ b/src/test/utils/testUtils.ts @@ -0,0 +1,23 @@ +import i18next, { type ParseKeys } from "i18next"; +import { vi } from "vitest"; + +/** + * Sets up the i18next mock. + * Includes a i18next.t mocked implementation only returning the raw key (`(key) => key`) + * + * @returns A spy/mock of i18next + */ +export function mockI18next() { + return vi.spyOn(i18next, "t").mockImplementation((key: ParseKeys) => key); +} + +/** + * Creates an array of range `start - end` + * + * @param start start number e.g. 1 + * @param end end number e.g. 10 + * @returns an array of numbers + */ +export function arrayOfRange(start: integer, end: integer) { + return Array.from({ length: end - start }, (_v, k) => k + start); +} diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index 5bca9632012..f4366ef9c65 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -3,24 +3,16 @@ import {addTextObject, TextStyle} from "./text"; import i18next from "i18next"; import * as Utils from "#app/utils"; - const hiddenX = -150; const shownX = 0; const baseY = 0; - export default class BgmBar extends Phaser.GameObjects.Container { private defaultWidth: number; private defaultHeight: number; private bg: Phaser.GameObjects.NineSlice; private musicText: Phaser.GameObjects.Text; - private noteText: Phaser.GameObjects.Text; - - private tween: Phaser.Tweens.Tween; - private autoHideTimer: NodeJS.Timeout; - private queue: (string)[] = []; - public shown: boolean; @@ -29,19 +21,15 @@ export default class BgmBar extends Phaser.GameObjects.Container { } setup(): void { - this.defaultWidth = 200; + this.defaultWidth = 230; this.defaultHeight = 100; - this.bg = this.scene.add.nineslice(-5, -5, "ability_bar_left", null, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); + this.bg = this.scene.add.nineslice(-5, -5, "bgm_bar", null, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); this.bg.setOrigin(0, 0); this.add(this.bg); - this.noteText = addTextObject(this.scene, 5, 5, "", TextStyle.MESSAGE, {fontSize: "72px"}); - this.noteText.setOrigin(0, 0); - this.add(this.noteText); - - this.musicText = addTextObject(this.scene, 30, 5, "", TextStyle.MESSAGE, {fontSize: "72px"}); + this.musicText = addTextObject(this.scene, 5, 5, "", TextStyle.BGM_BAR); this.musicText.setOrigin(0, 0); this.musicText.setWordWrapWidth(650, true); @@ -56,16 +44,15 @@ export default class BgmBar extends Phaser.GameObjects.Container { * @param {string} bgmName The name of the BGM to set. */ setBgmToBgmBar(bgmName: string): void { - this.noteText.setText(`${i18next.t("bgmName:music")}:`); - this.musicText.setText(`${this.getRealBgmName(bgmName)}`); + this.musicText.setText(`${i18next.t("bgmName:music")}${this.getRealBgmName(bgmName)}`); if (!(this.scene as BattleScene).showBgmBar) { return; } this.musicText.width = this.bg.width - 20; this.musicText.setWordWrapWidth(this.defaultWidth * 4); - this.bg.width = Math.min(this.defaultWidth, this.noteText.displayWidth + this.musicText.displayWidth + 30); + this.bg.width = Math.min(this.defaultWidth, this.musicText.displayWidth + 23); this.bg.height = Math.min(this.defaultHeight, this.musicText.displayHeight + 20); (this.scene as BattleScene).fieldUI.bringToTop(this); @@ -97,5 +84,3 @@ export default class BgmBar extends Phaser.GameObjects.Container { return i18next.t([`bgmName:${bgmName}`, "bgmName:missing_entries"], {name: Utils.formatText(bgmName)}); } } - - diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 7ddadf99a3d..e820c8cb0d2 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,12 +1,12 @@ import { CommandPhase, SelectModifierPhase } from "../phases"; import BattleScene from "../battle-scene"; import { PlayerPokemon, PokemonMove } from "../field/pokemon"; -import { addTextObject, TextStyle } from "./text"; +import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import MessageUiHandler from "./message-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; -import { PokemonFormChangeItemModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier } from "../modifier/modifier"; +import { PokemonBaseStatModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier } from "../modifier/modifier"; import { allMoves } from "../data/move"; import { getGenderColor, getGenderSymbol } from "../data/gender"; import { StatusEffect } from "../data/status-effect"; @@ -19,6 +19,7 @@ import {Button} from "#enums/buttons"; import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; import MoveInfoOverlay from "./move-info-overlay"; import i18next from "i18next"; +import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Moves } from "#enums/moves"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); @@ -85,7 +86,8 @@ export default class PartyUiHandler extends MessageUiHandler { private optionsCursor: integer = 0; private optionsScrollCursor: integer = 0; private optionsScrollTotal: integer = 0; - private optionsContainer: Phaser.GameObjects.Container; + /** This is only public for test/ui/transfer-item.test.ts */ + public optionsContainer: Phaser.GameObjects.Container; private optionsBg: Phaser.GameObjects.NineSlice; private optionsCursorObj: Phaser.GameObjects.Image; private options: integer[]; @@ -819,7 +821,7 @@ export default class PartyUiHandler extends MessageUiHandler { optionEndIndex = this.options.length; let widestOptionWidth = 0; - const optionTexts: Phaser.GameObjects.Text[] = []; + const optionTexts: BBCodeText[] = []; for (let o = optionStartIndex; o < optionEndIndex; o++) { const option = this.options[this.options.length - (o + 1)]; @@ -861,27 +863,42 @@ export default class PartyUiHandler extends MessageUiHandler { const move = learnableLevelMoves[option]; optionName = allMoves[move].name; altText = !pokemon.getSpeciesForm().getLevelMoves().find(plm => plm[1] === move); + } else if (option === PartyOption.ALL) { + optionName = i18next.t("partyUiHandler:ALL"); } else { - if (option === PartyOption.ALL) { - optionName = i18next.t("partyUiHandler:ALL"); - } else { - const itemModifier = itemModifiers[option]; - optionName = itemModifier.type.name; - /** For every item that has stack bigger than 1, display the current quantity selection */ - if (this.transferQuantitiesMax[option] > 1) { - optionName += ` (${this.transferQuantities[option]})`; - } - } + const itemModifier = itemModifiers[option]; + optionName = itemModifier.type.name; } const yCoord = -6 - 16 * o; - const optionText = addTextObject(this.scene, 0, yCoord - 16, optionName, TextStyle.WINDOW); + const optionText = addBBCodeTextObject(this.scene, 0, yCoord - 16, optionName, TextStyle.WINDOW, { maxLines: 1 }); if (altText) { optionText.setColor("#40c8f8"); optionText.setShadowColor("#006090"); } optionText.setOrigin(0, 0); + /** For every item that has stack bigger than 1, display the current quantity selection */ + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && this.transferQuantitiesMax[option] > 1) { + const itemModifier = itemModifiers[option]; + + /** Not sure why getMaxHeldItemCount had an error, but it only checks the Pokemon parameter if the modifier is PokemonBaseStatModifier */ + if (itemModifier === undefined || itemModifier instanceof PokemonBaseStatModifier) { + continue; + } + + let amountText = ` (${this.transferQuantities[option]})`; + + /** If the amount held is the maximum, display the count in red */ + if (this.transferQuantitiesMax[option] === itemModifier.getMaxHeldItemCount(undefined)) { + amountText = `[color=${getTextColor(TextStyle.SUMMARY_RED)}]${amountText}[/color]`; + } + + optionText.setText(optionName + amountText); + } + + optionText.setText(`[shadow]${optionText.text}[/shadow]`); + optionTexts.push(optionText); widestOptionWidth = Math.max(optionText.displayWidth, widestOptionWidth); diff --git a/src/ui/text.ts b/src/ui/text.ts index 91ada7f12ad..2a12e883deb 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -34,7 +34,8 @@ export enum TextStyle { MOVE_PP_HALF_FULL, MOVE_PP_NEAR_EMPTY, MOVE_PP_EMPTY, - SMALLER_WINDOW_ALT + SMALLER_WINDOW_ALT, + BGM_BAR } export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): Phaser.GameObjects.Text { @@ -146,6 +147,11 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty shadowXpos = 3; shadowYpos = 3; break; + case TextStyle.BGM_BAR: + styleOptions.fontSize = defaultFontSize - 24; + shadowXpos = 3; + shadowYpos = 3; + break; } const shadowColor = getTextColor(style, true, uiTheme); @@ -235,6 +241,8 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui return !shadow ? "#f88880" : "#f83018"; case TextStyle.SMALLER_WINDOW_ALT: return !shadow ? "#484848" : "#d0d0c8"; + case TextStyle.BGM_BAR: + return !shadow ? "#f8f8f8" : "#6b5a73"; } }