diff --git a/public/audio/bgm/battle_legendary_kor_mir.mp3 b/public/audio/bgm/battle_legendary_kor_mir.mp3 new file mode 100644 index 00000000000..681e26a75b7 Binary files /dev/null and b/public/audio/bgm/battle_legendary_kor_mir.mp3 differ diff --git a/public/audio/bgm/metropolis.mp3 b/public/audio/bgm/metropolis.mp3 index ff67771bdb9..7c8b1912346 100644 Binary files a/public/audio/bgm/metropolis.mp3 and b/public/audio/bgm/metropolis.mp3 differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index a41c3427e1f..1b1d1fde83f 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1875,6 +1875,8 @@ export default class BattleScene extends SceneBase { return 0.175; case "battle_legendary_ruinous": //SV Treasures of Ruin Battle return 6.333; + case "battle_legendary_kor_mir": //SV Depths of Area Zero Battle + return 6.442; case "battle_legendary_loyal_three": //SV Loyal Three Battle return 6.500; case "battle_legendary_ogerpon": //SV Ogerpon Battle diff --git a/src/battle.ts b/src/battle.ts index 349d8cc4618..c3a481e9956 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -311,6 +311,9 @@ export default class Battle { if (pokemon.species.speciesId === Species.WO_CHIEN || pokemon.species.speciesId === Species.CHIEN_PAO || pokemon.species.speciesId === Species.TING_LU || pokemon.species.speciesId === Species.CHI_YU) { return "battle_legendary_ruinous"; } + if (pokemon.species.speciesId === Species.KORAIDON || pokemon.species.speciesId === Species.MIRAIDON) { + return "battle_legendary_kor_mir"; + } if (pokemon.species.speciesId === Species.OKIDOGI || pokemon.species.speciesId === Species.MUNKIDORI || pokemon.species.speciesId === Species.FEZANDIPITI) { return "battle_legendary_loyal_three"; } diff --git a/src/data/move.ts b/src/data/move.ts index 8fefcf15254..a6ebc450419 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,5 +1,5 @@ import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleEndPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; +import { BattleEndPhase, MoveEffectPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; import { BattleStat, getBattleStatName } from "./battle-stat"; import { EncoreTag, PowerTrickTag, SemiInvulnerableTag } from "./battler-tags"; import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; @@ -3872,7 +3872,7 @@ export class DisableMoveAttr extends MoveEffectAttr { export class FrenzyAttr extends MoveEffectAttr { constructor() { - super(true, MoveEffectTrigger.HIT); + super(true, MoveEffectTrigger.HIT, false, true); } canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { @@ -5549,6 +5549,25 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(Abilities.COMATOSE); +/** + * Condition to apply effects only upon applying the move to its last target. + * Currently only used for Make It Rain. + * @param {Pokemon} user The user of the move. + * @param {Pokemon} target The current target of the move. + * @param {Move} move The move to which this condition applies. + * @returns true if the target is the last target to which the move applies. + */ +const lastTargetOnlyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { + const effectPhase = user.scene.getCurrentPhase(); + const targetIndex = target.getFieldIndex() + (target.isPlayer() ? 0 : BattlerIndex.ENEMY); + + if (effectPhase instanceof MoveEffectPhase) { + const activeTargets = effectPhase.getTargets(); + return (activeTargets.length === 0 || targetIndex >= activeTargets.at(-1).getBattlerIndex()); + } + return false; +}; + export type MoveAttrFilter = (attr: MoveAttr) => boolean; function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { @@ -8052,6 +8071,7 @@ export function initMoves() { new AttackMove(Moves.MYSTICAL_POWER, Type.PSYCHIC, MoveCategory.SPECIAL, 70, 90, 10, 100, 0, 8) .attr(StatChangeAttr, BattleStat.SPATK, 1, true), new AttackMove(Moves.RAGING_FURY, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 8) + .makesContact(false) .attr(FrenzyAttr) .attr(MissEffectAttr, frenzyMissFunc) .target(MoveTarget.RANDOM_NEAR_ENEMY), @@ -8070,6 +8090,7 @@ export function initMoves() { .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true) .punchingMove(), new AttackMove(Moves.BARB_BARRAGE, Type.POISON, MoveCategory.PHYSICAL, 60, 100, 10, 50, 0, 8) + .makesContact(false) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1) .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(Moves.ESPER_WING, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) @@ -8080,6 +8101,7 @@ export function initMoves() { new SelfStatusMove(Moves.SHELTER, Type.STEEL, -1, 10, 100, 0, 8) .attr(StatChangeAttr, BattleStat.DEF, 2, true), new AttackMove(Moves.TRIPLE_ARROWS, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) + .makesContact(false) .attr(HighCritAttr) .attr(StatChangeAttr, BattleStat.DEF, -1) .attr(FlinchAttr) @@ -8293,7 +8315,7 @@ export function initMoves() { .attr(RemoveScreensAttr), new AttackMove(Moves.MAKE_IT_RAIN, Type.STEEL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) .attr(MoneyAttr) - .attr(StatChangeAttr, BattleStat.SPATK, -1, true, null, true, false) + .attr(StatChangeAttr, BattleStat.SPATK, -1, true, lastTargetOnlyCondition, true, false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.PSYBLADE, Type.PSYCHIC, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) @@ -8316,8 +8338,7 @@ export function initMoves() { .target(MoveTarget.BOTH_SIDES), new SelfStatusMove(Moves.TIDY_UP, Type.NORMAL, -1, 10, -1, 0, 9) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPD ], 1, true, null, true, true) - .attr(RemoveArenaTrapAttr) - .target(MoveTarget.BOTH_SIDES), + .attr(RemoveArenaTrapAttr, true), new StatusMove(Moves.SNOWSCAPE, Type.ICE, -1, 10, -1, 0, 9) .attr(WeatherChangeAttr, WeatherType.SNOW) .target(MoveTarget.BOTH_SIDES), diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index ad93bec44e4..ae1532f0be0 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -639,6 +639,10 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.AEGISLASH, "shield", "blade", new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))), new SpeciesFormChange(Species.AEGISLASH, "blade", "shield", new SpeciesFormChangeActiveTrigger(false), true) ], + [Species.XERNEAS]: [ + new SpeciesFormChange(Species.XERNEAS, "neutral", "active", new SpeciesFormChangeActiveTrigger(true), true), + new SpeciesFormChange(Species.XERNEAS, "active", "neutral", new SpeciesFormChangeActiveTrigger(false), true) + ], [Species.ZYGARDE]: [ new SpeciesFormChange(Species.ZYGARDE, "50-pc", "complete", new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.ZYGARDE, "complete", "50-pc", new SpeciesFormChangeManualTrigger(), true), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 51b2b4f9c61..58f44dc8b3d 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3406,12 +3406,15 @@ export class EnemyPokemon extends Pokemon { public aiType: AiType; public bossSegments: integer; public bossSegmentIndex: integer; + /** To indicate of the instance was populated with a dataSource -> e.g. loaded & populated from session data */ + public readonly isPopulatedFromDataSource: boolean; constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource: PokemonData) { super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex, dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, null, dataSource ? dataSource.nature : undefined, dataSource); this.trainerSlot = trainerSlot; + this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource if (boss) { this.setBoss(boss, dataSource?.bossSegments); } @@ -3461,6 +3464,13 @@ export class EnemyPokemon extends Pokemon { } } + /** + * Sets the pokemons boss status. If true initializes the boss segments either from the arguments + * or through the the Scene.getEncounterBossSegments function + * + * @param boss if the pokemon is a boss + * @param bossSegments amount of boss segments (health-bar segments) + */ setBoss(boss: boolean = true, bossSegments: integer = 0): void { if (boss) { this.bossSegments = bossSegments || this.scene.getEncounterBossSegments(this.scene.currentBattle.waveIndex, this.level, this.species, true); diff --git a/src/locales/de/bgm-name.ts b/src/locales/de/bgm-name.ts index 30a3c5fae32..70698942c8b 100644 --- a/src/locales/de/bgm-name.ts +++ b/src/locales/de/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH Vs. Coronospa", "battle_legendary_birds_galar": "SWSH Vs. Legendäre Galar-Vögel", "battle_legendary_ruinous": "KAPU Vs. Schätze des Unheils", + "battle_legendary_kor_mir": "KAPU Die Tiefen von Zone Null", "battle_legendary_loyal_three": "KAPU Drei Gefährten", "battle_legendary_ogerpon": "KAPU Vs. Ogerpon", "battle_legendary_terapagos": "KAPU Vs. Terapagos", diff --git a/src/locales/de/menu.ts b/src/locales/de/menu.ts index 8f8beffc2cb..5bbd19be851 100644 --- a/src/locales/de/menu.ts +++ b/src/locales/de/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "HAFTUNGSAUSSCHLUSS", "disclaimerDescription": "Dieses Spiel ist ein unfertiges Produkt. Es kann spielbeinträchtigende Fehler (bis hin zum Verlust des Speicherstandes)\n aufweisen, sich ohne Vorankündigung ändern und es gibt keine Garantie dass es weiterentwickelt oder fertiggestellt wird.", "choosePokemon": "Choose a Pokémon.", + "errorServerDown": "Ups! Es gab einen Fehler beim Versuch\nden Server zu kontaktieren\nLasse dieses Fenster offen\nDu wirst automatisch neu verbunden.", } as const; diff --git a/src/locales/en/bgm-name.ts b/src/locales/en/bgm-name.ts index 1e35a2a095b..77ebb69df32 100644 --- a/src/locales/en/bgm-name.ts +++ b/src/locales/en/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH Calyrex Battle", "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "SV Loyal Three Battle", "battle_legendary_ogerpon": "SV Ogerpon Battle", "battle_legendary_terapagos": "SV Terapagos Battle", @@ -91,7 +92,7 @@ export const bgmName: SimpleTranslationEntries = { "cave": "PMD EoS Sky Peak Cave", "construction_site": "PMD EoS Boulder Quarry", "desert": "PMD EoS Northern Desert", - "dojo": "PMD EoS Marowa Dojo", + "dojo": "PMD EoS Marowak Dojo", "end": "PMD RTDX Sky Tower", "factory": "PMD EoS Concealed Ruins", "fairy_cave": "PMD EoS Star Cave", diff --git a/src/locales/en/dialogue.ts b/src/locales/en/dialogue.ts index 95faeacc9c7..dda8891b788 100644 --- a/src/locales/en/dialogue.ts +++ b/src/locales/en/dialogue.ts @@ -447,7 +447,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "My old associates need me... Are you going to get in my way?" }, "victory": { - 1: "How is this possible...?\nThe precious dream of Team Rocket has become little more than an illusion..." + 1: "How is this possible...? The precious dream of Team Rocket has become little more than an illusion..." }, "defeat": { 1: "Team Rocket will be reborn again, and I will rule the world!" @@ -466,7 +466,8 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "magma_boss_maxie_2": { "encounter": { - 1: "You are the final obstacle remaining between me and my goals.\nBrace yourself for my ultimate attack! Fuhahaha!" + 1: `You are the final obstacle remaining between me and my goals. + $Brace yourself for my ultimate attack! Fuhahaha!` }, "victory": { 1: "This... This is not.. Ngh..." @@ -499,7 +500,8 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_1": { "encounter": { - 1: "You were compelled to come here by such vacuous sentimentality.\nI will make you regret paying heed to your heart!" + 1: `You were compelled to come here by such vacuous sentimentality. + $I will make you regret paying heed to your heart!` }, "victory": { 1: "Interesting. And quite curious." @@ -510,7 +512,8 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_2": { "encounter": { - 1: "So we meet again. It seems our fates have become intertwined.\nBut here and now, I will finally break that bond!" + 1: `So we meet again. It seems our fates have become intertwined. + $But here and now, I will finally break that bond!` }, "victory": { 1: "How? How? HOW?!" diff --git a/src/locales/en/menu.ts b/src/locales/en/menu.ts index 9be87724ea8..47d19bd56b6 100644 --- a/src/locales/en/menu.ts +++ b/src/locales/en/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "DISCLAIMER", "disclaimerDescription": "This game is an unfinished product; it might have playability issues (including the potential loss of save data),\n change without notice, and may or may not be updated further or completed.", "choosePokemon": "Choose a Pokémon.", + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", } as const; diff --git a/src/locales/es/bgm-name.ts b/src/locales/es/bgm-name.ts index 2433c66bf98..383a495b7ed 100644 --- a/src/locales/es/bgm-name.ts +++ b/src/locales/es/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH - ¡Vs Calyrex!", "battle_legendary_birds_galar": "SWSH - ¡Vs Aves Legendarias de Galar!", "battle_legendary_ruinous": "SV - ¡Vs Tesoros Funestos!", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "SV - ¡Vs Compatrones!", "battle_legendary_ogerpon": "SV - ¡Vs Ogerpon!", "battle_legendary_terapagos": "SV - ¡Vs Terapagos!", diff --git a/src/locales/es/menu.ts b/src/locales/es/menu.ts index d5030c8d98c..8176af456e1 100644 --- a/src/locales/es/menu.ts +++ b/src/locales/es/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "AVISO", "disclaimerDescription": "Este juego es un producto inacabado; puede tener problemas de jugabilidad (incluyendo la posible pérdida\n de datos de guardado),cambiar sin avisar, y puede o no puede ser actualizado hasta ser completado.", "choosePokemon": "Choose a Pokémon.", + "errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta,\nel juego se reconectará automáticamente.", } as const; diff --git a/src/locales/fr/bgm-name.ts b/src/locales/fr/bgm-name.ts index 3a563df631a..156cedabbfc 100644 --- a/src/locales/fr/bgm-name.ts +++ b/src/locales/fr/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "ÉB - Vs. Sylveroy", "battle_legendary_birds_galar": "ÉB - Vs. Oiseaux Légendaires de Galar", "battle_legendary_ruinous": "ÉV - Vs. Trésors du fléau", + "battle_legendary_kor_mir": "ÉV - Profondeurs de la Zone Zéro (Combat)", "battle_legendary_loyal_three": "ÉV - Vs. Adoramis", "battle_legendary_ogerpon": "ÉV - Vs. Ogerpon", "battle_legendary_terapagos": "ÉV - Vs. Terapagos", diff --git a/src/locales/fr/dialogue.ts b/src/locales/fr/dialogue.ts index 0f69af356ff..ab192963cad 100644 --- a/src/locales/fr/dialogue.ts +++ b/src/locales/fr/dialogue.ts @@ -447,7 +447,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "Mes anciens collaborateurs m’attendent.\nComptes-tu m’en empêcher ?" }, "victory": { - 1: "Comment c'est possible… ?\nLe grand dessein de la Team Rocket n’est plus qu’une illusion…" + 1: "Comment c'est possible… ? Le grand dessein de la Team Rocket n’est plus qu’une illusion…" }, "defeat": { 1: "La Team Rocket renaitra, et je dominerai le monde !" @@ -499,7 +499,7 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_1": { "encounter": { - 1: "Tu t’es senti obligé de venir ici dans un acte vide de sens.\nJe vais te le faire regretter." + 1: "Tu t’es senti obligé de venir ici dans un acte vide de sens. Je vais te le faire regretter." }, "victory": { 1: "Intéressant. Et plutôt curieux." @@ -510,7 +510,7 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_2": { "encounter": { - 1: "Nous y revoilà. Il semblerait que nos destinées soient entremêlées.\nIl est l’heure d’y mettre un terme." + 1: "Nous y revoilà. Il semblerait que nos destinées soient entremêlées. Il est l’heure d’y mettre un terme." }, "victory": { 1: "Comment. Comment ?\nCOMMENT ?!" @@ -2954,7 +2954,7 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Mes anciens collaborateurs m’attendent.\nComptes-tu m’en empêcher ?" }, "victory": { - 1: "Comment c'est possible… ?\nLe grand dessein de la Team Rocket n’est plus qu’une illusion…" + 1: "Comment c'est possible… ? Le grand dessein de la Team Rocket n’est plus qu’une illusion…" }, "defeat": { 1: "La Team Rocket renaitra, et je dominerai le monde !" @@ -3006,7 +3006,7 @@ export const PGFdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_1": { "encounter": { - 1: "Tu t’es sentie obligée de venir ici dans un acte vide de sens.\nJe vais te le faire regretter." + 1: "Tu t’es sentie obligée de venir ici dans un acte vide de sens. Je vais te le faire regretter." }, "victory": { 1: "Intéressant. Et plutôt curieux." @@ -3017,7 +3017,7 @@ export const PGFdialogue: DialogueTranslationEntries = { }, "galactic_boss_cyrus_2": { "encounter": { - 1: "Nous y revoilà. Il semblerait que nos destinées soient entremêlées.\nIl est l’heure d’y mettre un terme." + 1: "Nous y revoilà. Il semblerait que nos destinées soient entremêlées. Il est l’heure d’y mettre un terme." }, "victory": { 1: "Comment. Comment ?\nCOMMENT ?!" diff --git a/src/locales/fr/menu.ts b/src/locales/fr/menu.ts index 8ed1273d6f7..0402bd4bfda 100644 --- a/src/locales/fr/menu.ts +++ b/src/locales/fr/menu.ts @@ -49,4 +49,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "AVERTISSEMENT", "disclaimerDescription": "Ce jeu n’est pas un produit fini et peut contenir des problèmes de jouabilité, dont de possibles pertes de sauvegardes,\ndes modifications sans avertissement et pourrait ou non encore être mis à jour ou terminé.", "choosePokemon": "Sélectionnez un Pokémon.", + "errorServerDown": "Oupsi ! Un problème de connexion au serveur est survenu.\n\nVous pouvez garder cette fenêtre ouverte,\nle jeu se reconnectera automatiquement.", } as const; diff --git a/src/locales/it/bgm-name.ts b/src/locales/it/bgm-name.ts index 1e35a2a095b..77ebb69df32 100644 --- a/src/locales/it/bgm-name.ts +++ b/src/locales/it/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH Calyrex Battle", "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "SV Loyal Three Battle", "battle_legendary_ogerpon": "SV Ogerpon Battle", "battle_legendary_terapagos": "SV Terapagos Battle", @@ -91,7 +92,7 @@ export const bgmName: SimpleTranslationEntries = { "cave": "PMD EoS Sky Peak Cave", "construction_site": "PMD EoS Boulder Quarry", "desert": "PMD EoS Northern Desert", - "dojo": "PMD EoS Marowa Dojo", + "dojo": "PMD EoS Marowak Dojo", "end": "PMD RTDX Sky Tower", "factory": "PMD EoS Concealed Ruins", "fairy_cave": "PMD EoS Star Cave", diff --git a/src/locales/it/menu.ts b/src/locales/it/menu.ts index 91d2a0251ce..3336373a6c3 100644 --- a/src/locales/it/menu.ts +++ b/src/locales/it/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "DISCLAIMER", "disclaimerDescription": "Questo gioco è un prodotto incompleto; si potrebbero riscontrare errori (inclusa la perdita dei dati di salvataggio),\ncambiamenti impercettibili, e non è detto che venga aggiornato nel tempo o mai completato del tutto.", "choosePokemon": "Choose a Pokémon.", + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", } as const; diff --git a/src/locales/ko/ability-trigger.ts b/src/locales/ko/ability-trigger.ts index 7bc38977278..58ba7bf9aa6 100644 --- a/src/locales/ko/ability-trigger.ts +++ b/src/locales/ko/ability-trigger.ts @@ -9,5 +9,5 @@ export const abilityTriggers: SimpleTranslationEntries = { "poisonHeal": "{{pokemonName}}[[는]] {{abilityName}}[[로]]인해\n조금 회복했다.", "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", "windPowerCharged": "{{pokemonName}}[[는]]\n{{moveName}}에 맞아 충전되었다!", - "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "quickDraw": "{{pokemonName}}[[는]]\n퀵드로에 의해 행동이 빨라졌다!", } as const; diff --git a/src/locales/ko/bgm-name.ts b/src/locales/ko/bgm-name.ts index 59ee2d1adf9..4e59e35f14c 100644 --- a/src/locales/ko/bgm-name.ts +++ b/src/locales/ko/bgm-name.ts @@ -62,6 +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_loyal_three": "SV 세벗들 배틀", "battle_legendary_ogerpon": "SV 오거폰 배틀", "battle_legendary_terapagos": "SV 테라파고스 배틀", diff --git a/src/locales/ko/menu.ts b/src/locales/ko/menu.ts index 7e0b61ef992..cf72dab8a37 100644 --- a/src/locales/ko/menu.ts +++ b/src/locales/ko/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "면책 조항", "disclaimerDescription": "이 게임은 완전히 개발되지 않았습니다- (세이브 데이터 소실을 포함) 플레이에 지장을 주는 문제가 생길 수 있으며,\n공지 없이 업데이트가 진행 혹은 중지될 수 있습니다.", "choosePokemon": "포켓몬을 선택하세요.", + "errorServerDown": "서버 연결 중 문제가 발생했습니다.\n\n이 창을 종료하지 않고 두면,\n게임은 자동으로 재접속됩니다.", } as const; diff --git a/src/locales/pt_BR/bgm-name.ts b/src/locales/pt_BR/bgm-name.ts index 1e35a2a095b..77ebb69df32 100644 --- a/src/locales/pt_BR/bgm-name.ts +++ b/src/locales/pt_BR/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH Calyrex Battle", "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "SV Loyal Three Battle", "battle_legendary_ogerpon": "SV Ogerpon Battle", "battle_legendary_terapagos": "SV Terapagos Battle", @@ -91,7 +92,7 @@ export const bgmName: SimpleTranslationEntries = { "cave": "PMD EoS Sky Peak Cave", "construction_site": "PMD EoS Boulder Quarry", "desert": "PMD EoS Northern Desert", - "dojo": "PMD EoS Marowa Dojo", + "dojo": "PMD EoS Marowak Dojo", "end": "PMD RTDX Sky Tower", "factory": "PMD EoS Concealed Ruins", "fairy_cave": "PMD EoS Star Cave", diff --git a/src/locales/pt_BR/menu.ts b/src/locales/pt_BR/menu.ts index ea6c94fe509..a200f2c9abe 100644 --- a/src/locales/pt_BR/menu.ts +++ b/src/locales/pt_BR/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "AVISO", "disclaimerDescription": "Este jogo é um produto inacabado; ele pode ter problemas de jogabilidade (incluindo possíveis\n perdas de dados salvos), sofrer alterações sem aviso prévio e pode ou não ser atualizado ou concluído.", "choosePokemon": "Escolha um Pokémon.", + "errorServerDown": "Opa! Não foi possível conectar-se ao servidor.\n\nVocê pode deixar essa janela aberta,\npois o jogo irá se reconectar automaticamente.", } as const; diff --git a/src/locales/zh_CN/bgm-name.ts b/src/locales/zh_CN/bgm-name.ts index 615511d4605..9b60449dff4 100644 --- a/src/locales/zh_CN/bgm-name.ts +++ b/src/locales/zh_CN/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "剑盾「战斗!蕾冠王」", "battle_legendary_birds_galar": "剑盾「战斗!传说的鸟宝可梦」", "battle_legendary_ruinous": "朱紫「战斗!灾厄宝可梦」", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "朱紫「战斗!宝伴」", "battle_legendary_ogerpon": "朱紫「战斗!厄鬼椪」", "battle_legendary_terapagos": "朱紫「战斗!太乐巴戈斯」", diff --git a/src/locales/zh_CN/menu.ts b/src/locales/zh_CN/menu.ts index 418693cc140..c19c41333e8 100644 --- a/src/locales/zh_CN/menu.ts +++ b/src/locales/zh_CN/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "免责声明", "disclaimerDescription": "这个游戏尚未完成; 可能存在游戏性问题(包括潜在的丢档风险)、\n 不经通知的调整、 未来可能会更新或完成更多内容", "choosePokemon": "选择一只宝可梦。", + "errorServerDown": "糟糕!访问服务器时发生了错误。\n\n你可以保持页面开启,\n游戏会自动重新连接。", } as const; diff --git a/src/locales/zh_CN/modifier-type.ts b/src/locales/zh_CN/modifier-type.ts index 594a85cbdfe..c6322991a93 100644 --- a/src/locales/zh_CN/modifier-type.ts +++ b/src/locales/zh_CN/modifier-type.ts @@ -96,12 +96,12 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "攻击以40/25/12.5%的伤害造成2/3/4次伤害", }, "TmModifierType": { - name: "招式学习器 {{moveId}} - {{moveName}}", + name: "招式学习器\n{{moveId}} - {{moveName}}", description: "教会一只宝可梦{{moveName}}。", }, "TmModifierTypeWithInfo": { - name: "招式学习器 {{moveId}} - {{moveName}}", - description: "教会一只宝可梦{{moveName}}\n(Hold C or Shift for more info)。", + name: "招式学习器\n{{moveId}} - {{moveName}}", + description: "教会一只宝可梦{{moveName}}\n(按住C或者Shift查看更多信息)。", }, "EvolutionItemModifierType": { description: "使某些宝可梦进化。", diff --git a/src/locales/zh_CN/splash-messages.ts b/src/locales/zh_CN/splash-messages.ts index dfa5cc4529d..1cf59635551 100644 --- a/src/locales/zh_CN/splash-messages.ts +++ b/src/locales/zh_CN/splash-messages.ts @@ -1,37 +1,37 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const splashMessages: SimpleTranslationEntries = { - "battlesWon": "Battles Won!", - "joinTheDiscord": "Join the Discord!", - "infiniteLevels": "Infinite Levels!", - "everythingStacks": "Everything Stacks!", - "optionalSaveScumming": "Optional Save Scumming!", - "biomes": "35 Biomes!", - "openSource": "Open Source!", - "playWithSpeed": "Play with 5x Speed!", - "liveBugTesting": "Live Bug Testing!", - "heavyInfluence": "Heavy RoR2 Influence!", - "pokemonRiskAndPokemonRain": "Pokémon Risk and Pokémon Rain!", - "nowWithMoreSalt": "Now with 33% More Salt!", - "infiniteFusionAtHome": "Infinite Fusion at Home!", - "brokenEggMoves": "Broken Egg Moves!", - "magnificent": "Magnificent!", - "mubstitute": "Mubstitute!", - "thatsCrazy": "That\'s Crazy!", - "oranceJuice": "Orance Juice!", - "questionableBalancing": "Questionable Balancing!", - "coolShaders": "Cool Shaders!", - "aiFree": "AI-Free!", - "suddenDifficultySpikes": "Sudden Difficulty Spikes!", - "basedOnAnUnfinishedFlashGame": "Based on an Unfinished Flash Game!", - "moreAddictiveThanIntended": "More Addictive than Intended!", - "mostlyConsistentSeeds": "Mostly Consistent Seeds!", - "achievementPointsDontDoAnything": "Achievement Points Don\'t Do Anything!", - "youDoNotStartAtLevel": "You Do Not Start at Level 2000!", - "dontTalkAboutTheManaphyEggIncident": "Don\'t Talk About the Manaphy Egg Incident!", - "alsoTryPokengine": "Also Try Pokéngine!", - "alsoTryEmeraldRogue": "Also Try Emerald Rogue!", - "alsoTryRadicalRed": "Also Try Radical Red!", - "eeveeExpo": "Eevee Expo!", + "battlesWon": "胜利场数!", + "joinTheDiscord": "加入Discord!", + "infiniteLevels": "无限等级!", + "everythingStacks": "叠加一切!", + "optionalSaveScumming": "可选SL!", + "biomes": "35种生态!", + "openSource": "开源!", + "playWithSpeed": "五倍速游玩!", + "liveBugTesting": "即时漏洞测试!", + "heavyInfluence": "深受雨中冒险2影响!", + "pokemonRiskAndPokemonRain": "宝可梦雨中冒险!", + "nowWithMoreSalt": "增加33%的盐!", + "infiniteFusionAtHome": "家庭版无限融合!", + "brokenEggMoves": "超模的蛋招式!", + "magnificent": "华丽!", + "mubstitute": "替身!", + "thatsCrazy": "疯狂!", + "oranceJuice": "橙汁!", + "questionableBalancing": "值得质疑的平衡性!", + "coolShaders": "炫酷的着色器!", + "aiFree": "不含AI!", + "suddenDifficultySpikes": "突然上升的难度曲线!", + "basedOnAnUnfinishedFlashGame": "基于未完成的flash游戏!", + "moreAddictiveThanIntended": "比预期的更上瘾!", + "mostlyConsistentSeeds": "多数情况下一致的种子!", + "achievementPointsDontDoAnything": "成就点没有任何用处!", + "youDoNotStartAtLevel": "你不能从2000级开始!", + "dontTalkAboutTheManaphyEggIncident": "禁止谈论玛纳霏蛋事件!", + "alsoTryPokengine": "也试试Pokéngine!", + "alsoTryEmeraldRogue": "也试试绿宝石肉鸽!", + "alsoTryRadicalRed": "也试试激进红!", + "eeveeExpo": "伊布博览会!", "ynoproject": "YNOproject!", } as const; diff --git a/src/locales/zh_TW/bgm-name.ts b/src/locales/zh_TW/bgm-name.ts index 1e35a2a095b..77ebb69df32 100644 --- a/src/locales/zh_TW/bgm-name.ts +++ b/src/locales/zh_TW/bgm-name.ts @@ -62,6 +62,7 @@ export const bgmName: SimpleTranslationEntries = { "battle_legendary_calyrex": "SWSH Calyrex Battle", "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", "battle_legendary_loyal_three": "SV Loyal Three Battle", "battle_legendary_ogerpon": "SV Ogerpon Battle", "battle_legendary_terapagos": "SV Terapagos Battle", @@ -91,7 +92,7 @@ export const bgmName: SimpleTranslationEntries = { "cave": "PMD EoS Sky Peak Cave", "construction_site": "PMD EoS Boulder Quarry", "desert": "PMD EoS Northern Desert", - "dojo": "PMD EoS Marowa Dojo", + "dojo": "PMD EoS Marowak Dojo", "end": "PMD RTDX Sky Tower", "factory": "PMD EoS Concealed Ruins", "fairy_cave": "PMD EoS Star Cave", diff --git a/src/locales/zh_TW/menu.ts b/src/locales/zh_TW/menu.ts index 4ecaf8d4d9d..6d89bc256c6 100644 --- a/src/locales/zh_TW/menu.ts +++ b/src/locales/zh_TW/menu.ts @@ -54,4 +54,5 @@ export const menu: SimpleTranslationEntries = { "disclaimer": "DISCLAIMER", "disclaimerDescription": "This game is an unfinished product; it might have playability issues (including the potential loss of save data),\n change without notice, and may or may not be updated further or completed.", "choosePokemon": "Choose a Pokémon.", + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", } as const; diff --git a/src/phases.ts b/src/phases.ts index fcaf3ca4160..263a1673671 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -868,9 +868,11 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer.loadAssets().then(() => battle.trainer.initSprite())); } else { + // This block only applies for double battles to init the boss segments (idk why it's split up like this) if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { for (const enemyPokemon of battle.enemyParty) { - if (enemyPokemon.isBoss()) { + // If the enemy pokemon is a boss and wasn't populated from data source, then set it up + if (enemyPokemon.isBoss() && !enemyPokemon.isPopulatedFromDataSource) { enemyPokemon.setBoss(true, Math.ceil(enemyPokemon.bossSegments * (enemyPokemon.getSpeciesForm().baseTotal / totalBst))); enemyPokemon.initBattleInfo(); } diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index e74155b8bdf..bfb8f45a11f 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -28,7 +28,7 @@ describe("Abilities - Dry Skin", () => { vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.DRY_SKIN); vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.CHARMANDER); - vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH); + vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.UNNERVE); vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.CHANDELURE); }); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 4e9a0573505..c4f8126d7a0 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -382,7 +382,7 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.turnData.hitCount).toBe(2); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to(MoveEndPhase, false); expect(enemyPokemon.hp).toBe(Math.ceil(enemyStartingHp * 0.25)); }, TIMEOUT @@ -413,7 +413,7 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.turnData.hitCount).toBe(2); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to(MoveEndPhase, false); expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); }, TIMEOUT diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts new file mode 100644 index 00000000000..b46efecab79 --- /dev/null +++ b/src/test/moves/make_it_rain.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 { + CommandPhase, + MoveEndPhase, + StatChangePhase, +} from "#app/phases"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import { BattleStat } from "#app/data/battle-stat.js"; + +describe("Moves - Make It Rain", () => { + 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, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.MAKE_IT_RAIN, Moves.SPLASH]); + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA); + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); + vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); + }); + + it("should only reduce Sp. Atk. once in a double battle", async () => { + await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + + const playerPokemon = game.scene.getPlayerField(); + expect(playerPokemon.length).toBe(2); + playerPokemon.forEach(p => expect(p).toBeDefined()); + + const enemyPokemon = game.scene.getEnemyField(); + expect(enemyPokemon.length).toBe(2); + enemyPokemon.forEach(p => expect(p).toBeDefined()); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); + + await game.phaseInterceptor.to(CommandPhase); + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(MoveEndPhase); + + expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); + }); + + it("should apply effects even if the target faints", async () => { + vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(1); // ensures the enemy will faint + vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(false); + vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + + await game.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon(); + expect(playerPokemon).toBeDefined(); + + const enemyPokemon = game.scene.getEnemyPokemon(); + expect(enemyPokemon).toBeDefined(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); + + await game.phaseInterceptor.to(StatChangePhase); + + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1); + }); +}); diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index cddcde2a1b3..2f6e1c08832 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -4,6 +4,7 @@ import { addTextObject, TextStyle } from "./text"; import { Mode } from "./ui"; import { updateUserInfo } from "#app/account"; import * as Utils from "#app/utils"; +import i18next from "i18next"; export default class UnavailableModalUiHandler extends ModalUiHandler { private reconnectTimer: NodeJS.Timeout; @@ -43,7 +44,7 @@ export default class UnavailableModalUiHandler extends ModalUiHandler { setup(): void { super.setup(); - const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", TextStyle.WINDOW, { fontSize: "48px", align: "center" }); + const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, i18next.t("menu:errorServerDown"), TextStyle.WINDOW, { fontSize: "48px", align: "center" }); label.setOrigin(0.5, 0.5); this.modalContainer.add(label);