diff --git a/index.css b/index.css index 9ca2cd60dff..c0791259002 100644 --- a/index.css +++ b/index.css @@ -146,8 +146,8 @@ body { margin-left: 10%; } -#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_ACCESSIBILITY']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn, -#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_ACCESSIBILITY']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer +#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn, +#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer { display: none; } diff --git a/public/images/ui/dawn_icon.png b/public/images/ui/dawn_icon.png deleted file mode 100644 index e04e6024aa0..00000000000 Binary files a/public/images/ui/dawn_icon.png and /dev/null differ diff --git a/public/images/ui/dawn_icon_bg.png b/public/images/ui/dawn_icon_bg.png new file mode 100644 index 00000000000..2983c2744a3 Binary files /dev/null and b/public/images/ui/dawn_icon_bg.png differ diff --git a/public/images/ui/dawn_icon_fg.png b/public/images/ui/dawn_icon_fg.png new file mode 100644 index 00000000000..e6c195bd371 Binary files /dev/null and b/public/images/ui/dawn_icon_fg.png differ diff --git a/public/images/ui/dawn_icon_mg.png b/public/images/ui/dawn_icon_mg.png new file mode 100644 index 00000000000..ff32b4418aa Binary files /dev/null and b/public/images/ui/dawn_icon_mg.png differ diff --git a/public/images/ui/day_icon.png b/public/images/ui/day_icon.png deleted file mode 100644 index fe41acffd9c..00000000000 Binary files a/public/images/ui/day_icon.png and /dev/null differ diff --git a/public/images/ui/day_icon_bg.png b/public/images/ui/day_icon_bg.png new file mode 100644 index 00000000000..9028e0309f7 Binary files /dev/null and b/public/images/ui/day_icon_bg.png differ diff --git a/public/images/ui/day_icon_fg.png b/public/images/ui/day_icon_fg.png new file mode 100644 index 00000000000..523f7b1be7e Binary files /dev/null and b/public/images/ui/day_icon_fg.png differ diff --git a/public/images/ui/day_icon_mg.png b/public/images/ui/day_icon_mg.png new file mode 100644 index 00000000000..7a236fa48d3 Binary files /dev/null and b/public/images/ui/day_icon_mg.png differ diff --git a/public/images/ui/dusk_icon.png b/public/images/ui/dusk_icon.png deleted file mode 100644 index e848fa31345..00000000000 Binary files a/public/images/ui/dusk_icon.png and /dev/null differ diff --git a/public/images/ui/dusk_icon_bg.png b/public/images/ui/dusk_icon_bg.png new file mode 100644 index 00000000000..57d22ec2b21 Binary files /dev/null and b/public/images/ui/dusk_icon_bg.png differ diff --git a/public/images/ui/dusk_icon_fg.png b/public/images/ui/dusk_icon_fg.png new file mode 100644 index 00000000000..e0ca1bce79b Binary files /dev/null and b/public/images/ui/dusk_icon_fg.png differ diff --git a/public/images/ui/dusk_icon_mg.png b/public/images/ui/dusk_icon_mg.png new file mode 100644 index 00000000000..4e6a880b37f Binary files /dev/null and b/public/images/ui/dusk_icon_mg.png differ diff --git a/public/images/ui/legacy/dawn_icon.png b/public/images/ui/legacy/dawn_icon.png deleted file mode 100644 index eb24a799ab9..00000000000 Binary files a/public/images/ui/legacy/dawn_icon.png and /dev/null differ diff --git a/public/images/ui/legacy/dawn_icon_bg.png b/public/images/ui/legacy/dawn_icon_bg.png new file mode 100644 index 00000000000..87c4d75cd94 Binary files /dev/null and b/public/images/ui/legacy/dawn_icon_bg.png differ diff --git a/public/images/ui/legacy/dawn_icon_fg.png b/public/images/ui/legacy/dawn_icon_fg.png new file mode 100644 index 00000000000..db985f953d3 Binary files /dev/null and b/public/images/ui/legacy/dawn_icon_fg.png differ diff --git a/public/images/ui/legacy/dawn_icon_mg.png b/public/images/ui/legacy/dawn_icon_mg.png new file mode 100644 index 00000000000..442cb1b674c Binary files /dev/null and b/public/images/ui/legacy/dawn_icon_mg.png differ diff --git a/public/images/ui/legacy/day_icon.png b/public/images/ui/legacy/day_icon.png deleted file mode 100644 index 310ba50dcf3..00000000000 Binary files a/public/images/ui/legacy/day_icon.png and /dev/null differ diff --git a/public/images/ui/legacy/day_icon_bg.png b/public/images/ui/legacy/day_icon_bg.png new file mode 100644 index 00000000000..3db0dab4589 Binary files /dev/null and b/public/images/ui/legacy/day_icon_bg.png differ diff --git a/public/images/ui/legacy/day_icon_fg.png b/public/images/ui/legacy/day_icon_fg.png new file mode 100644 index 00000000000..657c82e5dbd Binary files /dev/null and b/public/images/ui/legacy/day_icon_fg.png differ diff --git a/public/images/ui/legacy/day_icon_mg.png b/public/images/ui/legacy/day_icon_mg.png new file mode 100644 index 00000000000..90fce4a761c Binary files /dev/null and b/public/images/ui/legacy/day_icon_mg.png differ diff --git a/public/images/ui/legacy/dusk_icon.png b/public/images/ui/legacy/dusk_icon.png deleted file mode 100644 index f383ebf5246..00000000000 Binary files a/public/images/ui/legacy/dusk_icon.png and /dev/null differ diff --git a/public/images/ui/legacy/dusk_icon_bg.png b/public/images/ui/legacy/dusk_icon_bg.png new file mode 100644 index 00000000000..7610a2e67f8 Binary files /dev/null and b/public/images/ui/legacy/dusk_icon_bg.png differ diff --git a/public/images/ui/legacy/dusk_icon_fg.png b/public/images/ui/legacy/dusk_icon_fg.png new file mode 100644 index 00000000000..d4bbb98fdd1 Binary files /dev/null and b/public/images/ui/legacy/dusk_icon_fg.png differ diff --git a/public/images/ui/legacy/dusk_icon_mg.png b/public/images/ui/legacy/dusk_icon_mg.png new file mode 100644 index 00000000000..dc603f8ca79 Binary files /dev/null and b/public/images/ui/legacy/dusk_icon_mg.png differ diff --git a/public/images/ui/legacy/night_icon.png b/public/images/ui/legacy/night_icon.png deleted file mode 100644 index 1796081c05b..00000000000 Binary files a/public/images/ui/legacy/night_icon.png and /dev/null differ diff --git a/public/images/ui/legacy/night_icon_bg.png b/public/images/ui/legacy/night_icon_bg.png new file mode 100644 index 00000000000..c32ee1ebd2a Binary files /dev/null and b/public/images/ui/legacy/night_icon_bg.png differ diff --git a/public/images/ui/legacy/night_icon_fg.png b/public/images/ui/legacy/night_icon_fg.png new file mode 100644 index 00000000000..737cca3305e Binary files /dev/null and b/public/images/ui/legacy/night_icon_fg.png differ diff --git a/public/images/ui/legacy/night_icon_mg.png b/public/images/ui/legacy/night_icon_mg.png new file mode 100644 index 00000000000..f5fdff51cc8 Binary files /dev/null and b/public/images/ui/legacy/night_icon_mg.png differ diff --git a/public/images/ui/night_icon.png b/public/images/ui/night_icon.png deleted file mode 100644 index 1782303f73d..00000000000 Binary files a/public/images/ui/night_icon.png and /dev/null differ diff --git a/public/images/ui/night_icon_bg.png b/public/images/ui/night_icon_bg.png new file mode 100644 index 00000000000..1ce4007d54c Binary files /dev/null and b/public/images/ui/night_icon_bg.png differ diff --git a/public/images/ui/night_icon_fg.png b/public/images/ui/night_icon_fg.png new file mode 100644 index 00000000000..78741584312 Binary files /dev/null and b/public/images/ui/night_icon_fg.png differ diff --git a/public/images/ui/night_icon_mg.png b/public/images/ui/night_icon_mg.png new file mode 100644 index 00000000000..5583c8f799d Binary files /dev/null and b/public/images/ui/night_icon_mg.png differ diff --git a/public/images/ui/summary_bg.png b/public/images/ui/summary_bg.png index 1fa1d95fcc3..b77cdadd2ac 100644 Binary files a/public/images/ui/summary_bg.png and b/public/images/ui/summary_bg.png differ diff --git a/src/battle-scene-events.ts b/src/battle-scene-events.ts index 74fac97d2b7..aaeb590f8ba 100644 --- a/src/battle-scene-events.ts +++ b/src/battle-scene-events.ts @@ -20,6 +20,11 @@ export enum BattleSceneEventType { */ BERRY_USED = "onBerryUsed", + /** + * Triggers at the start of each new encounter + * @see {@linkcode EncounterPhaseEvent} + */ + ENCOUNTER_PHASE = "onEncounterPhase", /** * Triggers on the first turn of a new battle * @see {@linkcode TurnInitEvent} @@ -85,6 +90,15 @@ export class BerryUsedEvent extends Event { } } +/** + * Container class for {@linkcode BattleSceneEventType.ENCOUNTER_PHASE} events + * @extends Event +*/ +export class EncounterPhaseEvent extends Event { + constructor() { + super(BattleSceneEventType.ENCOUNTER_PHASE); + } +} /** * Container class for {@linkcode BattleSceneEventType.TURN_INIT} events * @extends Event diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 1629c47e95b..c2b766b9b55 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -11,7 +11,8 @@ import { Phase } from "./phase"; import { initGameSpeed } from "./system/game-speed"; import { Biome } from "./data/enums/biome"; import { Arena, ArenaBase } from "./field/arena"; -import { GameData, PlayerGender } from "./system/game-data"; +import { GameData } from "./system/game-data"; +import { PlayerGender } from "./data/enums/player-gender"; import { TextStyle, addTextObject } from "./ui/text"; import { Moves } from "./data/enums/moves"; import { allMoves } from "./data/move"; @@ -59,6 +60,7 @@ import {UiInputs} from "./ui-inputs"; import { MoneyFormat } from "./enums/money-format"; import { NewArenaEvent } from "./battle-scene-events"; import ArenaFlyout from "./ui/arena-flyout"; +import { EaseType } from "./ui/enums/ease-type"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -70,15 +72,20 @@ const expSpriteKeys: string[] = []; export let starterColors: StarterColors; interface StarterColors { - [key: string]: [string, string] + [key: string]: [string, string] } export interface PokeballCounts { - [pb: string]: integer; + [pb: string]: integer; } export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound | Phaser.Sound.NoAudioSound; +export interface InfoToggle { + toggleInfo(force?: boolean): void; + isActive(): boolean; +} + export default class BattleScene extends SceneBase { public rexUI: UIPlugin; public inputController: InputsController; @@ -94,8 +101,11 @@ export default class BattleScene extends SceneBase { public reroll: boolean = false; public showMovesetFlyout: boolean = true; public showArenaFlyout: boolean = true; + public showTimeOfDayWidget: boolean = true; + public timeOfDayAnimation: EaseType = EaseType.NONE; public showLevelUpStats: boolean = true; public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1"; + public enableMoveInfo: boolean = true; public enableRetries: boolean = false; /** * Determines the condition for a notification should be shown for Candy Upgrades @@ -120,22 +130,28 @@ export default class BattleScene extends SceneBase { public skipSeenDialogues: boolean = false; /** - * Defines the experience gain display mode. - * - * @remarks - * The `expParty` can have several modes: - * - `0` - Default: The normal experience gain display, nothing changed. - * - `1` - Level Up Notification: Displays the level up in the small frame instead of a message. - * - `2` - Skip: No level up frame nor message. - * - * Modes `1` and `2` are still compatible with stats display, level up, new move, etc. - * @default 0 - Uses the default normal experience gain display. - */ + * Defines the experience gain display mode. + * + * @remarks + * The `expParty` can have several modes: + * - `0` - Default: The normal experience gain display, nothing changed. + * - `1` - Level Up Notification: Displays the level up in the small frame instead of a message. + * - `2` - Skip: No level up frame nor message. + * + * Modes `1` and `2` are still compatible with stats display, level up, new move, etc. + * @default 0 - Uses the default normal experience gain display. + */ public expParty: integer = 0; public hpBarSpeed: integer = 0; public fusionPaletteSwaps: boolean = true; public enableTouchControls: boolean = false; public enableVibration: boolean = false; + /** + * Determines the selected battle style. + * - 0 = 'Shift' + * - 1 = 'Set' - The option to switch the active pokemon at the start of a battle will not display. + */ + public battleStyle: integer = 0; public disableMenu: boolean = false; @@ -209,6 +225,8 @@ export default class BattleScene extends SceneBase { public rngSeedOverride: string = ""; public rngOffset: integer = 0; + private infoToggles: InfoToggle[] = []; + /** * Allows subscribers to listen for events * @@ -515,7 +533,7 @@ export default class BattleScene extends SceneBase { this.playTimeTimer = this.time.addEvent({ delay: Utils.fixedInt(1000), repeat: -1, - callback: () => { + callback: () => { if (this.gameData) { this.gameData.gameStats.playTime++; } @@ -599,25 +617,25 @@ export default class BattleScene extends SceneBase { /*const loadPokemonAssets: Promise[] = []; - for (let s of Object.keys(speciesStarters)) { - const species = getPokemonSpecies(parseInt(s)); - loadPokemonAssets.push(species.loadAssets(this, false, 0, false)); - } + for (let s of Object.keys(speciesStarters)) { + const species = getPokemonSpecies(parseInt(s)); + loadPokemonAssets.push(species.loadAssets(this, false, 0, false)); + } - Promise.all(loadPokemonAssets).then(() => { - const starterCandyColors = {}; - const rgbaToHexFunc = (r, g, b) => [r, g, b].map(x => x.toString(16).padStart(2, '0')).join(''); + Promise.all(loadPokemonAssets).then(() => { + const starterCandyColors = {}; + const rgbaToHexFunc = (r, g, b) => [r, g, b].map(x => x.toString(16).padStart(2, '0')).join(''); - for (let s of Object.keys(speciesStarters)) { - const species = getPokemonSpecies(parseInt(s)); + for (let s of Object.keys(speciesStarters)) { + const species = getPokemonSpecies(parseInt(s)); - starterCandyColors[species.speciesId] = species.generateCandyColors(this).map(c => rgbaToHexFunc(c[0], c[1], c[2])); - } + starterCandyColors[species.speciesId] = species.generateCandyColors(this).map(c => rgbaToHexFunc(c[0], c[1], c[2])); + } - console.log(JSON.stringify(starterCandyColors)); + console.log(JSON.stringify(starterCandyColors)); - resolve(); - });*/ + resolve(); + });*/ resolve(); }); @@ -682,6 +700,16 @@ export default class BattleScene extends SceneBase { : ret; } + // store info toggles to be accessible by the ui + addInfoToggle(infoToggle: InfoToggle): void { + this.infoToggles.push(infoToggle); + } + + // return the stored info toggles; used by ui-inputs + getInfoToggles(activeOnly: boolean = false): InfoToggle[] { + return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; + } + getPokemonById(pokemonId: integer): Pokemon { const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); @@ -728,7 +756,7 @@ export default class BattleScene extends SceneBase { const container = this.add.container(x, y); const icon = this.add.sprite(0, 0, pokemon.getIconAtlasKey(ignoreOverride)); - icon.setFrame(pokemon.getIconId(true)); + icon.setFrame(pokemon.getIconId(true)); // Temporary fix to show pokemon's default icon if variant icon doesn't exist if (icon.frame.name !== pokemon.getIconId(true)) { console.log(`${pokemon.name}'s variant icon does not exist. Replacing with default.`); @@ -1314,6 +1342,14 @@ export default class BattleScene extends SceneBase { }); } + showEnemyModifierBar(): void { + this.enemyModifierBar.setVisible(true); + } + + hideEnemyModifierBar(): void { + this.enemyModifierBar.setVisible(false); + } + updateBiomeWaveText(): void { const isBoss = !(this.currentBattle.waveIndex % 10); const biomeString: string = getBiomeName(this.arena.biomeType); @@ -1328,7 +1364,7 @@ export default class BattleScene extends SceneBase { return; } const formattedMoney = - this.moneyFormat === MoneyFormat.ABBREVIATED ? Utils.formatFancyLargeNumber(this.money, 3) : this.money.toLocaleString(); + this.moneyFormat === MoneyFormat.ABBREVIATED ? Utils.formatFancyLargeNumber(this.money, 3) : this.money.toLocaleString(); this.moneyText.setText(`₽${formattedMoney}`); this.fieldUI.moveAbove(this.moneyText, this.luckText); if (forceVisible) { @@ -1419,7 +1455,7 @@ export default class BattleScene extends SceneBase { randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean, speciesFilter?: PokemonSpeciesFilter, filterAllEvolutions?: boolean): PokemonSpecies { if (fromArenaPool) { - return this.arena.randomSpecies(waveIndex, level); + return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party)); } const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => { if (!filterAllEvolutions) { @@ -1978,7 +2014,7 @@ export default class BattleScene extends SceneBase { const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier; newItemModifier.pokemonId = target.id; const matchingModifier = target.scene.findModifier(m => m instanceof PokemonHeldItemModifier - && (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier; + && (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier; let removeOld = true; if (matchingModifier) { const maxStackCount = matchingModifier.getMaxStackCount(target.scene); @@ -2082,8 +2118,8 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PersistentModifier type - */ + * Removes all modifiers from enemy of PersistentModifier type + */ clearEnemyModifiers(): void { const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PersistentModifier); for (const m of modifiersToRemove) { @@ -2093,8 +2129,8 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PokemonHeldItemModifier type - */ + * Removes all modifiers from enemy of PokemonHeldItemModifier type + */ clearEnemyHeldItemModifiers(): void { const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier); for (const m of modifiersToRemove) { diff --git a/src/battle.ts b/src/battle.ts index dd5feee0da4..4f941eaa653 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -8,7 +8,7 @@ import { Moves } from "./data/enums/moves"; import { TrainerType } from "./data/enums/trainer-type"; import { GameMode } from "./game-mode"; import { BattleSpec } from "./enums/battle-spec"; -import { PlayerGender } from "./system/game-data"; +import { PlayerGender } from "./data/enums/player-gender"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; import {trainerConfigs} from "#app/data/trainer-config"; diff --git a/src/data/ability.ts b/src/data/ability.ts index 31f41492ba7..4bb4f36350e 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -3,7 +3,7 @@ import { Type } from "./type"; import * as Utils from "../utils"; import { BattleStat, getBattleStatName } from "./battle-stat"; import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; -import { getPokemonMessage, getPokemonPrefix } from "../messages"; +import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; import { BattlerTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; @@ -23,7 +23,7 @@ import { Command } from "../ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; import { getPokeballName } from "./pokeball"; import { Species } from "./enums/species"; -import {BattlerIndex} from "#app/battle"; +import { BattlerIndex } from "#app/battle"; export class Ability implements Localizable { public id: Abilities; @@ -156,7 +156,7 @@ export class BlockRecoilDamageAttr extends AbAttr { } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { - return i18next.t("abilityTriggers:blockRecoilDamage", {pokemonName: `${getPokemonPrefix(pokemon)}${pokemon.name}`, abilityName: abilityName}); + return i18next.t("abilityTriggers:blockRecoilDamage", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName}); } } @@ -878,7 +878,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:perishBody", {pokemonName: `${getPokemonPrefix(pokemon)}${pokemon.name}`, abilityName: abilityName}); + return i18next.t("abilityTriggers:perishBody", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName}); } } @@ -2462,7 +2462,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { * @returns Returns true if healed from status, false if not */ applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - if (this.effects.includes(pokemon.status.effect)) { + if (this.effects.includes(pokemon.status?.effect)) { if (pokemon.getMaxHp() !== pokemon.hp) { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; @@ -2666,7 +2666,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { for (const opp of pokemon.getOpponents()) { if (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) { opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); - pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: `${getPokemonPrefix(opp)}${opp.name}`})); + pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); hadEffect = true; } @@ -2764,8 +2764,12 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @return true if the Dancer ability was resolved */ applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise { + // List of tags that prevent the Dancer from replicating the move + const forbiddenTags = [BattlerTagType.FLYING, BattlerTagType.UNDERWATER, + BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN]; // The move to replicate cannot come from the Dancer - if (source.getBattlerIndex() !== dancer.getBattlerIndex()) { + if (source.getBattlerIndex() !== dancer.getBattlerIndex() + && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))) { // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); @@ -2774,8 +2778,9 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); } + return true; } - return true; + return false; } /** diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 63ac9fd895d..50d4b62decb 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,6 +1,6 @@ import { CommonAnim, CommonBattleAnim } from "./battle-anims"; import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; -import { getPokemonMessage, getPokemonPrefix } from "../messages"; +import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; import Pokemon, { MoveResult, HitResult } from "../field/pokemon"; import { Stat, getStatName } from "./pokemon-stat"; import { StatusEffect } from "./status-effect"; @@ -200,6 +200,9 @@ export class InterruptedTag extends BattlerTag { } } +/** + * BattlerTag that represents the {@link https://bulbapedia.bulbagarden.net/wiki/Confusion_(status_condition)} + */ export class ConfusedTag extends BattlerTag { constructor(turnCount: integer, sourceMove: Moves) { super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove); @@ -235,7 +238,8 @@ export class ConfusedTag extends BattlerTag { pokemon.scene.queueMessage(getPokemonMessage(pokemon, " is\nconfused!")); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); - if (pokemon.randSeedInt(3)) { + // 1/3 chance of hitting self with a 40 base power move + if (pokemon.randSeedInt(3) === 0) { const atk = pokemon.getBattleStat(Stat.ATK); const def = pokemon.getBattleStat(Stat.DEF); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); @@ -799,7 +803,7 @@ export class ThunderCageTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` trapped\n${getPokemonPrefix(pokemon).toLowerCase()}${pokemon.name}!`); + return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` trapped\n${getPokemonNameWithAffix(pokemon)}!`); } } @@ -809,7 +813,7 @@ export class InfestationTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ` has been afflicted \nwith an infestation by ${getPokemonPrefix(pokemon.scene.getPokemonById(this.sourceId))}${pokemon.scene.getPokemonById(this.sourceId).name}!`); + return getPokemonMessage(pokemon, ` has been afflicted \nwith an infestation by ${getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId))}!`); } } diff --git a/src/data/enums/game-data-type.ts b/src/data/enums/game-data-type.ts new file mode 100644 index 00000000000..179817fe5be --- /dev/null +++ b/src/data/enums/game-data-type.ts @@ -0,0 +1,10 @@ +/** + * enum for the game data types + */ +export enum GameDataType { + SYSTEM, + SESSION, + SETTINGS, + TUTORIALS, + SEEN_DIALOGUES +} diff --git a/src/data/enums/passive.ts b/src/data/enums/passive.ts new file mode 100644 index 00000000000..b9c2dacd463 --- /dev/null +++ b/src/data/enums/passive.ts @@ -0,0 +1,7 @@ +/** + * enum for passive + */ +export enum Passive { + UNLOCKED = 1, + ENABLED = 2 +} diff --git a/src/data/enums/player-gender.ts b/src/data/enums/player-gender.ts new file mode 100644 index 00000000000..b6cd550b1f2 --- /dev/null +++ b/src/data/enums/player-gender.ts @@ -0,0 +1,8 @@ +/** + * enum for the players gender + */ +export enum PlayerGender { + UNSET, + MALE, + FEMALE +} diff --git a/src/data/pokemon-level-moves.ts b/src/data/pokemon-level-moves.ts index 18d83f452cf..69c661e8d3d 100644 --- a/src/data/pokemon-level-moves.ts +++ b/src/data/pokemon-level-moves.ts @@ -18539,12 +18539,14 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 8, Moves.DOUBLE_TEAM ], [ 12, Moves.ELECTRO_BALL ], [ 16, Moves.FEINT ], - [ 20, Moves.SPARK ], + [ 20, Moves.ZIPPY_ZAP ], //Custom [ 24, Moves.AGILITY ], [ 28, Moves.IRON_TAIL ], [ 32, Moves.DISCHARGE ], + [ 34, Moves.FLOATY_FALL ], //Custom [ 36, Moves.THUNDERBOLT ], [ 40, Moves.LIGHT_SCREEN ], + [ 42, Moves.SPLISHY_SPLASH ], //Custom [ 44, Moves.THUNDER ], [ 48, Moves.PIKA_PAPOW ], ], diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 793f4ff9927..562a79ea333 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -918,12 +918,12 @@ export function initSpecies() { new PokemonSpecies(Species.PIKACHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, GrowthRate.MEDIUM_FAST, 50, true, true, new PokemonForm("Normal", "", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), new PokemonForm("Partner", "partner", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, "", true), - new PokemonForm("Cosplay", "cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), - new PokemonForm("Cool Cosplay", "cool-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), - new PokemonForm("Beauty Cosplay", "beauty-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), - new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), - new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), - new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true), + new PokemonForm("Cosplay", "cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom + new PokemonForm("Cool Cosplay", "cool-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom + new PokemonForm("Beauty Cosplay", "beauty-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom + new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom + new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom + new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 420, 45, 60, 65, 100, 75, 75, 190, 50, 112), ), new PokemonSpecies(Species.RAICHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.8, 30, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 485, 60, 90, 55, 90, 80, 110, 75, 50, 243, GrowthRate.MEDIUM_FAST, 50, true), diff --git a/src/data/weather.ts b/src/data/weather.ts index f2b4136f51c..1df94b02da1 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -1,5 +1,5 @@ import { Biome } from "./enums/biome"; -import { getPokemonMessage, getPokemonPrefix } from "../messages"; +import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; import Pokemon from "../field/pokemon"; import { Type } from "./type"; import Move, { AttackMove } from "./move"; @@ -180,9 +180,9 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string { export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string { switch (weatherType) { case WeatherType.SANDSTORM: - return i18next.t("weather:sandstormDamageMessage", {pokemonPrefix: getPokemonPrefix(pokemon), pokemonName: pokemon.name}); + return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}); case WeatherType.HAIL: - return i18next.t("weather:hailDamageMessage", {pokemonPrefix: getPokemonPrefix(pokemon), pokemonName: pokemon.name}); + return i18next.t("weather:hailDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}); } return null; diff --git a/src/field/arena.ts b/src/field/arena.ts index f6390e40db5..6999eb39785 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -68,14 +68,20 @@ export class Arena { } } - randomSpecies(waveIndex: integer, level: integer, attempt?: integer): PokemonSpecies { + randomSpecies(waveIndex: integer, level: integer, attempt?: integer, luckValue?: integer): PokemonSpecies { const overrideSpecies = this.scene.gameMode.getOverrideSpecies(waveIndex); if (overrideSpecies) { return overrideSpecies; } const isBoss = !!this.scene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length && (this.biomeType !== Biome.END || this.scene.gameMode.isClassic || this.scene.gameMode.isWaveFinal(waveIndex)); - const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64); + const randVal = isBoss ? 64 : 512; + // luck influences encounter rarity + let luckModifier = 0; + if (typeof luckValue !== "undefined") { + luckModifier = luckValue * (isBoss ? 0.5 : 2); + } + const tierValue = Utils.randSeedInt(randVal - luckModifier); let tier = !isBoss ? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE : tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 2b8f28c4826..86909f056aa 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -211,8 +211,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.generateFusionSpecies(); } } - this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0); + this.fusionLuck = this.luck; } this.generateName(); @@ -1168,6 +1168,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.fusionSpecies) { const evolutionLevelMoves = levelMoves.slice(0, Math.max(levelMoves.findIndex(lm => !!lm[0]), 0)); const fusionLevelMoves = this.getFusionSpeciesForm(true).getLevelMoves(); + const fusionEvolutionLevelMoves = fusionLevelMoves.slice(0, Math.max(fusionLevelMoves.findIndex(flm => !!flm[0]), 0)); const newLevelMoves: LevelMoves = []; while (levelMoves.length && levelMoves[0][0] < startingLevel) { levelMoves.shift(); @@ -1179,6 +1180,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { for (const elm of evolutionLevelMoves.reverse()) { levelMoves.unshift(elm); } + for (const felm of fusionEvolutionLevelMoves.reverse()) { + fusionLevelMoves.unshift(felm); + } } for (let l = includeEvolutionMoves ? 0 : startingLevel; l <= this.level; l++) { if (l === 1 && startingLevel > 1) { @@ -1273,7 +1277,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns the shiny variant */ generateVariant(): Variant { - if (!this.shiny || !variantData.hasOwnProperty(this.species.speciesId)) { + const formIndex: number = this.formIndex; + let variantDataIndex: string | number = this.species.speciesId; + if (this.species.forms.length > 0) { + const formKey = this.species.forms[formIndex]?.formKey; + if (formKey) { + variantDataIndex = `${variantDataIndex}-${formKey}`; + } + } + // Checks if there is no variant data for both the index or index with form + if (!this.shiny || (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId))) { return 0; } const rand = Utils.randSeedInt(10); diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 7c108a3c30e..522962d5829 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -29,6 +29,7 @@ export class LoadingScene extends SceneBase { } preload() { + Utils.localPing(); this.load["manifest"] = this.game["manifest"]; if (!isMobile()) { @@ -95,10 +96,18 @@ export class LoadingScene extends SceneBase { this.loadImage("type_tera", "ui"); this.loadAtlas("type_bgs", "ui"); - this.loadImage("dawn_icon", "ui"); - this.loadImage("day_icon", "ui"); - this.loadImage("dusk_icon", "ui"); - this.loadImage("night_icon", "ui"); + this.loadImage("dawn_icon_fg", "ui"); + this.loadImage("dawn_icon_mg", "ui"); + this.loadImage("dawn_icon_bg", "ui"); + this.loadImage("day_icon_fg", "ui"); + this.loadImage("day_icon_mg", "ui"); + this.loadImage("day_icon_bg", "ui"); + this.loadImage("dusk_icon_fg", "ui"); + this.loadImage("dusk_icon_mg", "ui"); + this.loadImage("dusk_icon_bg", "ui"); + this.loadImage("night_icon_fg", "ui"); + this.loadImage("night_icon_mg", "ui"); + this.loadImage("night_icon_bg", "ui"); this.loadImage("pb_tray_overlay_player", "ui"); this.loadImage("pb_tray_overlay_enemy", "ui"); @@ -126,6 +135,7 @@ export class LoadingScene extends SceneBase { this.loadImage("summary_stats_overlay_exp", "ui"); this.loadImage("summary_moves", "ui"); this.loadImage("summary_moves_effect", "ui"); + this.loadImage("summary_moves_effect_type", "ui"); this.loadImage("summary_moves_overlay_row", "ui"); this.loadImage("summary_moves_overlay_pp", "ui"); this.loadAtlas("summary_moves_cursor", "ui"); @@ -352,14 +362,17 @@ export class LoadingScene extends SceneBase { const width = this.cameras.main.width; const height = this.cameras.main.height; - const logo = this.add.image(width / 2, 240, ""); + const midWidth = width / 2; + const midHeight = height / 2; + + const logo = this.add.image(midWidth, 240, ""); logo.setVisible(false); logo.setOrigin(0.5, 0.5); logo.setScale(4); const percentText = this.make.text({ - x: width / 2, - y: height / 2 - 24, + x: midWidth, + y: midHeight - 24, text: "0%", style: { font: "72px emerald", @@ -369,8 +382,8 @@ export class LoadingScene extends SceneBase { percentText.setOrigin(0.5, 0.5); const assetText = this.make.text({ - x: width / 2, - y: height / 2 + 48, + x: midWidth, + y: midHeight + 48, text: "", style: { font: "48px emerald", @@ -379,6 +392,32 @@ export class LoadingScene extends SceneBase { }); assetText.setOrigin(0.5, 0.5); + const disclaimerText = this.make.text({ + x: midWidth, + y: assetText.y + 152, + text: i18next.t("menu:disclaimer"), + style: { + font: "72px emerald", + color: "#DA3838", + }, + }); + disclaimerText.setOrigin(0.5, 0.5); + + const disclaimerDescriptionText = this.make.text({ + x: midWidth, + y: disclaimerText.y + 120, + text: i18next.t("menu:disclaimerDescription"), + style: { + font: "48px emerald", + color: "#ffffff", + align: "center" + }, + }); + disclaimerDescriptionText.setOrigin(0.5, 0.5); + + disclaimerText.setVisible(false); + disclaimerDescriptionText.setVisible(false); + const intro = this.add.video(0, 0); intro.setOrigin(0, 0); intro.setScale(3); @@ -388,7 +427,7 @@ export class LoadingScene extends SceneBase { percentText.setText(`${Math.floor(parsedValue * 100)}%`); progressBar.clear(); progressBar.fillStyle(0xffffff, 0.8); - progressBar.fillRect(width / 2 - 320, 360, 640 * parsedValue, 64); + progressBar.fillRect(midWidth - 320, 360, 640 * parsedValue, 64); }); this.load.on("fileprogress", file => { @@ -423,6 +462,8 @@ export class LoadingScene extends SceneBase { ease: "Sine.easeIn" }); loadingGraphics.map(g => g.setVisible(true)); + disclaimerText.setVisible(true); + disclaimerDescriptionText.setVisible(true); }); intro.play(); break; diff --git a/src/locales/de/battle.ts b/src/locales/de/battle.ts index 8c153aa77eb..d588fb327c6 100644 --- a/src/locales/de/battle.ts +++ b/src/locales/de/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "notDisabled": "{{pokemonName}}'s {{moveName}} ist\nnicht mehr deaktiviert!", "eggHatching": "Oh?", "ivScannerUseQuestion": "IV-Scanner auf {{pokemonName}} benutzen?", + "wildPokemonWithAffix": "{{pokemonName}} (wild)", + "foePokemonWithAffix": "{{pokemonName}} (Gegner)", + "useMove": "{{pokemonNameWithAffix}} setzt {{moveName}} ein!", "drainMessage": "{{pokemonName}} wurde Energie abgesaugt", "regainHealth": "KP von {{pokemonName}} wurden wieder aufgefrischt!" } as const; diff --git a/src/locales/de/menu.ts b/src/locales/de/menu.ts index 8901eb5b9c2..e1e5db72b9c 100644 --- a/src/locales/de/menu.ts +++ b/src/locales/de/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"Leer", "yes":"Ja", "no":"Nein", + "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." } as const; diff --git a/src/locales/de/modifier-type.ts b/src/locales/de/modifier-type.ts index 9f70c31ca63..ff9cf28632c 100644 --- a/src/locales/de/modifier-type.ts +++ b/src/locales/de/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "TM{{moveId}} - {{moveName}}", description: "Bringt einem Pokémon {{moveName}} bei", }, + "TmModifierTypeWithInfo": { + name: "TM{{moveId}} - {{moveName}}", + description: "Bringt einem Pokémon {{moveName}} bei\n(Halte C oder Shift für mehr Infos)", + }, "EvolutionItemModifierType": { description: "Erlaubt es bestimmten Pokémon sich zu entwickeln", }, diff --git a/src/locales/de/weather.ts b/src/locales/de/weather.ts index f6a6864bec7..ab1dde97639 100644 --- a/src/locales/de/weather.ts +++ b/src/locales/de/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "Ein Sandsturm kommt auf!", "sandstormLapseMessage": "Der Sandsturm tobt.", "sandstormClearMessage": "Der Sandsturm legt sich.", - "sandstormDamageMessage": " Der Sandsturm fügt {{pokemonPrefix}}{{pokemonName}} Schaden zu!", + "sandstormDamageMessage": " Der Sandsturm fügt {{pokemonNameWithAffix}} Schaden zu!", "hailStartMessage": "Es fängt an zu hageln!", "hailLapseMessage": "Der Hagelsturm tobt.", "hailClearMessage": "Der Hagelsturm legt sich.", - "hailDamageMessage": "{{pokemonPrefix}}{{pokemonName}} wird von Hagelkörnern getroffen!", + "hailDamageMessage": "{{pokemonNameWithAffix}} wird von Hagelkörnern getroffen!", "snowStartMessage": "Es fängt an zu schneien!", "snowLapseMessage": "Der Schneesturm tobt.", diff --git a/src/locales/en/battle.ts b/src/locales/en/battle.ts index a3fa41d9b76..083de089961 100644 --- a/src/locales/en/battle.ts +++ b/src/locales/en/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "Are you sure you want to skip taking an item?", "eggHatching": "Oh?", "ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/en/menu.ts b/src/locales/en/menu.ts index 12b197bf245..d43ac0983f4 100644 --- a/src/locales/en/menu.ts +++ b/src/locales/en/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"Empty", "yes":"Yes", "no":"No", + "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." } as const; diff --git a/src/locales/en/modifier-type.ts b/src/locales/en/modifier-type.ts index dac87e1d939..0f7b83d2b3e 100644 --- a/src/locales/en/modifier-type.ts +++ b/src/locales/en/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "TM{{moveId}} - {{moveName}}", description: "Teach {{moveName}} to a Pokémon", }, + "TmModifierTypeWithInfo": { + name: "TM{{moveId}} - {{moveName}}", + description: "Teach {{moveName}} to a Pokémon\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "Causes certain Pokémon to evolve", }, diff --git a/src/locales/en/weather.ts b/src/locales/en/weather.ts index 1e4602f362c..513c2181b00 100644 --- a/src/locales/en/weather.ts +++ b/src/locales/en/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "A sandstorm brewed!", "sandstormLapseMessage": "The sandstorm rages.", "sandstormClearMessage": "The sandstorm subsided.", - "sandstormDamageMessage": "{{pokemonPrefix}}{{pokemonName}} is buffeted\nby the sandstorm!", + "sandstormDamageMessage": "{{pokemonNameWithAffix}} is buffeted\nby the sandstorm!", "hailStartMessage": "It started to hail!", "hailLapseMessage": "Hail continues to fall.", "hailClearMessage": "The hail stopped.", - "hailDamageMessage": "{{pokemonPrefix}}{{pokemonName}} is pelted\nby the hail!", + "hailDamageMessage": "{{pokemonNameWithAffix}} is pelted\nby the hail!", "snowStartMessage": "It started to snow!", "snowLapseMessage": "The snow is falling down.", diff --git a/src/locales/es/battle.ts b/src/locales/es/battle.ts index af090153a04..c4d79cfdb93 100644 --- a/src/locales/es/battle.ts +++ b/src/locales/es/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "¿Estás seguro de que no quieres coger un objeto?", "eggHatching": "¿Y esto?", "ivScannerUseQuestion": "¿Quieres usar el Escáner de IVs en {{pokemonName}}?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/es/menu.ts b/src/locales/es/menu.ts index c369ecceaab..517569ff40b 100644 --- a/src/locales/es/menu.ts +++ b/src/locales/es/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"Vacío", "yes":"Sí", "no":"No", + "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." } as const; diff --git a/src/locales/es/modifier-type.ts b/src/locales/es/modifier-type.ts index 7b5b1e0c90b..f6565486bb1 100644 --- a/src/locales/es/modifier-type.ts +++ b/src/locales/es/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "MT{{moveId}} - {{moveName}}", description: "Enseña {{moveName}} a un Pokémon", }, + "TmModifierTypeWithInfo": { + name: "MT{{moveId}} - {{moveName}}", + description: "Enseña {{moveName}} a un Pokémon\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "Hace que ciertos Pokémon evolucionen", }, diff --git a/src/locales/es/pokemon-info-container.ts b/src/locales/es/pokemon-info-container.ts index 068c9ebb431..785f5fce275 100644 --- a/src/locales/es/pokemon-info-container.ts +++ b/src/locales/es/pokemon-info-container.ts @@ -1,11 +1,11 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const pokemonInfoContainer: SimpleTranslationEntries = { - "moveset": "Moveset", - "gender": "Gender:", - "ability": "Ability:", - "nature": "Nature:", - "epic": "Epic", - "rare": "Rare", - "common": "Common" + "moveset": "Movimientos", + "gender": "Género:", + "ability": "Habilid:", + "nature": "Natur:", + "epic": "Épico", + "rare": "Raro", + "common": "Común" } as const; diff --git a/src/locales/es/weather.ts b/src/locales/es/weather.ts index 04ebf977bc2..1e40544accd 100644 --- a/src/locales/es/weather.ts +++ b/src/locales/es/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "¡Se ha desatado una tormenta de arena!", "sandstormLapseMessage": "La tormenta de arena arrecia...", "sandstormClearMessage": "La tormenta de arena termino.", - "sandstormDamageMessage": "¡La tormenta de arena zarandea al\n{{pokemonName}}{{pokemonPrefix}}!", + "sandstormDamageMessage": "¡La tormenta de arena zarandea al\n{{pokemonNameWithAffix}}!", "hailStartMessage": "¡Ha empezado a granizar!", "hailLapseMessage": "Sigue granizando...", "hailClearMessage": "Had dejado de granizar.", - "hailDamageMessage": "El granizo golpea al\n{{pokemonName}}{{pokemonPrefix}}!", + "hailDamageMessage": "El granizo golpea al\n{{pokemonNameWithAffix}}!", "snowStartMessage": "¡Ha empezado a nevar!", "snowLapseMessage": "Sigue nevando...", diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index 535ed4b6ade..181b85f7cc8 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre d’objet ?", "eggHatching": "Oh ?", "ivScannerUseQuestion": "Utiliser le Scanner d’IV sur {{pokemonName}} ?", + "wildPokemonWithAffix": "{{pokemonName}} sauvage", + "foePokemonWithAffix": "{{pokemonName}} ennemi", + "useMove": "{{pokemonNameWithAffix}} utilise\n{{moveName}} !", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/fr/menu.ts b/src/locales/fr/menu.ts index a60afdf44a8..e955d4970c0 100644 --- a/src/locales/fr/menu.ts +++ b/src/locales/fr/menu.ts @@ -44,4 +44,6 @@ export const menu: SimpleTranslationEntries = { "empty":"Vide", "yes":"Oui", "no":"Non", + "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." } as const; diff --git a/src/locales/fr/modifier-type.ts b/src/locales/fr/modifier-type.ts index 8315910adb3..dd70fd9205e 100644 --- a/src/locales/fr/modifier-type.ts +++ b/src/locales/fr/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "CT{{moveId}} - {{moveName}}", description: "Apprend la capacité {{moveName}} à un Pokémon", }, + "TmModifierTypeWithInfo": { + name: "CT{{moveId}} - {{moveName}}", + description: "Apprend la capacité {{moveName}} à un Pokémon\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "Permet à certains Pokémon d’évoluer", }, diff --git a/src/locales/fr/weather.ts b/src/locales/fr/weather.ts index 76d56887578..5c483d71b44 100644 --- a/src/locales/fr/weather.ts +++ b/src/locales/fr/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "Une tempête de sable se prépare !", "sandstormLapseMessage": "La tempête de sable fait rage !", "sandstormClearMessage": "La tempête de sable se calme !", - "sandstormDamageMessage": "La tempête de sable inflige des dégâts\nà {{pokemonPrefix}}{{pokemonName}} !", + "sandstormDamageMessage": "La tempête de sable inflige des dégâts\nà {{pokemonNameWithAffix}} !", "hailStartMessage": "Il commence à grêler !", "hailLapseMessage": "La grêle continue de tomber !", "hailClearMessage": "La grêle s’est arrêtée !", - "hailDamageMessage": "La grêle inflige des dégâts\nà {{pokemonPrefix}}{{pokemonName}} !", + "hailDamageMessage": "La grêle inflige des dégâts\nà {{pokemonNameWithAffix}} !", "snowStartMessage": "Il commence à neiger !", "snowLapseMessage": "Il y a une tempête de neige !", diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index 5b8089e6677..3ea527339f0 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "Sei sicuro di non voler prendere nessun oggetto?", "eggHatching": "Oh!", "ivScannerUseQuestion": "Vuoi usare lo scanner di IV su {{pokemonName}}?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/it/menu.ts b/src/locales/it/menu.ts index afa6567836c..e891146f754 100644 --- a/src/locales/it/menu.ts +++ b/src/locales/it/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"Vuoto", "yes":"Si", "no":"No", + "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." } as const; diff --git a/src/locales/it/modifier-type.ts b/src/locales/it/modifier-type.ts index b311aa1e8fa..ac313e2444c 100644 --- a/src/locales/it/modifier-type.ts +++ b/src/locales/it/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "MT{{moveId}} - {{moveName}}", description: "Insegna {{moveName}} a un Pokémon", }, + "TmModifierTypeWithInfo": { + name: "MT{{moveId}} - {{moveName}}", + description: "Insegna {{moveName}} a un Pokémon\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "Fa evolvere determinate specie di Pokémon", }, diff --git a/src/locales/it/weather.ts b/src/locales/it/weather.ts index 3895fcebc46..ed5d41a80af 100644 --- a/src/locales/it/weather.ts +++ b/src/locales/it/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "Si è scatenata una tempesta di sabbia!", "sandstormLapseMessage": "La tempesta di sabbia infuria.", "sandstormClearMessage": "La tempesta di sabbia si è placata.", - "sandstormDamageMessage": "{{pokemonPrefix}}{{pokemonName}} è stato colpito\ndalla tempesta di sabbia!", + "sandstormDamageMessage": "{{pokemonNameWithAffix}} è stato colpito\ndalla tempesta di sabbia!", "hailStartMessage": "Ha iniziato a grandinare!", "hailLapseMessage": "La grandine continua a cadere.", "hailClearMessage": "Ha smesso di grandinare.", - "hailDamageMessage": "{{pokemonPrefix}}{{pokemonName}} è stato colpito\ndalla grandine!", + "hailDamageMessage": "{{pokemonNameWithAffix}} è stato colpito\ndalla grandine!", "snowStartMessage": "Ha iniziato a nevicare!", "snowLapseMessage": "La neve sta continuando a cadere.", diff --git a/src/locales/ko/battle.ts b/src/locales/ko/battle.ts index c6288d3d9f2..cc91141718c 100644 --- a/src/locales/ko/battle.ts +++ b/src/locales/ko/battle.ts @@ -55,5 +55,8 @@ export const battle: SimpleTranslationEntries = { "notDisabled": "{{pokemonName}}의\n{{moveName}} 사슬묶기가 풀렸다!", "skipItemQuestion": "아이템을 받지 않고 넘어가시겠습니까?", "eggHatching": "어라…?", - "ivScannerUseQuestion": "{{pokemonName}}에게 개체값탐지기를 사용하시겠습니까?" + "ivScannerUseQuestion": "{{pokemonName}}에게 개체값탐지기를 사용하시겠습니까?", + "wildPokemonWithAffix": "야생 {{pokemonName}}", + "foePokemonWithAffix": "상대 {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}}의 {{moveName}}!" } as const; diff --git a/src/locales/ko/dialogue.ts b/src/locales/ko/dialogue.ts index a2b0b2f28bc..83e49e33923 100644 --- a/src/locales/ko/dialogue.ts +++ b/src/locales/ko/dialogue.ts @@ -4,58 +4,58 @@ import { DialogueTranslationEntries, SimpleTranslationEntries } from "#app/plugi export const PGMdialogue: DialogueTranslationEntries = { "youngster": { "encounter": { - 1: "Hey, wanna battle?", - 2: "Are you a new trainer too?", - 3: "Hey, I haven't seen you before. Let's battle!", - 4: "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", - 5: "Have we met or not? I don't really remember. Well, I guess it's nice to meet you anyway!", - 6: "All right! Let's go!", - 7: "All right! Here I come! I'll show you my power!", - 8: "Haw haw haw... I'll show you how hawesome my Pokémon are!", - 9: "No need to waste time saying hello. Bring it on whenever you're ready!", - 10: "Don't let your guard down, or you may be crying when a kid beats you.", - 11: "I've raised my Pokémon with great care. You're not allowed to hurt them!", - 12: "Glad you made it! It won't be an easy job from here.", - 13: "The battles continue forever! Welcome to the world with no end!" + 1: "거기 너! 나와 배틀 어때?", + 2: "넌 새내기 트레이너구나. 맞지?", + 3: "거기 너! 처음보는 얼굴인데? 나랑 배틀하자!", + 4: "방금 배틀에서 져서 새로운 포켓몬을 찾는 중이야.\n잠깐! 넌 약해보이는데? 어서 나와 배틀하자!", + 5: "우리 만난 적이 있었던가? 잘 기억은 안나지만 어쨌든 만나서 반가워!", + 6: "좋아! 시작하자!", + 7: "좋아! 내가 왔다! 내 힘을 보여주지!", + 8: "하하하… 내 포켓몬이 얼마나 멋진지 보여주겠어!", + 9: "인사할 시간도 없어. 준비가 되었다면 이리 와!", + 10: "긴장을 늦추지마. 그렇지 않으면 어린이에게 맞아 울지도 몰라.", + 11: "난 내 포켓몬들을 소중히 키웠어. 내 포켓몬에게 상처를 입히게 놔두지 않겠어!", + 12: "여기까지 잘 왔구나! 하지만 지금부턴 쉽지 않을거야.", + 13: "배틀은 끝나지 않아! 끝없는 배틀의 세계에 온 것을 환영해!" }, "victory": { - 1: "Wow! You're strong!", - 2: "I didn't stand a chance, huh?", - 3: "I'll find you again when I'm older and beat you!", - 4: "Ugh. I don't have any more Pokémon.", - 5: "No way… NO WAY! How could I lose again…", - 6: "No! I lost!", - 7: "Whoa! You are incredible! I'm amazed and surprised!", - 8: "Could it be… How… My Pokémon and I are the strongest, though…", - 9: "I won't lose next time! Let's battle again sometime!", - 10: "Sheesh! Can't you see that I'm just a kid! It wasn't fair of you to go all out like that!", - 11: "Your Pokémon are more amazing! Trade with me!", - 12: "I got a little carried away earlier, but what job was I talking about?", - 13: "Ahaha! There it is! That's right! You're already right at home in this world!" + 1: "우와! 넌 강하구나!", + 2: "하? 난 기회가 없었어.", + 3: "내가 조금 더 큰 다음엔 널 찾아서 때리겠어!", + 4: "으.. 더이상 가지고 있는 포켓몬이 없어.", + 5: "말도 안돼… 안돼! 내가 또 지다니…", + 6: "안돼! 내가 지다니!", + 7: "우와! 정말 깜짝 놀랐어! 넌 정말 강하구나!", + 8: "이럴수가… 내 포켓몬과 난 최강인데… 어떻게…", + 9: "다음엔 지지 않을거야! 다음에 다시 배틀하자!", + 10: "쳇! 내가 어린애인게 보이지 않아?! 그렇게 최선을 다하는건 불공평해!", + 11: "네 포켓몬은 정말 굉장하구나! 나와 교환하자!", + 12: "내가 잠깐 정신이 나갔었나 봐. 내가 무슨 말을 하고 있었지?", + 13: "아하! 거기구나! 좋아! 넌 이미 이 세계에 머무를 곳이 있구나!" } }, "lass": { "encounter": { - 1: "Let's have a battle, shall we?", - 2: "You look like a new trainer. Let's have a battle!", - 3: "I don't recognize you. How about a battle?", - 4: "Let's have a fun Pokémon battle!", - 5: "I'll show you the ropes of how to really use Pokémon!", - 6: "A serious battle starts from a serious beginning! Are you sure you're ready?", - 7: "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", - 8: "You'd better go easy on me, OK? Though I'll be seriously fighting!", - 9: "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." + 1: "나랑 배틀하자, 어때?", + 2: "넌 신입 트레이너구나. 나랑 배틀하자!", + 3: "너 거기 있었구나? 나랑 배틀할래?", + 4: "재밌는 포켓몬 배틀하자!", + 5: "내가 포켓몬을 어떻게 다뤄야하는지 보여줄게!", + 6: "진정한 배틀은 진지한 자세부터 시작이야! 준비됐어?", + 7: "젊음이 한순간이듯 배틀에서 네 기회도 단 한번만 주어질거야. 곧 넌 추억속으로 사라질거야.", + 8: "나에겐 살살해도 돼, 알았지? 그래도 난 진지하게 싸울거야!", + 9: "학교는 지겨워. 나는 할 일이 없어. 하암~ 난 그저 시간을 때우기 위해 싸울뿐이야." }, "victory": { - 1: "That was impressive! I've got a lot to learn.", - 2: "I didn't think you'd beat me that bad…", - 3: "I hope we get to have a rematch some day.", - 4: "That was pretty amazingly fun! You've totally exhausted me…", - 5: "You actually taught me a lesson! You're pretty amazing!", - 6: "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", - 7: "I don't need memories like this. Deleting memory…", - 8: "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", - 9: "I'm actually getting tired of battling… There's gotta be something new to do…" + 1: "인상적이었어! 난 아직 배울게 많구나.", + 2: "내가 이렇게까지 크게 질 줄은 몰랐어…", + 3: "언젠가 우리가 다시 배틀할 수 있을 날을 기다릴게.", + 4: "놀라울 정도로 엄청 재미있었어! 넌 날 완전히 지치게 만들어버렸네…", + 5: "넌 나에게 진짜 교훈을 주었어! 넌 정말 대단해!", + 6: "세상에, 내가 지다니. 이거 정말 우울하지만… 넌 정말 멋있었어.", + 7: "난 이런 기억따윈 필요없어. 잊어버리겠어…", + 8: "거기 너! 살살하라고 했지! 그래도 넌 진지할때 정말 멋지구나!", + 9: "사실 배틀하는 것이 지루하던 참이야… 뭔가 새로운 것이 없을까?" } }, "breeder": { @@ -373,297 +373,297 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "brock": { "encounter": { - 1: "My expertise on Rock-type Pokémon will take you down! Come on!", - 2: "My rock-hard willpower will overwhelm you!", - 3: "Allow me to show you the true strength of my Pokémon!" + 1: "내 전문인 바위 타입 포켓몬으로 널 쓰러뜨려줄게! 덤벼!", + 2: "바위같은 의지로 널 압도하겠어!", + 3: "내 포켓몬의 진정한 힘을 보여줄게!" }, "victory": { - 1: "Your Pokémon's strength have overcome my rock-hard defenses!", - 2: "The world is huge! I'm glad to have had a chance to battle you.", - 3: "Perhaps I should go back to pursuing my dream as a Pokémon Breeder…" + 1: "네 포켓몬의 힘이 바위같은 내 방어를 이겼어!", + 2: "세상은 넓구나! 너랑 겨뤄볼 수 있어서 즐거웠어.", + 3: "아마도 난 포켓몬 브리더의 꿈을 이루러 가야할지도…" }, "defeat": { - 1: "The best offense is a good defense!\nThat's my way of doing things!", - 2: "Come study rocks with me next time to better learn how to fight them!", - 3: "Hah, all my traveling around the regions is paying off!" + 1: "최선의 공격은 적절한 방어지!\n그게 내 방식이야!", + 2: "다음에 나한테 더 배우러와. 바위타입과 어떻게 싸워야하는지 알려주지!", + 3: "아, 여러 지역을 돌아다니며 여행한 보람이 있군!" } }, "misty": { "encounter": { - 1: "My policy is an all out offensive with Water-type Pokémon!", - 2: "Hiya, I'll show you the strength of my aquatic Pokémon!", - 3: "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" + 1: "내 방침은 물타입 포켓몬으로 공격하고 공격하고 또 공격하는 거!", + 2: "아하핫, 너한테 내 물타입 포켓몬들의 힘을 보여줄게!", + 3: "내 꿈은 여행을 다니며 강한 트레이너들과 배틀하는 거였어…\n네가 그 충분한 도전자가 될 수 있는지 볼까?" }, "victory": { - 1: "You really are strong… I'll admit that you are skilled…", - 2: "Grrr… You know you just got lucky, right?!", - 3: "Wow, you're too much! I can't believe you beat me!" + 1: "너 정말로 강하구나… 그 실력 인정하도록 할게…", + 2: "으으… 너 그냥 운이 좋았던거야, 그치?!", + 3: "우와, 너 대단해! 날 이기다니 믿을 수 없어!" }, "defeat": { - 1: "Was the mighty Misty too much for you?", - 2: "I hope you saw my Pokémon's elegant swimming techniques!", - 3: "Your Pokémon were no match for my pride and joys!" + 1: "최강인 최이슬! 너한테 좀 심했나?", + 2: "내 포켓몬들의 우아한 수영 테크닉을 봤길 바랄게!", + 3: "내 프라이드와 즐거움엔 네 포켓몬들은 상대가 안 돼. " } }, "lt_surge": { "encounter": { - 1: "My Electric Pokémon saved me during the war! I'll show you how!", - 2: "Ten-hut! I'll shock you into surrender!", - 3: "I'll zap you just like I do to all my enemies in battle!" + 1: "마이 전기 포켓몬은 전쟁에서 미를 구했어요! 하우를 유에게 보여줄게요!", + 2: "헤이! 쇼크로 유를 항복시키겠어요!", + 3: "배틀에서 마이 에너미에게 했던 것처럼 유에게도 펀치를 날리겠어요!" }, "victory": { - 1: "Whoa! Your team's the real deal, kid!", - 2: "Aaargh, you're strong! Even my electric tricks lost against you.", - 3: "That was an absolutely shocking loss!" + 1: "와우, 키드! 유어 팀은 진짜 대단하군요!", + 2: "으흐흑, 유는 스트롱하네요! 마이 전기 트릭도 유에겐 로스트입니다.", + 3: "앱솔루트하고 쇼킹한 패배였어요!" }, "defeat": { - 1: "Oh yeah! When it comes to Electric-type Pokémon, I'm number one in the world!", - 2: "Hahaha! That was an electrifying battle, kid!", - 3: "A Pokémon battle is war, and I have showed you first-hand combat!" + 1: "오우 예! 전기 타입 포켓몬이라면, 미가 월드에서 넘버 원이에요!", + 2: "하하하! 키드, 이것이 찌릿찌릿 일렉트릭 배틀입니다!", + 3: "포켓몬 배틀은 전쟁, 앤드 나는 유에게 직접 전투를 보여줬습니다!" } }, "erika": { "encounter": { - 1: "Ah, the weather is lovely here…\nOh, a battle? Very well then.", - 2: "My Pokémon battling skills rival that of my flower arranging skills.", - 3: "Oh, I hope the pleasant aroma of my Pokémon doesn't put me to sleep again…", - 4: "Seeing flowers in a garden is so soothing." + 1: "아, 오늘은 날씨가 좋네요…\n음, 배틀일까요? 그럼 더 좋죠.", + 2: "제 포켓몬들의 배틀 실력은 제 꽃꽂이 실력만큼 대단하답니다.", + 3: "아, 제 포켓몬의 달콤한 향기가 저를 다시 잠들게 하지 않았으면 좋겠는데……", + 4: "정원에서 꽃을 보면 마음이 편안해져요.”." }, "victory": { - 1: "Oh! I concede defeat.", - 2: "That match was most delightful.", - 3: "Ah, it appears it is my loss…", - 4: "Oh, my goodness." + 1: "앗! 제 패배를 인정합니다.", + 2: "방금 경기 정말 달콤했어요.", + 3: "아, 제가 진 것 같네요…", + 4: "앗, 맙소사." }, "defeat": { - 1: "I was afraid I would doze off…", - 2: "Oh my, it seems my Grass Pokémon overwhelmed you.", - 3: "That battle was such a soothing experience.", - 4: "Oh… Is that all?" + 1: "저 조금 걱정했어요. 너무 졸려서…", + 2: "어머, 제 풀 포켓몬이 당신을 압도한 것 같네요.", + 3: "이 배틀 정말로 편안한 경험이었네요.", + 4: "어머… 이게 끝인가요?" } }, "janine": { "encounter": { - 1: "I am mastering the art of poisonous attacks.\nI shall spar with you today!", - 2: "Father trusts that I can hold my own.\nI will prove him right!", - 3: "My ninja techniques are only second to my Father's!\nCan you keep up?" + 1: "난 독을 사용하는 인술을 갈고 닦고 있어.\n오늘 수련에서는 너랑 대련할거야!", + 2: "아버지는 내가 잘해낼 수 있다고 신뢰하셔.\n 그게 맞는다는 걸 증명할게!", + 3: "내 인술은 아버지한테 뒤처지지 않아! 따라올 수 있겠어? " }, "victory": { - 1: "Even now, I still need training… I understand.", - 2: "Your battle technique has outmatched mine.", - 3: "I'm going to really apply myself and improve my skills." + 1: "역시 아직도, 난 더 수련이 필요해… 납득했어.", + 2: "네 배틀 기술이 내 인술보다 한 수위야.", + 3: "더 스스로 갈고 닦아서, 내 인술을 향상 시키겠어." }, "defeat": { - 1: "Fufufu… the poison has sapped all your strength to battle.", - 2: "Ha! You didn't stand a chance against my superior ninja skills!", - 3: "Father's faith in me has proven to not be misplaced." + 1: "후후후… 독이 네 기력을 모두 가져가버렸네.", + 2: "하핫, 너 내 인술에 맞설 기회를 잡지 못했구나!", + 3: "나를 향한 아버지의 신뢰, 틀리지 않았다는 걸 증명해냈어." } }, "sabrina": { "encounter": { - 1: "Through my psychic ability, I had a vision of your arrival!", - 2: "I dislike fighting, but if you wish, I will show you my powers!", - 3: "I can sense great ambition in you. I shall see if it not unfounded." + 1: "내 초능력을 통해서, 너의 도착은 예상하고 있었어!", + 2: "싸우는 건 좋아하지 않지만 네가 원한다면… 나의 힘을 보여줄게!", + 3: "네게서 큰 염원이 느껴져. 그것이 근거 없는 것이 아닌지 지켜보겠어." }, "victory": { - 1: "Your power… It far exceeds what I foresaw…", - 2: "I failed to accurately predict your power.", - 3: "Even with my immense psychic powers, I cannot sense another as strong as you." + 1: "너의 힘은… 내가 예견했던 것보다 훨씬 뛰어나…", + 2: "나는 너의 힘을 정확하게 예측하지 못했어.", + 3: "나 엄청난 초능력을 가지고도, 너처럼 강한 사람을 느끼지 못했네." }, "defeat": { - 1: "This victory… It is exactly as I foresaw in my visions!", - 2: "Perhaps it was another I sensed a great desire in…", - 3: "Hone your abilities before recklessly charging into battle.\nYou never know what the future may hold if you do…" + 1: "이 승리는… 내가 환상에서 예견한 그대로네!", + 2: "아마도 그건, 내가 깊이 느꼈던 또 다른 염원이었을거야…", + 3: "무모하게 배틀에 임하기 전에 능력을 갈고닦도록.\n넌 미래가 어떻게 될지 예지할 수 없으니까…" } }, "blaine": { "encounter": { - 1: "Hah! Hope you brought a Burn Heal!", - 2: "My fiery Pokémon will incinerate all challengers!", - 3: "Get ready to play with fire!" + 1: "우오오~옷! 화상치료제는 잘 준비했는가!", + 2: "나의 포켓몬은 모든 것을 불꽃으로 태워버리는 강한 녀석들뿐이다!", + 3: "불꽃과 함께할 준비는 됐는가!" }, "victory": { - 1: "I have burned down to nothing! Not even ashes remain!", - 2: "Didn't I stoke the flames high enough?", - 3: "I'm all burned out… But this makes my motivation to improve burn even hotter!" + 1: "아무것도 남지 않고 불타버렸다! 재조차 남지 않았어!", + 2: "내가 불을 너무 세게 피우지 않았나?", + 3: "불태웠다… 하지만 이건 불꽃을 향상시키려는 내 동기를 더욱 뜨겁게 만드는군!" }, "defeat": { - 1: "My raging inferno cannot be quelled!", - 2: "My Pokémon have been powered up with the heat from this victory!", - 3: "Hah! My passion burns brighter than yours!" + 1: "나의 타오르는 불길은 진압할 수 없다!", + 2: "내 포켓몬은 이번 승리의 열기로 더욱 강해졌다!", + 3: "하! 내 열정이 네 것보다 더 밝게 타오르고 있군!" } }, "giovanni": { "encounter": { - 1: "I, the leader of Team Rocket, will make you feel a world of pain!", - 2: "My training here will be vital before I am to face my old associates again.", - 3: "I do not think you are prepared for the level of failure you are about to experience!" + 1: "나, 로켓단의 리더가, 고통의 세계를 느끼게 해주마!", + 2: "옛 동료들과 다시 만나기 전, 이곳에서의 훈련은 매우 중요하겠군.", + 3: "너는 곧 경험하게 될 실패에 대한 준비가 되어 있지 않군!" }, "victory": { - 1: "WHAT! Me, lose?! There is nothing I wish to say to you!", - 2: "Hmph… You could never understand what I hope to achieve.", - 3: "This defeat is merely delaying the inevitable.\nI will rise Team Rocket from the ashes in due time." + 1: "하! 내가 졌다고?! 더 이상 할말이 없군!", + 2: "흐음… 넌 내가 이루고자 하는 것을 결코 이해할 수 없을 거다.", + 3: "이 패배는 피할 수 없는 것을 단지 지연시킬 뿐.\n때가 되면 잿더미에서 로켓단을 일으켜 세울 것이다." }, "defeat": { - 1: "Not being able to measure your own strength shows that you are still but a child.", - 2: "Do not try to interfere with me again.", - 3: "I hope you understand how foolish challenging me was." + 1: "자신의 힘을 스스로 잴수 없다는 것은 네가 아직 꼬맹이라는 것을 보여준다고 할 수 있지.", + 2: "다시는 나를 방해하지 말도록.", + 3: "나에게 도전하는 것이 얼마나 어리석은 짓인지 이해했으면 좋겠군." } }, "roxanne": { "encounter": { - 1: "Would you kindly demonstrate how you battle?", - 2: "You can learn many things by battling many trainers.", - 3: "Oh, you caught me strategizing.\nWould you like to battle?" + 1: "당신이 어떻게 싸우는지 보여주시겠어요?", + 2: "당신은 여러 트레이너와 싸우면서 많은 것을 배울 수 있을거예요.", + 3: "아, 전략짜는 거 들켰네요.\n배틀할까요?" }, "victory": { - 1: "Oh, I appear to have lost.\nI understand.", - 2: "It seems that I still have so much more to learn when it comes to battle.", - 3: "I'll take what I learned here today to heart." + 1: "아, 제가 진 것 같네요.\n승복하겠습니다.", + 2: "전 아직도 포켓몬 배틀에 대해서 한참 더 배워야할 것 같네요.", + 3: "오늘 여기서 배운 것들을 마음에 담아둬야겠어요." }, "defeat": { - 1: "I have learned many things from our battle.\nI hope you have too.", - 2: "I look forward to battling you again.\nI hope you'll use what you've learned here.", - 3: "I won due to everything I have learned." + 1: "전 방금 승부에서 많은 것을 배웠습니다.\n당신도 그랬길 바래요.", + 2: "다시 붙을 날이 기대되네요.\n당신이 여기서 배운 걸 활용할 수 있길 바랍니다.", + 3: "여태까지 공부해온 것 덕분에 이겼네요." } }, "brawly": { "encounter": { - 1: "Oh man, a challenger!\nLet's see what you can do!", - 2: "You seem like a big splash.\nLet's battle!", - 3: "Time to create a storm!\nLet's go!" + 1: "오, 도전자잖아!\n어디 한 번 볼까!", + 2: "넌 큰 파란을 일으킬 것 같군.\n승부다!", + 3: "폭풍을 일으킬 시간이야!\n가자!" }, "victory": { - 1: "Oh woah, you've washed me out!", - 2: "You surfed my wave and crashed me down!", - 3: "I feel like I'm lost in Granite Cave!" + 1: "우와, 너 날 씻겨버렸네!", + 2: "내 파도를 타고, 나까지 밀어내다니!", + 3: "바위 동굴에서 길을 잃은 기분이야!" }, "defeat": { - 1: "Haha, I surfed the big wave!\nChallenge me again sometime.", - 2: "Surf with me again some time!", - 3: "Just like the tides come in and out, I hope you return to challenge me again." + 1: "하핫, 난 큰 파도를 탔다고!\n언제 또 도전해주라.", + 2: "언젠가 또 같이 서핑하자고!", + 3: "파도가 밀려왔다가 밀려나듯, 언젠가 너도 다시 도전하러 와." } }, "wattson": { "encounter": { - 1: "Time to get shocked!\nWahahahaha!", - 2: "I'll make sparks fly!\nWahahahaha!", - 3: "I hope you brought Paralyz Heal!\nWahahahaha!" + 1: "찌릿찌릿해질 때가 됐군!\n와하하하핫!", + 2: "스파크가 튀도록 해주마!\n와하하하하!", + 3: "와하하하하!\n마비 치료제를 가져왔길 바라네!" }, "victory": { - 1: "Seems like I'm out of charge!\nWahahahaha!", - 2: "You've completely grounded me!\nWahahahaha!", - 3: "Thanks for the thrill!\nWahahahaha!" + 1: "이 몸 배터리가 다 됐군!\n와하하하하!", + 2: "자네 완전히 날 좌초시켰군!\n와하하하핫!", + 3: "스릴 넘치는 배틀, 고맙네!\n와하하하하하!" }, "defeat": { - 1: "Recharge your batteries and challenge me again sometime!\nWahahahaha!", - 2: "I hope you found our battle electrifying!\nWahahahaha!", - 3: "Aren't you shocked I won?\nWahahahaha!" + 1: "자네의 배터리 재충전하게. 그리고 나에게 도전하러 돌아오도록!\n와하하하핫!", + 2: "방금 배틀이 자네에게 짜릿짜릿했길 바란다네!\n와하하하하!", + 3: "자네 혹시 내가 이겨서 충격 받았나?\n와하하하핫!" } }, "flannery": { "encounter": { - 1: "Nice to meet you! Wait, no…\nI will crush you!", - 2: "I've only been a leader for a little while, but I'll smoke you!", - 3: "It's time to demonstrate the moves my grandfather has taught me! Let's battle!" + 1: "어서오세요! 잠깐, 아냐…\n너를 무너뜨려줄게!", + 2: "난 체육관 관장이 된지는 얼마 안됐지만, 널 태워버릴거야!", + 3: "할아버지에게 배운 기술을 한 수 보여줄게! 승부다!" }, "victory": { - 1: "You remind me of my grandfather…\nNo wonder I lost.", - 2: "Am I trying too hard?\nI should relax, can't get too heated.", - 3: "Losing isn't going to smother me out.\nTime to reignite training!" + 1: "너 우리 할아버지를 생각나게 하네…\n내가 진 게 놀랍진 않아.", + 2: "나 너무 열심히 하는 건가?\n너무 열 올리면 안되니깐, 진정해야겠어.", + 3: "패배는 날 꺼뜨릴 수 없어.\n트레이닝으로 다시 불을 붙일 때야!" }, "defeat": { - 1: "I hope I've made my grandfather proud…\nLet's battle again some time.", - 2: "I…I can't believe I won!\nDoing things my way worked!", - 3: "Let's exchange burning hot moves again soon!" + 1: "할아버지가 자랑스러워하시길…\n언젠가 다시 배틀하자.", + 2: "내…내가 이기다니!\n내 방식대로 한 게 통했어!", + 3: "조만간 다시 뜨겁게 불타오르는 배틀을 하자!" } }, "norman": { "encounter": { - 1: "I'm surprised you managed to get here.\nLet's battle.", - 2: "I'll do everything in my power as a Gym Leader to win.\nLet's go!", - 3: "You better give this your all.\nIt's time to battle!" + 1: "여기까지 오다니 놀랍군.\n한 번 겨뤄볼까.", + 2: "관장으로서 최선을 다해 널 이길 거란다.\n가자!", + 3: "최선을 다하는 게 좋을 거야.\n승부할 시간이다!" }, "victory": { - 1: "I lost to you…?\nRules are rules, though.", - 2: "Was moving from Olivine a mistake…?", - 3: "I can't believe it.\nThat was a great match." + 1: "내가 지다니…?\n규칙은 규칙이니, 흐음.", + 2: "담청시티에서 이사한 게 문제였나…?", + 3: "믿을 수 없구나.\n훌륭한 승부였어." }, "defeat": { - 1: "We both tried our best.\nI hope we can battle again soon.", - 2: "You should try challenging my kid instead.\nYou might learn something!", - 3: "Thank you for the excellent battle.\nBetter luck next time." + 1: "우린 둘 다 최선을 다했지.\n다시 대결할 수 있었으면 좋겠구나.", + 2: "우리 집 꼬마에게 도전해보는 것도 좋겠군.\n아마 뭔가 배울 수 있을거다!", + 3: "방금 전 배틀 완벽했어.\n다음에도 행운이 함께하길." } }, "winona": { "encounter": { - 1: "I've been soaring the skies looking for prey…\nAnd you're my target!", - 2: "No matter how our battle is, my Flying Pokémon and I will triumph with grace. Let's battle!", - 3: "I hope you aren't scared of heights.\nLet's ascend!" + 1: "저는 먹이를 찾아서 하늘을 날아다녔어요…\n그리고 당신은 제 타겟입니다!", + 2: "배틀이 어떻게 되든, 전 제 비행 포켓몬과 우아하게 승리하겠어요. 승부합시다!", + 3: "당신이 높은 곳을 무서워하지 않기를.\n자, 날아올라요!" }, "victory": { - 1: "You're the first Trainer I've seen with more grace than I.\nExcellently played.", - 2: "Oh, my Flying Pokémon have plummeted!\nVery well.", - 3: "Though I may have fallen, my Pokémon will continue to fly!" + 1: "저보다 우아하게 나서는 트레이너는 처음 봤습니다.\n훌륭하시네요.", + 2: "이런, 제 비행 포켓몬이 추락해버렸네요!\n훌륭한 배틀이었습니다.", + 3: "비록 전 떨어졌지만, 제 포켓몬은 다시 날아갈 겁니다!" }, "defeat": { - 1: "My Flying Pokémon and I will forever dance elegantly!", - 2: "I hope you enjoyed our show.\nOur graceful dance is finished.", - 3: "Won't you come see our elegant choreography again?" + 1: "제 비행 포켓몬과 영원히 우아하게 춤출게요.", + 2: "우리의 쇼가 즐거웠길 바라요.\우아한 춤은 끝났습니다.", + 3: "우리의 엘레강스한 안무를 다시 보러오지 않을래요?" } }, "tate": { "encounter": { - 1: "Hehehe…\nWere you surprised to see me without my sister?", - 2: "I can see what you're thinking…\nYou want to battle!", - 3: "How can you defeat someone…\nWho knows your every move?" + 1: "헤헤헤…\n내가 란과 같이 있지 않아서 놀랐지?", + 2: "네가 무슨 생각을 하는지 알아…\n승부하고 싶은거지!", + 3: "네 움직임을 모두 알고 있는데…\n어떻게 이기려고?" }, "victory": { - 1: "It can't be helped…\nI miss Liza…", - 2: "Your bond with your Pokémon was stronger than mine.", - 3: "If I were with Liza, we would have won.\nWe can finish each other's thoughts!" + 1: "어쩔 수 없지…\n란이 보고싶다아…", + 2: "너와 네 포켓몬과의 유대, 나보다 더 견고한걸.", + 3: "란이랑 함께였다면, 우리가 이겼어.\n둘이선 더 잘 할 수 있다구!" }, "defeat": { - 1: "My Pokémon and I are superior!", - 2: "If you can't even defeat me, you'll never be able to defeat Liza either.", - 3: "It's all thanks to my strict training with Liza.\nI can make myself one with Pokémon." + 1: "내 포켓몬과 나는 우수하다구!", + 2: "날 못 이긴다면, 넌 란한테도 절대로 못 이겨.", + 3: "란과 함께한 엄격한 훈련 덕이야.\n덕분에 포켓몬과 하나가 될 수 있었어." } }, "liza": { "encounter": { - 1: "Fufufu…\nWere you surprised to see me without my brother?", - 2: "I can determine what you desire…\nYou want to battle, don't you?", - 3: "How can you defeat someone…\nWho's one with their Pokémon?" + 1: "후후후…\n내가 풍과 같이 있지 않아서 놀랐지?", + 2: "네가 무얼 바라는지 알아…\n포켓몬 배틀, 맞지?", + 3: "포켓몬과 하나가 된 사람…\n어떻게 이기려고?" }, "victory": { - 1: "It can't be helped…\nI miss Tate…", - 2: "Your bond with your Pokémon…\nIt's stronger than mine.", - 3: "If I were with Tate, we would have won.\nWe can finish each other's sentences!" + 1: "어쩔 수 없지…\n풍이 보고싶다아…", + 2: "너와 네 포켓몬과의 유대, 나보다 더 견고한걸.", + 3: "풍이랑 함께였다면, 우리가 이겼어.\n둘이선 더 잘 할 수 있다구!" }, "defeat": { - 1: "My Pokémon and I are victorious.", - 2: "If you can't even defeat me, you'll never be able to defeat Tate either.", - 3: "It's all thanks to my strict training with Tate.\nI can synchronize myself with my Pokémon." + 1: "내 포켓몬과 내가 승리한거야.", + 2: "날 못 이긴다면, 넌 풍한테도 절대로 못 이겨.", + 3: "풍과 함께한 엄격한 훈련 덕이야.\n덕분에 포켓몬과 싱크로 될 수 있었어." } }, "juan": { "encounter": { - 1: "Now's not the time to act coy.\nLet's battle!", - 2: "Ahahaha, You'll be witness to my artistry with Water Pokémon!", - 3: "A typhoon approaches!\nWill you be able to test me?", - 4: "Please, you shall bear witness to our artistry.\nA grand illusion of water sculpted by my Pokémon and myself!" + 1: "지금은 겸양을 부릴 때가 아니군요.\n승부합시다!", + 2: "아하하하, 물 포켓몬과 함께 아트를 보여드리겠습니다!", + 3: "태풍이 다가오는군요!\n저를 테스트해주시겠습니까?", + 4: "자, 마음껏 봐주십시오.\n저와 포켓몬이 이루어내는 물의 일루전을!" }, "victory": { - 1: "You may be a genius who can take on Wallace!", - 2: "I focused on elegance while you trained.\nIt's only natural that you defeated me.", - 3: "Ahahaha!\nVery well, You have won this time.", - 4: "From you, I sense the brilliant shine of skill that will overcome all." + 1: "당신은 윤진 관장을 뛰어넘을 지니어스군요!", + 2: "당신이 훈련할 때 저는 엘레강스에 집중했습니다.\n당신이 이기는 건 당연하죠.", + 3: "아하하하하!\n잘했습니다, 이번엔 당신이 이겼네요.", + 4: "모든 것을 극복하는 브릴리언트 스킬, 당신에게 느껴지네요." }, "defeat": { - 1: "My Pokémon and I have sculpted an illusion of Water and come out victorious.", - 2: "Ahahaha, I have won, and you have lost.", - 3: "Shall I loan you my outfit? It may help you battle!\nAhahaha, I jest!", - 4: "I'm the winner! Which is to say, you lost." + 1: "저와 포켓몬이 이루어내는 물의 일루전이 승리했습니다.", + 2: "아하하핫, 저는 이겼고, 당신은 졌습니다.", + 3: "겉옷 빌려드릴까요? 아마도 배틀에 도움이 될겁니다!\n아하하하, 농담입니다!", + 4: "제가 승리자군요! 그리고, 당신은 졌네요." } }, "crasher_wake": { @@ -943,31 +943,31 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "ramos": { "encounter": { - 1: `Did yeh enjoy the garden playground I made with all these sturdy plants o' mine? - $Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?`, + 1: `그래, 올곧게 자란 초목을 모아서 만든 풀 정글짐은 어땠는가? + $자네가 느낀 그것이 나의 체육관 관장으로서의 실력이네! 한번 확인해 보겠나?`, }, "victory": { - 1: "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." + 1: "포켓몬은 자네를 믿고, 자네는 그들을 믿는다…가슴이 후련해지는 승부였구먼." }, "defeat": { - 1: "Hohoho… Indeed. Frail little blades o' grass'll break through even concrete." + 1: "호호호…연약해 보이는 풀잎은 콘크리트도 뚫을 수 있다네." } }, "viola": { "encounter": { - 1: `Whether it's the tears of frustration that follow a loss or the blossoming of joy that comes with victory… - $They're both great subjects for my camera! Fantastic! This'll be just fantastic! - $Now come at me!`, - 2: "My lens is always focused on victory--I won't let anything ruin this shot!" + 1: `패배의 분함도 승리의 순간도… + $둘 다 최고의 피사체야! 정말 멋져 멋져! + $자, 그럼 덤비렴!`, + 2: "나 비올라는 셔트 찬스를 노리는 것처럼--승리를 노릴 거야!" }, "victory": { - 1: "You and your Pokémon have shown me a whole new depth of field! Fantastic! Just fantastic!", - 2: `The world you see through a lens, and the world you see with a Pokémon by your side… - $The same world can look entirely different depending on your view.` + 1: "너와 네 포켓몬은 최고의 콤비구나! 정말 멋져 멋져!", + 2: `렌즈 너머의 세계와 포켓몬의 마음으로 보는 세계… + $똑같이 보이는 풍경이지만 다양한 세계가 겹쳐져 있는 거야.` }, "defeat": { - 1: "The photo from the moment of my victory will be a real winner, all right!", - 2: "Yes! I took some great photos!" + 1: "내가 승리한 순간을 찍은 사진은 정말 멋져 멋져!", + 2: "좋아! 멋진 사진을 찍었어!" } }, "candice": { @@ -1046,24 +1046,24 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "kofu": { "encounter": { - 1: "I'mma serve you a full course o' Water-type Pokémon! Don't try to eat 'em, though!" + 1: "물포켓몬의 풀코스를! 배 터지게 먹여 주도록 하마!" }, "victory": { - 1: "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" + 1: "우옷! 우오오옷! 이렇게 팔팔한 트레이너가 다 있다니!" }, "defeat": { - 1: "You come back to see me again now, ya hear?" + 1: "젊은 친구! 다음에 또 만나기를 기대하고 있으마!" } }, "tulip": { "encounter": { - 1: "Allow me to put my skills to use to make your cute little Pokémon even more beautiful!" + 1: "리파의 기술로 너의 포켓몬들을 지금보다 훨~씬 아름답게 만들어 줄게!" }, "victory": { - 1: "Your strength has a magic to it that cannot be washed away." + 1: "너의 강함은 풀 수 없는 매직이구나." }, "defeat": { - 1: "You know, in my line of work, people who lack talent in one area or the other often fade away quickly—never to be heard of again." + 1: "…리파의 업계에서는 어중간한 재능을 가진 사람은 대체로 금방 사라져 버려." } }, "sidney": { @@ -1193,13 +1193,13 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "rika": { "encounter": { - 1: "I'd say I'll go easy on you, but… I'd be lying! Think fast!" + 1: "실컷 귀여워해 줄 테니까… 한 번 열심히 해 보라고!" }, "victory": { - 1: "Not bad, kiddo." + 1: "너, 꽤 하는구나!" }, "defeat": { - 1: "Nahahaha! You really are something else, kiddo!" + 1: "아하하! 제법인데! 역시 너는 재밌는 녀석이라니까!" } }, "bruno": { @@ -1295,14 +1295,14 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "poppy": { "encounter": { - 1: "Oooh! Do you wanna have a Pokémon battle with me?" + 1: "우와~! 뽀삐와 포켓몬 승부가 하고 싶으세요?" }, "victory": { - 1: "Uagh?! Mmmuuuggghhh…" + 1: "훌쩍, 으에엥~" }, "defeat": { - 1: `Yaaay! I did it! I de-feet-ed you! You can come for… For… An avenge match? - $Come for an avenge match anytime you want!`, + 1: `만세~! 만세~ 목수, 성공했어요! + $에헴! 리벤지 매치는 언제든지 받아 줄게요!`, } }, "agatha": { @@ -1390,14 +1390,14 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "larry_elite": { "encounter": { - 1: `Hello there… It's me, Larry. - $I serve as a member of the Elite Four too, yes… Unfortunately for me.`, + 1: `…안녕하십니까, 청목입니다. + $귀찮게도 저는 사천왕도 겸임하고 있습니다.`, }, "victory": { - 1: "Well, that took the wind from under our wings…" + 1: "날고 있는 새포켓몬도 떨어뜨릴 기세로군요." }, "defeat": { - 1: "It's time for a meeting with the boss." + 1: "치프와 만나기로 한 시각이군요." } }, "lance": { @@ -1483,14 +1483,14 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "hassel": { "encounter": { - 1: "Prepare to learn firsthand how the fiery breath of ferocious battle feels!" + 1: "맹렬하게 몰아치는 승부의 숨결을 직접 가르쳐 드리겠습니다!!" }, "victory": { - 1: `Fortune smiled on me this time, but… - $Judging from how the match went, who knows if I will be so lucky next time.`, + 1: `이번에는 당신이 승리를 쟁취했군요. + $하지만, 시합의 흐름을 보니… 다음 승부는 또 어떻게 될지 모르겠네요.`, }, "defeat": { - 1: "That was an ace!" + 1: "저에게 더 배우고 싶은 것이 있으시다면 또 승부하도록 하죠." } }, "blue": { @@ -1662,13 +1662,13 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "katy": { "encounter": { - 1: "Don't let your guard down unless you would like to find yourself knocked off your feet!" + 1: "쓰러지고 싶지 않다면 방심하지 말고 열심히 해 봐~" }, "victory": { - 1: "All of my sweet little Pokémon dropped like flies!" + 1: "내 포켓몬들 모두 지쳐서 헤벌레~ 해졌어~" }, "defeat": { - 1: "Eat up, my cute little Vivillon!" + 1: "비비용~ 많~이 먹으렴~" } }, "pryce": { @@ -1969,60 +1969,60 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "brassius": { "encounter": { - 1: "I assume you are ready? Let our collaborative work of art begin!" + 1: "준비는 됐겠지!? 그럼, 우리 둘의 예술적인 합작품을 한번 만들어 보도록 할까!" }, "victory": { - 1: "Ahhh…vant-garde!" + 1: "아… 아방가르드!!" }, "defeat": { - 1: "I will begin on a new piece at once!" + 1: "바로 신작을 만들러 가야 하니 이만 실례하겠다!" } }, "iono": { "encounter": { - 1: `How're ya feelin' about this battle? + 1: `자~ 오늘의 각오는~ 모야모야~? $... - $Let's get this show on the road! How strong is our challenger? - $I 'unno! Let's find out together!`, + $그럼, 이제 시작해 볼까! + $도전자님의 실력은 과연 과연~!?`, }, "victory": { - 1: "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" + 1: "너의 반짝임은 1000만볼트!" }, "defeat": { - 1: "Your eyeballs are MINE!" + 1: "당신의 눈길을 일렉트릭네트로 뾰로롱!" } }, "larry": { "encounter": { - 1: "When all's said and done, simplicity is strongest." + 1: "그렇습니다. 심플한 것이 가장 강한 것입니다!" }, "victory": { - 1: "A serving of defeat, huh?" + 1: "허, 이걸로 한 방 먹은 게 되었군요." }, "defeat": { - 1: "I'll call it a day." + 1: "오늘은 저는 이만 실례하겠습니다." } }, "ryme": { "encounter": { - 1: "Come on, baby! Rattle me down to the bone!" + 1: "나의 영혼 흔들어 봐 Come On!" }, "victory": { - 1: "You're cool, my friend—you move my SOUL!" + 1: "너의 Cool한 Youth 나의 Soul이 Move!" }, "defeat": { - 1: "Later, baby!" + 1: "Bye Bye Baby~!" } }, "grusha": { "encounter": { - 1: "All I need to do is make sure the power of my Pokémon chills you to the bone!" + 1: "내가 너를 철저하게 얼려 버리면 고민할 것도 없겠지!" }, "victory": { - 1: "Your burning passion… I kinda like it, to be honest." + 1: "너의 그 열기… 싫지 않아." }, "defeat": { - 1: "Things didn't heat up for you." + 1: "너에겐 아직 열기가 부족하구나." } }, "marnie_elite": { diff --git a/src/locales/ko/menu.ts b/src/locales/ko/menu.ts index 8d46dafc721..b91d674521e 100644 --- a/src/locales/ko/menu.ts +++ b/src/locales/ko/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"빈 슬롯", "yes":"예", "no":"아니오", + "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." } as const; diff --git a/src/locales/ko/modifier-type.ts b/src/locales/ko/modifier-type.ts index 5d54018cc96..fce9d25b629 100644 --- a/src/locales/ko/modifier-type.ts +++ b/src/locales/ko/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "No.{{moveId}} {{moveName}}", description: "포켓몬에게 {{moveName}}[[를]] 가르침", }, + "TmModifierTypeWithInfo": { + name: "No.{{moveId}} {{moveName}}", + description: "포켓몬에게 {{moveName}}를(을) 가르침\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "어느 특정 포켓몬을 진화", }, diff --git a/src/locales/ko/weather.ts b/src/locales/ko/weather.ts index 70654d247b6..81340d9567a 100644 --- a/src/locales/ko/weather.ts +++ b/src/locales/ko/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "모래바람이 불기 시작했다!", "sandstormLapseMessage": "모래바람이 세차게 분다", "sandstormClearMessage": "모래바람이 가라앉았다!", - "sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!", + "sandstormDamageMessage": "모래바람이\n{{pokemonNameWithAffix}}[[를]] 덮쳤다!", "hailStartMessage": "싸라기눈이 내리기 시작했다!", "hailLapseMessage": "싸라기눈이 계속 내리고 있다", "hailClearMessage": "싸라기눈이 그쳤다!", - "hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!", + "hailDamageMessage": "싸라기눈이\n{{pokemonNameWithAffix}}[[를]] 덮쳤다!", "snowStartMessage": "눈이 내리기 시작했다!", "snowLapseMessage": "눈이 계속 내리고 있다", diff --git a/src/locales/pt_BR/battle.ts b/src/locales/pt_BR/battle.ts index f6f471e838a..47a74fe17b1 100644 --- a/src/locales/pt_BR/battle.ts +++ b/src/locales/pt_BR/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "Tem certeza de que não quer escolher um item?", "eggHatching": "Opa?", "ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?", + "wildPokemonWithAffix": "{{pokemonName}} selvagem", + "foePokemonWithAffix": "{{pokemonName}} adversário", + "useMove": "{{pokemonNameWithAffix}} usou {{moveName}}!", "drainMessage": "{{pokemonName}} teve sua\nenergia drenada!", "regainHealth": "{{pokemonName}} recuperou\npontos de saúde!" } as const; diff --git a/src/locales/pt_BR/game-stats-ui-handler.ts b/src/locales/pt_BR/game-stats-ui-handler.ts index dc03ba64cf2..396b30b4c78 100644 --- a/src/locales/pt_BR/game-stats-ui-handler.ts +++ b/src/locales/pt_BR/game-stats-ui-handler.ts @@ -27,7 +27,7 @@ export const gameStatsUiHandler: SimpleTranslationEntries = { "subLegendsHatched": "Sub-Lendários Chocados", "legendsSeen": "Lendários Vistos", "legendsCaught": "Lendários Capturados", - "legendsHatched": "Legendários Chocados", + "legendsHatched": "Lendários Chocados", "mythicalsSeen": "Míticos Vistos", "mythicalsCaught": "Míticos Capturados", "mythicalsHatched": "Míticos Chocados", diff --git a/src/locales/pt_BR/menu.ts b/src/locales/pt_BR/menu.ts index d96d4b68051..000ffb1e397 100644 --- a/src/locales/pt_BR/menu.ts +++ b/src/locales/pt_BR/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty": "Vazio", "yes": "Sim", "no": "Não", + "disclaimer": "AVISO", + "disclaimerDescription": "Este jogo é um produto inacabado; ele pode ter problemas de jogabilidade (incluindo possíveis perdas de dados salvos),\n sofrer alterações sem aviso prévio e pode ou não ser atualizado ou concluído." } as const; diff --git a/src/locales/pt_BR/modifier-type.ts b/src/locales/pt_BR/modifier-type.ts index 4865cfb64a2..adae36adc26 100644 --- a/src/locales/pt_BR/modifier-type.ts +++ b/src/locales/pt_BR/modifier-type.ts @@ -46,7 +46,7 @@ export const modifierType: ModifierTypeTranslationEntries = { }, "PokemonNatureChangeModifierType": { name: "Hortelã {{natureName}}", - description: "Muda a natureza de um Pokémon para {{natureName}} e a desbloqueia permanentemente para seu inicial", + description: "Muda a natureza do Pokémon para {{natureName}} e a desbloqueia permanentemente", }, "DoubleBattleChanceBoosterModifierType": { description: "Dobra as chances de encontrar uma batalha em dupla por {{battleCount}} batalhas", @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "TM{{moveId}} - {{moveName}}", description: "Ensina {{moveName}} a um Pokémon", }, + "TmModifierTypeWithInfo": { + name: "TM{{moveId}} - {{moveName}}", + description: "Ensina {{moveName}} a um Pokémon\n(Segure C ou Shift para mais informações)", + }, "EvolutionItemModifierType": { description: "Faz certos Pokémon evoluírem", }, diff --git a/src/locales/pt_BR/tutorial.ts b/src/locales/pt_BR/tutorial.ts index 4d71d0b7e53..bc0c5610e60 100644 --- a/src/locales/pt_BR/tutorial.ts +++ b/src/locales/pt_BR/tutorial.ts @@ -9,7 +9,7 @@ export const tutorial: SimpleTranslationEntries = { $Se o jogo estiver rodando lentamente, certifique-se de que a 'Aceleração de hardware' esteja ativada $nas configurações do seu navegador.`, - "accessMenu": `Para acessar o menu, aperte M ou Esc. + "accessMenu": `Para acessar o menu, pressione M ou Esc. $O menu contém configurações e diversas funções.`, "menu": `A partir deste menu, você pode acessar as configurações. diff --git a/src/locales/pt_BR/weather.ts b/src/locales/pt_BR/weather.ts index 269ba0e3726..07854512fdc 100644 --- a/src/locales/pt_BR/weather.ts +++ b/src/locales/pt_BR/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "Uma tempestade de areia se formou!", "sandstormLapseMessage": "A tempestade de areia é violenta.", "sandstormClearMessage": "A tempestade de areia diminuiu.", - "sandstormDamageMessage": "{{pokemonPrefix}}{{pokemonName}} é atingido\npela tempestade de areia!", + "sandstormDamageMessage": "{{pokemonNameWithAffix}} é atingido\npela tempestade de areia!", "hailStartMessage": "Começou a chover granizo!", "hailLapseMessage": "Granizo cai do céu.", "hailClearMessage": "O granizo parou.", - "hailDamageMessage": "{{pokemonPrefix}}{{pokemonName}} é atingido\npelo granizo!", + "hailDamageMessage": "{{pokemonNameWithAffix}} é atingido\npelo granizo!", "snowStartMessage": "Começou a nevar!", "snowLapseMessage": "A neve continua caindo.", diff --git a/src/locales/zh_CN/battle.ts b/src/locales/zh_CN/battle.ts index cd6357608ac..0fdb962c679 100644 --- a/src/locales/zh_CN/battle.ts +++ b/src/locales/zh_CN/battle.ts @@ -56,6 +56,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "你确定要跳过拾取道具吗?", "eggHatching": "咦?", "ivScannerUseQuestion": "对 {{pokemonName}} 使用个体值扫描仪?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/zh_CN/menu.ts b/src/locales/zh_CN/menu.ts index 99e4b35e9ce..d8cad6b05af 100644 --- a/src/locales/zh_CN/menu.ts +++ b/src/locales/zh_CN/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty": "空", "yes": "是", "no": "否", + "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." } as const; diff --git a/src/locales/zh_CN/modifier-type.ts b/src/locales/zh_CN/modifier-type.ts index 7230f21e330..456cc7bb8fb 100644 --- a/src/locales/zh_CN/modifier-type.ts +++ b/src/locales/zh_CN/modifier-type.ts @@ -99,6 +99,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "招式学习器 {{moveId}} - {{moveName}}", description: "教会一只宝可梦{{moveName}}", }, + "TmModifierTypeWithInfo": { + name: "招式学习器 {{moveId}} - {{moveName}}", + description: "教会一只宝可梦{{moveName}}\n(Hold C or Shift for more info)", + }, "EvolutionItemModifierType": { description: "使某些宝可梦进化", }, diff --git a/src/locales/zh_CN/weather.ts b/src/locales/zh_CN/weather.ts index 2049b8a429c..874b17f8fe8 100644 --- a/src/locales/zh_CN/weather.ts +++ b/src/locales/zh_CN/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "开始刮沙暴了!", "sandstormLapseMessage": "沙暴肆虐。", "sandstormClearMessage": "沙暴停止了!", - "sandstormDamageMessage": "沙暴袭击了{{pokemonPrefix}}{{pokemonName}}!", + "sandstormDamageMessage": "沙暴袭击了{{pokemonNameWithAffix}}!", "hailStartMessage": "开始下冰雹了!", "hailLapseMessage": "冰雹继续肆虐。", "hailClearMessage": "冰雹不再下了。", - "hailDamageMessage": "冰雹袭击了{{pokemonPrefix}}{{pokemonName}}!", + "hailDamageMessage": "冰雹袭击了{{pokemonNameWithAffix}}!", "snowStartMessage": "开始下雪了!", "snowLapseMessage": "雪继续下。", diff --git a/src/locales/zh_TW/ability.ts b/src/locales/zh_TW/ability.ts index f760a9614f6..a99d0acd10c 100644 --- a/src/locales/zh_TW/ability.ts +++ b/src/locales/zh_TW/ability.ts @@ -3,58 +3,59 @@ import { AbilityTranslationEntries } from "#app/plugins/i18n.js"; export const ability: AbilityTranslationEntries = { stench: { name: "惡臭", - description: "通過釋放臭臭的氣味,在攻\n擊的時候,有時會使對手畏\n縮。", + description: "發出臭氣,在攻擊的時候,\n有時會使對手畏縮。", }, - drizzle: { name: "降雨", description: "出場時,會將天氣變爲下雨\n。" }, + drizzle: { name: "降雨", description: "出場時,會將天氣變為下雨\n。" }, speedBoost: { name: "加速", description: "每一回合速度會變快。" }, battleArmor: { name: "戰鬥盔甲", - description: "被堅硬的甲殼守護着,不會\n被對手的攻擊擊中要害。", + description: "被堅硬的甲殼守護著,不會\n被對手的攻擊擊中要害。", }, sturdy: { name: "結實", description: - "在HP全滿時,即使受到招\n式攻擊,也不會被一擊打倒\n。一擊必殺的招式也沒有效\n果。", + "在HP全滿時受到招式攻擊\n不會被一擊打倒。一擊必殺\n的招式也沒有效果。", + }, damp: { - name: "溼氣", - description: "通過把周圍都弄溼,使誰都\n無法使用自爆等爆炸類的招\n式。", + name: "濕氣", + description: "透過把周圍都弄溼,使誰都\n無法使用自爆等爆炸類的招\n式。", }, - limber: { name: "柔軟", description: "因爲身體柔軟,不會變爲麻\n痹狀態。" }, - sandVeil: { name: "沙隱", description: "在沙暴的時候,閃避率會提\n高。" }, + limber: { name: "柔軟", description: "因為身體柔軟,不會變為麻\n痹狀態。" }, + sandVeil: { name: "沙隱", description: "在沙暴中閃避率會提高。" }, static: { name: "靜電", - description: "身上帶有靜電,有時會讓接\n觸到的對手麻痹。", + description: "身上帶有靜電,有時會令接\n觸到的對手麻痹。", }, voltAbsorb: { name: "蓄電", - description: "受到電屬性的招式攻擊時,\n不會受到傷害,而是會回覆。", + description: "受到電屬性的招式攻擊時,\n不會受到傷害,而是會回復。", }, waterAbsorb: { name: "儲水", - description: "受到水屬性的招式攻擊時,\n不會受到傷害,而是會回覆。", + description: "受到水屬性的招式攻擊時,\n不會受到傷害,而是會回復。", }, oblivious: { name: "遲鈍", description: - "因爲感覺遲鈍,不會變爲着\n迷和被挑釁狀態。對威嚇也\n毫不動搖。", + "感覺遲鈍,不會陷入著迷和\n被挑釁狀態。面對威嚇也不\n會動搖。", }, cloudNine: { name: "無關天氣", description: "任何天氣的影響都會消失。" }, compoundEyes: { name: "複眼", - description: "因爲擁有複眼,招式的命中\n率會提高。", + description: "因為擁有複眼,會提高招式\n的命中率。", }, insomnia: { name: "不眠", - description: "因爲有着睡不着的體質,所\n以不會陷入睡眠狀態。", + description: "因為有著睡不著的體質,所\n以不會陷入睡眠狀態。", }, colorChange: { name: "變色", - description: "自己的屬性會變爲從對手處\n所受招式的屬性。", + description: "自己的屬性會變為擊中自己\n的對手招式的屬性。", }, immunity: { name: "免疫", - description: "因爲體內擁有免疫能力,不\n會變爲中毒狀態。", + description: "因為體內擁有免疫能力,不\n會變為中毒狀態。", }, flashFire: { name: "引火", @@ -63,19 +64,19 @@ export const ability: AbilityTranslationEntries = { }, shieldDust: { name: "鱗粉", - description: "被鱗粉守護着,不會受到招\n式的追加效果影響。", + description: "被鱗粉守護著,不會受到招\n式的追加效果影響。", }, ownTempo: { name: "我行我素", - description: "因爲我行我素,不會變爲混\n亂狀態。對威嚇也毫不動搖。", + description: "因為我行我素,不會陷入混\n亂狀態。面對威嚇也不會動\n搖。", }, suctionCups: { name: "吸盤", - description: "用吸盤牢牢貼在地面上,讓\n替換寶可夢的招式和道具無\n效。", + description: "用吸盤將自己牢牢吸附在地\n面上,讓替換寶可夢的招式\n和道具失效。", }, intimidate: { name: "威嚇", - description: "出場時威嚇對手,讓其退縮\n,降低對手的攻擊。", + description: "出場時威嚇對手,使其退縮\n,從而降低對手的攻擊。", }, shadowTag: { name: "踩影", @@ -96,7 +97,7 @@ export const ability: AbilityTranslationEntries = { effectSpore: { name: "孢子", description: - "受到攻擊時,有時會把接觸\n到自己的對手變爲中毒、麻\n痹或睡眠狀態。", + "受到攻擊時,有時會把接觸\n到自己的對手變為中毒、麻\n痹或睡眠狀態。", }, synchronize: { name: "同步", @@ -104,48 +105,48 @@ export const ability: AbilityTranslationEntries = { }, clearBody: { name: "恆淨之軀", - description: "不會因爲對手的招式或特性\n而被降低能力。", + description: "不會因對手的招式或特性而\n被降低能力。", }, naturalCure: { - name: "自然回覆", - description: "回到同行隊伍後,異常狀態\n就會被治癒。", + name: "自然回復", + description: "異常狀態會在離場後治癒。", }, lightningRod: { name: "避雷針", description: - "將電屬性的招式吸引到自己\n身上,不會受到傷害,而是\n會提高特攻。", + "將電屬性的招式吸引到自己\n身上,不但不會受到傷害,\n反而會提高特攻。", }, sereneGrace: { name: "天恩", - description: "託天恩的福,招式的追加效\n果容易出現。", + description: "受到上天保佑,容易出現招式的追加效果。", }, - swiftSwim: { name: "悠遊自如", description: "下雨天氣時,速度會提高。" }, - chlorophyll: { name: "葉綠素", description: "晴朗天氣時,速度會提高。" }, + swiftSwim: { name: "悠遊自如", description: "天氣為下雨時,速度會提高。" }, + chlorophyll: { name: "葉綠素", description: "天氣為晴朗時,速度會提高。" }, illuminate: { name: "發光", - description: "通過讓周圍變亮來保持命中\n率不會被降低。", + description: "透過讓周圍變亮,命中率不\n會被降低。", }, trace: { name: "複製", - description: "出場時,複製對手的特性,\n變爲與之相同的特性。", + description: "出場時,複製對手的特性,\n變為與之相同的特性。", }, - hugePower: { name: "大力士", description: "物理攻擊的威力會變爲2倍\n。" }, + hugePower: { name: "大力士", description: "物理攻擊的威力會變為2倍\n。" }, poisonPoint: { name: "毒刺", - description: "有時會讓接觸到自己的對手\n變爲中毒狀態。", + description: "有時會讓接觸到自己的對手\n變為中毒狀態。", }, innerFocus: { name: "精神力", description: - "擁有經過鍛鍊的精神,而不\n會因對手的攻擊而畏縮。對\n威嚇也毫不動搖。", + "靠著經過鍛鍊的精神,不會\n因對手的攻擊而畏縮。面對\n威嚇也不會動搖。", }, magmaArmor: { name: "熔岩鎧甲", - description: "將熾熱的熔岩覆蓋在身上,\n不會變爲冰凍狀態。", + description: "將熾熱的熔岩覆蓋在身上,\n不會陷入冰凍狀態。", }, waterVeil: { name: "水幕", - description: "將水幕裹在身上,不會變爲\n灼傷狀態。", + description: "將水幕裹在身上,不會陷入\n灼傷狀態。", }, magnetPull: { name: "磁力", @@ -153,10 +154,10 @@ export const ability: AbilityTranslationEntries = { }, soundproof: { name: "隔音", - description: "通過屏蔽聲音,不受到聲音\n招式的影響。", + description: "透過遮蔽聲音,不受到聲音\n招式的影響。", }, - rainDish: { name: "雨盤", description: "下雨天氣時,會緩緩回覆\nHP。" }, - sandStream: { name: "揚沙", description: "出場時,會把天氣變爲沙暴。" }, + rainDish: { name: "雨盤", description: "天氣為下雨時,會緩緩回復\nHP。" }, + sandStream: { name: "揚沙", description: "出場時,會把天氣變為沙暴。" }, pressure: { name: "壓迫感", description: "給予對手壓迫感,大量減少\n其使用招式的PP。", @@ -164,70 +165,70 @@ export const ability: AbilityTranslationEntries = { thickFat: { name: "厚脂肪", description: - "因爲被厚厚的脂肪保護着,\n會讓火屬性和冰屬性的招式\n傷害減半。", + "被厚厚的脂肪保護著,能夠\n讓火屬性和冰屬性招式的傷\n害減半。", }, earlyBird: { name: "早起", - description: "即使變爲睡眠狀態,也能以\n2倍的速度提早醒來。", + description: "即使陷入睡眠狀態,也能以\n2倍的速度提早醒來。", }, flameBody: { name: "火焰之軀", - description: "有時會讓接觸到自己的對手\n變爲灼傷狀態。", + description: "有時會讓接觸到自己的對手\n變為灼傷狀態。", }, - runAway: { name: "逃跑", description: "一定能從野生寶可夢那兒逃\n走。" }, + runAway: { name: "逃跑", description: "一定能從野生寶可夢那裡逃\n走。" }, keenEye: { name: "銳利目光", - description: "多虧了銳利的目光,命中率\n不會被降低。", + description: "靠著銳利的目光,命中率不\n會被降低。", }, hyperCutter: { name: "怪力鉗", - description: "因爲擁有以力量自豪的鉗子,\n不會被對手降低攻擊。", + description: "因為擁有以力量自豪的鉗子,\n不會被對手降低攻擊。", }, pickup: { name: "撿拾", description: "有時會撿來對手用過的道具,\n冒險過程中也會撿到。", }, - truant: { name: "懶惰", description: "如果使出招式,下一回合就\n會休息。" }, - hustle: { name: "活力", description: "自己的攻擊變高,但命中率\n會降低。" }, + truant: { name: "懶惰", description: "如果使出招式,下一回合就\n需要休息。" }, + hustle: { name: "活力", description: "自己的攻擊雖會變高,但命\n中率會降低。" }, cuteCharm: { name: "迷人之軀", - description: "有時會讓接觸到自己的對手\n着迷。", + description: "有時會讓接觸到自己的對手陷\n入著迷狀態。", }, plus: { name: "正電", description: - "出場的夥伴之間如果有正電\n或負電特性的寶可夢,自己\n的特攻會提高。", + "場上的夥伴之中,如果有正\n電或負電特性的寶可夢,自\n己的特攻會提高。", }, minus: { name: "負電", description: - "出場的夥伴之間如果有正電\n或負電特性的寶可夢,自己\n的特攻會提高。", + "場上的夥伴之中,如果有正\n電或負電特性的寶可夢,自\n己的特攻會提高。", }, forecast: { name: "陰晴不定", description: - "受天氣的影響,會變爲水屬\n性、火屬性或冰屬性中的某\n一個。", + "在天氣的影響下,會變成水\n屬性、火屬性或冰屬性之中\n的一種。", }, stickyHold: { - name: "黏着", - description: "因爲道具是粘在黏性身體上\n的,所以不會被對手奪走。", + name: "黏著", + description: "道具會黏在具有黏性的身體\n上,不會被對手奪走。", }, shedSkin: { name: "蛻皮", - description: "通過蛻去身上的皮,有時會\n治癒異常狀態。", + description: "透過蛻去身上的皮,有時會\n治癒異常狀態。", }, guts: { name: "毅力", - description: "如果變爲異常狀態,會拿出\n毅力,攻擊會提高。", + description: "陷入異常狀態時,會拿出毅\n力,攻擊會提高。", }, marvelScale: { name: "神奇鱗片", - description: "如果變爲異常狀態,神奇鱗\n片會發生反應,防禦會提高。", + description: "陷入異常狀態時,神奇鱗片\n會發生反應,防禦會提高。", }, liquidOoze: { name: "污泥漿", description: - "吸收了污泥漿的對手會因強\n烈的惡臭而受到傷害,減少\nHP。", + "吸收了污泥漿的對手會因為\n強烈的惡臭而使得HP減少。", }, overgrow: { name: "茂盛", @@ -249,52 +250,52 @@ export const ability: AbilityTranslationEntries = { name: "堅硬腦袋", description: "即使使出會受反作用力傷害\n的招式,HP也不會減少。", }, - drought: { name: "日照", description: "出場時,會將天氣變爲晴朗。" }, + drought: { name: "日照", description: "出場時,會將天氣變為晴朗。" }, arenaTrap: { name: "沙穴", description: "在戰鬥中讓對手無法逃走。" }, vitalSpirit: { name: "幹勁", - description: "通過激發出幹勁,不會變爲\n睡眠狀態。", + description: "透過激發出幹勁,不會變為\n睡眠狀態。", }, whiteSmoke: { name: "白色煙霧", - description: "被白色煙霧保護着,不會被\n對手降低能力。", + description: "被白色煙霧保護著,不會被\n對手降低能力。", }, purePower: { name: "瑜伽之力", - description: "因瑜伽的力量,物理攻擊的\n威力會變爲2倍。", + description: "因瑜伽的力量,物理攻擊的\n威力會變為2倍。", }, shellArmor: { name: "硬殼盔甲", - description: "被堅硬的殼保護着,對手的\n攻擊不會擊中要害。", + description: "被堅硬的殼保護著,對手的\n攻擊不會擊中要害。", }, airLock: { name: "氣閘", description: "所有天氣的影響都會消失。" }, tangledFeet: { name: "蹣跚", - description: "在混亂狀態時,閃避率會提\n高。", + description: "陷入混亂狀態時,閃避率會\n提高。", }, motorDrive: { name: "電氣引擎", description: - "受到電屬性的招式攻擊時,\n不會受到傷害,而是速度會\n提高。", + "受到電屬性的招式攻擊時,\n不但不會受到傷害,反而速\n度會提高。", }, rivalry: { name: "鬥爭心", description: - "面對性別相同的對手,會燃\n起鬥爭心,變得更強。而面\n對性別不同的,則會變弱。", + "面對性別相同的對手,會燃\n起鬥爭心,變得更強。面對\n性別不同的對手時則會變弱。", }, steadfast: { name: "不屈之心", description: "每次畏縮時,不屈之心就會\n燃起,速度也會提高。", }, - snowCloak: { name: "雪隱", description: "下雪天氣時,閃避率會提高。" }, + snowCloak: { name: "雪隱", description: "天氣為下雪時,閃避率會提\n高。" }, gluttony: { - name: "貪喫鬼", + name: "貪吃鬼", description: - "原本HP變得很少時纔會喫\n樹果,在HP還有一半時就\n會把它喫掉。", + "原本HP變得很少時才會吃\n樹果,在HP還有一半時就\n會把它吃掉。", }, angerPoint: { name: "憤怒穴位", - description: "要害被擊中時,會大發雷霆\n,攻擊力變爲最大。", + description: "要害被擊中時會大發雷霆。\n攻擊力會提高到最大。", }, unburden: { name: "輕裝", @@ -302,23 +303,23 @@ export const ability: AbilityTranslationEntries = { }, heatproof: { name: "耐熱", - description: "耐熱的體質會讓火屬性的招\n式傷害減半。", + description: "靠著耐熱的體質,讓火屬性\n的招式傷害減半。", }, - simple: { name: "單純", description: "能力變化會變爲平時的2倍。" }, + simple: { name: "單純", description: "能力變化會變為平時的2倍。" }, drySkin: { name: "乾燥皮膚", description: - "下雨天氣時和受到水屬性的\n招式時,HP會回覆。晴朗\n天氣時和受到火屬性的招式\n時,HP會減少。", + "下雨天氣時和受到水屬性的\n招式時,HP會回復。晴朗\n天氣時和受到火屬性的招式\n時,HP會減少。", }, download: { name: "下載", description: "比較對手的防禦和特防,根\n據較低的那項能力相應地提\n高自己的攻擊或特攻。", }, - ironFist: { name: "鐵拳", description: "使用拳類招式的威力會提高。" }, + ironFist: { name: "鐵拳", description: "使用到拳頭的招式威力會\n提高。" }, poisonHeal: { name: "毒療", - description: "變爲中毒狀態時,HP不會\n減少,反而會增加起來。", + description: "陷入中毒狀態時,HP不會\n減少,反而會漸漸增加。", }, adaptability: { name: "適應力", @@ -326,133 +327,133 @@ export const ability: AbilityTranslationEntries = { }, skillLink: { name: "連續攻擊", - description: "如果使用連續招式,總是能\n使出最高次數。", + description: "使用連續招式時,每回都能\n以最多次數進行攻擊。", }, hydration: { - name: "溼潤之軀", - description: "下雨天氣時,異常狀態會治\n愈。", + name: "濕潤之軀", + description: "天氣為下雨時,會治癒異常\n狀態。", }, solarPower: { name: "太陽之力", - description: "晴朗天氣時,特攻會提高,\n而每回合HP會減少。", + description: "天氣為晴朗時特攻會提高,\n但每回合HP會減少。", }, quickFeet: { name: "飛毛腿", - description: "變爲異常狀態時,速度會提\n高。", + description: "陷入異常狀態時,速度會提\n高。", }, normalize: { name: "一般皮膚", description: - "無論是什麼屬性的招式,全\n部會變爲一般屬性。威力會\n少量提高。", + "無論是什麼屬性的招式,全\n部都會變為一般屬性。威力\n會少量提高。", }, - sniper: { name: "狙擊手", description: "擊中要害時,威力會變得更\n強。" }, + sniper: { name: "狙擊手", description: "擊中要害時,威力會進一步\n提高。" }, magicGuard: { name: "魔法防守", description: "不會受到攻擊以外的傷害。" }, noGuard: { name: "無防守", description: "由於無防守戰術,雙方使出\n的招式都必定會擊中。", }, - stall: { name: "慢出", description: "使出招式的順序必定會變爲\n最後。" }, + stall: { name: "慢出", description: "使出招式的順序必定會變為\n最後。" }, technician: { name: "技術高手", - description: "攻擊時可以將低威力招式的\n威力提高。", + description: "可讓威力低的招式提高威力\n來進行攻擊。", }, leafGuard: { name: "葉子防守", - description: "晴朗天氣時,不會變爲異常\n狀態。", + description: "天氣為晴朗時,不會陷入異\n常狀態。", }, klutz: { name: "笨拙", description: "無法使用持有的道具。" }, moldBreaker: { name: "破格", - description: "可以不受對手特性的干擾,\n向對手使出招式。", + description: "可不受特性影響,向對手使\n出招式。", }, superLuck: { name: "超幸運", - description: "因爲擁有超幸運,攻擊容易\n擊中對手的要害。", + description: "因為非常幸運,容易擊中對\n手的要害。", }, aftermath: { name: "引爆", - description: "變爲瀕死時,會對接觸到自\n己的對手造成傷害。", + description: "瀕死時,會對接觸到自己的\n對手造成傷害。", }, anticipation: { name: "危險預知", - description: "可以察覺到對手擁有的危險\n招式。", + description: "察覺對手持有的危險招式。", }, forewarn: { name: "預知夢", - description: "出場時,只讀取1個對手擁\n有的招式。", + description: "出場時,預見1個對手持有\n的招式。", }, unaware: { name: "純樸", - description: "可以無視對手能力的變化,\n進行攻擊。", + description: "可無視對手能力的變化,進\n行攻擊。", }, tintedLens: { name: "有色眼鏡", - description: "可以將效果不好的招式以通\n常的威力使出。", + description: "可將效果不好的招式以正常\n的威力使出。", }, filter: { name: "過濾", - description: "受到效果絕佳的攻擊時,可\n以減弱其威力。", + description: "受到效果絕佳的攻擊時,可\n減弱其威力。", }, slowStart: { name: "慢啓動", - description: "在5回合內,攻擊和速度減\n半。", + description: "在5回合內,攻擊和速度會\n減半。", }, scrappy: { name: "膽量", description: - "一般屬性和格鬥屬性的招式\n可以擊中幽靈屬性的寶可夢\n。對威嚇也毫不動搖。", + "一般屬性和格鬥屬性的招式\n可擊中幽靈屬性的寶可夢。\n面對威嚇也不會動搖。", }, stormDrain: { name: "引水", description: - "將水屬性的招式引到自己身\n上,不會受到傷害,而是會\n提高特攻。", + "將水屬性的招式引到自己身\n上,不但不會受到傷害,反\n而會提高特攻。", }, iceBody: { name: "冰凍之軀", - description: "下雪天氣時,會緩緩回覆\nHP。", + description: "天氣為下雪時,會漸漸回復\nHP。", }, solidRock: { name: "堅硬岩石", - description: "受到效果絕佳的攻擊時,可\n以減弱其威力。", + description: "受到效果絕佳的攻擊時,可\n減弱其威力。", }, - snowWarning: { name: "降雪", description: "出場時,會將天氣變爲下雪。" }, + snowWarning: { name: "降雪", description: "出場時,會將天氣變為下雪。" }, honeyGather: { name: "採蜜", description: "The Pokémon gathers Honey after a battle. The Honey is then sold for money.", }, frisk: { name: "察覺", - description: "進入戰鬥時,神奇寶貝可以檢查對方神奇寶貝的能力。", + description: "出場時,可以察覺對手的特\n性。", }, reckless: { name: "捨身", - description: "自己會因反作用力受傷的招\n式,其威力會提高。", + description: "會讓自己因反作用力而受傷\n的招式威力會提高。", }, multitype: { name: "多屬性", - description: "自己的屬性會根據持有的石\n板而改變。", + description: "自己的屬性會依持有的石板\n而改變。", }, flowerGift: { name: "花之禮", - description: "晴朗天氣時,自己與同伴的\n攻擊和特防能力會提高。", + description: "天氣為晴朗時,自己和同伴\n的攻擊和特防能力會提高。", }, - badDreams: { name: "夢魘", description: "給予睡眠狀態的對手傷害。" }, + badDreams: { name: "夢魘", description: "給予陷入睡眠狀態的對手傷\n害。" }, pickpocket: { name: "順手牽羊", description: "盜取接觸到自己的對手的道\n具。", }, sheerForce: { name: "強行", - description: "招式的追加效果消失,但因\n此能以更高的威力使出招式\n。", + description: "招式會失去追加效果,但可\n以用更高的威力使出招式。", }, contrary: { name: "唱反調", description: - "能力的變化發生逆轉,原本\n提高時會降低,而原本降低\n時會提高。", + "能力的變化會逆轉,原本提\n高時會降低,原本降低時會\n提高。", }, unnerve: { name: "緊張感", - description: "讓對手緊張,使其無法食用\n樹果。", + description: "讓對手感到緊張,無法吃樹\n果。", }, defiant: { name: "不服輸", @@ -460,35 +461,35 @@ export const ability: AbilityTranslationEntries = { }, defeatist: { name: "軟弱", - description: "HP減半時,會變得軟弱,\n攻擊和特攻會減半。", + description: "HP降到一半以下時,會變\n得軟弱而使得攻擊和特攻減\n半。", }, cursedBody: { name: "詛咒之軀", - description: "受到攻擊時,有時會把對手\n的招式變爲定身法狀態。", + description: "受到攻擊時,有時會把對手\n的招式變為定身法狀態。", }, - healer: { name: "治癒之心", description: "有時會治癒異常狀態的同伴。" }, - friendGuard: { name: "友情防守", description: "可以減少我方的傷害。" }, + healer: { name: "治癒之心", description: "有時會治癒同伴的異常狀態。" }, + friendGuard: { name: "友情防守", description: "可以減少我方受到的傷害。" }, weakArmor: { name: "碎裂鎧甲", - description: "受到物理招式的傷害時,防\n御會降低,速度會大幅提高。", + description: "因物理招式受到傷害時,防\n禦會降低,速度會大幅提高。", }, - heavyMetal: { name: "重金屬", description: "自身的重量會變爲2倍。" }, - lightMetal: { name: "輕金屬", description: "自身的重量會減半。" }, + heavyMetal: { name: "重金屬", description: "自己的重量會變為2倍。" }, + lightMetal: { name: "輕金屬", description: "自己的重量會減半。" }, multiscale: { name: "多重鱗片", description: "HP全滿時,受到的傷害會\n變少。", }, toxicBoost: { name: "中毒激升", - description: "變爲中毒狀態時,物理招式\n的威力會提高。", + description: "陷入中毒狀態時,物理招式\n的威力會提高。", }, flareBoost: { name: "受熱激升", - description: "變爲灼傷狀態時,特殊招式\n的威力會提高。", + description: "陷入灼傷狀態時,特殊招式\n的威力會提高。", }, harvest: { name: "收穫", - description: "可以多次製作出已被使用掉\n的樹果。", + description: "可多次採收已被使用過的樹果。", }, telepathy: { name: "心靈感應", @@ -505,62 +506,62 @@ export const ability: AbilityTranslationEntries = { }, poisonTouch: { name: "毒手", - description: "只通過接觸就有可能讓對手\n變爲中毒狀態。", + description: "有時僅是接觸就能讓對手中\n毒。", }, regenerator: { name: "再生力", description: "退回同行隊伍後,HP會少\n量回復。", }, - bigPecks: { name: "健壯胸肌", description: "不會受到防禦降低的效果。" }, - sandRush: { name: "撥沙", description: "沙暴天氣時,速度會提高。" }, + bigPecks: { name: "健壯胸肌", description: "不會受到降低防禦的效果影\n響。" }, + sandRush: { name: "撥沙", description: "天氣為沙暴時,速度會提高。" }, wonderSkin: { name: "奇蹟皮膚", - description: "成爲不易受到變化招式攻擊\n的身體。", + description: "不易受到變化類招式攻擊的\n身體。", }, analytic: { name: "分析", - description: "如果在最後使出招式,招式\n的威力會提高。", + description: "如果在最後使出招式,招式\n的威力就會變強。", }, illusion: { name: "幻覺", - description: "假扮成同行隊伍中的最後一\n只寶可夢出場,迷惑對手。", + description: "假扮成同行隊伍中的最後一\n隻寶可夢出場,迷惑對手。", }, - imposter: { name: "變身者", description: "變身爲當前面對的寶可夢。" }, + imposter: { name: "變身者", description: "變身為當前面對的寶可夢。" }, infiltrator: { name: "穿透", - description: "可以穿透對手的壁障或替身\n進行攻擊。", + description: "可穿透對手的屏障或替身進\n行攻擊。", }, mummy: { name: "木乃伊", - description: "被對手接觸到後,會將對手\n變爲木乃伊。", + description: "被對手接觸到後,會將對手\n變為木乃伊。", }, moxie: { name: "自信過度", - description: "如果打倒對手,就會充滿自\n信,攻擊會提高。", + description: "如果打倒對手,會充滿自信\n並提高攻擊。", }, justified: { name: "正義之心", - description: "受到惡屬性的招式攻擊時,\n因爲正義感,攻擊會提高。", + description: "受到惡屬性的招式攻擊時,\n因為正義感,攻擊會提高。", }, rattled: { name: "膽怯", description: - "受到惡屬性、幽靈屬性和蟲\n屬性的攻擊或威嚇時,會因\n膽怯而速度提高。", + "受到惡屬性、幽靈屬性和蟲\n屬性的招式攻擊,或受到威\n嚇時,會因膽怯而使得速度\n提高。", }, magicBounce: { name: "魔法鏡", - description: "可以不受到由對手使出的變\n化招式影響,並將其反彈。", + description: "可不受到由對手使出的變化\n類招式所影響,並將其反彈。", }, sapSipper: { name: "食草", description: - "受到草屬性的招式攻擊時,\n不會受到傷害,而是攻擊會\n提高。", + "受到草屬性的招式攻擊時,\n不但不會受到傷害,反而攻\n擊會提高。", }, - prankster: { name: "惡作劇之心", description: "可以率先使出變化招式。" }, + prankster: { name: "惡作劇之心", description: "可以搶先使出變化類招式。" }, sandForce: { name: "沙之力", description: - "沙暴天氣時,岩石屬性、地\n面屬性和鋼屬性的招式威力\n會提高。", + "天氣為沙暴時,岩石屬性、\n地面屬性和鋼屬性招式的威\n力會提高。", }, ironBarbs: { name: "鐵刺", @@ -568,7 +569,7 @@ export const ability: AbilityTranslationEntries = { }, zenMode: { name: "達摩模式", - description: "HP變爲一半以下時,樣子\n會改變。", + description: "HP變為一半以下時,樣子\n會改變。", }, victoryStar: { name: "勝利之星", @@ -584,24 +585,24 @@ export const ability: AbilityTranslationEntries = { }, aromaVeil: { name: "芳香幕", - description: "可以防住向自己和同伴發出\n的心靈攻擊。", + description: "可防住向自己和同伴發出的\n心靈攻擊。", }, flowerVeil: { name: "花幕", - description: "我方的草屬性寶可夢能力不\n會降低,也不會變爲異常狀\n態。", + description: "我方的草屬性寶可夢能力不\n會降低。也不會陷入異常狀\n態。", }, cheekPouch: { name: "頰囊", - description: "無論是哪種樹果,食用後,\nHP都會回覆。", + description: "無論是哪種樹果,吃下去後\nHP都會回復。", }, protean: { name: "變幻自如", description: - "變爲與自己使出的招式相同\n的屬性。每次出場戰鬥僅生\n效一次。", + "每次出場戰鬥時,變為與自\n己使出的招式相同的屬性1\n次。", }, furCoat: { name: "毛皮大衣", - description: "對手給予的物理招式的傷害\n會減半。", + description: "對手的物理招式造成的傷害\n會減半。", }, magician: { name: "魔術師", @@ -609,32 +610,32 @@ export const ability: AbilityTranslationEntries = { }, bulletproof: { name: "防彈", - description: "可以防住對手的球和彈類招\n式。", + description: "可防住對手的球和彈類的招\n式。", }, competitive: { name: "好勝", - description: "如果被對手降低能力,特攻\n會大幅提高。", + description: "被對手降低能力時,特攻會\n大幅提高。", }, strongJaw: { name: "強壯之顎", - description: "因爲顎部強壯,啃咬類招式\n的威力會提高。", + description: "顎部強壯,會提高啃咬類招\n式的威力。", }, refrigerate: { name: "冰凍皮膚", - description: "一般屬性的招式會變爲冰屬\n性。威力會少量提高。", + description: "一般屬性的招式會變為冰屬\n性。威力會少量提高。", }, sweetVeil: { name: "甜幕", - description: "自己和同伴的寶可夢不會變\n爲睡眠狀態。", + description: "自己和我方的寶可夢不會陷\n入睡眠狀態。", }, stanceChange: { name: "戰鬥切換", description: - "如果使出攻擊招式,會變爲\n刀劍形態,如果使出招式“\n王者盾牌”,會變爲盾牌形\n態。", + "若使出攻擊招式,會變為刀\n劍形態,若使出招式「王者\n盾牌」,會變為盾牌形態。", }, galeWings: { name: "疾風之翼", - description: "HP全滿時,飛行屬性的招\n式可以率先使出。", + description: "HP全滿時,可以搶先在對\n手之前使出飛行屬性的招式。", }, megaLauncher: { name: "超級發射器", @@ -648,43 +649,43 @@ export const ability: AbilityTranslationEntries = { toughClaws: { name: "硬爪", description: "接觸到對手的招式威力會提\n高。" }, pixilate: { name: "妖精皮膚", - description: "一般屬性的招式會變爲妖精\n屬性。威力會少量提高。", + description: "一般屬性的招式會變為妖精\n屬性。威力會少量提高。", }, gooey: { name: "黏滑", - description: "對於用攻擊接觸到自己的對\n手,會降低其速度。", + description: "對手用攻擊接觸到自己時,\n降低此對手的速度。", }, aerilate: { name: "飛行皮膚", - description: "一般屬性的招式會變爲飛行\n屬性。威力會少量提高。", + description: "一般屬性的招式會變為飛行\n屬性。威力會少量提高。", }, - parentalBond: { name: "親子愛", description: "親子倆可以合計攻擊2次。" }, + parentalBond: { name: "親子愛", description: "親子倆可合計攻擊2次。" }, darkAura: { name: "暗黑氣場", description: "全體的惡屬性招式變強。" }, fairyAura: { name: "妖精氣場", description: "全體的妖精屬性招式變強。" }, auraBreak: { name: "氣場破壞", - description: "讓氣場的效果發生逆轉,降\n低威力。", + description: "讓氣場的效果逆轉,並降低\n威力。", }, primordialSea: { name: "始源之海", - description: "變爲不會受到火屬性攻擊的\n天氣。", + description: "變為讓火屬性攻擊失效的天\n氣。", }, desolateLand: { name: "終結之地", - description: "變爲不會受到水屬性攻擊的\n天氣。", + description: "變為讓水屬性攻擊失效的天\n氣。", }, deltaStream: { name: "德爾塔氣流", - description: "變爲令飛行屬性的弱點消失\n的天氣。", + description: "變為令飛行屬性的弱點消失\n的天氣。", }, stamina: { name: "持久力", description: "受到攻擊時,防禦會提高。" }, wimpOut: { name: "躍躍欲逃", - description: "HP變爲一半時,會慌慌張\n張逃走,退回同行隊伍中。", + description: "HP變為一半時,會慌慌張\n張逃走,退回同行隊伍中。", }, emergencyExit: { name: "危險迴避", - description: "HP變爲一半時,爲了迴避\n危險,會退回到同行隊伍中。", + description: "HP減到一半時,為了避開\n危險,會退回到同行隊伍中。", }, waterCompaction: { name: "遇水凝固", @@ -696,11 +697,11 @@ export const ability: AbilityTranslationEntries = { }, shieldsDown: { name: "界限盾殼", - description: "HP變爲一半時,殼會壞掉,\n變得有攻擊性。", + description: "HP變為一半時,殼會壞掉,\n變得更有攻擊性。", }, stakeout: { name: "蹲守", - description: "可以對替換出場的對手以2\n倍的傷害進行攻擊。", + description: "可以向替換出場的對手以2\n倍的傷害進行攻擊。", }, waterBubble: { name: "水泡", @@ -709,52 +710,52 @@ export const ability: AbilityTranslationEntries = { steelworker: { name: "鋼能力者", description: "鋼屬性的招式威力會提高。" }, berserk: { name: "怒火沖天", - description: "因對手的攻擊HP變爲一半\n時,特攻會提高。", + description: "HP因對手的攻擊降到一半\n時,特攻會提高。", }, - slushRush: { name: "撥雪", description: "下雪天氣時,速度會提高。" }, + slushRush: { name: "撥雪", description: "天氣為下雪時,速度會提高。" }, longReach: { name: "遠隔", description: "可以不接觸對手就使出所有\n的招式。", }, liquidVoice: { name: "溼潤之聲", - description: "所有的聲音招式都變爲水屬\n性。", + description: "所有的聲音招式都變為水屬\n性。", }, - triage: { name: "先行治療", description: "可以率先使出回覆招式。" }, + triage: { name: "先行治療", description: "可以搶先使出回復招式。" }, galvanize: { name: "電氣皮膚", - description: "一般屬性的招式會變爲電屬\n性。威力會少量提高。", + description: "一般屬性的招式會變為電屬\n性。威力會少量提高。", }, surgeSurfer: { name: "衝浪之尾", - description: "電氣場地時,速度會變爲2\n倍。", + description: "電氣場地時,速度會變為2\n倍。", }, schooling: { name: "魚羣", description: - "HP多的時候會聚起來變強。\nHP剩餘量變少時,羣體\n會分崩離析。", + "HP多的時候會聚起來變強。\nHP剩餘量變少時,群體\n會分崩離析。", }, disguise: { name: "畫皮", - description: "通過畫皮覆蓋住身體,可以\n防住1次攻擊。", + description: "用畫皮覆蓋住身體,可防住\n1次攻擊。", }, battleBond: { name: "牽絆變身", description: - "打倒對手時,與訓練家的牽\n絆會增強,自己的攻擊、特\n攻、速度會提高。", + "打倒對手時,與訓練家的牽\n絆會加深,自己的攻擊、特\n攻和速度會提高。", }, powerConstruct: { - name: "羣聚變形", - description: "HP變爲一半時,細胞們會\n趕來支援,變爲完全體形態。", + name: "群聚變形", + description: "HP變為一半時,細胞們會\n趕來支援,變為完全體形態。", }, corrosion: { name: "腐蝕", - description: "可以使鋼屬性和毒屬性的寶\n可夢也陷入中毒狀態。", + description: "就算對方是鋼屬性或毒屬性\n寶可夢,也可讓對方陷入中\n毒狀態。", }, comatose: { name: "絕對睡眠", description: - "總是半夢半醒的狀態,絕對\n不會醒來。可以就這麼睡着\n進行攻擊。", + "總是半夢半醒的狀態,絕對\n不會醒來。可在睡著的狀況\n下進行攻擊。", }, queenlyMajesty: { name: "女王的威嚴", @@ -766,13 +767,13 @@ export const ability: AbilityTranslationEntries = { }, dancer: { name: "舞者", - description: "有誰使出跳舞招式時,自己\n也能就這麼接着使出跳舞招\n式。", + description: "當有誰使出跳舞招式時,自\n己也能接著使出跳舞招式。", }, battery: { name: "蓄電池", description: "會提高我方的特殊招式的威\n力。" }, fluffy: { name: "毛茸茸", description: - "會將對手所給予的接觸類招\n式的傷害減半,但火屬性招\n式的傷害會變爲2倍。", + "會將對手所給予的接觸類招\n式的傷害減半,但火屬性招\n式的傷害會變為2倍。", }, dazzling: { name: "鮮豔之軀", @@ -780,44 +781,44 @@ export const ability: AbilityTranslationEntries = { }, soulHeart: { name: "魂心", - description: "寶可夢每次變爲瀕死狀態時\n,特攻會提高。", + description: "每當場上有寶可夢陷入瀕死\n狀態時,特攻就會提高。", }, tanglingHair: { name: "捲髮", - description: "對於用攻擊接觸到自己的對\n手,會降低其速度。", + description: "對手用攻擊接觸到自己時,\n降低此對手的速度。", }, receiver: { name: "接球手", - description: "繼承被打倒的同伴的特性,\n變爲相同的特性。", + description: "繼承被打倒的同伴的特性,\n變為相同的特性。", }, powerOfAlchemy: { name: "化學之力", - description: "繼承被打倒的同伴的特性,\n變爲相同的特性。", + description: "繼承被打倒的同伴的特性,\n變為相同的特性。", }, beastBoost: { name: "異獸提升", - description: "打倒對手的時候,自己最高\n的那項能力會提高。", + description: "打倒對手的時候,會提高自\n己最高的那項能力。", }, rksSystem: { name: "AR系統", - description: "根據持有的存儲碟,自己的\n屬性會改變。", + description: "根據持有的記憶碟,自己的\n屬性會改變。", }, electricSurge: { name: "電氣製造者", - description: "出場時,會佈下電氣場地。", + description: "出場時,會布下電氣場地。", }, psychicSurge: { name: "精神製造者", - description: "出場時,會佈下精神場地。", + description: "出場時,會布下精神場地。", }, - mistySurge: { name: "薄霧製造者", description: "出場時,會佈下薄霧場地。" }, + mistySurge: { name: "薄霧製造者", description: "出場時,會布下薄霧場地。" }, grassySurge: { name: "青草製造者", - description: "出場時,會佈下青草場地。", + description: "出場時,會布下青草場地。", }, fullMetalBody: { name: "金屬防護", - description: "不會因爲對手的招式或特性\n而被降低能力。", + description: "不會因對手的招式或特性而\n被降低能力。", }, shadowShield: { name: "幻影防守", @@ -825,33 +826,33 @@ export const ability: AbilityTranslationEntries = { }, prismArmor: { name: "棱鏡裝甲", - description: "受到效果絕佳的攻擊時,可\n以減弱其威力。", + description: "受到效果絕佳的攻擊時,可\n減弱其威力。", }, neuroforce: { name: "腦核之力", - description: "效果絕佳的攻擊,威力會變\n得更強。", + description: "可進一步提升效果絕佳招式\n的威力。", }, intrepidSword: { name: "不撓之劍", - description: "首次出場時,攻擊會提高。", + description: "在戰鬥中首次出場時,攻擊\n會提高。", }, dauntlessShield: { name: "不屈之盾", - description: "首次出場時,防禦會提高。", + description: "在戰鬥中首次出場時,防禦\n會提高。", }, libero: { name: "自由者", description: - "變爲與自己使出的招式相同\n的屬性。每次出場戰鬥僅生\n效一次。", + "每次出場戰鬥時,變為與自\n己使出的招式相同的屬性1\n次。", }, ballFetch: { name: "撿球", - description: "沒有攜帶道具時,會拾取第\n1個投出後捕捉失敗的精靈\n球。", + description: "當寶可夢沒有攜帶道具時,\n會撿回第1個投出後捕捉失\n敗的精靈球。", }, cottonDown: { name: "棉絮", description: - "受到攻擊後撒下棉絮,降低\n除自己以外的所有寶可夢的\n速度。", + "受到攻擊時會撒下棉絮,降\n低除自己以外的所有寶可夢\n的速度。", }, propellerTail: { name: "螺旋尾鰭", @@ -864,7 +865,7 @@ export const ability: AbilityTranslationEntries = { gulpMissile: { name: "一口導彈", description: - "衝浪或潛水時會叼來獵物。\n受到傷害時,會吐出獵物進\n行攻擊。", + "衝浪或潛水時會叼來獵物。\n當受到傷害時,會吐出獵物\n攻擊對手。", }, stalwart: { name: "堅毅", @@ -872,18 +873,18 @@ export const ability: AbilityTranslationEntries = { }, steamEngine: { name: "蒸汽機", - description: "受到水屬性或火屬性的招式\n攻擊時,速度會巨幅提高。", + description: "受到水屬性或火屬性招式攻\n擊時,速度會極大幅提高。", }, punkRock: { name: "龐克搖滾", - description: "聲音招式的威力會提高。受\n到的聲音招式傷害會減半。", + description: "聲音招式的威力會提高。受\n到聲音招式的傷害會減半。", }, - sandSpit: { name: "吐沙", description: "受到攻擊時,會颳起沙暴。" }, + sandSpit: { name: "吐沙", description: "受到攻擊時,會刮起沙暴。" }, iceScales: { name: "冰鱗粉", - description: "由於有冰鱗粉的守護,受到\n的特殊攻擊傷害會減半。", + description: "得到冰鱗粉的守護,受到的\n特殊攻擊傷害會減半。", }, - ripen: { name: "熟成", description: "使樹果成熟,效果變爲2倍。" }, + ripen: { name: "熟成", description: "讓樹果成熟,使效果變為2\n倍。" }, iceFace: { name: "結凍頭", description: @@ -891,11 +892,11 @@ export const ability: AbilityTranslationEntries = { }, powerSpot: { name: "能量點", - description: "只要處在相鄰位置,招式的\n威力就會提高。", + description: "只要站在旁邊,招式的威力\n就會提高。", }, mimicry: { name: "擬態", - description: "寶可夢的屬性會根據場地的\n狀態而變化。", + description: "寶可夢的屬性會根據場地的\n狀態而改變。", }, screenCleaner: { name: "除障", @@ -909,7 +910,7 @@ export const ability: AbilityTranslationEntries = { perishBody: { name: "滅亡之軀", description: - "受到接觸類招式攻擊時,雙\n方都會在3回合後變爲瀕死\n狀態。替換後效果消失。", + "在受到接觸類招式攻擊時,\n3個回合後雙方都會陷入瀕\n死。替換寶可夢後效果就\n會消失。", }, wanderingSpirit: { name: "遊魂", @@ -917,30 +918,30 @@ export const ability: AbilityTranslationEntries = { }, gorillaTactics: { name: "一猩一意", - description: "雖然攻擊會提高,但是隻能\n使出一開始所選的招式。", + description: "攻擊雖然會提高,但只能使\n出最初選擇的招式。", }, neutralizingGas: { name: "化學變化氣體", description: - "特性爲化學變化氣體的寶可\n夢在場時,場上所有寶可夢\n的特性效果都會消失或者無\n法生效。", + "當場上有特性是化學變化氣\n體的寶可夢時,所有寶可夢\n的特性效果都會消失或無\n法發動。", }, pastelVeil: { name: "粉彩護幕", - description: "自己和同伴都不會陷入中毒\n的異常狀態。", + description: "自己和我方同伴都不會陷入\n中毒的異常狀態。", }, hungerSwitch: { name: "飽了又餓", - description: "每回合結束時會在滿腹花紋\n與空腹花紋之間交替改變樣\n子。", + description: "在每個回合結束時,會在滿\n腹花紋和空腹花紋之間交替\n改變樣子。", }, quickDraw: { name: "速擊", description: "有時能比對手先一步行動。" }, unseenFist: { name: "無形拳", description: - "如果使出的是接觸到對手的\n招式,就可以無視守護效果\n進行攻擊。", + "只要是接觸到對手的招式,\n就可以無視對手的防守效果\n進行攻擊。", }, curiousMedicine: { name: "怪藥", - description: "出場時會從貝殼撒藥,將我\n方的能力變化復原。", + description: "出場時,會從貝殼撒藥,將\n我方的能力變化復原。", }, transistor: { name: "電晶體", description: "電屬性的招式威力會提高。" }, dragonsMaw: { name: "龍顎", description: "龍屬性的招式威力會提高。" }, @@ -954,15 +955,15 @@ export const ability: AbilityTranslationEntries = { }, asOneGlastrier: { name: "人馬一體", - description: "兼備蕾冠王的緊張感和靈幽\n馬的漆黑嘶鳴這兩種特性。", + description: "兼備蕾冠王的緊張感和雪暴\n馬的蒼白嘶鳴這2種特性。", }, asOneSpectrier: { name: "人馬一體", - description: "兼備蕾冠王的緊張感和靈幽\n馬的漆黑嘶鳴這兩種特性。", + description: "兼備蕾冠王的緊張感和靈幽\n馬的漆黑嘶鳴這2種特性。", }, lingeringAroma: { name: "甩不掉的氣味", - description: "被對手接觸到後,甩不掉的\n氣味會沾上對手。", + description: "被對手接觸到時,甩不掉的\n氣味會沾染給對手。", }, seedSower: { name: "掉出種子", @@ -971,123 +972,122 @@ export const ability: AbilityTranslationEntries = { thermalExchange: { name: "熱交換", description: - "受到火屬性的招式攻擊時,\n攻擊會提高,且不會陷入灼\n傷狀態。", + "受到火屬性的招式攻擊時,\n攻擊會提高,不會陷入灼傷\n狀態。", }, angerShell: { name: "憤怒甲殼", description: - "因被對手攻擊而HP變爲一\n半時,會因憤怒降低防禦和\n特防。但攻擊、特攻、速度\n會提高。", + "HP因對手的攻擊降到一半\n時,會因憤怒而降低防禦和\n特防,但攻擊、特攻和速度\n會提高。", }, purifyingSalt: { name: "潔淨之鹽", description: - "因潔淨的鹽而不會陷入異常\n狀態。會讓幽靈屬性的招式\n傷害減半。", + "因潔淨的鹽而不會陷入異常\n狀態。能夠讓幽靈屬性招式\n的傷害減半。", }, wellBakedBody: { name: "焦香之軀", description: - "受到火屬性的招式攻擊時,\n不會受到傷害,而是會大幅\n提高防禦。", + "受到火屬性的招式攻擊時,\n不但不會受到傷害,反而防\n禦會大幅提高", }, windRider: { name: "乘風", description: - "吹起了順風或受到風的招式\n攻擊時,不會受到傷害,而\n是會提高攻擊。", + "吹起順風或受到風的招式攻\n擊時,不但不會受到傷害,\n反而攻擊會提高。", }, guardDog: { name: "看門犬", description: - "受到威嚇時,攻擊會提高。\n讓替換寶可夢的招式和道具\n無效。", + "受到威嚇時,攻擊會提高。\n會讓替換寶可夢的招式和道\n具失效。", }, rockyPayload: { name: "搬巖", description: "岩石屬性的招式威力會提高。" }, windPower: { name: "風力發電", - description: "受到風的招式攻擊時,會變\n爲充電狀態。", + description: "受到風的招式攻擊時,會變\n成充電狀態。", }, zeroToHero: { name: "全能變身", - description: "回到同行隊伍後,會變爲全\n能形態。", + description: "離場後會變為全能形態。", }, commander: { name: "發號施令", description: - "出場時,若我方當中有喫吼\n霸,就會進入其口中,並從\n其口中發出指令。", + "出場時,若我方有吃吼霸,\n便會進入吃吼霸的口中,從\n那裡發號施令。", }, electromorphosis: { name: "電力轉換", - description: "受到傷害時,會變爲充電狀\n態。", + description: "受到傷害時,會變成充電狀\n態。", }, protosynthesis: { name: "古代活性", - description: "攜帶着驅勁能量或天氣爲晴\n朗時,數值最高的能力會提\n高。", + description: "攜帶著驅勁能量或天氣為晴\n朗時,數值最高的能力會提\n高。", }, quarkDrive: { name: "夸克充能", description: - "攜帶着驅勁能量或在電氣場\n地上時,數值最高的能力會\n提高。", + "攜帶著驅勁能量或在電氣場\n地上時,數值最高的能力會\n提高。", }, goodAsGold: { name: "黃金之軀", - description: "不會氧化的堅固黃金身軀不\n會受到對手的變化招式的影\n響。", + description: "既不氧化又堅韌的黃金之軀\n不會受到對手的變化類招式\n攻擊。", }, vesselOfRuin: { name: "災禍之鼎", - description: "以能呼喚災厄的鼎的力量降\n低除自己以外的寶可夢的特\n攻。", + description: "在喚來災厄之鼎的力量下,\n除自己以外的特攻會變弱。", }, swordOfRuin: { name: "災禍之劍", - description: "以能呼喚災厄的劍的力量降\n低除自己以外的寶可夢的防\n御。", + description: "在喚來災厄之劍的力量下,\n除自己以外的防禦會變弱。", }, tabletsOfRuin: { name: "災禍之簡", - description: "以能呼喚災厄的簡的力量降\n低除自己以外的寶可夢的攻\n擊。", + description: "在喚來災厄之木簡的力量下\n,除自己以外的攻擊會變弱。", }, beadsOfRuin: { name: "災禍之玉", - description: - "以能呼喚災厄的勾玉的力量\n降低除自己以外的寶可夢的\n特防。", + description: "在喚來災厄之木簡的力量下\n,除自己以外的特防會變弱。", }, orichalcumPulse: { name: "緋紅脈動", description: - "出場時,會將天氣變爲晴朗\n。日照強烈時,會通過古代\n的脈動升高攻擊。", + "出場時,會將天氣變為晴朗\n。日照很強時,會因為古代\n的脈動而使攻擊升高。", }, hadronEngine: { name: "強子引擎", description: - "出場時,會佈下電氣場地。\n處於電氣場地時,會通過未\n來的機關升高特攻。", + "出場時,會布下電氣場地。\n在電氣場地時,會因為未來\n的機關而使特攻升高。", }, opportunist: { name: "跟風", - description: "對手的能力提高時,自己也\n會趁機同樣地提高能力。", + description: "對手的能力提高時,自己也\n會跟著提高能力。", }, cudChew: { name: "反芻", - description: "喫了樹果後,會在下一回合\n結束時從胃反芻出來再喫1\n次。", + description: "食用樹果後,會在下一回合\n結束時從胃裡取出,以1次\n為限再次食用。", }, - sharpness: { name: "鋒銳", description: "提高切割對手的招式的威力。" }, + sharpness: { name: "鋒銳", description: "切斬對手的招式威力會提高。" }, supremeOverlord: { name: "大將", description: - "出場時,攻擊和特攻會按照\n目前被打倒的同伴數量逐漸\n提升,被打倒越多,提升越\n多。", + "出場時,先前每有1隻同伴\n被打倒,攻擊和特攻就會提\n高少許。", }, - costar: { name: "同臺共演", description: "出場時,複製同伴的能力變\n化。" }, + costar: { name: "同台共演", description: "出場時,會複製同伴的能力\n變化。" }, toxicDebris: { name: "毒滿地", - description: "受到物理招式的傷害時,會\n在對手腳下散佈毒菱。", + description: "因物理招式受到傷害時,會\n在對手腳下散布毒菱。", }, armorTail: { name: "尾甲", - description: "包裹頭部的神祕尾巴使對手\n無法對我方使出先制招式。", + description: "包覆著頭部的神秘尾巴使對\n手無法對我方使出先制招式。", }, earthEater: { name: "食土", description: - "受到地面屬性的招式攻擊時\n,不會受到傷害,而是會得\n到回覆。", + "受到地面屬性的招式攻擊時\n,不會受到傷害,而是會回\n復。", }, myceliumMight: { name: "菌絲之力", description: - "使出變化招式時,雖然行動\n必定會變慢,但能不受對手\n的特性妨礙。", + "使出變化類招式時,行動一\n定會變緩慢,但不會受到對\n手特性的干擾。", }, mindsEye: { name: "心眼", @@ -1097,50 +1097,50 @@ export const ability: AbilityTranslationEntries = { supersweetSyrup: { name: "甘露之蜜", description: - "首次出場時,會散發出甜膩\n的蜜的香味來降低對手的閃\n避率。", + "在對戰中首次出場時,會四\n處散播甜膩的蜜香,降低對\n手的閃避率。", }, hospitality: { name: "款待", - description: "出場時款待同伴,回覆其少\n量HP。", + description: "出場時款待同伴,使其回復\n少量HP。", }, toxicChain: { name: "毒鎖鏈", description: - "憑藉含有毒素的鎖鏈的力量,\n有時能讓被招式擊中的對\n手陷入劇毒狀態。", + "靠著含有毒素的鎖鏈的力量\n,有時會讓被招式擊中的對\n手陷入劇毒狀態。", }, embodyAspectTeal: { name: "面影輝映", - description: "將回憶映於心中,讓水井面\n具發出光輝,提高自己的特\n防。", + description: "將回憶映於心中,使碧草面\n具發出光輝,提高自己的速\n度。", }, embodyAspectWellspring: { name: "面影輝映", - description: "將回憶映於心中,讓碧草面\n具發出光輝,提高自己的速\n度。", + description: "將回憶映於心中,使水井面\n具發出光輝,提高自己的特\n防。", }, embodyAspectHearthflame: { name: "面影輝映", - description: "將回憶映於心中,讓火竈面\n具發出光輝,提高自己的攻\n擊。", + description: "將回憶映於心中,使火灶面\n具發出光輝,提高自己的攻\n擊。", }, embodyAspectCornerstone: { name: "面影輝映", - description: "將回憶映於心中,讓礎石面\n具發出光輝,提高自己的防\n御。", + description: "將回憶映於心中,使礎石面\n具發出光輝,提高自己的防\n御。", }, teraShift: { name: "太晶變形", - description: "出場時,會吸收周圍的能量\n,變爲太晶形態。", + description: "出場時,會吸收周圍的能量\n,變為太晶形態。", }, teraShell: { name: "太晶甲殼", description: - "甲殼蘊藏着全部屬性的力量\n,會將自己HP全滿時受到\n的傷害全都變爲效果不好。", + "蘊藏著所有屬性力量的甲殼\n會將自身HP全滿時受到的\n傷害全都變為效果不好。", }, teraformZero: { name: "歸零化境", description: - "太樂巴戈斯變爲星晶形態時\n,蘊藏在它身上的力量會將\n天氣和場地的影響全部歸零。", + "太樂巴戈斯變為星晶形態時\n,蘊藏其身的力量會將天氣\n和場地的影響全部歸零。", }, poisonPuppeteer: { name: "毒傀儡", description: - "因桃歹郎的招式而陷入中毒\n狀態的對手同時也會陷入混\n亂狀態。", + "因為桃歹郎的招式而陷入中\n毒狀態的對手同時也會陷入\n混亂狀態。", }, } as const; diff --git a/src/locales/zh_TW/battle.ts b/src/locales/zh_TW/battle.ts index c0846e1cb18..4255aeaef99 100644 --- a/src/locales/zh_TW/battle.ts +++ b/src/locales/zh_TW/battle.ts @@ -53,6 +53,9 @@ export const battle: SimpleTranslationEntries = { "skipItemQuestion": "你要跳過拾取道具嗎?", "eggHatching": "咦?", "ivScannerUseQuestion": "對 {{pokemonName}} 使用個體值掃描?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", "drainMessage": "{{pokemonName}} had its\nenergy drained!", "regainHealth": "{{pokemonName}} regained\nhealth!" } as const; diff --git a/src/locales/zh_TW/menu.ts b/src/locales/zh_TW/menu.ts index 41813457c32..680db51e8ac 100644 --- a/src/locales/zh_TW/menu.ts +++ b/src/locales/zh_TW/menu.ts @@ -49,4 +49,6 @@ export const menu: SimpleTranslationEntries = { "empty":"空", "yes":"是", "no":"否", + "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." } as const; diff --git a/src/locales/zh_TW/modifier-type.ts b/src/locales/zh_TW/modifier-type.ts index 1ad51965937..a6e73ebfc28 100644 --- a/src/locales/zh_TW/modifier-type.ts +++ b/src/locales/zh_TW/modifier-type.ts @@ -98,6 +98,10 @@ export const modifierType: ModifierTypeTranslationEntries = { name: "招式學習器 {{moveId}} - {{moveName}}", description: "教會一隻寶可夢{{moveName}}", }, + TmModifierTypeWithInfo: { + name: "TM{{moveId}} - {{moveName}}", + description: "教會一隻寶可夢{{moveName}}\n(Hold C or Shift for more info)", + }, EvolutionItemModifierType: { description: "使某些寶可夢進化" }, FormChangeItemModifierType: { description: "使某些寶可夢更改形態" }, FusePokemonModifierType: { diff --git a/src/locales/zh_TW/weather.ts b/src/locales/zh_TW/weather.ts index 0a235b3b10c..03f3425aa43 100644 --- a/src/locales/zh_TW/weather.ts +++ b/src/locales/zh_TW/weather.ts @@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = { "sandstormStartMessage": "開始刮沙暴了!", "sandstormLapseMessage": "沙暴肆虐。", "sandstormClearMessage": "沙暴停止了。", - "sandstormDamageMessage": "沙暴襲擊了{{pokemonPrefix}}{{pokemonName}}!", + "sandstormDamageMessage": "沙暴襲擊了{{pokemonNameWithAffix}}!", "hailStartMessage": "開始下冰雹了!", "hailLapseMessage": "冰雹繼續肆虐。", "hailClearMessage": "冰雹不再下了。", - "hailDamageMessage": "冰雹襲擊了{{pokemonPrefix}}{{pokemonName}}!", + "hailDamageMessage": "冰雹襲擊了{{pokemonNameWithAffix}}!", "snowStartMessage": "開始下雪了!", "snowLapseMessage": "雪繼續下。", diff --git a/src/messages.ts b/src/messages.ts index a8549c7356b..e2387094502 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,19 +1,40 @@ import { BattleSpec } from "./enums/battle-spec"; import Pokemon from "./field/pokemon"; +import i18next from "./plugins/i18n"; +/** + * Builds a message by concatenating the Pokemon name with its potential affix and the given text + * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance for {@linkcode getPokemonNameWithAffix} + * @param {string} content any text + * @returns {string} ex: "Wild Gengar fainted!", "Ectoplasma sauvage est K.O!" + * @see {@linkcode getPokemonNameWithAffix} for the Pokemon's name and potentiel affix + */ export function getPokemonMessage(pokemon: Pokemon, content: string): string { - return `${getPokemonPrefix(pokemon)}${pokemon.name}${content}`; + return `${getPokemonNameWithAffix(pokemon)}${content}`; } -export function getPokemonPrefix(pokemon: Pokemon): string { - let prefix: string; +/** + * Retrieves the Pokemon's name, potentially with an affix indicating its role (wild or foe) in the current battle context, translated + * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance + * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage" + */ +export function getPokemonNameWithAffix(pokemon: Pokemon): string { switch (pokemon.scene.currentBattle.battleSpec) { case BattleSpec.DEFAULT: - prefix = !pokemon.isPlayer() ? pokemon.hasTrainer() ? "Foe " : "Wild " : ""; - break; + return !pokemon.isPlayer() + ? pokemon.hasTrainer() + ? i18next.t("battle:foePokemonWithAffix", { + pokemonName: pokemon.name, + }) + : i18next.t("battle:wildPokemonWithAffix", { + pokemonName: pokemon.name, + }) + : pokemon.name; case BattleSpec.FINAL_BOSS: - prefix = !pokemon.isPlayer() ? "Foe " : ""; - break; + return !pokemon.isPlayer() + ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.name }) + : pokemon.name; + default: + return pokemon.name; } - return prefix; } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 5f1fb1d2956..3d03f1710f7 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -23,6 +23,7 @@ import { ModifierTier } from "./modifier-tier"; import { Nature, getNatureName, getNatureStatMultiplier } from "#app/data/nature"; import i18next from "#app/plugins/i18n"; import { getModifierTierTextTint } from "#app/ui/text"; +import * as Overrides from "../overrides"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -721,7 +722,7 @@ export class TmModifierType extends PokemonModifierType { } getDescription(scene: BattleScene): string { - return i18next.t("modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name }); + return i18next.t(scene.enableMoveInfo ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" : "modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name }); } } @@ -1322,6 +1323,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), new WeightedModifierType(modifierTypes.TERA_SHARD, 1), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0), + new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1), ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), @@ -1357,7 +1359,6 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.EXP_SHARE, 12), new WeightedModifierType(modifierTypes.EXP_BALANCE, 4), new WeightedModifierType(modifierTypes.TERA_ORB, (party: Pokemon[]) => Math.min(Math.max(Math.floor(party[0].scene.currentBattle.waveIndex / 50) * 2, 1), 4), 4), - new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount, 0) : 0, 3), new WeightedModifierType(modifierTypes.WIDE_LENS, 4), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; @@ -1382,6 +1383,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, 18), new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 8, 32), new WeightedModifierType(modifierTypes.DYNAMAX_BAND, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 8, 32), + new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(5 - rerollCount * 2, 0) : 0, 5), ].map(m => { m.setTier(ModifierTier.ROGUE); return m; }), @@ -1390,7 +1392,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.SHINY_CHARM, 14), new WeightedModifierType(modifierTypes.HEALING_CHARM, 18), new WeightedModifierType(modifierTypes.MULTI_LENS, 18), - new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(9 - rerollCount * 3, 0) : 0, 9), + new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(6 - rerollCount * 2, 0) : 0, 6), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24), new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 1 : 0, 1), ].map(m => { @@ -1672,6 +1674,14 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo } options.push(candidate); }); + // OVERRIDE IF NECESSARY + if (Overrides.ITEM_REWARD_OVERRIDE?.length) { + options.forEach((mod, i) => { + // @ts-ignore: keeps throwing don't use string as index error in typedoc run + const override = modifierTypes[Overrides.ITEM_REWARD_OVERRIDE[i]]?.(); + mod.type = (override instanceof ModifierTypeGenerator ? override.generateType(party) : override) || mod.type; + }); + } return options; } @@ -1874,8 +1884,9 @@ export class ModifierTypeOption { } export function getPartyLuckValue(party: Pokemon[]): integer { - return Phaser.Math.Clamp(party.map(p => p.isFainted() ? 0 : p.getLuck()) + const luck = Phaser.Math.Clamp(party.map(p => p.isFainted() ? 0 : p.getLuck()) .reduce((total: integer, value: integer) => total += value, 0), 0, 14); + return luck || 0; } export function getLuckString(luckValue: integer): string { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 28bf2f61c75..26d68d74e87 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -22,6 +22,7 @@ import { Nature } from "#app/data/nature"; import { BattlerTagType } from "#app/data/enums/battler-tag-type"; import * as Overrides from "../overrides"; import { ModifierType, modifierTypes } from "./modifier-type"; +import { Command } from "#app/ui/command-ui-handler.js"; export type ModifierPredicate = (modifier: Modifier) => boolean; @@ -776,7 +777,10 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier { if (!bypassSpeed.value && pokemon.randSeedInt(10) < this.getStackCount()) { bypassSpeed.value = true; - if (this.type instanceof ModifierTypes.PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW") { + const isCommandFight = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT; + const hasQuickClaw = this.type instanceof ModifierTypes.PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW"; + + if (isCommandFight && hasQuickClaw) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, " used its quick claw to move faster!")); } return true; @@ -2191,7 +2195,8 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi super(type, stackCount); this.effect = effect; - this.chance = (chancePercent || 5) / 100; + //Hardcode temporarily + this.chance = .025 * ((this.effect === StatusEffect.BURN || this.effect === StatusEffect.POISON) ? 2 : 1); } match(modifier: Modifier): boolean { @@ -2226,7 +2231,8 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier constructor(type: ModifierType, chancePercent: number, stackCount?: integer) { super(type, stackCount); - this.chance = (chancePercent || 2.5) / 100; + //Hardcode temporarily + this.chance = .025; } match(modifier: Modifier): boolean { @@ -2264,7 +2270,8 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier { constructor(type: ModifierType, chancePercent?: number, stackCount?: integer) { super(type, stackCount || 10); - this.chance = (chancePercent || 2) / 100; + //Hardcode temporarily + this.chance = .02; } match(modifier: Modifier) { diff --git a/src/overrides.ts b/src/overrides.ts index 661f2d14253..148dc352ae9 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -110,3 +110,11 @@ export const OPP_MODIFIER_OVERRIDE: Array = []; export const STARTING_HELD_ITEMS_OVERRIDE: Array = []; export const OPP_HELD_ITEMS_OVERRIDE: Array = []; + +/** + * An array of items by keys as defined in the "modifierTypes" object in the "modifier/modifier-type.ts" file. + * Items listed will replace the normal rolls. + * If less items are listed than rolled, only some items will be replaced + * If more items are listed than rolled, only the first X items will be shown, where X is the number of items rolled. + */ +export const ITEM_REWARD_OVERRIDE: Array = []; diff --git a/src/phases.ts b/src/phases.ts index c9c93ab414d..c43ab6e1c7f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -23,7 +23,7 @@ import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFu import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { BattlerTagLapseType, EncoreTag, HideSpriteTag as HiddenTag, ProtectedTag, TrappedTag } from "./data/battler-tags"; import { BattlerTagType } from "./data/enums/battler-tag-type"; -import { getPokemonMessage, getPokemonPrefix } from "./messages"; +import { getPokemonMessage, getPokemonNameWithAffix } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; @@ -43,7 +43,8 @@ import { EggHatchPhase } from "./egg-hatch-phase"; import { Egg } from "./data/egg"; import { vouchers } from "./system/voucher"; import { loggedInUser, updateUserInfo } from "./account"; -import { PlayerGender, SessionSaveData } from "./system/game-data"; +import { SessionSaveData } from "./system/game-data"; +import { PlayerGender } from "./data/enums/player-gender"; import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims"; import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms"; import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue"; @@ -61,7 +62,7 @@ import { Abilities } from "./data/enums/abilities"; import * as Overrides from "./overrides"; import { TextStyle, addTextObject } from "./ui/text"; import { Type } from "./data/type"; -import { BerryUsedEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./battle-scene-events"; +import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./battle-scene-events"; export class LoginPhase extends Phase { @@ -740,6 +741,8 @@ export class EncounterPhase extends BattlePhase { this.scene.initSession(); + this.scene.eventTarget.dispatchEvent(new EncounterPhaseEvent()); + // Failsafe if players somehow skip floor 200 in classic mode if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { this.scene.unshiftPhase(new GameOverPhase(this.scene)); @@ -1664,6 +1667,11 @@ export class CheckSwitchPhase extends BattlePhase { const pokemon = this.scene.getPlayerField()[this.fieldIndex]; + if (this.scene.battleStyle === 1) { + super.end(); + return; + } + if (this.scene.field.getAll().indexOf(pokemon) === -1) { this.scene.unshiftPhase(new SummonMissingPhase(this.scene, this.fieldIndex)); super.end(); @@ -2273,7 +2281,7 @@ export class TurnEndPhase extends FieldPhase { pokemon.lapseTags(BattlerTagLapseType.TURN_END); if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) { - this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: `${getPokemonPrefix(pokemon)}${pokemon.name}`, moveName: allMoves[pokemon.summonData.disabledMove].name }))); + this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: allMoves[pokemon.summonData.disabledMove].name }))); pokemon.summonData.disabledMove = Moves.NONE; } @@ -2583,7 +2591,7 @@ export class MovePhase extends BattlePhase { this.scene.getPlayerField().forEach(pokemon => { applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); }); - this.scene.getEnemyParty().forEach(pokemon => { + this.scene.getEnemyField().forEach(pokemon => { applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); }); } @@ -2640,7 +2648,10 @@ export class MovePhase extends BattlePhase { if (this.move.getMove().hasAttr(ChargeAttr)) { const lastMove = this.pokemon.getLastXMoves() as TurnMove[]; if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) { - this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500); + this.scene.queueMessage(i18next.t("battle:useMove", { + pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), + moveName: this.move.getName() + }), 500); return; } } @@ -2649,7 +2660,10 @@ export class MovePhase extends BattlePhase { return; } - this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500); + this.scene.queueMessage(i18next.t("battle:useMove", { + pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), + moveName: this.move.getName() + }), 500); applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true), this.move.getMove()); } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 0b81c4014b5..8ed7f521507 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -36,28 +36,11 @@ import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-event import { Device } from "#app/enums/devices.js"; import { EnemyAttackStatusEffectChanceModifier } from "../modifier/modifier"; import { StatusEffect } from "#app/data/status-effect.js"; +import { PlayerGender } from "#app/data/enums/player-gender"; +import { GameDataType } from "#app/data/enums/game-data-type"; const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary -export enum GameDataType { - SYSTEM, - SESSION, - SETTINGS, - TUTORIALS, - SEEN_DIALOGUES -} - -export enum PlayerGender { - UNSET, - MALE, - FEMALE -} - -export enum Passive { - UNLOCKED = 1, - ENABLED = 2 -} - export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): string { switch (dataType) { case GameDataType.SYSTEM: diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index c020282cf35..75fc8185c89 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -3,10 +3,11 @@ import i18next from "i18next"; import BattleScene from "../../battle-scene"; import { hasTouchscreen } from "../../touch-controls"; import { updateWindowType } from "../../ui/ui-theme"; -import { PlayerGender } from "../game-data"; +import { PlayerGender } from "#app/data/enums/player-gender"; import { CandyUpgradeNotificationChangedEvent } from "#app/battle-scene-events.js"; import { MoneyFormat } from "../../enums/money-format"; import SettingsUiHandler from "#app/ui/settings/settings-ui-handler"; +import { EaseType } from "#app/ui/enums/ease-type.js"; const MUTE = "Mute"; const VOLUME_OPTIONS = new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : MUTE); @@ -18,7 +19,8 @@ const AUTO_DISABLED = ["Auto", "Disabled"]; */ export enum SettingType { GENERAL, - ACCESSIBILITY + DISPLAY, + AUDIO } export interface Setting { @@ -36,32 +38,35 @@ export interface Setting { */ export const SettingKeys = { Game_Speed: "GAME_SPEED", - Master_Volume: "MASTER_VOLUME", - BGM_Volume: "BGM_VOLUME", - SE_Volume: "SE_VOLUME", - Language: "LANGUAGE", - Damage_Numbers: "DAMAGE_NUMBERS", - UI_Theme: "UI_THEME", - Window_Type: "WINDOW_TYPE", - Tutorials: "TUTORIALS", - Enable_Retries: "ENABLE_RETRIES", - Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", - Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION", - Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY", - Money_Format: "MONEY_FORMAT", - Sprite_Set: "SPRITE_SET", - Music_Preference: "MUSIC_PREFERENCE", - Move_Animations: "MOVE_ANIMATIONS", - Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT", - Show_Arena_Flyout: "SHOW_ARENA_FLYOUT", - Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS", + HP_Bar_Speed: "HP_BAR_SPEED", EXP_Gains_Speed: "EXP_GAINS_SPEED", EXP_Party_Display: "EXP_PARTY_DISPLAY", - HP_Bar_Speed: "HP_BAR_SPEED", + Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", + Battle_Style: "BATTLE_STYLE", + Enable_Retries: "ENABLE_RETRIES", + Tutorials: "TUTORIALS", + Touch_Controls: "TOUCH_CONTROLS", + Vibration: "VIBRATION", + Language: "LANGUAGE", + UI_Theme: "UI_THEME", + Window_Type: "WINDOW_TYPE", + Money_Format: "MONEY_FORMAT", + Damage_Numbers: "DAMAGE_NUMBERS", + Move_Animations: "MOVE_ANIMATIONS", + Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS", + Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION", + Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY", + Move_Info: "MOVE_INFO", + Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT", + Show_Arena_Flyout: "SHOW_ARENA_FLYOUT", + Show_Time_Of_Day_Widget: "SHOW_TIME_OF_DAY_WIDGET", + Time_Of_Day_Animation: "TIME_OF_DAY_ANIMATION", + Sprite_Set: "SPRITE_SET", Fusion_Palette_Swaps: "FUSION_PALETTE_SWAPS", Player_Gender: "PLAYER_GENDER", - Touch_Controls: "TOUCH_CONTROLS", - Vibration: "VIBRATION" + Master_Volume: "MASTER_VOLUME", + BGM_Volume: "BGM_VOLUME", + SE_Volume: "SE_VOLUME" }; /** @@ -76,141 +81,10 @@ export const Setting: Array = [ type: SettingType.GENERAL }, { - key: SettingKeys.Master_Volume, - label: "Master Volume", - options: VOLUME_OPTIONS, - default: 5, - type: SettingType.GENERAL - }, - { - key: SettingKeys.BGM_Volume, - label: "BGM Volume", - options: VOLUME_OPTIONS, - default: 10, - type: SettingType.GENERAL - }, - { - key: SettingKeys.SE_Volume, - label: "SE Volume", - options: VOLUME_OPTIONS, - default: 10, - type: SettingType.GENERAL - }, - { - key: SettingKeys.Language, - label: "Language", - options: ["English", "Change"], + key: SettingKeys.HP_Bar_Speed, + label: "HP Bar Speed", + options: ["Normal", "Fast", "Faster", "Skip"], default: 0, - type: SettingType.GENERAL, - requireReload: true - }, - { - key: SettingKeys.Damage_Numbers, - label: "Damage Numbers", - options: ["Off", "Simple", "Fancy"], - default: 0, - type: SettingType.GENERAL - }, - { - key: SettingKeys.UI_Theme, - label: "UI Theme", - options: ["Default", "Legacy"], - default: 0, - type: SettingType.GENERAL, - requireReload: true - }, - { - key: SettingKeys.Window_Type, - label: "Window Type", - options: new Array(5).fill(null).map((_, i) => (i + 1).toString()), - default: 0, - type: SettingType.GENERAL - }, - { - key: SettingKeys.Tutorials, - label: "Tutorials", - options: OFF_ON, - default: 1, - type: SettingType.GENERAL - }, - { - key: SettingKeys.Enable_Retries, - label: "Enable Retries", - options: OFF_ON, - default: 0, - type: SettingType.ACCESSIBILITY - }, - { - key: SettingKeys.Skip_Seen_Dialogues, - label: "Skip Seen Dialogues", - options: OFF_ON, - default: 0, - type: SettingType.GENERAL - }, - { - key: SettingKeys.Candy_Upgrade_Notification, - label: "Candy Upgrade Notification", - options: ["Off", "Passives Only", "On"], - default: 0, - type: SettingType.ACCESSIBILITY - }, - { - key: SettingKeys.Candy_Upgrade_Display, - label: "Candy Upgrade Display", - options: ["Icon", "Animation"], - default: 0, - type: SettingType.ACCESSIBILITY, - requireReload: true - }, - { - key: SettingKeys.Money_Format, - label: "Money Format", - options: ["Normal", "Abbreviated"], - default: 0, - type: SettingType.ACCESSIBILITY - }, - { - key: SettingKeys.Sprite_Set, - label: "Sprite Set", - options: ["Consistent", "Mixed Animated"], - default: 0, - type: SettingType.GENERAL, - requireReload: true - }, - { - key: SettingKeys.Music_Preference, - label: "Music Preference", - options: ["Consistent", "Mixed"], - default: 0, - type: SettingType.GENERAL, - requireReload: true - }, - { - key: SettingKeys.Move_Animations, - label: "Move Animations", - options: OFF_ON, - default: 1, - type: SettingType.GENERAL - }, - { - key: SettingKeys.Show_Moveset_Flyout, - label: "Show Moveset Flyout", - options: OFF_ON, - default: 1, - type: SettingType.ACCESSIBILITY - }, - { - key: SettingKeys.Show_Arena_Flyout, - label: "Show Battle Effects Flyout", - options: OFF_ON, - default: 1, - type: SettingType.ACCESSIBILITY - }, - { - key: SettingKeys.Show_Stats_on_Level_Up, - label: "Show Stats on Level Up", - options: OFF_ON, - default: 1, type: SettingType.GENERAL }, { @@ -228,26 +102,33 @@ export const Setting: Array = [ type: SettingType.GENERAL }, { - key: SettingKeys.HP_Bar_Speed, - label: "HP Bar Speed", - options: ["Normal", "Fast", "Faster", "Skip"], + key: SettingKeys.Skip_Seen_Dialogues, + label: "Skip Seen Dialogues", + options: OFF_ON, default: 0, type: SettingType.GENERAL }, { - key: SettingKeys.Fusion_Palette_Swaps, - label: "Fusion Palette Swaps", + key: SettingKeys.Battle_Style, + label: "Battle Style", + options: ["Switch", "Set"], + default: 0, + type: SettingType.GENERAL + }, + { + key: SettingKeys.Enable_Retries, + label: "Enable Retries", + options: OFF_ON, + default: 0, + type: SettingType.GENERAL + }, + { + key: SettingKeys.Tutorials, + label: "Tutorials", options: OFF_ON, default: 1, type: SettingType.GENERAL }, - { - key: SettingKeys.Player_Gender, - label: "Player Gender", - options: ["Boy", "Girl"], - default: 0, - type: SettingType.GENERAL - }, { key: SettingKeys.Touch_Controls, label: "Touch Controls", @@ -261,6 +142,151 @@ export const Setting: Array = [ options: AUTO_DISABLED, default: 0, type: SettingType.GENERAL + }, + { + key: SettingKeys.Language, + label: "Language", + options: ["English", "Change"], + default: 0, + type: SettingType.DISPLAY, + requireReload: true + }, + { + key: SettingKeys.UI_Theme, + label: "UI Theme", + options: ["Default", "Legacy"], + default: 0, + type: SettingType.DISPLAY, + requireReload: true + }, + { + key: SettingKeys.Window_Type, + label: "Window Type", + options: new Array(5).fill(null).map((_, i) => (i + 1).toString()), + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Money_Format, + label: "Money Format", + options: ["Normal", "Abbreviated"], + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Damage_Numbers, + label: "Damage Numbers", + options: ["Off", "Simple", "Fancy"], + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Move_Animations, + label: "Move Animations", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Show_Stats_on_Level_Up, + label: "Show Stats on Level Up", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Candy_Upgrade_Notification, + label: "Candy Upgrade Notification", + options: ["Off", "Passives Only", "On"], + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Candy_Upgrade_Display, + label: "Candy Upgrade Display", + options: ["Icon", "Animation"], + default: 0, + type: SettingType.DISPLAY, + requireReload: true + }, + { + key: SettingKeys.Move_Info, + label: "Move Info", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Show_Moveset_Flyout, + label: "Show Moveset Flyout", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Show_Arena_Flyout, + label: "Show Battle Effects Flyout", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Show_Time_Of_Day_Widget, + label: "Show Time of Day Widget", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY, + requireReload: true, + }, + { + key: SettingKeys.Time_Of_Day_Animation, + label: "Time of Day Animation", + options: ["Bounce", "Back"], + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Sprite_Set, + label: "Sprite Set", + options: ["Consistent", "Mixed Animated"], + default: 0, + type: SettingType.DISPLAY, + requireReload: true + }, + { + key: SettingKeys.Fusion_Palette_Swaps, + label: "Fusion Palette Swaps", + options: OFF_ON, + default: 1, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Player_Gender, + label: "Player Gender", + options: ["Boy", "Girl"], + default: 0, + type: SettingType.DISPLAY + }, + { + key: SettingKeys.Master_Volume, + label: "Master Volume", + options: VOLUME_OPTIONS, + default: 5, + type: SettingType.AUDIO + }, + { + key: SettingKeys.BGM_Volume, + label: "BGM Volume", + options: VOLUME_OPTIONS, + default: 10, + type: SettingType.AUDIO + }, + { + key: SettingKeys.SE_Volume, + label: "SE Volume", + options: VOLUME_OPTIONS, + default: 10, + type: SettingType.AUDIO } ]; @@ -321,12 +347,18 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Tutorials: scene.enableTutorials = Setting[index].options[value] === "On"; break; + case SettingKeys.Move_Info: + scene.enableMoveInfo = Setting[index].options[value] === "On"; + break; case SettingKeys.Enable_Retries: scene.enableRetries = Setting[index].options[value] === "On"; break; case SettingKeys.Skip_Seen_Dialogues: scene.skipSeenDialogues = Setting[index].options[value] === "On"; break; + case SettingKeys.Battle_Style: + scene.battleStyle = value; + break; case SettingKeys.Candy_Upgrade_Notification: if (scene.candyUpgradeNotification === value) { break; @@ -354,9 +386,6 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): scene.initExpSprites(); } break; - case SettingKeys.Music_Preference: - scene.musicPreference = value; - break; case SettingKeys.Move_Animations: scene.moveAnimations = Setting[index].options[value] === "On"; break; @@ -366,6 +395,12 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Show_Arena_Flyout: scene.showArenaFlyout = Setting[index].options[value] === "On"; break; + case SettingKeys.Show_Time_Of_Day_Widget: + scene.showTimeOfDayWidget = Setting[index].options[value] === "On"; + break; + case SettingKeys.Time_Of_Day_Animation: + scene.timeOfDayAnimation = Setting[index].options[value] === "Bounce" ? EaseType.BOUNCE : EaseType.BACK; + break; case SettingKeys.Show_Stats_on_Level_Up: scene.showLevelUpStats = Setting[index].options[value] === "On"; break; diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index d4815ad5a7c..d068cbebde7 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -9,7 +9,8 @@ import {Button} from "./enums/buttons"; import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler"; import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; import BattleScene from "./battle-scene"; -import SettingsAccessibilityUiHandler from "./ui/settings/settings-accessiblity-ui-handler"; +import SettingsDisplayUiHandler from "./ui/settings/settings-display-ui-handler"; +import SettingsAudioUiHandler from "./ui/settings/settings-audio-ui-handler"; type ActionKeys = Record void>; @@ -114,6 +115,11 @@ export class UiInputs { } buttonStats(pressed: boolean = true): void { + // allow access to Button.STATS as a toggle for other elements + for (const t of this.scene.getInfoToggles(true)) { + t.toggleInfo(pressed); + } + // handle normal pokemon battle ui for (const p of this.scene.getField().filter(p => p?.isActive(true))) { p.toggleStats(pressed); } @@ -164,7 +170,7 @@ export class UiInputs { } buttonCycleOption(button: Button): void { - const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsAccessibilityUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler]; + const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsDisplayUiHandler, SettingsAudioUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler]; const uiHandler = this.scene.ui?.getHandler(); if (whitelist.some(handler => uiHandler instanceof handler)) { this.scene.ui.processInput(button); diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index 925bbefc930..568c8208eac 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -14,15 +14,17 @@ export interface OptionSelectConfig { maxOptions?: integer; delay?: integer; noCancel?: boolean; + supportHover?: boolean; } export interface OptionSelectItem { label: string; handler: () => boolean; + onHover?: () => void; keepOpen?: boolean; overrideSound?: boolean; item?: string; - itemArgs?: any[] + itemArgs?: any[]; } const scrollUpLabel = "↑"; @@ -193,6 +195,10 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { } break; } + if (this.config?.supportHover) { + // handle hover code if the element supports hover-handlers and the option has the optional hover-handler set. + this.config?.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))]?.onHover?.(); + } } if (success && playSound) { diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index 77996625fed..e854de2006b 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -1,4 +1,3 @@ -import * as Utils from "../utils"; import { addTextObject, TextStyle } from "./text"; import BattleScene from "#app/battle-scene.js"; import { ArenaTagSide } from "#app/data/arena-tag.js"; @@ -8,7 +7,8 @@ import { addWindow, WindowVariant } from "./ui-theme"; import { ArenaEvent, ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js"; import { BattleSceneEventType, TurnEndEvent } from "#app/battle-scene-events.js"; import { ArenaTagType } from "#app/data/enums/arena-tag-type.js"; -import { TimeOfDay } from "#app/data/enums/time-of-day.js"; +import TimeOfDayWidget from "./time-of-day-widget"; +import * as Utils from "../utils"; /** Enum used to differentiate {@linkcode Arena} effects */ enum ArenaEffectType { @@ -60,8 +60,7 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { /** The {@linkcode Phaser.GameObjects.Text} that goes inside of the header */ private flyoutTextHeader: Phaser.GameObjects.Text; - /** The {@linkcode Phaser.GameObjects.Sprite} that represents the current time of day */ - private timeOfDayIcon: Phaser.GameObjects.Sprite; + private timeOfDayWidget: TimeOfDayWidget; /** The {@linkcode Phaser.GameObjects.Text} header used to indicate the player's effects */ private flyoutTextHeaderPlayer: Phaser.GameObjects.Text; @@ -82,7 +81,6 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { // Stores callbacks in a variable so they can be unsubscribed from when destroyed private readonly onNewArenaEvent = (event: Event) => this.onNewArena(event); - private readonly onTurnInitEvent = (event: Event) => this.onTurnInit(event); private readonly onTurnEndEvent = (event: Event) => this.onTurnEnd(event); private readonly onFieldEffectChangedEvent = (event: Event) => this.onFieldEffectChanged(event); @@ -117,10 +115,8 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { this.flyoutContainer.add(this.flyoutTextHeader); - this.timeOfDayIcon = this.scene.add.sprite((this.flyoutWidth / 2) + (this.flyoutWindowHeader.displayWidth / 2), 0, "dawn_icon").setOrigin(); - this.timeOfDayIcon.setVisible(false); - - this.flyoutContainer.add(this.timeOfDayIcon); + this.timeOfDayWidget = new TimeOfDayWidget(this.scene, (this.flyoutWidth / 2) + (this.flyoutWindowHeader.displayWidth / 2)); + this.flyoutContainer.add(this.timeOfDayWidget); this.flyoutTextHeaderPlayer = addTextObject(this.scene, 6, 5, "Player", TextStyle.SUMMARY_BLUE); this.flyoutTextHeaderPlayer.setFontSize(54); @@ -172,18 +168,9 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { // Subscribes to required events available on game start this.battleScene.eventTarget.addEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent); - this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_INIT, this.onTurnInitEvent); this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent); } - private setTimeOfDayIcon() { - this.timeOfDayIcon.setTexture(TimeOfDay[this.battleScene.arena.getTimeOfDay()].toLowerCase() + "_icon"); - } - - private onTurnInit(event: Event) { - this.setTimeOfDayIcon(); - } - private onNewArena(event: Event) { this.fieldEffectInfo.length = 0; @@ -192,8 +179,6 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent); this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent); this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent); - - this.setTimeOfDayIcon(); } /** @@ -360,17 +345,18 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container { * Animates the flyout to either show or hide it by applying a fade and translation * @param visible Should the flyout be shown? */ - toggleFlyout(visible: boolean): void { + public toggleFlyout(visible: boolean): void { this.scene.tweens.add({ targets: this.flyoutParent, x: visible ? this.anchorX : this.anchorX - this.translationX, duration: Utils.fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, + onComplete: () => this.timeOfDayWidget.parentVisible = visible, }); } - destroy(fromScene?: boolean): void { + public destroy(fromScene?: boolean): void { this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent); this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent); diff --git a/src/ui/enums/ease-type.ts b/src/ui/enums/ease-type.ts new file mode 100644 index 00000000000..fbe06fd536d --- /dev/null +++ b/src/ui/enums/ease-type.ts @@ -0,0 +1,15 @@ +export enum EaseType { + NONE, + LINEAR = "Linear", + QUADRATIC = "Quad", + CUBIC = "Cubic", + QUARTIC = "Quart", + QUINTIC = "Quint", + SINUSOIDAL = "Sine", + EXPONENTIAL = "Expo", + CIRCULAR = "Circ", + ELASTIC = "Elastic", + BACK = "Back", + BOUNCE = "Bounce", + STEPPED = "Stepped", +} diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 060cb172b05..a0b0c5da66b 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -4,7 +4,7 @@ import { Mode } from "./ui"; import * as Utils from "../utils"; import { addWindow } from "./ui-theme"; import MessageUiHandler from "./message-ui-handler"; -import { GameDataType } from "../system/game-data"; +import { GameDataType } from "#app/data/enums/game-data-type"; import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; import { Tutorial, handleTutorial } from "../tutorial"; import { updateUserInfo } from "../account"; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 37718243b8b..f6738a33d98 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -1,5 +1,5 @@ import BattleScene from "../battle-scene"; -import { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption } from "../modifier/modifier-type"; +import { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption, TmModifierType } from "../modifier/modifier-type"; import { getPokeballAtlasKey, PokeballType } from "../data/pokeball"; import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; import AwaitableUiHandler from "./awaitable-ui-handler"; @@ -7,6 +7,8 @@ import { Mode } from "./ui"; import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { handleTutorial, Tutorial } from "../tutorial"; import {Button} from "../enums/buttons"; +import MoveInfoOverlay from "./move-info-overlay"; +import { allMoves } from "../data/move"; export const SHOP_OPTIONS_ROW_LIMIT = 6; @@ -17,6 +19,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { private transferButtonContainer: Phaser.GameObjects.Container; private rerollCostText: Phaser.GameObjects.Text; private lockRarityButtonText: Phaser.GameObjects.Text; + private moveInfoOverlay : MoveInfoOverlay; private rowCursor: integer = 0; private player: boolean; @@ -73,6 +76,21 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.lockRarityButtonText = addTextObject(this.scene, -4, -2, "Lock Rarities", TextStyle.PARTY); this.lockRarityButtonText.setOrigin(0, 0); this.lockRarityButtonContainer.add(this.lockRarityButtonText); + + // prepare move overlay + const overlayScale = 1; + this.moveInfoOverlay = new MoveInfoOverlay(this.scene, { + delayVisibility: true, + scale: overlayScale, + onSide: true, + right: true, + x: 1, + y: -MoveInfoOverlay.getHeight(overlayScale, true) -1, + width: (this.scene.game.canvas.width / 6) - 2, + }); + ui.add(this.moveInfoOverlay); + // register the overlay to receive toggle events + this.scene.addInfoToggle(this.moveInfoOverlay); } show(args: any[]): boolean { @@ -293,6 +311,8 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.cursorObj.setScale(this.rowCursor === 1 ? 2 : this.rowCursor >= 2 ? 1.5 : 1); + // the modifier selection has been updated, always hide the overlay + this.moveInfoOverlay.clear(); if (this.rowCursor) { const sliceWidth = (this.scene.game.canvas.width / 6) / (options.length + 2); if (this.rowCursor < 2) { @@ -300,7 +320,13 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { } else { this.cursorObj.setPosition(sliceWidth * (cursor + 1) + (sliceWidth * 0.5) - 16, (-this.scene.game.canvas.height / 12 - this.scene.game.canvas.height / 32) - (-16 + 28 * (this.rowCursor - (this.shopOptionsRows.length - 1)))); } - ui.showText(options[this.cursor].modifierTypeOption.type.getDescription(this.scene)); + + const type = options[this.cursor].modifierTypeOption.type; + ui.showText(type.getDescription(this.scene)); + if (type instanceof TmModifierType) { + // prepare the move overlay to be shown with the toggle + this.moveInfoOverlay.show(allMoves[type.moveId]); + } } else if (!cursor) { this.cursorObj.setPosition(6, this.lockRarityButtonContainer.visible ? -72 : -60); ui.showText("Spend money to reroll your item options."); diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts new file mode 100644 index 00000000000..3b947cb842d --- /dev/null +++ b/src/ui/move-info-overlay.ts @@ -0,0 +1,195 @@ +import BattleScene, {InfoToggle} from "../battle-scene"; +import { TextStyle, addTextObject } from "./text"; +import { addWindow } from "./ui-theme"; +import * as Utils from "../utils"; +import Move, { MoveCategory } from "../data/move"; +import { Type } from "../data/type"; +import i18next from "i18next"; + +export interface MoveInfoOverlaySettings { + delayVisibility?: boolean; // if true, showing the overlay will only set it to active and populate the fields and the handler using this field has to manually call setVisible later. + scale?:number; // scale the box? A scale of 0.5 is recommended + top?: boolean; // should the effect box be on top? + right?: boolean; // should the effect box be on the right? + onSide?: boolean; // should the effect be on the side? ignores top argument if true + //location and width of the component; unaffected by scaling + x?: number; + y?: number; + width?: number; // default is always half the screen, regardless of scale +} + +const EFF_HEIGHT = 46; +const EFF_WIDTH = 82; +const DESC_HEIGHT = 46; +const BORDER = 8; +const GLOBAL_SCALE = 6; + +export default class MoveInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle { + public active: boolean = false; + + private move: Move; + + private desc: Phaser.GameObjects.Text; + private descScroll : Phaser.Tweens.Tween = null; + + private val: Phaser.GameObjects.Container; + private pp: Phaser.GameObjects.Text; + private pow: Phaser.GameObjects.Text; + private acc: Phaser.GameObjects.Text; + private typ: Phaser.GameObjects.Sprite; + private cat: Phaser.GameObjects.Sprite; + + private options : MoveInfoOverlaySettings; + + constructor(scene: BattleScene, options?: MoveInfoOverlaySettings) { + if (options?.onSide) { + options.top = false; + } + super(scene, options?.x, options?.y); + const scale = options?.scale || 1; // set up the scale + this.setScale(scale); + this.options = options || {}; + + // prepare the description box + const width = (options?.width || MoveInfoOverlay.getWidth(scale, scene)) / scale; // divide by scale as we always want this to be half a window wide + const descBg = addWindow(scene, (options?.onSide && !options?.right ? EFF_WIDTH : 0), options?.top ? EFF_HEIGHT : 0, width - (options?.onSide ? EFF_WIDTH : 0), DESC_HEIGHT); + descBg.setOrigin(0, 0); + this.add(descBg); + + // set up the description; wordWrap uses true pixels, unaffected by any scaling, while other values are affected + this.desc = addTextObject(scene, (options?.onSide && !options?.right ? EFF_WIDTH : 0) + BORDER, (options?.top ? EFF_HEIGHT : 0) + BORDER - 2, "", TextStyle.BATTLE_INFO, { wordWrap: { width: (width - (BORDER - 2) * 2 - (options?.onSide ? EFF_WIDTH : 0)) * GLOBAL_SCALE } }); + + // limit the text rendering, required for scrolling later on + const maskPointOrigin = { + x: (options?.x || 0), + y: (options?.y || 0), + }; + if (maskPointOrigin.x < 0) { + maskPointOrigin.x += this.scene.game.canvas.width / GLOBAL_SCALE; + } + if (maskPointOrigin.y < 0) { + maskPointOrigin.y += this.scene.game.canvas.height / GLOBAL_SCALE; + } + + const moveDescriptionTextMaskRect = this.scene.make.graphics(); + moveDescriptionTextMaskRect.fillStyle(0xFF0000); + moveDescriptionTextMaskRect.fillRect( + maskPointOrigin.x + ((options?.onSide && !options?.right ? EFF_WIDTH : 0) + BORDER) * scale, maskPointOrigin.y + ((options?.top ? EFF_HEIGHT : 0) + BORDER - 2) * scale, + width - ((options?.onSide ? EFF_WIDTH : 0) - BORDER * 2) * scale, (DESC_HEIGHT - (BORDER - 2) * 2) * scale); + moveDescriptionTextMaskRect.setScale(6); + const moveDescriptionTextMask = this.createGeometryMask(moveDescriptionTextMaskRect); + + this.add(this.desc); + this.desc.setMask(moveDescriptionTextMask); + + // prepare the effect box + this.val = new Phaser.GameObjects.Container(scene, options?.right ? width - EFF_WIDTH : 0, options?.top || options?.onSide ? 0 : DESC_HEIGHT); + this.add(this.val); + + const valuesBg = addWindow(scene, 0, 0, EFF_WIDTH, EFF_HEIGHT); + valuesBg.setOrigin(0, 0); + this.val.add(valuesBg); + + this.typ = this.scene.add.sprite(25, EFF_HEIGHT - 35,`types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}` , "unknown"); + this.typ.setScale(0.8); + this.val.add(this.typ); + + this.cat = this.scene.add.sprite(57, EFF_HEIGHT - 35, "categories", "physical"); + this.val.add(this.cat); + + const ppTxt = addTextObject(scene, 12, EFF_HEIGHT - 25, "PP", TextStyle.MOVE_INFO_CONTENT); + ppTxt.setOrigin(0.0, 0.5); + ppTxt.setText(i18next.t("fightUiHandler:pp")); + this.val.add(ppTxt); + + this.pp = addTextObject(scene, 70, EFF_HEIGHT - 25, "--", TextStyle.MOVE_INFO_CONTENT); + this.pp.setOrigin(1, 0.5); + this.val.add(this.pp); + + const powTxt = addTextObject(scene, 12, EFF_HEIGHT - 17, "POWER", TextStyle.MOVE_INFO_CONTENT); + powTxt.setOrigin(0.0, 0.5); + powTxt.setText(i18next.t("fightUiHandler:power")); + this.val.add(powTxt); + + this.pow = addTextObject(scene, 70, EFF_HEIGHT - 17, "---", TextStyle.MOVE_INFO_CONTENT); + this.pow.setOrigin(1, 0.5); + this.val.add(this.pow); + + const accTxt = addTextObject(scene, 12, EFF_HEIGHT - 9, "ACC", TextStyle.MOVE_INFO_CONTENT); + accTxt.setOrigin(0.0, 0.5); + accTxt.setText(i18next.t("fightUiHandler:accuracy")); + this.val.add(accTxt); + + this.acc = addTextObject(scene, 70, EFF_HEIGHT - 9, "---", TextStyle.MOVE_INFO_CONTENT); + this.acc.setOrigin(1, 0.5); + this.val.add(this.acc); + + // hide this component for now + this.setVisible(false); + } + + // show this component with infos for the specific move + show(move : Move):boolean { + if (!(this.scene as BattleScene).enableMoveInfo) { + return; // move infos have been disabled + } + this.move = move; + this.pow.setText(move.power >= 0 ? move.power.toString() : "---"); + this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---"); + this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---"); + this.typ.setTexture(`types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, Type[move.type].toLowerCase()); + this.cat.setFrame(MoveCategory[move.category].toLowerCase()); + + this.desc.setText(move?.effect || ""); + + // stop previous scrolling effects and reset y position + if (this.descScroll) { + this.descScroll.remove(); + this.descScroll = null; + this.desc.y = (this.options?.top ? EFF_HEIGHT : 0) + BORDER - 2; + } + + // determine if we need to add new scrolling effects + const moveDescriptionLineCount = Math.floor(this.desc.displayHeight * (96 / 72) / 14.83); + if (moveDescriptionLineCount > 3) { + // generate scrolling effects + this.descScroll = this.scene.tweens.add({ + targets: this.desc, + delay: Utils.fixedInt(2000), + loop: -1, + hold: Utils.fixedInt(2000), + duration: Utils.fixedInt((moveDescriptionLineCount - 3) * 2000), + y: `-=${14.83 * (72 / 96) * (moveDescriptionLineCount - 3)}` + }); + } + + if (!this.options.delayVisibility) { + this.setVisible(true); + } + this.active = true; + return true; + } + + clear() { + this.setVisible(false); + this.active = false; + } + + toggleInfo(force?: boolean): void { + this.setVisible(force ?? !this.visible); + } + + isActive(): boolean { + return this.active; + } + + // width of this element + static getWidth(scale:number, scene: BattleScene):number { + return scene.game.canvas.width / GLOBAL_SCALE / 2; + } + + // height of this element + static getHeight(scale:number, onSide?: boolean):number { + return (onSide ? Math.max(EFF_HEIGHT, DESC_HEIGHT) : (EFF_HEIGHT + DESC_HEIGHT)) * scale; + } +} diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index b62242e3c18..c51fea747a9 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -17,6 +17,7 @@ import { addWindow } from "./ui-theme"; import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; import { getVariantTint } from "#app/data/variant"; import {Button} from "../enums/buttons"; +import MoveInfoOverlay from "./move-info-overlay"; const defaultMessage = "Choose a Pokémon."; @@ -73,6 +74,7 @@ export default class PartyUiHandler extends MessageUiHandler { private partySlots: PartySlot[]; private partyCancelButton: PartyCancelButton; private partyMessageBox: Phaser.GameObjects.NineSlice; + private moveInfoOverlay: MoveInfoOverlay; private optionsMode: boolean; private optionsScroll: boolean; @@ -179,6 +181,17 @@ export default class PartyUiHandler extends MessageUiHandler { this.iconAnimHandler = new PokemonIconAnimHandler(); this.iconAnimHandler.setup(this.scene); + // prepare move overlay. in case it appears to be too big, set the overlayScale to .5 + const overlayScale = 1; + this.moveInfoOverlay = new MoveInfoOverlay(this.scene, { + scale: overlayScale, + top: true, + x: 1, + y: -MoveInfoOverlay.getHeight(overlayScale) - 1, //this.scene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29, + width: this.scene.game.canvas.width / 12 - 30, + }); + ui.add(this.moveInfoOverlay); + this.options = []; this.partySlots = []; @@ -191,6 +204,9 @@ export default class PartyUiHandler extends MessageUiHandler { super.show(args); + // reset the infoOverlay + this.moveInfoOverlay.clear(); + this.partyUiMode = args[0] as PartyUiMode; this.fieldIndex = args.length > 1 ? args[1] as integer : -1; @@ -244,6 +260,8 @@ export default class PartyUiHandler extends MessageUiHandler { ui.playSelect(); return true; } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && option !== PartyOption.CANCEL) { + // clear overlay on cancel + this.moveInfoOverlay.clear(); const filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); if (filterResult === null) { this.selectCallback(this.cursor, option); @@ -408,6 +426,19 @@ export default class PartyUiHandler extends MessageUiHandler { success = this.setCursor(this.optionsCursor < this.options.length - 1 ? this.optionsCursor + 1 : 0); /** Move cursor */ break; } + + // show move description + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + const option = this.options[this.optionsCursor]; + const pokemon = this.scene.getParty()[this.cursor]; + const move = allMoves[pokemon.getLearnableLevelMoves()[option]]; + if (move) { + this.moveInfoOverlay.show(move); + } else { + // or hide the overlay, in case it's the cancel button + this.moveInfoOverlay.clear(); + } + } } } else { if (button === Button.ACTION) { @@ -625,6 +656,11 @@ export default class PartyUiHandler extends MessageUiHandler { ? pokemon.getLearnableLevelMoves() : null; + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && learnableLevelMoves?.length) { + // show the move overlay with info for the first move + this.moveInfoOverlay.show(allMoves[learnableLevelMoves[0]]); + } + const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER ? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === pokemon.id) as PokemonHeldItemModifier[] @@ -874,6 +910,8 @@ export default class PartyUiHandler extends MessageUiHandler { } clearOptions() { + // hide the overlay + this.moveInfoOverlay.clear(); this.optionsMode = false; this.optionsScroll = false; this.optionsScrollCursor = 0; @@ -895,6 +933,8 @@ export default class PartyUiHandler extends MessageUiHandler { clear() { super.clear(); + // hide the overlay + this.moveInfoOverlay.clear(); this.partyContainer.setVisible(false); this.clearPartySlots(); } diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index b731b0d22b4..1ffa32d2394 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -73,13 +73,16 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } setup(): void { + this.setName("container-pkmn-info"); const currentLanguage = i18next.resolvedLanguage; const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); const textSettings = languageSettings[langSettingKey]; const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); infoBg.setOrigin(0.5, 0.5); + infoBg.setName("window-info-bg"); this.pokemonMovesContainer = this.scene.add.container(6, 14); + this.pokemonMovesContainer.setName("container-pkmn-moves"); this.movesContainerInitialX = this.pokemonMovesContainer.x; @@ -89,21 +92,26 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { const movesBg = addWindow(this.scene, 0, 0, 58, 52); movesBg.setOrigin(1, 0); + movesBg.setName("window-moves-bg"); this.pokemonMovesContainer.add(movesBg); const movesLabel = addTextObject(this.scene, -movesBg.width / 2, 6, i18next.t("pokemonInfoContainer:moveset"), TextStyle.WINDOW, { fontSize: "64px" }); movesLabel.setOrigin(0.5, 0); + movesLabel.setName("text-moves"); this.pokemonMovesContainer.add(movesLabel); for (let m = 0; m < 4; m++) { const moveContainer = this.scene.add.container(-6, 18 + 7 * m); moveContainer.setScale(0.5); + moveContainer.setName("container-move"); const moveBg = this.scene.add.nineslice(0, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2); moveBg.setOrigin(1, 0); + moveBg.setName("nineslice-move-bg"); const moveLabel = addTextObject(this.scene, -moveBg.width / 2, 0, "-", TextStyle.PARTY); moveLabel.setOrigin(0.5, 0); + moveLabel.setName("text-move-label"); this.pokemonMoveBgs.push(moveBg); this.pokemonMoveLabels.push(moveLabel); @@ -132,38 +140,46 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonGenderLabelText = addTextObject(this.scene, infoContainerLabelXPos, 18, i18next.t("pokemonInfoContainer:gender"), TextStyle.WINDOW, { fontSize: infoContainerTextSize }); this.pokemonGenderLabelText.setOrigin(1, 0); this.pokemonGenderLabelText.setVisible(false); + this.pokemonGenderLabelText.setName("text-pkmn-gender-label"); this.add(this.pokemonGenderLabelText); this.pokemonGenderText = addTextObject(this.scene, infoContainerTextXPos, 18, "", TextStyle.WINDOW, { fontSize: infoContainerTextSize }); this.pokemonGenderText.setOrigin(0, 0); this.pokemonGenderText.setVisible(false); + this.pokemonGenderText.setName("text-pkmn-gender"); this.add(this.pokemonGenderText); this.pokemonAbilityLabelText = addTextObject(this.scene, infoContainerLabelXPos, 28, i18next.t("pokemonInfoContainer:ability"), TextStyle.WINDOW, { fontSize: infoContainerTextSize }); this.pokemonAbilityLabelText.setOrigin(1, 0); + this.pokemonAbilityLabelText.setName("text-pkmn-ability-label"); this.add(this.pokemonAbilityLabelText); this.pokemonAbilityText = addTextObject(this.scene, infoContainerTextXPos, 28, "", TextStyle.WINDOW, { fontSize: infoContainerTextSize }); this.pokemonAbilityText.setOrigin(0, 0); + this.pokemonAbilityText.setName("text-pkmn-ability"); this.add(this.pokemonAbilityText); this.pokemonNatureLabelText = addTextObject(this.scene, infoContainerLabelXPos, 38, i18next.t("pokemonInfoContainer:nature"), TextStyle.WINDOW, { fontSize: infoContainerTextSize }); this.pokemonNatureLabelText.setOrigin(1, 0); + this.pokemonNatureLabelText.setName("text-pkmn-nature-label"); this.add(this.pokemonNatureLabelText); this.pokemonNatureText = addBBCodeTextObject(this.scene, infoContainerTextXPos, 38, "", TextStyle.WINDOW, { fontSize: infoContainerTextSize, lineSpacing: 3, maxLines: 2 }); this.pokemonNatureText.setOrigin(0, 0); + this.pokemonNatureText.setName("text-pkmn-nature"); this.add(this.pokemonNatureText); this.pokemonShinyIcon = this.scene.add.image(-43.5, 48.5, "shiny_star"); this.pokemonShinyIcon.setOrigin(0, 0); this.pokemonShinyIcon.setScale(0.75); this.pokemonShinyIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains); + this.pokemonShinyIcon.setName("img-pkmn-shiny-icon"); this.add(this.pokemonShinyIcon); this.pokemonFusionShinyIcon = this.scene.add.image(this.pokemonShinyIcon.x, this.pokemonShinyIcon.y, "shiny_star_2"); this.pokemonFusionShinyIcon.setOrigin(0, 0); this.pokemonFusionShinyIcon.setScale(0.75); + this.pokemonFusionShinyIcon.setName("img-pkmn-fusion-shiny-icon"); this.add(this.pokemonFusionShinyIcon); this.setVisible(false); @@ -246,6 +262,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.setVisible(true); this.shown = true; + this.scene.hideEnemyModifierBar(); }); } @@ -266,6 +283,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { hide(speedMultiplier: number = 1): Promise { return new Promise(resolve => { if (!this.shown) { + this.scene.showEnemyModifierBar(); return resolve(); } @@ -286,6 +304,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonShinyIcon.off("pointerover"); this.pokemonShinyIcon.off("pointerout"); (this.scene as BattleScene).ui.hideTooltip(); + this.scene.showEnemyModifierBar(); resolve(); } }); diff --git a/src/ui/settings/navigationMenu.ts b/src/ui/settings/navigationMenu.ts index d9664276872..c35e60d3ac7 100644 --- a/src/ui/settings/navigationMenu.ts +++ b/src/ui/settings/navigationMenu.ts @@ -27,11 +27,12 @@ export class NavigationManager { constructor() { this.modes = [ Mode.SETTINGS, - Mode.SETTINGS_ACCESSIBILITY, + Mode.SETTINGS_DISPLAY, + Mode.SETTINGS_AUDIO, Mode.SETTINGS_GAMEPAD, Mode.SETTINGS_KEYBOARD, ]; - this.labels = ["General", "Accessibility", "Gamepad", "Keyboard"]; + this.labels = ["General", "Display", "Audio", "Gamepad", "Keyboard"]; } public reset() { diff --git a/src/ui/settings/settings-accessiblity-ui-handler.ts b/src/ui/settings/settings-audio-ui-handler.ts similarity index 81% rename from src/ui/settings/settings-accessiblity-ui-handler.ts rename to src/ui/settings/settings-audio-ui-handler.ts index ef700a7a9ab..47606d3c54c 100644 --- a/src/ui/settings/settings-accessiblity-ui-handler.ts +++ b/src/ui/settings/settings-audio-ui-handler.ts @@ -4,7 +4,7 @@ import { Mode } from "../ui"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { Setting, SettingType } from "#app/system/settings/settings"; -export default class SettingsAccessibilityUiHandler extends AbstractSettingsUiHandler { +export default class SettingsAudioUiHandler extends AbstractSettingsUiHandler { /** * Creates an instance of SettingsGamepadUiHandler. * @@ -13,8 +13,8 @@ export default class SettingsAccessibilityUiHandler extends AbstractSettingsUiHa */ constructor(scene: BattleScene, mode?: Mode) { super(scene, mode); - this.title = "Accessibility"; - this.settings = Setting.filter(s => s.type === SettingType.ACCESSIBILITY); + this.title = "Audio"; + this.settings = Setting.filter(s => s.type === SettingType.AUDIO); this.localStorageKey = "settings"; } } diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts new file mode 100644 index 00000000000..9e61c2ba0b2 --- /dev/null +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -0,0 +1,20 @@ +import BattleScene from "../../battle-scene"; +import { Mode } from "../ui"; +"#app/inputs-controller.js"; +import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; +import { Setting, SettingType } from "#app/system/settings/settings"; + +export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler { + /** + * Creates an instance of SettingsGamepadUiHandler. + * + * @param scene - The BattleScene instance. + * @param mode - The UI mode, optional. + */ + constructor(scene: BattleScene, mode?: Mode) { + super(scene, mode); + this.title = "Display"; + this.settings = Setting.filter(s => s.type === SettingType.DISPLAY); + this.localStorageKey = "settings"; + } +} diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index abba4a081df..1dbb60915ca 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -20,7 +20,8 @@ import { Type } from "../data/type"; import { Button } from "../enums/buttons"; import { GameModes, gameModes } from "../game-mode"; import { TitlePhase } from "../phases"; -import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, Passive as PassiveAttr, StarterFormMoveData, StarterMoveset } from "../system/game-data"; +import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, StarterFormMoveData, StarterMoveset } from "../system/game-data"; +import { Passive as PassiveAttr } from "#app/data/enums/passive"; import { Tutorial, handleTutorial } from "../tutorial"; import * as Utils from "../utils"; import { OptionSelectItem } from "./abstact-option-select-ui-handler"; @@ -30,6 +31,7 @@ import { StatsContainer } from "./stats-container"; import { TextStyle, addBBCodeTextObject, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import MoveInfoOverlay from "./move-info-overlay"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -191,6 +193,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; private statsContainer: StatsContainer; private pokemonFormText: Phaser.GameObjects.Text; + private moveInfoOverlay : MoveInfoOverlay; private genMode: boolean; private statsMode: boolean; @@ -221,6 +224,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private canCycleNature: boolean; private canCycleVariant: boolean; private value: integer = 0; + private canAddParty: boolean; private assetLoadCancelled: Utils.BooleanHolder; private cursorObj: Phaser.GameObjects.Image; @@ -659,6 +663,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.message.setOrigin(0, 0); this.starterSelectMessageBoxContainer.add(this.message); + const overlayScale = 1; // scale for the move info. "2/3" might be another good option... + this.moveInfoOverlay = new MoveInfoOverlay(this.scene, { + scale: overlayScale, + top: true, + x: 1, + y: this.scene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29, + }); + this.starterSelectContainer.add(this.moveInfoOverlay); + const date = new Date(); date.setUTCHours(0, 0, 0, 0); @@ -1035,6 +1048,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.tryStart(); } this.updateInstructions(); + + /** + * If the user can't select a pokemon anymore, + * go to start button. + */ + if (!this.canAddParty) { + this.startCursorObj.setVisible(true); + this.setGenMode(true); + } + ui.playSelect(); } else { ui.playError(); @@ -1056,6 +1079,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const showSwapOptions = (moveset: StarterMoveset) => { ui.setMode(Mode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectMoveSwapOut"), null, () => { + this.moveInfoOverlay.show(allMoves[moveset[0]]); + ui.setModeWithoutClear(Mode.OPTION_SELECT, { options: moveset.map((m: Moves, i: number) => { const option: OptionSelectItem = { @@ -1063,8 +1088,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { handler: () => { ui.setMode(Mode.STARTER_SELECT).then(() => { ui.showText(`${i18next.t("starterSelectUiHandler:selectMoveSwapWith")} ${allMoves[m].name}.`, null, () => { + const possibleMoves = this.speciesStarterMoves.filter((sm: Moves) => sm !== m); + this.moveInfoOverlay.show(allMoves[possibleMoves[0]]); + ui.setModeWithoutClear(Mode.OPTION_SELECT, { - options: this.speciesStarterMoves.filter((sm: Moves) => sm !== m).map(sm => { + options: possibleMoves.map(sm => { // make an option for each available starter move const option = { label: allMoves[sm].name, @@ -1072,7 +1100,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.switchMoveHandler(i, sm, m); showSwapOptions(this.starterMoveset); return true; - } + }, + onHover: () => { + this.moveInfoOverlay.show(allMoves[sm]); + }, }; return option; }).concat({ @@ -1080,25 +1111,37 @@ export default class StarterSelectUiHandler extends MessageUiHandler { handler: () => { showSwapOptions(this.starterMoveset); return true; - } + }, + onHover: () => { + this.moveInfoOverlay.clear(); + }, }), + supportHover: true, maxOptions: 8, yOffset: 19 }); }); }); return true; - } + }, + onHover: () => { + this.moveInfoOverlay.show(allMoves[m]); + }, }; return option; }).concat({ label: i18next.t("menu:cancel"), handler: () => { + this.moveInfoOverlay.clear(); this.clearText(); ui.setMode(Mode.STARTER_SELECT); return true; - } + }, + onHover: () => { + this.moveInfoOverlay.clear(); + }, }), + supportHover: true, maxOptions: 8, yOffset: 19 }); @@ -2099,11 +2142,43 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.time.delayedCall(Utils.fixedInt(500), () => this.tryUpdateValue()); return false; } + + /** + * this loop is used to set the Sprite's alpha value and check if the user can select other pokemon more. + */ + this.canAddParty = false; + const remainValue = valueLimit - newValue; for (let g = 0; g < this.genSpecies.length; g++) { for (let s = 0; s < this.genSpecies[g].length; s++) { - (this.starterSelectGenIconContainers[g].getAt(s) as Phaser.GameObjects.Sprite).setAlpha((newValue + this.scene.gameData.getSpeciesStarterValue(this.genSpecies[g][s].speciesId)) > valueLimit ? 0.375 : 1); + /** Cost of pokemon species */ + const speciesStarterValue = this.scene.gameData.getSpeciesStarterValue(this.genSpecies[g][s].speciesId); + /** Used to detect if this pokemon is registered in starter */ + const speciesStarterDexEntry = this.scene.gameData.dexData[this.genSpecies[g][s].speciesId]; + /** {@linkcode Phaser.GameObjects.Sprite} object of Pokémon for setting the alpha value */ + const speciesSprite = this.starterSelectGenIconContainers[g].getAt(s) as Phaser.GameObjects.Sprite; + + /** + * If remainValue greater than or equal pokemon species, the user can select. + * so that the alpha value of pokemon sprite set 1. + * + * If speciesStarterDexEntry?.caughtAttr is true, this species registered in stater. + * we change to can AddParty value to true since the user has enough cost to choose this pokemon and this pokemon registered too. + */ + if (remainValue >= speciesStarterValue) { + speciesSprite.setAlpha(1); + if (speciesStarterDexEntry?.caughtAttr) { + this.canAddParty = true; + } + } else { + /** + * If remainValue less than pokemon, the use can't select. + * so that the alpha value of pokemon sprite set 0.375. + */ + speciesSprite.setAlpha(0.375); + } } } + this.value = newValue; return true; } diff --git a/src/ui/stats-container.ts b/src/ui/stats-container.ts index f5b88773dd8..6dccba18fa8 100644 --- a/src/ui/stats-container.ts +++ b/src/ui/stats-container.ts @@ -23,6 +23,7 @@ export class StatsContainer extends Phaser.GameObjects.Container { } setup() { + this.setName("container-stats"); const ivChartBgData = new Array(6).fill(null).map((_, i: integer) => [ ivChartSize * ivChartStatCoordMultipliers[ivChartStatIndexes[i]][0], ivChartSize * ivChartStatCoordMultipliers[ivChartStatIndexes[i]][1] ] ).flat(); const ivChartBg = this.scene.add.polygon(48, 44, ivChartBgData, 0xd8e0f0, 0.625); diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 1133e7a755c..5a1f883d94f 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -17,7 +17,7 @@ import { StatusEffect } from "../data/status-effect"; import { getBiomeName } from "../data/biomes"; import { Nature, getNatureStatMultiplier } from "../data/nature"; import { loggedInUser } from "../account"; -import { PlayerGender } from "../system/game-data"; +import { PlayerGender } from "#app/data/enums/player-gender"; import { Variant, getVariantTint } from "#app/data/variant"; import {Button} from "../enums/buttons"; import { Ability } from "../data/ability.js"; diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts new file mode 100644 index 00000000000..fd5a8b0b15e --- /dev/null +++ b/src/ui/time-of-day-widget.ts @@ -0,0 +1,172 @@ +import * as Utils from "../utils"; +import BattleScene from "#app/battle-scene.js"; +import { TimeOfDay } from "#app/data/enums/time-of-day.js"; +import { BattleSceneEventType } from "#app/battle-scene-events.js"; +import { EaseType } from "./enums/ease-type"; + +/** A small self contained UI element that displays the time of day as an icon */ +export default class TimeOfDayWidget extends Phaser.GameObjects.Container { + /** An alias for the scene typecast to a {@linkcode BattleScene} */ + private battleScene: BattleScene; + + /** The {@linkcode Phaser.GameObjects.Sprite} that represents the foreground of the current time of day */ + private readonly timeOfDayIconFgs: Phaser.GameObjects.Sprite[] = new Array(2); + /** The {@linkcode Phaser.GameObjects.Sprite} that represents the middle-ground of the current time of day */ + private readonly timeOfDayIconMgs: Phaser.GameObjects.Sprite[] = new Array(2); + /** The {@linkcode Phaser.GameObjects.Sprite} that represents the background of the current time of day */ + private readonly timeOfDayIconBgs: Phaser.GameObjects.Sprite[] = new Array(2); + + /** An array containing all timeOfDayIcon objects for easier iteration */ + private timeOfDayIcons: Phaser.GameObjects.Sprite[]; + + /** A map containing all timeOfDayIcon arrays with a matching string key for easier iteration */ + private timeOfDayIconPairs: Map = new Map([ + ["bg", this.timeOfDayIconBgs], + ["mg", this.timeOfDayIconMgs], + ["fg", this.timeOfDayIconFgs],]); + + /** The current time of day */ + private currentTime: TimeOfDay = TimeOfDay.ALL; + /** The previous time of day */ + private previousTime: TimeOfDay = TimeOfDay.ALL; + + // Subscribes to required events available on game start + private readonly onEncounterPhaseEvent = (event: Event) => this.onEncounterPhase(event); + + private _parentVisible: boolean; + /** Is the parent object visible? */ + public get parentVisible(): boolean { + return this._parentVisible; + } + /** On set, resumes any paused tweens if true */ + public set parentVisible(visible: boolean) { + if (visible && !this._parentVisible) { // Only resume the tweens if parent is newly visible + this.timeOfDayIcons?.forEach( + icon => this.scene.tweens.getTweensOf(icon).forEach( + tween => tween.resume())); + } + + this._parentVisible = visible; + } + + constructor(scene: Phaser.Scene, x: number = 0, y: number = 0) { + super(scene, x, y); + this.battleScene = this.scene as BattleScene; + + this.setVisible(this.battleScene.showTimeOfDayWidget); + if (!this.battleScene.showTimeOfDayWidget) { + return; + } + + // Initialize all sprites + this.timeOfDayIconPairs.forEach( + (icons, key) => { + for (let i = 0; i < icons.length; i++) { + icons[i] = this.scene.add.sprite(0, 0, "dawn_icon_" + key).setOrigin(); + } + }); + // Store a flat array of all icons for later + this.timeOfDayIcons = [this.timeOfDayIconBgs, this.timeOfDayIconMgs, this.timeOfDayIconFgs].flat(); + this.add(this.timeOfDayIcons); + + this.battleScene.eventTarget.addEventListener(BattleSceneEventType.ENCOUNTER_PHASE, this.onEncounterPhaseEvent); + } + + /** + * Creates a tween animation based on the 'Back' ease algorithm + * @returns an array of all tweens in the animation + */ + private getBackTween(): Phaser.Types.Tweens.TweenBuilderConfig[] { + const rotate = { + targets: [this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1]], + angle: "+=90", + duration: Utils.fixedInt(1500), + ease: "Back.easeOut", + paused: !this.parentVisible, + }; + const fade = { + targets: [this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1]], + alpha: 0, + duration: Utils.fixedInt(500), + ease: "Linear", + paused: !this.parentVisible, + }; + + return [rotate, fade]; + } + + /** + * Creates a tween animation based on the 'Bounce' ease algorithm + * @returns an array of all tweens in the animation + */ + private getBounceTween(): Phaser.Types.Tweens.TweenBuilderConfig[] { + const bounce = { + targets: [this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1]], + angle: "+=90", + duration: Utils.fixedInt(2000), + ease: "Bounce.easeOut", + paused: !this.parentVisible, + }; + const fade = { + targets: [this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1]], + alpha: 0, + duration: Utils.fixedInt(800), + ease: "Linear", + paused: !this.parentVisible, + }; + + return [bounce, fade]; + } + + /** Resets all icons to the proper depth, texture, and alpha so they are ready to tween */ + private resetIcons() { + this.moveBelow(this.timeOfDayIconBgs[0], this.timeOfDayIconBgs[1]); + this.moveBelow(this.timeOfDayIconMgs[0], this.timeOfDayIconBgs[1]); + this.moveBelow(this.timeOfDayIconFgs[0], this.timeOfDayIconFgs[1]); + + this.timeOfDayIconPairs.forEach( + (icons, key) => { + icons[0].setTexture(TimeOfDay[this.currentTime].toLowerCase() + "_icon_" + key); + icons[1].setTexture(TimeOfDay[this.previousTime].toLowerCase() + "_icon_" + key); + }); + this.timeOfDayIconMgs[0].setRotation(-90 * (3.14/180)); + + this.timeOfDayIcons.forEach(icon => icon.setAlpha(1)); + } + + /** Adds the proper tween for all icons */ + private tweenTimeOfDayIcon() { + this.scene.tweens.killTweensOf(this.timeOfDayIcons); + + this.resetIcons(); + + // Tween based on the player setting + (this.battleScene.timeOfDayAnimation === EaseType.BACK ? this.getBackTween() : this.getBounceTween()) + .forEach(tween => this.scene.tweens.add(tween)); + + // Swaps all elements of the icon arrays by shifting the first element onto the end of the array + // This ensures index[0] is always the new time of day icon and index[1] is always the current one + this.timeOfDayIconPairs.forEach( + icons => icons.push(icons.shift())); + } + + /** + * Grabs the current time of day from the arena and calls {@linkcode tweenTimeOfDayIcon} + * @param event {@linkcode Event} being sent + */ + private onEncounterPhase(event: Event) { + const newTime = this.battleScene.arena.getTimeOfDay(); + + if (this.currentTime === newTime) { + return; + } + + this.currentTime = newTime; + this.previousTime = this.currentTime - 1; + if (this.previousTime < TimeOfDay.DAWN) { + this.previousTime = TimeOfDay.NIGHT; + } + + this.tweenTimeOfDayIcon(); + } +} diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 0f33a9cb6c7..aaf764f501f 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -38,11 +38,12 @@ import OutdatedModalUiHandler from "./outdated-modal-ui-handler"; import SessionReloadModalUiHandler from "./session-reload-modal-ui-handler"; import {Button} from "../enums/buttons"; import i18next, {ParseKeys} from "i18next"; -import {PlayerGender} from "#app/system/game-data"; +import { PlayerGender } from "#app/data/enums/player-gender"; import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler"; import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler"; -import SettingsAccessibilityUiHandler from "./settings/settings-accessiblity-ui-handler"; +import SettingsDisplayUiHandler from "./settings/settings-display-ui-handler"; +import SettingsAudioUiHandler from "./settings/settings-audio-ui-handler"; export enum Mode { MESSAGE, @@ -63,7 +64,8 @@ export enum Mode { MENU, MENU_OPTION_SELECT, SETTINGS, - SETTINGS_ACCESSIBILITY, + SETTINGS_DISPLAY, + SETTINGS_AUDIO, SETTINGS_GAMEPAD, GAMEPAD_BINDING, SETTINGS_KEYBOARD, @@ -101,7 +103,8 @@ const noTransitionModes = [ Mode.GAMEPAD_BINDING, Mode.KEYBOARD_BINDING, Mode.SETTINGS, - Mode.SETTINGS_ACCESSIBILITY, + Mode.SETTINGS_AUDIO, + Mode.SETTINGS_DISPLAY, Mode.SETTINGS_GAMEPAD, Mode.SETTINGS_KEYBOARD, Mode.ACHIEVEMENTS, @@ -154,7 +157,8 @@ export default class UI extends Phaser.GameObjects.Container { new MenuUiHandler(scene), new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT), new SettingsUiHandler(scene), - new SettingsAccessibilityUiHandler(scene), + new SettingsDisplayUiHandler(scene), + new SettingsAudioUiHandler(scene), new SettingsGamepadUiHandler(scene), new GamepadBindingUiHandler(scene), new SettingsKeyboardUiHandler(scene), @@ -174,6 +178,7 @@ export default class UI extends Phaser.GameObjects.Container { } setup(): void { + this.setName("container-ui"); for (const handler of this.handlers) { handler.setup(); } diff --git a/src/utils.ts b/src/utils.ts index 6d965369ca3..68f6e323af1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -263,6 +263,8 @@ export const isLocal = ( // Set the server URL based on whether it's local or not export const serverUrl = isLocal ? `${window.location.hostname}:${window.location.port}` : ""; export const apiUrl = isLocal ? serverUrl : "https://api.pokerogue.net"; +// used to disable api calls when isLocal is true and a server is not found +export let isLocalServerConnected = false; export function setCookie(cName: string, cValue: string): void { const expiration = new Date(); @@ -285,8 +287,22 @@ export function getCookie(cName: string): string { return ""; } +/** + * When locally running the game, "pings" the local server + * with a GET request to verify if a server is running, + * sets isLocalServerConnected based on results + */ +export function localPing() { + if (isLocal) { + apiFetch("game/titlestats") + .then(resolved => isLocalServerConnected = true, + rejected => isLocalServerConnected = false + ); + } +} + export function apiFetch(path: string, authed: boolean = false): Promise { - return new Promise((resolve, reject) => { + return (isLocal && isLocalServerConnected) || !isLocal ? new Promise((resolve, reject) => { const request = {}; if (authed) { const sId = getCookie(sessionIdKey); @@ -297,11 +313,11 @@ export function apiFetch(path: string, authed: boolean = false): Promise resolve(response)) .catch(err => reject(err)); - }); + }) : new Promise(() => {}); } export function apiPost(path: string, data?: any, contentType: string = "application/json", authed: boolean = false): Promise { - return new Promise((resolve, reject) => { + return (isLocal && isLocalServerConnected) || !isLocal ? new Promise((resolve, reject) => { const headers = { "Accept": contentType, "Content-Type": contentType, @@ -315,7 +331,7 @@ export function apiPost(path: string, data?: any, contentType: string = "applica fetch(`${apiUrl}/${path}`, { method: "POST", headers: headers, body: data }) .then(response => resolve(response)) .catch(err => reject(err)); - }); + }) : new Promise(() => {}); } export class BooleanHolder {