Merge branch 'main' into plates_memories

This commit is contained in:
Madmadness65 2024-05-29 18:09:13 -05:00
commit 03f533fb42
79 changed files with 10638 additions and 284 deletions

View File

@ -1,7 +0,0 @@
dist/*
build/*
coverage/*
public/*
.github/*
node_modules/*
.vscode/*

View File

@ -1,35 +0,0 @@
{
"parser": "@typescript-eslint/parser", // Specifies the ESLint parser for TypeScript
"plugins": ["@typescript-eslint", "import"], // Includes TypeScript and import plugins
"overrides": [
{
"files": ["src/**/*.{ts,tsx,js,jsx}"], // Applies these rules to all TypeScript and JavaScript files in the src directory
"rules": {
// General rules that apply to all files
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and !=
"indent": ["error", 2], // Enforces a 2-space indentation
"quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
"ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the rest.
}],
"eol-last": ["error", "always"], // Enforces at least one newline at the end of files
"@typescript-eslint/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax
"semi": "off", // Disables the general semi rule for TypeScript files
"@typescript-eslint/no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@typescript-eslint/brace-style": ["error", "1tbs"],
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments
}],
"space-before-blocks": ["error", "always"], // Enforces a space before blocks
"keyword-spacing": ["error", { "before": true, "after": true }] // Enforces spacing before and after keywords
}
}
]
}

View File

@ -28,4 +28,4 @@ jobs:
run: npm ci # Use 'npm ci' to install dependencies
- name: eslint # Step to run linters
uses: icrawl/action-eslint@v1
run: npm run eslint-ci

42
eslint.config.js Normal file
View File

@ -0,0 +1,42 @@
import tseslint from '@typescript-eslint/eslint-plugin';
import parser from '@typescript-eslint/parser';
import imports from 'eslint-plugin-import';
export default [
{
files: ["src/**/*.{ts,tsx,js,jsx}"],
ignores: ["dist/*", "build/*", "coverage/*", "public/*", ".github/*", "node_modules/*", ".vscode/*"],
languageOptions: {
parser: parser
},
plugins: {
imports: imports.configs.recommended,
'@typescript-eslint': tseslint
},
rules: {
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and !=
"indent": ["error", 2], // Enforces a 2-space indentation
"quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
"ignoreRestSiblings": true // Allows unused variables that are part of a rest property in object destructuring. Useful for excluding certain properties from an object while using the rest.
}],
"eol-last": ["error", "always"], // Enforces at least one newline at the end of files
"@typescript-eslint/semi": ["error", "always"], // Requires semicolons for TypeScript-specific syntax
"semi": "off", // Disables the general semi rule for TypeScript files
"no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@typescript-eslint/brace-style": ["error", "1tbs"],
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments
}],
"space-before-blocks": ["error", "always"], // Enforces a space before blocks
"keyword-spacing": ["error", { "before": true, "after": true }] // Enforces spacing before and after keywords
}
}
]

16
package-lock.json generated
View File

@ -18,6 +18,7 @@
},
"devDependencies": {
"@eslint/js": "^9.3.0",
"@types/node": "^20.12.13",
"@typescript-eslint/eslint-plugin": "^7.10.0",
"@typescript-eslint/parser": "^7.10.0",
"@vitest/coverage-istanbul": "^1.4.0",
@ -1179,6 +1180,15 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/node": {
"version": "20.12.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz",
"integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz",
@ -6201,6 +6211,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",

View File

@ -11,10 +11,12 @@
"test": "vitest run",
"test:cov": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"eslint": "eslint --fix ."
"eslint": "eslint --fix .",
"eslint-ci": "eslint ."
},
"devDependencies": {
"@eslint/js": "^9.3.0",
"@types/node": "^20.12.13",
"@typescript-eslint/eslint-plugin": "^7.10.0",
"@typescript-eslint/parser": "^7.10.0",
"@vitest/coverage-istanbul": "^1.4.0",

Binary file not shown.

Binary file not shown.

View File

@ -146,14 +146,14 @@
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 3,
"h": 3
"w": 66,
"h": 55
},
"frame": {
"x": 132,
"y": 0,
"w": 3,
"h": 3
"x": 0,
"y": 112,
"w": 66,
"h": 55
}
},
{

View File

@ -670,6 +670,10 @@ export default class BattleScene extends SceneBase {
species = getPokemonSpecies(Overrides.OPP_SPECIES_OVERRIDE);
}
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource);
if (Overrides.OPP_LEVEL_OVERRIDE !== 0) {
pokemon.level = Overrides.OPP_LEVEL_OVERRIDE;
}
if (Overrides.OPP_GENDER_OVERRIDE !== null) {
pokemon.gender = Overrides.OPP_GENDER_OVERRIDE;
}

View File

@ -63,6 +63,8 @@ export default class Battle {
private battleSeedState: string;
public moneyScattered: number;
public lastUsedPokeball: PokeballType;
public playerFaints: number; // The amount of times pokemon on the players side have fainted
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
private rngCounter: integer = 0;
@ -89,6 +91,8 @@ export default class Battle {
this.battleSeedState = null;
this.moneyScattered = 0;
this.lastUsedPokeball = null;
this.playerFaints = 0;
this.enemyFaints = 0;
}
private initBattleSpec(): void {

28
src/configs/pad_procon.ts Normal file
View File

@ -0,0 +1,28 @@
/**
* Nintendo Pro Controller mapping
*/
const pad_procon = {
padID: "Pro Controller",
padType: "Nintendo",
gamepadMapping: {
RC_S: 1,
RC_E: 0,
RC_W: 3,
RC_N: 2,
START: 9, // +
SELECT: 8, // -
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16, // Home
},
};
export default pad_procon;

82
src/data/ability.ts Normal file → Executable file
View File

@ -1083,6 +1083,37 @@ export class LowHpMoveTypePowerBoostAbAttr extends MoveTypePowerBoostAbAttr {
}
}
/**
* Abilities which cause a variable amount of power increase.
* @extends VariableMovePowerAbAttr
* @see {@link applyPreAttack}
*/
export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
private mult: (user: Pokemon, target: Pokemon, move: Move) => number;
/**
* @param mult A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier.
* @param {boolean} showAbility Whether to show the ability when it activates.
*/
constructor(mult: (user: Pokemon, target: Pokemon, move: Move) => number, showAbility: boolean = true) {
super(showAbility);
this.mult = mult;
}
/**
* @override
*/
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
const multiplier = this.mult(pokemon, defender, move.getMove());
if (multiplier !== 1) {
(args[0] as Utils.NumberHolder).value *= multiplier;
return true;
}
return false;
}
}
export class FieldVariableMovePowerAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder;
@ -2725,14 +2756,20 @@ export class RunSuccessAbAttr extends AbAttr {
}
}
type ArenaTrapCondition = (user: Pokemon, target: Pokemon) => boolean;
/**
* Base class for checking if a Pokemon is trapped by arena trap
* @extends AbAttr
* @field {@linkcode arenaTrapCondition} Conditional for trapping abilities.
* For example, Magnet Pull will only activate if opponent is Steel type.
* @see {@linkcode applyCheckTrapped}
*/
export class CheckTrappedAbAttr extends AbAttr {
constructor() {
protected arenaTrapCondition: ArenaTrapCondition;
constructor(condition: ArenaTrapCondition) {
super(false);
this.arenaTrapCondition = condition;
}
applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise<boolean> {
@ -2749,6 +2786,9 @@ export class CheckTrappedAbAttr extends AbAttr {
export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
/**
* Checks if enemy Pokemon is trapped by an Arena Trap-esque ability
* If the enemy is a Ghost type, it is not trapped
* If the user has Magnet Pull and the enemy is not a Steel type, it is not trapped.
* If the user has Arena Trap and the enemy is not grounded, it is not trapped.
* @param pokemon The {@link Pokemon} with this {@link AbAttr}
* @param passive N/A
* @param trapped {@link Utils.BooleanHolder} indicating whether the other Pokemon is trapped or not
@ -2757,12 +2797,16 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
* @returns if enemy Pokemon is trapped or not
*/
applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean {
if (otherPokemon.getTypes().includes(Type.GHOST)) {
trapped.value = false;
return false;
if (this.arenaTrapCondition(pokemon, otherPokemon)) {
if (otherPokemon.getTypes(true).includes(Type.GHOST) || (otherPokemon.getTypes(true).includes(Type.STELLAR) && otherPokemon.getTypes().includes(Type.GHOST))) {
trapped.value = false;
return false;
}
trapped.value = true;
return true;
}
trapped.value = true;
return true;
trapped.value = false;
return false;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
@ -3402,7 +3446,12 @@ export function initAbilities() {
new Ability(Abilities.INTIMIDATE, 3)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, -1, false, true),
new Ability(Abilities.SHADOW_TAG, 3)
.attr(ArenaTrapAbAttr),
.attr(ArenaTrapAbAttr, (user, target) => {
if (target.hasAbility(Abilities.SHADOW_TAG)) {
return false;
}
return true;
}),
new Ability(Abilities.ROUGH_SKIN, 3)
.attr(PostDefendContactDamageAbAttr, 8)
.bypassFaint(),
@ -3459,9 +3508,12 @@ export function initAbilities() {
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
.ignorable(),
new Ability(Abilities.MAGNET_PULL, 3)
/*.attr(ArenaTrapAbAttr)
.condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL))*/
.unimplemented(),
.attr(ArenaTrapAbAttr, (user, target) => {
if (target.getTypes(true).includes(Type.STEEL) || (target.getTypes(true).includes(Type.STELLAR) && target.getTypes().includes(Type.STEEL))) {
return true;
}
return false;
}),
new Ability(Abilities.SOUNDPROOF, 3)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.SOUND_BASED))
.ignorable(),
@ -3538,7 +3590,12 @@ export function initAbilities() {
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY),
new Ability(Abilities.ARENA_TRAP, 3)
.attr(ArenaTrapAbAttr)
.attr(ArenaTrapAbAttr, (user, target) => {
if (target.isGrounded()) {
return true;
}
return false;
})
.attr(DoubleBattleChanceAbAttr),
new Ability(Abilities.VITAL_SPIRIT, 3)
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
@ -4249,7 +4306,8 @@ export function initAbilities() {
new Ability(Abilities.SHARPNESS, 9)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
new Ability(Abilities.SUPREME_OVERLORD, 9)
.unimplemented(),
.attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5))
.partial(),
new Ability(Abilities.COSTAR, 9)
.unimplemented(),
new Ability(Abilities.TOXIC_DEBRIS, 9)

View File

@ -86,6 +86,10 @@ export enum MoveFlags {
WIND_MOVE = 1 << 14,
TRIAGE_MOVE = 1 << 15,
IGNORE_ABILITIES = 1 << 16,
/**
* Enables all hits of a multi-hit move to be accuracy checked individually
*/
CHECK_ALL_HITS = 1 << 17,
}
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
@ -140,8 +144,8 @@ export default class Move implements Localizable {
localize(): void {
const i18nKey = Moves[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`).toString()}${this.nameAppend}` : "";
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`).toString()}${this.nameAppend}` : "";
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : "";
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
}
getAttrs(attrType: { new(...args: any[]): MoveAttr }): MoveAttr[] {
@ -346,6 +350,11 @@ export default class Move implements Localizable {
return this;
}
checkAllHits(checkAllHits?: boolean): this {
this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits);
return this;
}
checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean {
switch (flag) {
case MoveFlags.MAKES_CONTACT:
@ -955,8 +964,7 @@ export enum MultiHitType {
_2,
_2_TO_5,
_3,
_3_INCR,
_1_TO_10,
_10,
BEAT_UP,
}
@ -1287,37 +1295,8 @@ export class MultiHitAttr extends MoveAttr {
case MultiHitType._3:
hitTimes = 3;
break;
case MultiHitType._3_INCR:
hitTimes = 3;
// TODO: Add power increase for every hit
break;
case MultiHitType._1_TO_10:
{
const rand = user.randSeedInt(90);
const hitValue = new Utils.IntegerHolder(rand);
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
if (hitValue.value >= 81) {
hitTimes = 1;
} else if (hitValue.value >= 73) {
hitTimes = 2;
} else if (hitValue.value >= 66) {
hitTimes = 3;
} else if (hitValue.value >= 60) {
hitTimes = 4;
} else if (hitValue.value >= 54) {
hitTimes = 5;
} else if (hitValue.value >= 49) {
hitTimes = 6;
} else if (hitValue.value >= 44) {
hitTimes = 7;
} else if (hitValue.value >= 40) {
hitTimes = 8;
} else if (hitValue.value >= 36) {
hitTimes = 9;
} else {
hitTimes = 10;
}
}
case MultiHitType._10:
hitTimes = 10;
break;
case MultiHitType.BEAT_UP:
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
@ -2032,10 +2011,10 @@ export class PostVictoryStatChangeAttr extends MoveAttr {
}
applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void {
if (this.condition && !this.condition(user, target, move)) {
return false;
return;
}
const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage);
statChangeAttr.apply(user, target, move);
statChangeAttr.apply(user, target, move, undefined);
}
}
@ -2751,6 +2730,43 @@ export class WaterShurikenPowerAttr extends VariablePowerAttr {
}
}
/**
* Attribute used for multi-hit moves that increase power in increments of the
* move's base power for each hit, namely Triple Kick and Triple Axel.
* @extends VariablePowerAttr
* @see {@linkcode apply}
*/
export class MultiHitPowerIncrementAttr extends VariablePowerAttr {
/** The max number of base power increments allowed for this move */
private maxHits: integer;
constructor(maxHits: integer) {
super();
this.maxHits = maxHits;
}
/**
* Increases power of move in increments of the base power for the amount of times
* the move hit. In the case that the move is extended, it will circle back to the
* original base power of the move after incrementing past the maximum amount of
* hits.
* @param user {@linkcode Pokemon} that used the move
* @param target {@linkcode Pokemon} that the move was used on
* @param move {@linkcode Move} with this attribute
* @param args [0] {@linkcode Utils.NumberHolder} for final calculated power of move
* @returns true if attribute application succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0);
const power = args[0] as Utils.NumberHolder;
power.value = move.power * (1 + hitsTotal % this.maxHits);
return true;
}
}
export class VariableAtkAttr extends MoveAttr {
constructor() {
super();
@ -5480,12 +5496,9 @@ export function initMoves() {
.attr(SketchAttr)
.ignoresVirtual(),
new AttackMove(Moves.TRIPLE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, 0, 2)
.attr(MultiHitAttr, MultiHitType._3_INCR)
.attr(MissEffectAttr, (user: Pokemon, move: Move) => {
user.turnData.hitsLeft = 1;
return true;
})
.partial(),
.attr(MultiHitAttr, MultiHitType._3)
.attr(MultiHitPowerIncrementAttr, 3)
.checkAllHits(),
new AttackMove(Moves.THIEF, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, -1, 0, 2)
.attr(StealHeldItemChanceAttr, 0.3),
new StatusMove(Moves.SPIDER_WEB, Type.BUG, -1, 10, -1, 0, 2)
@ -7337,12 +7350,9 @@ export function initMoves() {
new AttackMove(Moves.FLIP_TURN, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8)
.attr(ForceSwitchOutAttr, true, false),
new AttackMove(Moves.TRIPLE_AXEL, Type.ICE, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 8)
.attr(MultiHitAttr, MultiHitType._3_INCR)
.attr(MissEffectAttr, (user: Pokemon, move: Move) => {
user.turnData.hitsLeft = 1;
return true;
})
.partial(),
.attr(MultiHitAttr, MultiHitType._3)
.attr(MultiHitPowerIncrementAttr, 3)
.checkAllHits(),
new AttackMove(Moves.DUAL_WINGBEAT, Type.FLYING, MoveCategory.PHYSICAL, 40, 90, 10, -1, 0, 8)
.attr(MultiHitAttr, MultiHitType._2),
new AttackMove(Moves.SCORCHING_SANDS, Type.GROUND, MoveCategory.SPECIAL, 70, 100, 10, 30, 0, 8)
@ -7567,10 +7577,7 @@ export function initMoves() {
.attr(ConfuseAttr)
.recklessMove(),
new AttackMove(Moves.LAST_RESPECTS, Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9)
.attr(MovePowerMultiplierAttr, (user, target, move) => {
return user.scene.getParty().reduce((acc, pokemonInParty) => acc + (pokemonInParty.status?.effect === StatusEffect.FAINT ? 1 : 0),
1,);
})
.attr(MovePowerMultiplierAttr, (user, target, move) => 1 + Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 100))
.makesContact(false),
new AttackMove(Moves.LUMINA_CRASH, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.SPDEF, -2),
@ -7585,9 +7592,9 @@ export function initMoves() {
new AttackMove(Moves.SPIN_OUT, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, 100, 0, 9)
.attr(StatChangeAttr, BattleStat.SPD, -2, true),
new AttackMove(Moves.POPULATION_BOMB, Type.NORMAL, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 9)
.attr(MultiHitAttr, MultiHitType._1_TO_10)
.attr(MultiHitAttr, MultiHitType._10)
.slicingMove()
.partial(),
.checkAllHits(),
new AttackMove(Moves.ICE_SPINNER, Type.ICE, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9)
.attr(ClearTerrainAttr),
new AttackMove(Moves.GLAIVE_RUSH, Type.DRAGON, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 9)

View File

@ -358,7 +358,7 @@ export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: Specie
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
const prefix = !pokemon.isPlayer() ? pokemon.hasTrainer() ? "Foe " : "Wild " : "Your ";
if (isMega) {
return `${prefix}${preName} mega-evolved\ninto ${pokemon.name}!`;
return `${prefix}${preName} Mega Evolved\ninto ${pokemon.name}!`;
}
if (isGmax) {
return `${prefix}${preName} Gigantamaxed\ninto ${pokemon.name}!`;

View File

@ -233,7 +233,8 @@ export class TrainerConfig {
}
// If a special double trainer class was set, set it as the sprite key
if (this.trainerTypeDouble && female && isDouble) {
ret = TrainerType[this.trainerTypeDouble].toString().toLowerCase();
// Get the derived type for the double trainer since the sprite key is based on the derived type
ret = TrainerType[this.getDerivedType(this.trainerTypeDouble)].toString().toLowerCase();
}
return ret;
}
@ -271,9 +272,13 @@ export class TrainerConfig {
}
getDerivedType(): TrainerType {
let trainerType = this.trainerType;
/**
* Returns the derived trainer type for a given trainer type.
* @param trainerTypeToDeriveFrom - The trainer type to derive from. (If null, the this.trainerType property will be used.)
* @returns {TrainerType} - The derived trainer type.
*/
getDerivedType(trainerTypeToDeriveFrom: TrainerType = null): TrainerType {
let trainerType = trainerTypeToDeriveFrom ? trainerTypeToDeriveFrom : this.trainerType;
switch (trainerType) {
case TrainerType.RIVAL_2:
case TrainerType.RIVAL_3:

View File

@ -12,10 +12,14 @@ import { achvs } from "./system/achv";
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
import { EggTier } from "./data/enums/egg-type";
import PokemonInfoContainer from "./ui/pokemon-info-container";
import EggsToHatchCountContainer from "./ui/eggs-to-hatch-count-container";
export class EggHatchPhase extends Phase {
private egg: Egg;
private eggsToHatchCount: integer;
private eggsToHatchCountContainer: EggsToHatchCountContainer;
private eggHatchHandler: EggHatchSceneHandler;
private eggHatchContainer: Phaser.GameObjects.Container;
private eggHatchBg: Phaser.GameObjects.Image;
@ -36,10 +40,11 @@ export class EggHatchPhase extends Phase {
private skipped: boolean;
private evolutionBgm: AnySound;
constructor(scene: BattleScene, egg: Egg) {
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
super(scene);
this.egg = egg;
this.eggsToHatchCount = eggsToHatchCount;
}
start() {
@ -84,6 +89,11 @@ export class EggHatchPhase extends Phase {
this.eggContainer.add(this.eggLightraysOverlay);
this.eggHatchContainer.add(this.eggContainer);
this.eggsToHatchCountContainer = new EggsToHatchCountContainer(this.scene, this.eggsToHatchCount);
this.eggsToHatchCountContainer.setup();
this.eggHatchContainer.add(this.eggsToHatchCountContainer);
const getPokemonSprite = () => {
const ret = this.scene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, "pkmn__sub");
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
@ -259,6 +269,13 @@ export class EggHatchPhase extends Phase {
}
doReveal(): void {
// Update/reduce count of hatching eggs when revealed if count is at least 1
// If count is 0, hide eggsToHatchCountContainer instead
if (this.eggsToHatchCount > 1) {
this.eggsToHatchCount -= 1;
} else {
this.eggsToHatchCountContainer.setVisible(false);
}
const isShiny = this.pokemon.isShiny();
if (this.pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
@ -280,6 +297,10 @@ export class EggHatchPhase extends Phase {
this.pokemonSprite.setPipelineData("variant", this.pokemon.variant);
this.pokemonSprite.setVisible(true);
this.scene.time.delayedCall(Utils.fixedInt(250), () => {
if (this.eggsToHatchCount < 10) {
this.eggsToHatchCountContainer.setWindowToDefaultSize();
}
this.eggsToHatchCountContainer.eggCountText.setText(`${this.eggsToHatchCount}`);
this.pokemon.cry();
if (isShiny) {
this.scene.time.delayedCall(Utils.fixedInt(500), () => {

View File

@ -328,12 +328,17 @@ export default class Trainer extends Phaser.GameObjects.Container {
}
// If useNewSpeciesPool is true, we need to generate a new species from the new species pool, otherwise we generate a random species
const species = useNewSpeciesPool
let species = useNewSpeciesPool
? getPokemonSpecies(newSpeciesPool[Math.floor(Math.random() * newSpeciesPool.length)])
: template.isSameSpecies(index) && index > offset
? getPokemonSpecies(battle.enemyParty[offset].species.getTrainerSpeciesForLevel(level, false, template.getStrength(offset)))
: this.genNewPartyMemberSpecies(level, strength);
// If the species is from newSpeciesPool, we need to adjust it based on the level and strength
if (newSpeciesPool) {
species = getPokemonSpecies(species.getSpeciesForLevel(level, true, true, strength));
}
ret = this.scene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER);
}, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8));

View File

@ -1,11 +1,13 @@
import Phaser, {Time} from "phaser";
import Phaser from "phaser";
import * as Utils from "./utils";
import {initTouchControls} from "./touch-controls";
import pad_generic from "./configs/pad_generic";
import pad_unlicensedSNES from "./configs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/pad_xbox360";
import pad_dualshock from "./configs/pad_dualshock";
import pad_procon from "./configs/pad_procon";
import {Button} from "./enums/buttons";
import BattleScene from "./battle-scene";
export interface GamepadMapping {
[key: string]: number;
@ -47,16 +49,17 @@ const repeatInputDelayMillis = 250;
*/
export class InputsController {
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private gamepads: Array<string> = new Array();
private scene: Phaser.Scene;
private gamepads: Phaser.Input.Gamepad.Gamepad[] = new Array();
private scene: BattleScene;
private buttonLock: Button;
private buttonLock2: Button;
private interactions: Map<Button, Map<string, boolean>> = new Map();
private time: Time;
private player: Map<String, GamepadMapping> = new Map();
private time: Phaser.Time.Clock;
private player: GamepadMapping;
private gamepadSupport: boolean = true;
public events: Phaser.Events.EventEmitter;
/**
* Initializes a new instance of the game control system, setting up initial state and configurations.
@ -69,7 +72,7 @@ export class InputsController {
* Specific buttons like MENU and STATS are set not to repeat their actions.
* It concludes by calling the `init` method to complete the setup.
*/
constructor(scene: Phaser.Scene) {
constructor(scene: BattleScene) {
this.scene = scene;
this.time = this.scene.time;
this.buttonKeys = [];
@ -108,7 +111,6 @@ export class InputsController {
}, this);
// Check to see if the gamepad has already been setup by the browser
this.scene.input.gamepad.refreshPads();
if (this.scene.input.gamepad.total) {
this.refreshGamepads();
for (const thisGamepad of this.gamepads) {
@ -201,7 +203,7 @@ export class InputsController {
setupGamepad(thisGamepad: Phaser.Input.Gamepad.Gamepad): void {
const gamepadID = thisGamepad.id.toLowerCase();
const mappedPad = this.mapGamepad(gamepadID);
this.player["mapping"] = mappedPad.gamepadMapping;
this.player = mappedPad.gamepadMapping;
}
/**
@ -236,26 +238,26 @@ export class InputsController {
*/
getActionGamepadMapping(): ActionGamepadMapping {
const gamepadMapping = {};
if (!this.player?.mapping) {
if (!this?.player) {
return gamepadMapping;
}
gamepadMapping[this.player.mapping.LC_N] = Button.UP;
gamepadMapping[this.player.mapping.LC_S] = Button.DOWN;
gamepadMapping[this.player.mapping.LC_W] = Button.LEFT;
gamepadMapping[this.player.mapping.LC_E] = Button.RIGHT;
gamepadMapping[this.player.mapping.TOUCH] = Button.SUBMIT;
gamepadMapping[this.player.mapping.RC_S] = this.scene.abSwapped ? Button.CANCEL : Button.ACTION;
gamepadMapping[this.player.mapping.RC_E] = this.scene.abSwapped ? Button.ACTION : Button.CANCEL;
gamepadMapping[this.player.mapping.SELECT] = Button.STATS;
gamepadMapping[this.player.mapping.START] = Button.MENU;
gamepadMapping[this.player.mapping.RB] = Button.CYCLE_SHINY;
gamepadMapping[this.player.mapping.LB] = Button.CYCLE_FORM;
gamepadMapping[this.player.mapping.LT] = Button.CYCLE_GENDER;
gamepadMapping[this.player.mapping.RT] = Button.CYCLE_ABILITY;
gamepadMapping[this.player.mapping.RC_W] = Button.CYCLE_NATURE;
gamepadMapping[this.player.mapping.RC_N] = Button.CYCLE_VARIANT;
gamepadMapping[this.player.mapping.LS] = Button.SPEED_UP;
gamepadMapping[this.player.mapping.RS] = Button.SLOW_DOWN;
gamepadMapping[this.player.LC_N] = Button.UP;
gamepadMapping[this.player.LC_S] = Button.DOWN;
gamepadMapping[this.player.LC_W] = Button.LEFT;
gamepadMapping[this.player.LC_E] = Button.RIGHT;
gamepadMapping[this.player.TOUCH] = Button.SUBMIT;
gamepadMapping[this.player.RC_S] = this.scene.abSwapped ? Button.CANCEL : Button.ACTION;
gamepadMapping[this.player.RC_E] = this.scene.abSwapped ? Button.ACTION : Button.CANCEL;
gamepadMapping[this.player.SELECT] = Button.STATS;
gamepadMapping[this.player.START] = Button.MENU;
gamepadMapping[this.player.RB] = Button.CYCLE_SHINY;
gamepadMapping[this.player.LB] = Button.CYCLE_FORM;
gamepadMapping[this.player.LT] = Button.CYCLE_GENDER;
gamepadMapping[this.player.RT] = Button.CYCLE_ABILITY;
gamepadMapping[this.player.RC_W] = Button.CYCLE_NATURE;
gamepadMapping[this.player.RC_N] = Button.CYCLE_VARIANT;
gamepadMapping[this.player.LS] = Button.SPEED_UP;
gamepadMapping[this.player.RS] = Button.SLOW_DOWN;
return gamepadMapping;
}
@ -421,6 +423,7 @@ export class InputsController {
* - If the ID includes both '081f' and 'e401', it is identified as an unlicensed SNES gamepad.
* - If the ID contains 'xbox' and '360', it is identified as an Xbox 360 gamepad.
* - If the ID contains '054c', it is identified as a DualShock gamepad.
* - If the ID includes both '057e' and '2009', it is identified as a Pro controller gamepad.
* If no specific identifiers are recognized, a generic gamepad configuration is returned.
*/
mapGamepad(id: string): GamepadConfig {
@ -432,6 +435,8 @@ export class InputsController {
return pad_xbox360;
} else if (id.includes("054c")) {
return pad_dualshock;
} else if (id.includes("057e") && id.includes("2009")) {
return pad_procon;
}
return pad_generic;

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "Täglicher Run (Beta)",
"loadGame": "Spiel laden",
"newGame": "Neues Spiel",
"settings": "Einstellungen",
"selectGameMode": "Wähle einen Spielmodus",
"logInOrCreateAccount": "Melde dich an oder erstelle einen Account zum starten. Keine Email nötig!",
"username": "Benutzername",

View File

@ -14,7 +14,8 @@ export const tutorial: SimpleTranslationEntries = {
$Dort kannst du u. A. die Spielgeschwin-\ndigkeit und das Fensterdesign ändern.
$Das Menü verbirgt noch andere Funktionen - probier' sie gerne aus!`,
"starterSelect": `Hier kannst du deine Starter-Pokémon auswählen.\nSie begleiten dich am Anfang deines Abenteuers.
"starterSelect": `In diesem Bildschirm kannst du mit Z oder Leertaste deine\nStarter auswählen.
$Sie begleiten dich am Anfang deines Abenteuers.
$Jeder Starter hat einen Preis. Dein Team kann bis zu sechs\nMitglieder haben, solange der Gesamtpreis max. 10 beträgt.
$Du kannst Geschlecht, Fähigkeit und Form beliebig auswählen,\nsobald du sie mindestens einmal gefangen hast.
$Die DVs ergeben sich aus den Höchstwerten aller Pokémon,\ndie du bereits gefangen hast.

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "Daily Run (Beta)",
"loadGame": "Load Game",
"newGame": "New Game",
"settings": "Settings",
"selectGameMode": "Select a game mode.",
"logInOrCreateAccount": "Log in or create an account to start. No email required!",
"username": "Username",

View File

@ -12,7 +12,7 @@ export const tutorial: SimpleTranslationEntries = {
$From the settings you can change game speed, window style, and other options.
$There are also various other features here, so be sure to check them all!`,
"starterSelect": `From this screen, you can select your starters.\nThese are your initial party members.
"starterSelect": `From this screen, you can select your starters by pressing\nZ or the Space bar. These are your initial party members.
$Each starter has a value. Your party can have up to\n6 members as long as the total does not exceed 10.
$You can also select gender, ability, and form depending on\nthe variants you've caught or hatched.
$The IVs for a species are also the best of every one you've\ncaught or hatched, so try to get lots of the same species!`,

View File

@ -3,8 +3,8 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const battle: SimpleTranslationEntries = {
"bossAppeared": "¡{{bossName}} te corta el paso!",
"trainerAppeared": "¡{{trainerName}}\nte desafía!",
"trainerAppearedDouble": "{{trainerName}}\nwould like to battle!",
"trainerSendOut": "{{trainerName}} sent out\n{{pokemonName}}!",
"trainerAppearedDouble": "¡{{trainerName}}\nwould te desafían!",
"trainerSendOut": "¡{{trainerName}} saca a\n{{pokemonName}}!",
"singleWildAppeared": "¡Un {{pokemonName}} salvaje te corta el paso!",
"multiWildAppeared": "¡Un {{pokemonName1}} y un {{pokemonName2}} salvajes\nte cortan el paso!",
"playerComeBack": "¡{{pokemonName}}, ven aquí!",
@ -13,9 +13,9 @@ export const battle: SimpleTranslationEntries = {
"trainerGo": "¡{{trainerName}} saca a {{pokemonName}}!",
"switchQuestion": "¿Quieres cambiar a\n{{pokemonName}}?",
"trainerDefeated": "¡Has derrotado a\n{{trainerName}}!",
"moneyWon": "You got\n₽{{moneyAmount}} for winning!",
"moneyWon": "¡Has ganado\n₽{{moneyAmount}} por vencer!",
"pokemonCaught": "¡{{pokemonName}} atrapado!",
"partyFull": "Your party is full.\nRelease a Pokémon to make room for {{pokemonName}}?",
"partyFull": "Tu equipo esta completo.\n¿Quieres liberar un Pokémon para meter a {{pokemonName}}?",
"pokemon": "Pokémon",
"sendOutPokemon": "¡Adelante, {{pokemonName}}!",
"hitResultCriticalHit": "!Un golpe crítico!",
@ -39,7 +39,7 @@ export const battle: SimpleTranslationEntries = {
"learnMoveAnd": "Y…",
"levelCapUp": "¡Se ha incrementado el\nnivel máximo a {{levelCap}}!",
"moveNotImplemented": "{{moveName}} aún no está implementado y no se puede seleccionar.",
"moveNoPP": "There's no PP left for\nthis move!",
"moveNoPP": "¡No hay suficientes PP\npara este movimiento!",
"moveDisabled": "!No puede usar {{moveName}} porque ha sido anulado!",
"noPokeballForce": "Una fuerza misteriosa\nte impide usar Poké Balls.",
"noPokeballTrainer": "¡No puedes atrapar a los\nPokémon de los demás!",

View File

@ -1,40 +1,40 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const biome: SimpleTranslationEntries = {
"unknownLocation": "Somewhere you can\'t remember",
"TOWN": "Town",
"PLAINS": "Plains",
"GRASS": "Grassy Field",
"TALL_GRASS": "Tall Grass",
"METROPOLIS": "Metropolis",
"FOREST": "Forest",
"SEA": "Sea",
"SWAMP": "Swamp",
"BEACH": "Beach",
"LAKE": "Lake",
"SEABED": "Seabed",
"MOUNTAIN": "Mountain",
"unknownLocation": "En algún lugar que no puedes recordar",
"TOWN": "Ciudad",
"PLAINS": "Valle",
"GRASS": "Campo",
"TALL_GRASS": "Pradera de Hierba Alta",
"METROPOLIS": "Metrópolis",
"FOREST": "Bosque",
"SEA": "Mar",
"SWAMP": "Pantano",
"BEACH": "Playa",
"LAKE": "Lago",
"SEABED": "Fondo del mar",
"MOUNTAIN": "Montaña",
"BADLANDS": "Badlands",
"CAVE": "Cave",
"DESERT": "Desert",
"ICE_CAVE": "Ice Cave",
"MEADOW": "Meadow",
"POWER_PLANT": "Power Plant",
"VOLCANO": "Volcano",
"GRAVEYARD": "Graveyard",
"CAVE": "Cueva",
"DESERT": "Desierto",
"ICE_CAVE": "Cueva Helada",
"MEADOW": "Prado",
"POWER_PLANT": "Central Eléctrica",
"VOLCANO": "Volcán",
"GRAVEYARD": "Cementerio",
"DOJO": "Dojo",
"FACTORY": "Factory",
"RUINS": "Ancient Ruins",
"WASTELAND": "Wasteland",
"ABYSS": "Abyss",
"SPACE": "Space",
"CONSTRUCTION_SITE": "Construction Site",
"JUNGLE": "Jungle",
"FAIRY_CAVE": "Fairy Cave",
"TEMPLE": "Temple",
"SLUM": "Slum",
"SNOWY_FOREST": "Snowy Forest",
"ISLAND": "Island",
"LABORATORY": "Laboratory",
"FACTORY": "Fábrica",
"RUINS": "Ruinas Antiguas",
"WASTELAND": "Páramo",
"ABYSS": "Abismo",
"SPACE": "Espacio",
"CONSTRUCTION_SITE": "Obra",
"JUNGLE": "Jungla",
"FAIRY_CAVE": "Cueva de Hadas",
"TEMPLE": "Templo",
"SLUM": "Suburbio",
"SNOWY_FOREST": "Bosque nevado",
"ISLAND": "Isla",
"LABORATORY": "Laboratorio",
"END": "???",
} as const;

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "Reto diario (Beta)",
"loadGame": "Cargar partida",
"newGame": "Nueva partida",
"settings": "Settings",
"selectGameMode": "Elige un modo de juego.",
"logInOrCreateAccount": "Inicia sesión o crea una cuenta para empezar. ¡No se requiere correo electrónico!",
"username": "Usuario",

View File

@ -2,17 +2,17 @@ import {SimpleTranslationEntries} from "#app/plugins/i18n";
// Titles of special trainers like gym leaders, elite four, and the champion
export const titles: SimpleTranslationEntries = {
"elite_four": "Elite Four",
"elite_four_female": "Elite Four",
"gym_leader": "Gym Leader",
"gym_leader_female": "Gym Leader",
"gym_leader_double": "Gym Leader Duo",
"champion": "Champion",
"champion_female": "Champion",
"champion_double": "Champion Duo",
"elite_four": "Alto Mando",
"elite_four_female": "Alto Mando",
"gym_leader": "Líder de gimnasio",
"gym_leader_female": "Líder de gimnasio",
"gym_leader_double": "Líderes de Gimnasio",
"champion": "Campeón",
"champion_female": "Campeona",
"champion_double": "Campeones",
"rival": "Rival",
"professor": "Professor",
"frontier_brain": "Frontier Brain",
"professor": "Profesor",
"frontier_brain": "As del Frente Batalla",
// Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc.
} as const;

View File

@ -4564,7 +4564,7 @@ export const PGMbattleSpecDialogue: SimpleTranslationEntries = {
$Tu es la seule présence ici, bien que jai le sentiment den ressentir une autre.
$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?
$Commençons.`,
"firstStageWin": `Je vois. Cette précence était bien réelle.\nJe nai donc plus besoin de retenir mes coups.
"firstStageWin": `Je vois. Cette présence était bien réelle.\nJe nai donc plus besoin de retenir mes coups.
$Ne me déçoit pas.`,
"secondStageWin": "… Magnifique."
};
@ -4579,7 +4579,7 @@ export const PGFbattleSpecDialogue: SimpleTranslationEntries = {
$Tu es la seule présence ici, bien que jai le sentiment den ressentir une autre.
$Vas-tu enfin me livrer un affrontement digne de ce nom ?\nCe challenge dont je rêve depuis un millénaire ?
$Commençons.`,
"firstStageWin": `Je vois. Cette précence était bien réelle.\nJe nai donc plus besoin de retenir mes coups.
"firstStageWin": `Je vois. Cette présence était bien réelle.\nJe nai donc plus besoin de retenir mes coups.
$Ne me déçoit pas.`,
"secondStageWin": "… Magnifique."
};

View File

@ -6,6 +6,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "Défi du jour (Bêta)",
"loadGame": "Charger la partie",
"newGame": "Nouvelle partie",
"settings": "Paramètres",
"selectGameMode": "Sélectionnez un mode de jeu.",
"logInOrCreateAccount": "Connectez-vous ou créez un compte pour commencer. Aucun e-mail requis !",
"username": "Nom dutilisateur",

View File

@ -16,7 +16,7 @@ export const tutorial: SimpleTranslationEntries = {
$Il y a également toute une variété dautres fonctionnalités,
$jetez-y un œil !`,
"starterSelect": `Choisissez vos starters depuis cet écran.\nIls formeront votre équipe de départ.
"starterSelect": `Choisissez vos starters depuis cet écran avec Z ou Espace.\nIls formeront votre équipe de départ.
$Chacun possède une valeur. Votre équipe peut avoir jusquà\n6 membres, tant que vous ne dépassez pas un cout de 10.
$Vous pouvez aussi choisir le sexe, le talent et la forme en\nfonction des variants déjà capturés ou éclos.
$Les IVs dun starter sont les meilleurs de tous ceux de son\nespèce déjà obtenus. Essayez donc den obtenir plusieurs !`,

View File

@ -10,6 +10,7 @@ export const menu: SimpleTranslationEntries = {
"continue": "Continua",
"newGame": "Nuova Partita",
"loadGame": "Carica Partita",
"settings": "Settings",
"dailyRun": "Corsa Giornaliera (Beta)",
"selectGameMode": "Seleziona una modalità di gioco.",
"logInOrCreateAccount": "Accedi o crea un nuovo account per iniziare. Non è richiesta un'email!",

View File

@ -0,0 +1,6 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}}(는)은 {{abilityName}} 때문에\n반동 데미지를 받지 않는다!",
"badDreams": "{{pokemonName}} is tormented!",
} as const;

1252
src/locales/ko/ability.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const battleMessageUiHandler: SimpleTranslationEntries = {
"ivBest": "최고",
"ivFantastic": "훌륭하다",
"ivVeryGood": "굉장히 좋다",
"ivPrettyGood": "상당히 좋다",
"ivDecent": "적당하다",
"ivNoGood": "별로인 듯",
} as const;

59
src/locales/ko/battle.ts Normal file
View File

@ -0,0 +1,59 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const battle: SimpleTranslationEntries = {
"bossAppeared": "{{bossName}}(이)가 나타났다.",
"trainerAppeared": "{{trainerName}}(이)가\n승부를 걸어왔다!",
"trainerAppearedDouble": "{{trainerName}}(이)가\n승부를 걸어왔다!",
"trainerSendOut": "{{trainerName}}(는)은\n{{pokemonName}}(를)을 내보냈다!",
"singleWildAppeared": "앗! 야생 {{pokemonName}}(이)가\n튀어나왔다!",
"multiWildAppeared": "야생 {{pokemonName1}}(과)와\n{{pokemonName2}}(이)가 튀어나왔다!",
"playerComeBack": "돌아와, {{pokemonName}}!",
"trainerComeBack": "{{trainerName}}(는)은 {{pokemonName}}를(을) 넣어버렸다!",
"playerGo": "가랏! {{pokemonName}}!",
"trainerGo": "{{trainerName}}(는)은 {{pokemonName}}를(을) 내보냈다!",
"switchQuestion": "{{pokemonName}}를(을)\n교체하시겠습니까?",
"trainerDefeated": "{{trainerName}}과(와)의\n승부에서 이겼다!",
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
"pokemonCaught": "신난다-!\n{{pokemonName}}(를)을 잡았다!",
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}(를)\n대신해 포켓몬을 놓아주시겠습니까?",
"pokemon": "포켓몬",
"sendOutPokemon": "가랏! {{pokemonName}}!",
"hitResultCriticalHit": "급소에 맞았다!",
"hitResultSuperEffective": "효과가 굉장했다!",
"hitResultNotVeryEffective": "효과가 별로인 듯하다…",
"hitResultNoEffect": "{{pokemonName}}에게는\n효과가 없는 것 같다…",
"hitResultOneHitKO": "일격필살!",
"attackFailed": "하지만 실패했다!",
"attackHitsCount": "{{count}}번 맞았다!",
"expGain": "{{pokemonName}}(는)은\n{{exp}} 경험치를 얻었다!",
"levelUp": "{{pokemonName}}(는)은\n레벨 {{level}}(으)로 올랐다!",
"learnMove": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배웠다!",
"learnMovePrompt": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배우고 싶다!…",
"learnMoveLimitReached": "그러나 {{pokemonName}}(는)은 기술을 4개\n알고 있으므로 더 이상 배울 수 없다!",
"learnMoveReplaceQuestion": "{{moveName}} 대신 다른 기술을 잊게 하겠습니까?",
"learnMoveStopTeaching": "그럼… {{moveName}}를(을)\n배우는 것을 포기하겠습니까?",
"learnMoveNotLearned": "{{pokemonName}}(는)은 {{moveName}}를(을)\n결국 배우지 않았다!",
"learnMoveForgetQuestion": "어느 기술을 잊게 하고싶은가?",
"learnMoveForgetSuccess": "{{pokemonName}}(는)은 {{moveName}}를(을) 깨끗이 잊었다!",
"countdownPoof": "@d{32}1, @d{15}2, @d{15}… @d{15}… @d{30}@s{pb_bounce_1}짠!",
"learnMoveAnd": "그리고…",
"levelCapUp": "레벨의 최대치가\n{{levelCap}}까지 상승했다!",
"moveNotImplemented": "{{moveName}}(는)은 아직 구현되지 않아 사용할 수 없다…",
"moveNoPP": "기술의 남은 포인트가 없다!",
"moveDisabled": "{{moveName}}를(을) 쓸 수 없다!",
"noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.",
"noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!",
"noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!",
"noPokeballStrong": "너무 강해서 잡을 수가 없다!\n먼저 약화시켜야 한다!",
"noEscapeForce": "본 적 없는 힘이\n도망칠 수 없게 한다.",
"noEscapeTrainer": "안돼! 승부 도중에\n상대에게 등을 보일 순 없어!",
"noEscapePokemon": "{{pokemonName}}의 {{moveName}}때문에\n{{escapeVerb}} 수 없다!",
"runAwaySuccess": "무사히 도망쳤다!",
"runAwayCannotEscape": "도망칠 수 없었다!",
"escapeVerbSwitch": "교체할",
"escapeVerbFlee": "도망칠",
"notDisabled": "{{pokemonName}}의\n{{moveName}} 사슬묶기가 풀렸다!",
"skipItemQuestion": "아이템을 받지 않고 넘어가시겠습니까?",
"eggHatching": "어라…?",
"ivScannerUseQuestion": "{{pokemonName}}에게 개체값탐지기를 사용하시겠습니까?"
} as const;

48
src/locales/ko/berry.ts Normal file
View File

@ -0,0 +1,48 @@
import { BerryTranslationEntries } from "#app/plugins/i18n";
export const berry: BerryTranslationEntries = {
"SITRUS": {
name: "자뭉열매",
effect: "지니게 하면 HP가 50% 미만일 때 HP를 25% 회복",
},
"LUM": {
name: "리샘열매",
effect: "지니게 하면 모든 상태 이상과 혼란을 회복",
},
"ENIGMA": {
name: "의문열매",
effect: "지니게 하면 효과가 뛰어난 기술에 당했을 때 HP를 25% 회복",
},
"LIECHI": {
name: "치리열매",
effect: "지니게 하면 HP가 25% 미만일 때 자신의 공격이 상승",
},
"GANLON": {
name: "용아열매",
effect: "지니게 하면 HP가 25% 미만일 때 자신의 방어가 상승",
},
"PETAYA": {
name: "야타비열매",
effect: "지니게 하면 HP가 25% 미만일 때 자신의 특수공격이 상승",
},
"APICOT": {
name: "규살열매",
effect: "지니게 하면 HP가 25% 미만일 때 자신의 특수방어가 상승",
},
"SALAC": {
name: "캄라열매",
effect: "지니게 하면 HP가 25% 미만일 때 자신의 스피드가 상승",
},
"LANSAT": {
name: "랑사열매",
effect: "지니게 하면 HP가 25% 미만일 때 공격이 급소를 맞히기 쉬워짐",
},
"STARF": {
name: "스타열매",
effect: "지니게 하면 HP가 25% 미만일 때 능력 중의 하나가 크게 상승",
},
"LEPPA": {
name: "과사열매",
effect: "지니게 하면 기술의 PP가 0이 되었을 때 PP를 10만큼 회복",
},
} as const;

40
src/locales/ko/biome.ts Normal file
View File

@ -0,0 +1,40 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const biome: SimpleTranslationEntries = {
"unknownLocation": "기억할 수 없는 곳",
"TOWN": "마을",
"PLAINS": "평야",
"GRASS": "풀숲",
"TALL_GRASS": "높은 풀숲",
"METROPOLIS": "대도시",
"FOREST": "숲",
"SEA": "바다",
"SWAMP": "늪지",
"BEACH": "해변",
"LAKE": "호수",
"SEABED": "해저",
"MOUNTAIN": "산",
"BADLANDS": "악지",
"CAVE": "동굴",
"DESERT": "사막",
"ICE_CAVE": "얼음 동굴",
"MEADOW": "목초지",
"POWER_PLANT": "발전소",
"VOLCANO": "화산",
"GRAVEYARD": "묘지",
"DOJO": "도장",
"FACTORY": "공장",
"RUINS": "고대 폐허",
"WASTELAND": "황무지",
"ABYSS": "심연",
"SPACE": "성층권",
"CONSTRUCTION_SITE": "공사장",
"JUNGLE": "정글",
"FAIRY_CAVE": "요정 동굴",
"TEMPLE": "사원",
"SLUM": "슬럼",
"SNOWY_FOREST": "눈덮인 숲",
"ISLAND": "섬",
"LABORATORY": "연구소",
"END": "???",
} as const;

View File

@ -0,0 +1,9 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const commandUiHandler: SimpleTranslationEntries = {
"fight": "싸우다",
"ball": "볼",
"pokemon": "포켓몬",
"run": "도망치다",
"actionMessage": "{{pokemonName}}(는)은 무엇을 할까?",
} as const;

74
src/locales/ko/config.ts Normal file
View File

@ -0,0 +1,74 @@
import { ability } from "./ability";
import { abilityTriggers } from "./ability-trigger";
import { battle } from "./battle";
import { commandUiHandler } from "./command-ui-handler";
import { egg } from "./egg";
import { fightUiHandler } from "./fight-ui-handler";
import { growth } from "./growth";
import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler";
import { modifierType } from "./modifier-type";
import { move } from "./move";
import { nature } from "./nature";
import { pokeball } from "./pokeball";
import { pokemon } from "./pokemon";
import { pokemonInfo } from "./pokemon-info";
import { splashMessages } from "./splash-messages";
import { starterSelectUiHandler } from "./starter-select-ui-handler";
import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial";
import { weather } from "./weather";
import { battleMessageUiHandler } from "./battle-message-ui-handler";
import { berry } from "./berry";
import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { voucher } from "./voucher";
import {
PGMdialogue,
PGFdialogue,
PGMbattleSpecDialogue,
PGFbattleSpecDialogue,
PGMmiscDialogue,
PGFmiscDialogue, PGMdoubleBattleDialogue, PGFdoubleBattleDialogue
} from "./dialogue";
import { biome } from "./biome";
import { pokemonInfoContainer } from "./pokemon-info-container";
export const koConfig = {
ability: ability,
abilityTriggers: abilityTriggers,
battle: battle,
commandUiHandler: commandUiHandler,
egg: egg,
fightUiHandler: fightUiHandler,
growth: growth,
menu: menu,
menuUiHandler: menuUiHandler,
modifierType: modifierType,
move: move,
nature: nature,
pokeball: pokeball,
pokemon: pokemon,
pokemonInfo: pokemonInfo,
splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler,
titles: titles,
trainerClasses: trainerClasses,
trainerNames: trainerNames,
tutorial: tutorial,
weather: weather,
battleMessageUiHandler: battleMessageUiHandler,
berry: berry,
gameStatsUiHandler: gameStatsUiHandler,
voucher: voucher,
biome: biome,
pokemonInfoContainer: pokemonInfoContainer,
PGMdialogue: PGMdialogue,
PGFdialogue: PGFdialogue,
PGMbattleSpecDialogue: PGMbattleSpecDialogue,
PGFbattleSpecDialogue: PGFbattleSpecDialogue,
PGMmiscDialogue: PGMmiscDialogue,
PGFmiscDialogue: PGFmiscDialogue,
PGMdoubleBattleDialogue: PGMdoubleBattleDialogue,
PGFdoubleBattleDialogue: PGFdoubleBattleDialogue
};

2442
src/locales/ko/dialogue.ts Normal file

File diff suppressed because it is too large Load Diff

21
src/locales/ko/egg.ts Normal file
View File

@ -0,0 +1,21 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const egg: SimpleTranslationEntries = {
"egg": "알",
"greatTier": "레어",
"ultraTier": "에픽",
"masterTier": "레전더리",
"defaultTier": "커먼",
"hatchWavesMessageSoon": "안에서 소리가 들려온다! 이제 곧 태어날 것 같아!",
"hatchWavesMessageClose": "가끔 움직이고 있는 것 같다. 태어나기까지 이제 조금 남은 건가?",
"hatchWavesMessageNotClose": "무엇이 태어날까? 태어나기까지 아직 시간이 걸릴 것 같다.",
"hatchWavesMessageLongTime": "이 알은 태어나기까지 상당한 시간이 걸릴 것 같다.",
"gachaTypeLegendary": "레전더리 확률 업",
"gachaTypeMove": "희귀 알 기술 확률 업",
"gachaTypeShiny": "색이 다른 포켓몬 확률 업",
"selectMachine": "사용할 뽑기 기계를 골라주세요.",
"notEnoughVouchers": "바우처가 충분하지 않습니다!",
"tooManyEggs": "알을 너무 많이 갖고 있습니다!",
"pull": "뽑기",
"pulls": "뽑기"
} as const;

View File

@ -0,0 +1,7 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const fightUiHandler: SimpleTranslationEntries = {
"pp": "PP",
"power": "위력",
"accuracy": "명중률",
} as const;

View File

@ -0,0 +1,44 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const gameStatsUiHandler: SimpleTranslationEntries = {
"stats": "통계",
"playTime": "플레이타임",
"totalBattles": "배틀 수",
"starters": "스타팅",
"shinyStarters": "색다른 스타팅",
"speciesSeen": "만난 종류",
"speciesCaught": "잡은 종류",
"ribbonsOwned": "리본 소지",
"classicRuns": "클래식 플레이",
"classicWins": "클래식 클리어",
"dailyRunAttempts": "데일리 런 플레이",
"dailyRunWins": "데일리 런 클리어",
"endlessRuns": "엔드리스 플레이",
"highestWaveEndless": "최대 웨이브(엔드리스)",
"highestMoney": "최고 소지금",
"highestDamage": "최대 데미지",
"highestHPHealed": "최대 회복량",
"pokemonEncountered": "만난 포켓몬",
"pokemonDefeated": "쓰러뜨린 포켓몬",
"pokemonCaught": "잡은 포켓몬",
"eggsHatched": "부화시킨 알",
"subLegendsSeen": "만난 준전설",
"subLegendsCaught": "잡은 준전설",
"subLegendsHatched": "부화시킨 준전설",
"legendsSeen": "만난 전설",
"legendsCaught": "잡은 전설",
"legendsHatched": "부화시킨 전설",
"mythicalsSeen": "만난 환상",
"mythicalsCaught": "잡은 환상",
"mythicalsHatched": "부화시킨 환상",
"shiniesSeen": "만난 색다른",
"shiniesCaught": "잡은 색다른",
"shiniesHatched": "부화시킨 색다른",
"pokemonFused": "포켓몬 합체",
"trainersDefeated": "트레이너 승리",
"eggsPulled": "뽑은 알",
"rareEggsPulled": "뽑은 레어 알",
"epicEggsPulled": "뽑은 에픽 알",
"legendaryEggsPulled": "뽑은 레전더리 알",
"manaphyEggsPulled": "뽑은 마나피 알",
} as const;

10
src/locales/ko/growth.ts Normal file
View File

@ -0,0 +1,10 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const growth: SimpleTranslationEntries = {
"Erratic": "불규칙",
"Fast": "빠름",
"Medium_Fast": "중간 빠름",
"Medium_Slow": "중간 느림",
"Slow": "느림",
"Fluctuating": "변동"
} as const;

View File

@ -0,0 +1,23 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const menuUiHandler: SimpleTranslationEntries = {
"GAME_SETTINGS": "게임 설정",
"ACHIEVEMENTS": "업적",
"STATS": "통계",
"VOUCHERS": "바우처",
"EGG_LIST": "알 목록",
"EGG_GACHA": "알 뽑기",
"MANAGE_DATA": "데이터 관리",
"COMMUNITY": "커뮤니티",
"SAVE_AND_QUIT": "저장 후 나가기",
"LOG_OUT": "로그아웃",
"slot": "슬롯 {{slotNumber}}",
"importSession": "세션 불러오기",
"importSlotSelect": "불러올 슬롯을 골라주세요.",
"exportSession": "세션 내보내기",
"exportSlotSelect": "내보낼 슬롯을 골라주세요.",
"importData": "데이터 불러오기",
"exportData": "데이터 내보내기",
"cancel": "취소",
"losingProgressionWarning": "전투 시작으로부터의 진행 상황을 잃게 됩니다. 계속하시겠습니까?"
} as const;

51
src/locales/ko/menu.ts Normal file
View File

@ -0,0 +1,51 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
/**
* The menu namespace holds most miscellaneous text that isn't directly part of the game's
* contents or directly related to Pokemon data. This includes menu navigation, settings,
* account interactions, descriptive text, etc.
*/
export const menu: SimpleTranslationEntries = {
"cancel": "취소",
"continue": "계속하기",
"dailyRun": "데일리 런 (베타)",
"loadGame": "불러오기",
"newGame": "새 게임",
"selectGameMode": "게임 모드를 선택해주세요.",
"logInOrCreateAccount": "로그인 또는 등록을 해 주세요. 개인정보를 요구하지 않습니다!",
"username": "이름",
"password": "비밀번호",
"login": "로그인",
"register": "등록",
"emptyUsername": "이름은 비워둘 수 없습니다",
"invalidLoginUsername": "사용할 수 없는 이름입니다",
"invalidRegisterUsername": "이름은 알파벳, 숫자, 언더바(_)만 사용할 수 있습니다",
"invalidLoginPassword": "사용할 수 없는 비밀번호입니다",
"invalidRegisterPassword": "비밀번호는 여섯글자 이상이어야 합니다",
"usernameAlreadyUsed": "이미 사용중인 이름입니다",
"accountNonExistent": "등록되지 않은 이름입니다",
"unmatchingPassword": "비밀번호가 틀립니다",
"passwordNotMatchingConfirmPassword": "비밀번호가 일치하지 않습니다",
"confirmPassword": "비밀번호 재입력",
"registrationAgeWarning": "13세 이상입니다.",
"backToLogin": "로그인 화면으로",
"failedToLoadSaveData": "데이터를 불러올 수 없었습니다. 페이지를 새로고침해주세요.\n문제가 지속된다면, 디스코드 #Announcements 채널을 확인해주세요.",
"sessionSuccess": "세션 불러오기 성공.",
"failedToLoadSession": "세션을 불러올 수 없었습니다.\n파일이 손상되었을 수 있습니다.",
"boyOrGirl": "너는 남자니?\n아니면 여자니?",
"boy": "남자",
"girl": "여자",
"evolving": "…오잉!?\n{{pokemonName}}의 모습이…!",
"stoppedEvolving": "얼라리…?\n{{pokemonName}}의 변화가 멈췄다!",
"pauseEvolutionsQuestion": "{{pokemonName}}를(을) 진화하지 않게 만드시겠습니까?\n포켓몬 화면에서 다시 활성화시킬 수 있습니다.",
"evolutionsPaused": "{{pokemonName}}의 진화가 비활성화되었다.",
"evolutionDone": "축하합니다! {{pokemonName}}(는)은\n{{evolvedPokemonName}}(으)로 진화했습니다!",
"dailyRankings": "일간 랭킹",
"weeklyRankings": "주간 랭킹",
"noRankings": "랭킹 정보 없음",
"loading": "로딩 중…",
"playersOnline": "플레이어 온라인",
"empty":"빈 슬롯",
"yes":"예",
"no":"아니오",
} as const;

View File

@ -0,0 +1,424 @@
import { ModifierTypeTranslationEntries } from "#app/plugins/i18n";
export const modifierType: ModifierTypeTranslationEntries = {
ModifierType: {
"AddPokeballModifierType": {
name: "{{pokeballName}} {{modifierCount}}개",
description: "{{pokeballName}} {{modifierCount}}개 (현재: {{pokeballAmount}}개)를 획득한다.\n포획률: {{catchRate}}",
},
"AddVoucherModifierType": {
name: "{{voucherTypeName}} {{modifierCount}}장",
description: "{{voucherTypeName}} {{modifierCount}}장을 획득",
},
"PokemonHeldItemModifierType": {
extra: {
"inoperable": "{{pokemonName}}(는)은\n이 아이템을 얻을 수 없다!",
"tooMany": "{{pokemonName}}(는)은\n이 아이템을 너무 많이 갖고 있다!",
}
},
"PokemonHpRestoreModifierType": {
description: "포켓몬 1마리의 HP를 {{restorePoints}} 또는 {{restorePercent}}% 중\n높은 수치만큼 회복",
extra: {
"fully": "포켓몬 1마리의 HP를 모두 회복",
"fullyWithStatus": "포켓몬 1마리의 HP와 상태 이상을 모두 회복",
}
},
"PokemonReviveModifierType": {
description: "기절해 버린 포켓몬 1마리의 HP를 {{restorePercent}}%까지 회복",
},
"PokemonStatusHealModifierType": {
description: "포켓몬 1마리의 상태 이상을 모두 회복",
},
"PokemonPpRestoreModifierType": {
description: "포켓몬이 기억하고 있는 기술 중 1개의 PP를 {{restorePoints}}만큼 회복",
extra: {
"fully": "포켓몬이 기억하고 있는 기술 중 1개의 PP를 모두 회복",
}
},
"PokemonAllMovePpRestoreModifierType": {
description: "포켓몬이 기억하고 있는 4개의 기술 PP를 {{restorePoints}}씩 회복",
extra: {
"fully": "포켓몬이 기억하고 있는 4개의 기술 PP를 모두 회복",
}
},
"PokemonPpUpModifierType": {
description: "포켓몬이 기억하고 있는 기술 중 1개의 PP 최대치를 5마다 {{upPoints}}씩 상승 (최대 3)",
},
"PokemonNatureChangeModifierType": {
name: "{{natureName}}민트",
description: "포켓몬의 성격을 {{natureName}}(으)로 바꾸고 스타팅에도 등록한다.",
},
"DoubleBattleChanceBoosterModifierType": {
description: "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률 두 배",
},
"TempBattleStatBoosterModifierType": {
description: "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}(이)가 한 단계 증가"
},
"AttackTypeBoosterModifierType": {
description: "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승",
},
"PokemonLevelIncrementModifierType": {
description: "포켓몬 1마리의 레벨이 1만큼 상승",
},
"AllPokemonLevelIncrementModifierType": {
description: "자신의 모든 포켓몬의 레벨이 1씩 상승",
},
"PokemonBaseStatBoosterModifierType": {
description: "지니게 하면 {{statName}} 종족값을 10% 올려준다. 개체값이 높을수록 더 많이 누적시킬 수 있다.",
},
"AllPokemonFullHpRestoreModifierType": {
description: "자신의 포켓몬의 HP를 모두 회복",
},
"AllPokemonFullReviveModifierType": {
description: "자신의 포켓몬의 HP를 기절해 버렸더라도 모두 회복",
},
"MoneyRewardModifierType": {
description: "{{moneyMultiplier}} 양의 돈을 획득 (₽{{moneyAmount}})",
extra: {
"small": "적은",
"moderate": "적당한",
"large": "많은",
},
},
"ExpBoosterModifierType": {
description: "포켓몬이 받는 경험치가 늘어나는 부적. {{boostPercent}}% 증가",
},
"PokemonExpBoosterModifierType": {
description: "지니게 한 포켓몬은 받을 수 있는 경험치가 {{boostPercent}}% 증가",
},
"PokemonFriendshipBoosterModifierType": {
description: "배틀 승리로 얻는 친밀도가 50% 증가",
},
"PokemonMoveAccuracyBoosterModifierType": {
description: "기술의 명중률이 {{accuracyAmount}} 증가 (최대 100)",
},
"PokemonMultiHitModifierType": {
description: "공격이 가진 갯수에 따라 60/75/82.5%의 위력으로 한번 더 명중",
},
"TmModifierType": {
name: "No.{{moveId}} {{moveName}}",
description: "포켓몬에게 {{moveName}}를(을) 가르침",
},
"EvolutionItemModifierType": {
description: "어느 특정 포켓몬을 진화",
},
"FormChangeItemModifierType": {
description: "어느 특정 포켓몬을 폼 체인지",
},
"FusePokemonModifierType": {
description: "두 포켓몬을 결합 (특성 변환, 종족값과 타입 분배, 기술폭 공유)",
},
"TerastallizeModifierType": {
name: "테라피스 {{teraType}}",
description: "지니게 하면 10번의 배틀 동안 {{teraType}} 테라스탈타입으로 테라스탈",
},
"ContactHeldItemTransferChanceModifierType": {
description: "공격했을 때, {{chancePercent}}%의 확률로 상대의 도구를 도둑질",
},
"TurnHeldItemTransferModifierType": {
description: "매 턴, 지닌 포켓몬은 상대로부터 도구를 하나 획득",
},
"EnemyAttackStatusEffectChanceModifierType": {
description: "공격했을 때 {{statusEffect}} 상태로 만들 확률 {{chancePercent}}% 추가",
},
"EnemyEndureChanceModifierType": {
description: "받은 공격을 버텨낼 확률 {{chancePercent}}% 추가",
},
"RARE_CANDY": { name: "이상한사탕" },
"RARER_CANDY": { name: "더이상한사탕" },
"MEGA_BRACELET": { name: "메가링", description: "메가스톤을 사용 가능" },
"DYNAMAX_BAND": { name: "다이맥스 밴드", description: "다이버섯을 사용 가능" },
"TERA_ORB": { name: "테라스탈오브", description: "테라피스를 사용 가능" },
"MAP": { name: "지도", description: "갈림길에서 목적지 선택 가능" },
"POTION": { name: "상처약" },
"SUPER_POTION": { name: "좋은상처약" },
"HYPER_POTION": { name: "고급상처약" },
"MAX_POTION": { name: "풀회복약" },
"FULL_RESTORE": { name: "회복약" },
"REVIVE": { name: "기력의조각" },
"MAX_REVIVE": { name: "기력의덩어리" },
"FULL_HEAL": { name: "만병통치제" },
"SACRED_ASH": { name: "성스러운분말" },
"REVIVER_SEED": { name: "부활의씨앗", description: "포켓몬이 쓰러지려 할 때 HP를 절반 회복" },
"ETHER": { name: "PP에이드" },
"MAX_ETHER": { name: "PP회복" },
"ELIXIR": { name: "PP에이더" },
"MAX_ELIXIR": { name: "PP맥스" },
"PP_UP": { name: "포인트업" },
"PP_MAX": { name: "포인트맥스" },
"LURE": { name: "더블배틀코롱" },
"SUPER_LURE": { name: "실버코롱" },
"MAX_LURE": { name: "골드코롱" },
"MEMORY_MUSHROOM": { name: "기억버섯", description: "포켓몬의 잊어버린 기술을 떠올림" },
"EXP_SHARE": { name: "학습장치", description: "배틀에 참여하지 않아도 20%의 경험치를 받을 수 있는 장치" },
"EXP_BALANCE": { name: "균형학습장치", description: "레벨이 낮은 포켓몬이 받는 경험치를 가중" },
"OVAL_CHARM": { name: "Oval Charm", description: "여러 마리의 포켓몬이 배틀에 참여할 경우, 전체 경험치의 10%씩을 추가로 획득" },
"EXP_CHARM": { name: "경험부적" },
"SUPER_EXP_CHARM": { name: "좋은경험부적" },
"GOLDEN_EXP_CHARM": { name: "황금경험부적" },
"LUCKY_EGG": { name: "행복의알" },
"GOLDEN_EGG": { name: "황금의알" },
"SOOTHE_BELL": { name: "평온의방울" },
"SOUL_DEW": { name: "마음의물방울", description: "지닌 포켓몬의 성격의 효과가 10% 증가 (합연산)" },
"NUGGET": { name: "금구슬" },
"BIG_NUGGET": { name: "큰금구슬" },
"RELIC_GOLD": { name: "고대의금화" },
"AMULET_COIN": { name: "부적금화", description: "받는 돈이 20% 증가" },
"GOLDEN_PUNCH": { name: "골든펀치", description: "주는 데미지의 50%만큼 돈을 획득" },
"COIN_CASE": { name: "동전케이스", description: "매 열 번째 배틀마다, 가진 돈의 10%를 이자로 획득" },
"LOCK_CAPSULE": { name: "록캡슐", description: "받을 아이템을 갱신할 때 희귀도를 고정 가능" },
"GRIP_CLAW": { name: "끈기갈고리손톱" },
"WIDE_LENS": { name: "광각렌즈" },
"MULTI_LENS": { name: "멀티렌즈" },
"HEALING_CHARM": { name: "치유의부적", description: "HP를 회복하는 기술을 썼을 때 효율이 10% 증가 (부활 제외)" },
"CANDY_JAR": { name: "사탕단지", description: "이상한사탕 종류의 아이템이 올려주는 레벨 1 증가" },
"BERRY_POUCH": { name: "열매주머니", description: "사용한 나무열매가 소모되지 않을 확률 33% 추가" },
"FOCUS_BAND": { name: "기합의머리띠", description: "기절할 듯한 데미지를 받아도 HP를 1 남겨서 견딜 확률 10% 추가" },
"QUICK_CLAW": { name: "선제공격손톱", description: "상대보다 먼저 행동할 수 있게 될 확률 10% 추가 (우선도 처리 이후)" },
"KINGS_ROCK": { name: "왕의징표석", description: "공격해서 데미지를 줄 때 상대를 풀죽일 확률 10% 추가" },
"LEFTOVERS": { name: "먹다남은음식", description: "포켓몬의 HP가 매 턴 최대 체력의 1/16씩 회복" },
"SHELL_BELL": { name: "조개껍질방울", description: "포켓몬이 준 데미지의 1/8씩 회복" },
"BATON": { name: "바톤", description: "포켓몬을 교체할 때 효과를 넘겨줄 수 있으며, 함정의 영향을 받지 않게 함" },
"SHINY_CHARM": { name: "빛나는부적", description: "야생 포켓몬이 색이 다른 포켓몬으로 등장할 확률을 급격히 증가" },
"ABILITY_CHARM": { name: "특성부적", description: "야생 포켓몬이 숨겨진 특성을 가지고 등장할 확률을 급격히 증가" },
"IV_SCANNER": { name: "개체값탐지기", description: "야생 포켓몬의 개체값을 확인 가능하다. 높은 값이 먼저 표시되며 확인할 수 있는 개체값을 두 종류씩 추가" },
"DNA_SPLICERS": { name: "유전자쐐기" },
"MINI_BLACK_HOLE": { name: "미니 블랙 홀" },
"GOLDEN_POKEBALL": { name: "황금몬스터볼", description: "전투 후 획득하는 아이템의 선택지를 하나 더 추가" },
"ENEMY_DAMAGE_BOOSTER": { name: "데미지 토큰", description: "주는 데미지를 5% 증가" },
"ENEMY_DAMAGE_REDUCTION": { name: "보호 토큰", description: "받는 데미지를 2.5% 감소" },
"ENEMY_HEAL": { name: "회복 토큰", description: "매 턴 최대 체력의 2%를 회복" },
"ENEMY_ATTACK_POISON_CHANCE": { name: "독 토큰" },
"ENEMY_ATTACK_PARALYZE_CHANCE": { name: "마비 토큰" },
"ENEMY_ATTACK_SLEEP_CHANCE": { name: "잠듦 토큰" },
"ENEMY_ATTACK_FREEZE_CHANCE": { name: "얼음 토큰" },
"ENEMY_ATTACK_BURN_CHANCE": { name: "화상 토큰" },
"ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "만병통치 토큰", description: "매 턴 상태이상에서 회복될 확률 10% 추가" },
"ENEMY_ENDURE_CHANCE": { name: "버티기 토큰" },
"ENEMY_FUSED_CHANCE": { name: "합체 토큰", description: "야생 포켓몬이 합체할 확률 1% 추가" },
},
TempBattleStatBoosterItem: {
"x_attack": "플러스파워",
"x_defense": "디펜드업",
"x_sp_atk": "스페셜업",
"x_sp_def": "스페셜가드",
"x_speed": "스피드업",
"x_accuracy": "잘-맞히기",
"dire_hit": "크리티컬커터",
},
AttackTypeBoosterItem: {
"silk_scarf": "실크스카프",
"black_belt": "검은띠",
"sharp_beak": "예리한부리",
"poison_barb": "독바늘",
"soft_sand": "부드러운모래",
"hard_stone": "딱딱한돌",
"silver_powder": "은빛가루",
"spell_tag": "저주의부적",
"metal_coat": "금속코트",
"charcoal": "목탄",
"mystic_water": "신비의물방울",
"miracle_seed": "기적의씨",
"magnet": "자석",
"twisted_spoon": "휘어진스푼",
"never_melt_ice": "녹지않는얼음",
"dragon_fang": "용의이빨",
"black_glasses": "검은안경",
"fairy_feather": "요정의깃털",
},
BaseStatBoosterItem: {
"hp_up": "맥스업",
"protein": "타우린",
"iron": "사포닌",
"calcium": "리보플라빈",
"zinc": "키토산",
"carbos": "알칼로이드",
},
EvolutionItem: {
"NONE": "None",
"LINKING_CORD": "연결의끈",
"SUN_STONE": "태양의돌",
"MOON_STONE": "달의돌",
"LEAF_STONE": "리프의돌",
"FIRE_STONE": "불꽃의돌",
"WATER_STONE": "물의돌",
"THUNDER_STONE": "천둥의돌",
"ICE_STONE": "얼음의돌",
"DUSK_STONE": "어둠의돌",
"DAWN_STONE": "각성의돌",
"SHINY_STONE": "빛의돌",
"CRACKED_POT": "깨진포트",
"SWEET_APPLE": "달콤한사과",
"TART_APPLE": "새콤한사과",
"STRAWBERRY_SWEET": "딸기사탕공예",
"UNREMARKABLE_TEACUP": "범작찻잔",
"CHIPPED_POT": "이빠진포트",
"BLACK_AUGURITE": "검은휘석",
"GALARICA_CUFF": "가라두구팔찌",
"GALARICA_WREATH": "가라두구머리장식",
"PEAT_BLOCK": "피트블록",
"AUSPICIOUS_ARMOR": "축복받은갑옷",
"MALICIOUS_ARMOR": "저주받은갑옷",
"MASTERPIECE_TEACUP": "걸작찻잔",
"METAL_ALLOY": "복합금속",
"SCROLL_OF_DARKNESS": "악의 족자",
"SCROLL_OF_WATERS": "물의 족자",
"SYRUPY_APPLE": "꿀맛사과",
},
FormChangeItem: {
"NONE": "None",
"ABOMASITE": "눈설왕나이트",
"ABSOLITE": "앱솔나이트",
"AERODACTYLITE": "프테라나이트",
"AGGRONITE": "보스로라나이트",
"ALAKAZITE": "후딘나이트",
"ALTARIANITE": "파비코리나이트",
"AMPHAROSITE": "전룡나이트",
"AUDINITE": "다부니나이트",
"BANETTITE": "깜까미나이트",
"BEEDRILLITE": "독침붕나이트",
"BLASTOISINITE": "거북왕나이트",
"BLAZIKENITE": "번치코나이트",
"CAMERUPTITE": "폭타나이트",
"CHARIZARDITE_X": "리자몽나이트 X",
"CHARIZARDITE_Y": "리자몽나이트 Y",
"DIANCITE": "디안시나이트",
"GALLADITE": "엘레이드나이트",
"GARCHOMPITE": "한카리아스나이트",
"GARDEVOIRITE": "가디안나이트",
"GENGARITE": "팬텀나이트",
"GLALITITE": "얼음귀신나이트",
"GYARADOSITE": "갸라도스나이트",
"HERACRONITE": "헤라크로스나이트",
"HOUNDOOMINITE": "헬가나이트",
"KANGASKHANITE": "캥카나이트",
"LATIASITE": "라티아스나이트",
"LATIOSITE": "라티오스나이트",
"LOPUNNITE": "이어롭나이트",
"LUCARIONITE": "루카리오나이트",
"MANECTITE": "썬더볼트나이트",
"MAWILITE": "입치트나이트",
"MEDICHAMITE": "요가램나이트",
"METAGROSSITE": "메타그로스나이트",
"MEWTWONITE_X": "뮤츠나이트 X",
"MEWTWONITE_Y": "뮤츠나이트 Y",
"PIDGEOTITE": "피죤투나이트",
"PINSIRITE": "쁘사이저나이트",
"RAYQUAZITE": "레쿠쟈나이트",
"SABLENITE": "깜까미나이트",
"SALAMENCITE": "보만다나이트",
"SCEPTILITE": "나무킹나이트",
"SCIZORITE": "핫삼나이트",
"SHARPEDONITE": "샤크니아나이트",
"SLOWBRONITE": "야도란나이트",
"STEELIXITE": "강철톤나이트",
"SWAMPERTITE": "대짱이나이트",
"TYRANITARITE": "마기라스나이트",
"VENUSAURITE": "이상해꽃나이트",
"BLUE_ORB": "쪽빛구슬",
"RED_ORB": "주홍구슬",
"SHARP_METEORITE": "뾰족한운석",
"HARD_METEORITE": "단단한운석",
"SMOOTH_METEORITE": "부드러운운석",
"ADAMANT_CRYSTAL": "큰금강옥",
"LUSTROUS_ORB": "큰백옥",
"GRISEOUS_CORE": "큰백금옥",
"REVEAL_GLASS": "비추는거울",
"GRACIDEA": "그라시데아꽃",
"MAX_MUSHROOMS": "다이버섯",
"DARK_STONE": "다크스톤",
"LIGHT_STONE": "라이트스톤",
"PRISON_BOTTLE": "굴레의항아리",
"N_LUNARIZER": "네크로플러스루나",
"N_SOLARIZER": "네크로플러스솔",
"RUSTED_SWORD": "녹슨검",
"RUSTED_SHIELD": "녹슨방패",
"ICY_REINS_OF_UNITY": "차가운유대의고삐",
"SHADOW_REINS_OF_UNITY": "검은유대의고삐",
"WELLSPRING_MASK": "우물의가면",
"HEARTHFLAME_MASK": "화덕의가면",
"CORNERSTONE_MASK": "주춧돌의가면",
"SHOCK_DRIVE": "번개카세트",
"BURN_DRIVE": "블레이즈카세트",
"CHILL_DRIVE": "프리즈카세트",
"DOUSE_DRIVE": "아쿠아카세트",
"FIST_PLATE": "주먹플레이트",
"SKY_PLATE": "푸른하늘플레이트",
"TOXIC_PLATE": "맹독플레이트",
"EARTH_PLATE": "대지플레이트",
"STONE_PLATE": "암석플레이트",
"INSECT_PLATE": "비단벌레플레이트",
"SPOOKY_PLATE": "원령플레이트",
"IRON_PLATE": "강철플레이트",
"FLAME_PLATE": "불구슬플레이트",
"SPLASH_PLATE": "물방울플레이트",
"MEADOW_PLATE": "초록플레이트",
"ZAP_PLATE": "우뢰플레이트",
"MIND_PLATE": "이상한플레이트",
"ICICLE_PLATE": "고드름플레이트",
"DRACO_PLATE": "용의플레이트",
"DREAD_PLATE": "공포플레이트",
"PIXIE_PLATE": "정령플레이트",
"BLANK_PLATE": "순백플레이트",
"LEGEND_PLATE": "레전드플레이트",
"FIGHTING_MEMORY": "파이팅메모리",
"FLYING_MEMORY": "플라잉메모리",
"POISON_MEMORY": "포이즌메모리",
"GROUND_MEMORY": "그라운드메모리",
"ROCK_MEMORY": "록메모리",
"BUG_MEMORY": "버그메모리",
"GHOST_MEMORY": "고스트메모리",
"STEEL_MEMORY": "스틸메모리",
"FIRE_MEMORY": "파이어메모리",
"WATER_MEMORY": "워터메모리",
"GRASS_MEMORY": "그래스메모리",
"ELECTRIC_MEMORY": "일렉트릭메모리",
"PSYCHIC_MEMORY": "사이킥메모리",
"ICE_MEMORY": "아이스메모리",
"DRAGON_MEMORY": "드래곤메모리",
"DARK_MEMORY": "다크메모리",
"FAIRY_MEMORY": "페어리메모리",
},
} as const;

3839
src/locales/ko/move.ts Normal file

File diff suppressed because it is too large Load Diff

29
src/locales/ko/nature.ts Normal file
View File

@ -0,0 +1,29 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const nature: SimpleTranslationEntries = {
"Hardy": "노력",
"Lonely": "외로움",
"Brave": "용감",
"Adamant": "고집",
"Naughty": "개구쟁이",
"Bold": "대담",
"Docile": "온순",
"Relaxed": "무사태평",
"Impish": "장난꾸러기",
"Lax": "촐랑",
"Timid": "겁쟁이",
"Hasty": "성급",
"Serious": "성실",
"Jolly": "명랑",
"Naive": "천진난만",
"Modest": "조심",
"Mild": "의젓",
"Quiet": "냉정",
"Bashful": "수줍음",
"Rash": "덜렁",
"Calm": "차분",
"Gentle": "얌전",
"Sassy": "건방",
"Careful": "신중",
"Quirky": "변덕"
} as const;

View File

@ -0,0 +1,10 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokeball: SimpleTranslationEntries = {
"pokeBall": "몬스터볼",
"greatBall": "슈퍼볼",
"ultraBall": "하이퍼볼",
"rogueBall": "로그볼",
"masterBall": "마스터볼",
"luxuryBall": "럭셔리볼",
} as const;

View File

@ -0,0 +1,11 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokemonInfoContainer: SimpleTranslationEntries = {
"moveset": "기술",
"gender": "성별:",
"ability": "특성:",
"nature": "성격:",
"epic": "에픽",
"rare": "레어",
"common": "커먼"
} as const;

View File

@ -0,0 +1,41 @@
import { PokemonInfoTranslationEntries } from "#app/plugins/i18n";
export const pokemonInfo: PokemonInfoTranslationEntries = {
Stat: {
"HP": "HP",
"HPshortened": "HP",
"ATK": "공격",
"ATKshortened": "공격",
"DEF": "방어",
"DEFshortened": "방어",
"SPATK": "특수공격",
"SPATKshortened": "특공",
"SPDEF": "특수방어",
"SPDEFshortened": "특방",
"SPD": "스피드",
"SPDshortened": "스피드"
},
Type: {
"UNKNOWN": "Unknown",
"NORMAL": "노말",
"FIGHTING": "격투",
"FLYING": "비행",
"POISON": "독",
"GROUND": "땅",
"ROCK": "바위",
"BUG": "벌레",
"GHOST": "고스트",
"STEEL": "강철",
"FIRE": "불꽃",
"WATER": "물",
"GRASS": "풀",
"ELECTRIC": "전기",
"PSYCHIC": "에스퍼",
"ICE": "얼음",
"DRAGON": "드래곤",
"DARK": "악",
"FAIRY": "페어리",
"STELLAR": "스텔라",
},
} as const;

1086
src/locales/ko/pokemon.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const splashMessages: SimpleTranslationEntries = {
"battlesWon": "전투에서 승리하세요!",
"joinTheDiscord": "디스코드에 가입하세요!",
"infiniteLevels": "무한한 레벨!",
"everythingStacks": "모든 것이 누적됩니다!",
"optionalSaveScumming": "원한다면 세이브 노가다를!",
"biomes": "35개의 지역!",
"openSource": "오픈소스!",
"playWithSpeed": "5배속으로 플레이해봐요!",
"liveBugTesting": "라이브 버그 테스트!",
"heavyInfluence": "RoR2에 강하게 영감을 받았어요!",
"pokemonRiskAndPokemonRain": "포켓몬 '리스크' 오브 포켓몬 '레인'!",
"nowWithMoreSalt": "이제 33% 더 과몰입 가능!",
"infiniteFusionAtHome": "집에서 인피니트 퓨전을 즐겨보세요!",
"brokenEggMoves": "알기술까기!",
"magnificent": "Magnificent!",
"mubstitute": "Mubstitute!",
"thatsCrazy": "미쳤습니다!",
"oranceJuice": "오렌지 쥬스!",
"questionableBalancing": "의심스러운 밸런싱!",
"coolShaders": "쿨한 셰이더!",
"aiFree": "AI-Free!",
"suddenDifficultySpikes": "돌연사 가능!",
"basedOnAnUnfinishedFlashGame": "무한 플래시게임 기반!",
"moreAddictiveThanIntended": "기획보다 중독적이에요!",
"mostlyConsistentSeeds": "일관성있는 랜덤 시드!",
"achievementPointsDontDoAnything": "업적 포인트는 아무것도 하지 않습니다!",
"youDoNotStartAtLevel": "레벨 2000에서 시작하지 않아요!",
"dontTalkAboutTheManaphyEggIncident": "마나피 알 사건에 대해선 이야기하지 맙시다!",
"alsoTryPokengine": "Pokéngine도 해봐요!",
"alsoTryEmeraldRogue": "Emerald Rogue도 해봐요!",
"alsoTryRadicalRed": "Radical Red도 해봐요!",
"eeveeExpo": "Eevee Expo!",
"ynoproject": "YNOproject!",
} as const;

View File

@ -0,0 +1,44 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
/**
* The menu namespace holds most miscellaneous text that isn't directly part of the game's
* contents or directly related to Pokemon data. This includes menu navigation, settings,
* account interactions, descriptive text, etc.
*/
export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam":"이 포켓몬들로 시작하시겠습니까?",
"gen1": "1세대",
"gen2": "2세대",
"gen3": "3세대",
"gen4": "4세대",
"gen5": "5세대",
"gen6": "6세대",
"gen7": "7세대",
"gen8": "8세대",
"gen9": "9세대",
"growthRate": "성장 속도",
"ability": "특성:",
"passive": "패시브:",
"nature": "성격:",
"eggMoves": "알 기술",
"start": "시작",
"addToParty": "파티에 추가",
"toggleIVs": "개체값 토글",
"manageMoves": "기술 관리",
"useCandies": "사탕 사용",
"selectMoveSwapOut": "교체할 기술을 선택해주세요.",
"selectMoveSwapWith": "교체될 기술을 선택해주세요. 대상:",
"unlockPassive": "패시브 해금",
"reduceCost": "코스트 줄이기",
"cycleShiny": "R: 색상 전환",
"cycleForm": "F: 폼 체인지",
"cycleGender": "G: 암수 전환",
"cycleAbility": "E: 특성 전환",
"cycleNature": "N: 성격 전환",
"cycleVariant": "V: 형태 전환",
"enablePassive": "패시브 활성화",
"disablePassive": "패시브 비활성화",
"locked": "잠김",
"disabled": "비활성",
"uncaught": "미등록",
};

260
src/locales/ko/trainers.ts Normal file
View File

@ -0,0 +1,260 @@
import {SimpleTranslationEntries} from "#app/plugins/i18n";
// Titles of special trainers like gym leaders, elite four, and the champion
export const titles: SimpleTranslationEntries = {
"elite_four": "사천왕",
"elite_four_female": "사천왕",
"gym_leader": "체육관 관장",
"gym_leader_female": "체육관 관장",
"gym_leader_double": "체육관 관장 듀오",
"champion": "챔피언",
"champion_female": "챔피언",
"champion_double": "챔피언 듀오",
"rival": "라이벌",
"professor": "박사",
"frontier_brain": "프런티어 브레인",
// Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc.
} as const;
// Titles of trainers like "Youngster" or "Lass"
export const trainerClasses: SimpleTranslationEntries = {
"ace_trainer": "엘리트 트레이너",
"ace_trainer_female": "엘리트 트레이너",
"ace_duo": "엘리트 콤비",
"artist": "예술가",
"artist_female": "예술가",
"backers": "팬클럽",
"backpacker": "백팩커",
"backpacker_female": "백팩커",
"backpackers": "백팩커",
"baker": "제빵사",
"battle_girl": "배틀걸",
"beauty": "아가씨",
"beginners": "반바지 꼬마 & 짧은 치마", // 확인 필요
"biker": "폭주족",
"black_belt": "태권왕",
"breeder": "포켓몬 브리더",
"breeder_female": "포켓몬 브리더",
"breeders": "포켓몬 브리더",
"clerk": "비즈니스맨",
"clerk_female": "여사원",
"colleagues": "비즈니스 파트너",
"crush_kin": "배틀 커플", // 임의번역
"cyclist": "사이클링",
"cyclist_female": "사이클링",
"cyclists": "사이클링",
"dancer": "댄서",
"dancer_female": "댄서",
"depot_agent": "역무원",
"doctor": "의사",
"doctor_female": "간호사", // doctor_f.png 파일이 간호사
"fisherman": "낚시꾼",
"fisherman_female": "낚시꾼",
"gentleman": "신사",
"guitarist": "기타리스트",
"guitarist_female": "기타리스트",
"harlequin": "어릿광대",
"hiker": "등산가",
"hooligans": "폭주족 & 빡빡이", // 확인 필요
"hoopster": "농구선수",
"infielder": "야구선수",
"janitor": "청소부",
"lady": "아기씨",
"lass": "짧은 치마",
"linebacker": "미식축구선수",
"maid": "메이드",
"madame": "마담",
"medical_team": "의료팀",
"musician": "뮤지션",
"hex_maniac": "오컬트마니아",
"nurse": "간호사",
"nursery_aide": "보육사",
"officer": "경찰관",
"parasol_lady": "파라솔 아가씨",
"pilot": "파일럿",
"pokéfan": "애호가클럽",
"pokéfan_female": "애호가클럽",
"pokéfan_family": "애호가부부",
"preschooler": "보육원아",
"preschooler_female": "보육원아",
"preschoolers": "보육원아",
"psychic": "초능력자",
"psychic_female": "초능력자",
"psychics": "초능력자",
"pokémon_ranger": "포켓몬 레인저",
"pokémon_ranger_female": "포켓몬 레인저",
"pokémon_rangers": "포켓몬 레인저",
"ranger": "포켓몬 레인저",
"restaurant_staff": "요리사", // 혹은 오너로 추정
"rich": "신사",
"rich_female": "마담",
"rich_boy": "도련님",
"rich_couple": "신사 & 마담", // 확인 필요
"rich_kid": "도련님",
"rich_kid_female": "아가씨",
"rich_kids": "도련님 & 아가씨", // 확인 필요
"roughneck": "빡빡이",
"scientist": "연구원",
"scientist_female": "연구원",
"scientists": "연구원",
"smasher": "테니스선수",
"snow_worker": "작업원",
"snow_worker_female": "작업원",
"striker": "축구선수",
"school_kid": "학원끝난 아이",
"school_kid_female": "학원끝난 아이",
"school_kids": "학원끝난 아이",
"swimmer": "수연팬티 소년",
"swimmer_female": "비키니 아가씨",
"swimmers": "수영팬티 소년 & 비키니 아가씨", // 확인 필요
"twins": "쌍둥이",
"veteran": "베테랑 트레이너",
"veteran_female": "베테랑 트레이너",
"veteran_duo": "베테랑 콤비",
"waiter": "웨이터",
"waitress": "웨이트리스",
"worker": "작업원",
"worker_female": "작업원",
"workers": "작업원",
"youngster": "반바지 꼬마"
} as const;
// Names of special trainers like gym leaders, elite four, and the champion
export const trainerNames: SimpleTranslationEntries = {
"brock": "웅",
"misty": "이슬",
"lt_surge": "마티스",
"erika": "민화",
"janine": "도희",
"sabrina": "초련",
"blaine": "강연",
"giovanni": "비주기",
"falkner": "비상",
"bugsy": "호일",
"whitney": "꼭두",
"morty": "유빈",
"chuck": "사도",
"jasmine": "규리",
"pryce": "류옹",
"clair": "이향",
"roxanne": "원규",
"brawly": "철구",
"wattson": "암페어",
"flannery": "민지",
"norman": "종길",
"winona": "은송",
"tate": "풍",
"liza": "란",
"juan": "아단",
"roark": "강석",
"gardenia": "유채",
"maylene": "자두",
"crasher_wake": "맥실러",
"fantina": "멜리사",
"byron": "동관",
"candice": "무청",
"volkner": "전진",
"cilan": "덴트",
"chili": "팟",
"cress": "콘",
"cheren": "체렌",
"lenora": "알로에",
"roxie": "보미카",
"burgh": "아티",
"elesa": "카밀레",
"clay": "야콘",
"skyla": "풍란",
"brycen": "담죽",
"drayden": "사간",
"marlon": "시즈",
"viola": "비올라",
"grant": "자크로",
"korrina": "코르니",
"ramos": "후쿠지",
"clemont": "시트론",
"valerie": "마슈",
"olympia": "고지카",
"wulfric": "우르프",
"milo": "아킬",
"nessa": "야청",
"kabu": "순무",
"bea": "채두",
"allister": "어니언",
"opal": "포플러",
"bede": "비트",
"gordie": "마쿠와",
"melony": "멜론",
"piers": "두송",
"marnie": "마리",
"raihan": "금랑",
"katy": "단풍",
"brassius": "콜사",
"iono": "모야모",
"kofu": "곤포",
"larry": "청목",
"ryme": "라임",
"tulip": "리파",
"grusha": "그루샤",
"lorelei": "칸나",
"bruno": "시바",
"agatha": "국화",
"lance": "목호",
"will": "일목",
"koga": "독수",
"karen": "카렌",
"sidney": "혁진",
"phoebe": "회연",
"glacia": "미혜",
"drake": "권수",
"aaron": "충호",
"bertha": "들국화",
"flint": "대엽",
"lucian": "오엽",
"shauntal": "망초",
"marshal": "연무",
"grimsley": "블래리",
"caitlin": "카틀레야",
"malva": "파키라",
"siebold": "즈미",
"wikstrom": "간피",
"drasna": "드라세나",
"hala": "할라",
"molayne": "멀레인",
"olivia": "라이치",
"acerola": "아세로라",
"kahili": "카일리",
"rika": "칠리",
"poppy": "뽀삐",
"hassel": "팔자크",
"crispin": "하솔",
"amarys": "네리네",
"lacey": "타로",
"drayton": "제빈",
"blue": "그린",
"red": "레드",
"steven": "성호",
"wallace": "윤진",
"cynthia": "난천",
"alder": "노간주",
"iris": "아이리스",
"diantha": "카르네",
"hau": "하우",
"geeta": "테사",
"nemona": "네모",
"kieran": "카지",
"leon": "단델",
"rival": "핀",
"rival_female": "아이비",
// Double Names
"blue_red_double": "그린 & 레드",
"red_blue_double": "레드 & 그린",
"tate_liza_double": "풍 & 란",
"liza_tate_double": "란 & 풍",
"steven_wallace_double": "성호 & 윤진",
"wallace_steven_double": "윤진 & 성호",
"alder_iris_double": "노간주 & 아이리스",
"iris_alder_double": "아이리스 & 노간주",
"marnie_piers_double": "마리 & 두송",
"piers_marnie_double": "두송 & 마리",
} as const;

View File

@ -0,0 +1,42 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const tutorial: SimpleTranslationEntries = {
intro: `포켓로그에 오신 것을 환영합니다! 로그라이트 요소가 가미된 전투 중심의 포켓몬 팬게임입니다.
$이 Pokémon .
$게임은 .\n버그 .
$게임이 '하드웨어 가속' .`,
accessMenu: "메뉴에 액세스하려면 입력을 기다리는 동안 M 또는 Esc를 누릅니다.\n메뉴에는 설정과 다양한 기능이 포함되어 있습니다.",
menu: `이 메뉴에서 설정에 액세스할 수 있습니다.
$설정에서 , .
$여기에는 !`,
starterSelect: `이 화면에서 스타팅을 선택할 수 있습니다.\n이들은 당신의 첫 번째 파티 멤버들입니다.
$최대 6 \n포켓몬에 10 .
$계속 \n선택할 , , .
$개체값도 ,\n같은 !`,
pokerus: `매일 랜덤 스타팅 세 종류에 보라색 테두리가 쳐집니다.
$등록된 ,\n파티에 !`,
statChange: `포켓몬은 교체하지 않으면 다음 전투에서도 능력치 변화가 유지됩니다.
$대신 .
$C Shift를 .`,
selectItem: `전투가 끝날때마다 무작위 아이템 3개 중 하나를 선택하여 얻습니다.
$종류는 , , .
$대부분의 .
$진화용과 .
$지닌 .
$지닌 .
$돈으로 , .
$아이템을 , .`,
eggGacha: `이 화면에서 포켓몬 알 바우처를\n사용해 뽑기를 할 수 있습니다.
$알은 .\n희귀할 .
$부화시킨 \n스타팅에 .
$알에서 \n야생에서 .
$일부 .
$각 ,\n원하는 !`,
} as const;

11
src/locales/ko/voucher.ts Normal file
View File

@ -0,0 +1,11 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const voucher: SimpleTranslationEntries = {
"vouchers": "바우처",
"eggVoucher": "에그 바우처",
"eggVoucherPlus": "에그 바우처 플러스",
"eggVoucherPremium": "에그 바우처 프리미엄",
"eggVoucherGold": "에그 바우처 골드",
"locked": "미획득",
"defeatTrainer" : "{{trainerName}}에게 승리",
} as const;

45
src/locales/ko/weather.ts Normal file
View File

@ -0,0 +1,45 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
/**
* The weather namespace holds text displayed when weather is active during a battle
*/
export const weather: SimpleTranslationEntries = {
"sunnyStartMessage": "햇살이 강해졌다!",
"sunnyLapseMessage": "햇살이 강하다",
"sunnyClearMessage": "햇살이 원래대로 되돌아왔다!",
"rainStartMessage": "비가 내리기 시작했다!",
"rainLapseMessage": "비가 계속 내리고 있다",
"rainClearMessage": "비가 그쳤다!",
"sandstormStartMessage": "모래바람이 불기 시작했다!",
"sandstormLapseMessage": "모래바람이 세차게 분다",
"sandstormClearMessage": "모래바람이 가라앉았다!",
"sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!",
"hailStartMessage": "싸라기눈이 내리기 시작했다!",
"hailLapseMessage": "싸라기눈이 계속 내리고 있다",
"hailClearMessage": "싸라기눈이 그쳤다!",
"hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!",
"snowStartMessage": "눈이 내리기 시작했다!",
"snowLapseMessage": "눈이 계속 내리고 있다",
"snowClearMessage": "눈이 그쳤다!",
// 이하 LapseMessage 임의번역
"fogStartMessage": "발밑이 안개로 자욱해졌다!",
"fogLapseMessage": "발밑이 안개로 자욱하다",
"fogClearMessage": "발밑의 안개가 사라졌다!",
"heavyRainStartMessage": "강한 비가 내리기 시작했다!",
"heavyRainLapseMessage": "강한 비가 계속 내리고 있다",
"heavyRainClearMessage": "강한 비가 그쳤다!",
"harshSunStartMessage": "햇살이 아주 강해졌다!",
"harshSunLapseMessage": "햇살이 아주 강하다",
"harshSunClearMessage": "햇살이 원래대로 되돌아왔다!",
"strongWindsStartMessage": "수수께끼의 난기류가\n비행포켓몬을 지킨다!",
"strongWindsLapseMessage": "수수께끼의 난기류가 강렬하게 불고 있다",
"strongWindsClearMessage": "수수께끼의 난기류가 멈췄다!" // 임의번역
};

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "Desafio Diário (Beta)",
"loadGame": "Carregar Jogo",
"newGame": "Novo Jogo",
"settings": "Configurações",
"selectGameMode": "Escolha um modo de jogo.",
"logInOrCreateAccount": "Inicie uma sessão ou crie uma conta para começar. Não é necessário email!",
"username": "Nome de Usuário",

View File

@ -18,7 +18,8 @@ export const tutorial: SimpleTranslationEntries = {
$Existem também vários outros recursos disponíveis aqui.
$Não deixe de conferir todos eles!`,
"starterSelect": `Aqui você pode escolher seus iniciais.\nEsses serão os primeiro Pokémon da sua equipe.
"starterSelect": `Aqui você pode escolher seus iniciais apertando a tecla Z ou\na Barra de Espaço.
$Esses serão os primeiro Pokémon da sua equipe.
$Cada inicial tem seu custo. Sua equipe pode ter até 6\nmembros, desde que a soma dos custos não ultrapasse 10.
$Você pode escolher o gênero, a habilidade\ne até a forma do seu inicial.
$Essas opções dependem das variantes dessa\nespécie que você capturou ou chocou.

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "每日挑战 (Beta)",
"loadGame": "加载游戏",
"newGame": "新游戏",
"settings": "设置",
"selectGameMode": "选择一个游戏模式",
"logInOrCreateAccount": "登录或创建账户以开始游戏。无需邮箱!",
"username": "用户名",

View File

@ -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": "招式",
"gender": "性别:",
"ability": "特性:",
"nature": "性格:",
"epic": "史诗",
"rare": "稀有",
"common": "常见"
} as const;

View File

@ -13,7 +13,7 @@ export const tutorial: SimpleTranslationEntries = {
$在设置中\n和其他选项
$这里还有各种其他功能`,
"starterSelect": `在此页面中,您可以选择您的初始宝可梦。\n这些是您最初的队伍成员。
"starterSelect": `在此页面中,您可以通过按Z或空格键选择\n您的初始宝可梦。这些是您最初的队伍成员。
$每个初始宝可梦都有一个费用值\n最多可以拥有6名成员10
$您还可以根据您捕获或孵化的变种选择性别\n
$一个物种个体值是您捕获或孵化的所有宝可\n梦中最好的`,

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "每日挑戰 (Beta)",
"loadGame": "加載遊戲",
"newGame": "新遊戲",
"settings": "Settings",
"selectGameMode": "選擇遊戲模式",
"logInOrCreateAccount": "登入或註冊即可開始遊戲,無需郵箱!",
"username": "用戶名",

View File

@ -5,7 +5,6 @@ import { version } from "../package.json";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin";
import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin.js";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin.js";
import { LoadingScene } from "./loading-scene";
@ -72,23 +71,82 @@ const config: Phaser.Types.Core.GameConfig = {
version: version
};
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
const setPositionRelative = function (guideObject: any, x: number, y: number) {
if (guideObject && guideObject instanceof Phaser.GameObjects.GameObject) {
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
return;
}
this.setPosition(x, y);
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
};
declare module "phaser" {
namespace GameObjects {
interface Container {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
interface Sprite {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
interface Image {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
interface NineSlice {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
interface Text {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
interface Rectangle {
/**
* Sets this object's position relative to another object with a given offset
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
* @param x The relative x position
* @param y The relative y position
*/
setPositionRelative(guideObject: any, x: number, y: number): void;
}
}
}
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative;
BBCodeText.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative;
document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems"));

View File

@ -1230,11 +1230,11 @@ const modifierPool: ModifierPool = {
return thresholdPartyMemberCount;
}, 3),
new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3;
}, 9),
new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount;
}, 3),
new WeightedModifierType(modifierTypes.LURE, 2),
@ -1275,11 +1275,11 @@ const modifierPool: ModifierPool = {
return thresholdPartyMemberCount;
}, 3),
new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3;
}, 9),
new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount;
}, 3),
new WeightedModifierType(modifierTypes.DIRE_HIT, 4),

View File

@ -73,6 +73,7 @@ export const VARIANT_OVERRIDE: Variant = 0;
*/
export const OPP_SPECIES_OVERRIDE: Species | integer = 0;
export const OPP_LEVEL_OVERRIDE: number = 0;
export const OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
export const OPP_PASSIVE_ABILITY_OVERRIDE = Abilities.NONE;
export const OPP_GENDER_OVERRIDE: Gender = null;

View File

@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, HealFromBerryUseAbAttr } from "./data/ability";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -262,6 +262,14 @@ export class TitlePhase extends Phase {
return true;
},
keepOpen: true
},
{
label: i18next.t("menu:settings"),
handler: () => {
this.scene.ui.setOverlayMode(Mode.SETTINGS);
return true;
},
keepOpen: true
});
const config: OptionSelectConfig = {
options: options,
@ -1015,7 +1023,6 @@ export class EncounterPhase extends BattlePhase {
}
}
}
handleTutorial(this.scene, Tutorial.Access_Menu).then(() => super.end());
}
@ -2826,9 +2833,13 @@ export class MoveEffectPhase extends PokemonPhase {
const user = this.getUserPokemon();
// Hit check only calculated on first hit for multi-hit moves
// Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits.
// However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal
// multi-hit move and proceed with all hits
if (user.turnData.hitsLeft < user.turnData.hitCount) {
return true;
if (!this.move.getMove().hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) {
return true;
}
}
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
@ -3480,6 +3491,13 @@ export class FaintPhase extends PokemonPhase {
doFaint(): void {
const pokemon = this.getPokemon();
// Track total times pokemon have been KO'd for supreme overlord/last respects
if (pokemon.isPlayer()) {
this.scene.currentBattle.playerFaints += 1;
} else {
this.scene.currentBattle.enemyFaints += 1;
}
this.scene.queueMessage(getPokemonMessage(pokemon, " fainted!"), null, true);
if (pokemon.turnData?.attacksReceived?.length) {
@ -5018,11 +5036,16 @@ export class EggLapsePhase extends Phase {
return Overrides.IMMEDIATE_HATCH_EGGS_OVERRIDE ? true : --egg.hatchWaves < 1;
});
if (eggsToHatch.length) {
let eggsToHatchCount: integer = eggsToHatch.length;
if (eggsToHatchCount) {
this.scene.queueMessage(i18next.t("battle:eggHatching"));
for (const egg of eggsToHatch) {
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg));
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggsToHatchCount));
if (eggsToHatchCount > 0) {
eggsToHatchCount--;
}
}
}

View File

@ -9,6 +9,7 @@ import { itConfig } from "#app/locales/it/config.js";
import { ptBrConfig } from "#app/locales/pt_BR/config.js";
import { zhCnConfig } from "#app/locales/zh_CN/config.js";
import { zhTWConfig } from "#app/locales/zh_TW/config.js";
import { koConfig } from "#app/locales/ko/config.js";
export interface SimpleTranslationEntries {
[key: string]: string
@ -77,6 +78,30 @@ export interface Localizable {
localize(): void;
}
const alternativeFonts = {
"ko": [
new FontFace("emerald", "url(./fonts/PokePT_Wansung.ttf)")
],
};
function loadFont(language: string) {
const altFontLanguages = Object.keys(alternativeFonts);
if (!alternativeFonts[language]) {
language = language.split(/[-_/]/)[0];
}
if (alternativeFonts[language]) {
alternativeFonts[language].forEach(f => {
document.fonts.add(f);
});
altFontLanguages.splice(altFontLanguages.indexOf(language), 0);
}
altFontLanguages.forEach(f=> {
if (f && f.status === "loaded") {
document.fonts.delete(f);
}
});
}
export function initI18n(): void {
// Prevent reinitialization
if (isInitialized) {
@ -89,7 +114,10 @@ export function initI18n(): void {
lang = localStorage.getItem("prLang");
}
loadFont(lang);
i18next.on("languageChanged", lng=> {
loadFont(lng);
});
/**
* i18next is a localization library for maintaining and using translation resources.
@ -111,7 +139,7 @@ export function initI18n(): void {
lng: lang,
nonExplicitSupportedLngs: true,
fallbackLng: "en",
supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt"],
supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt", "ko"],
debug: true,
interpolation: {
escapeValue: false,
@ -140,7 +168,10 @@ export function initI18n(): void {
},
zh_TW: {
...zhTWConfig
}
},
ko: {
...koConfig
},
},
});
}

View File

@ -19,27 +19,33 @@ export function initGameSpeed() {
const originalAddEvent = this.time.addEvent;
this.time.addEvent = function (config: Phaser.Time.TimerEvent | Phaser.Types.Time.TimerEventConfig) {
if (config.delay) {
if (!(config instanceof Phaser.Time.TimerEvent) && config.delay) {
config.delay = transformValue(config.delay);
}
return originalAddEvent.apply(this, [ config ]);
};
const originalTweensAdd = this.tweens.add;
this.tweens.add = function (config: Phaser.Types.Tweens.TweenBuilderConfig | Phaser.Types.Tweens.TweenChainBuilderConfig | Phaser.Tweens.Tween | Phaser.Tweens.TweenChain) {
if (config.duration) {
config.duration = transformValue(config.duration);
}
if (config.delay) {
config.delay = transformValue(config.delay);
}
if (config.repeatDelay) {
config.repeatDelay = transformValue(config.repeatDelay);
}
if (config.loopDelay) {
config.loopDelay = transformValue(config.loopDelay);
config.loopDelay = transformValue(config.loopDelay as number);
}
if (config.hold) {
config.hold = transformValue(config.hold);
if (!(config instanceof Phaser.Tweens.TweenChain) ) {
if (config.duration) {
config.duration = transformValue(config.duration);
}
if (!(config instanceof Phaser.Tweens.Tween)) {
if (config.delay) {
config.delay = transformValue(config.delay as number);
}
if (config.repeatDelay) {
config.repeatDelay = transformValue(config.repeatDelay);
}
if (config.hold) {
config.hold = transformValue(config.hold);
}
}
}
return originalTweensAdd.apply(this, [ config ]);
};
@ -51,13 +57,13 @@ export function initGameSpeed() {
t.duration = transformValue(t.duration);
}
if (t.delay) {
t.delay = transformValue(t.delay);
t.delay = transformValue(t.delay as number);
}
if (t.repeatDelay) {
t.repeatDelay = transformValue(t.repeatDelay);
}
if (t.loopDelay) {
t.loopDelay = transformValue(t.loopDelay);
t.loopDelay = transformValue(t.loopDelay as number);
}
if (t.hold) {
t.hold = transformValue(t.hold);
@ -78,7 +84,7 @@ export function initGameSpeed() {
config.repeatDelay = transformValue(config.repeatDelay);
}
if (config.loopDelay) {
config.loopDelay = transformValue(config.loopDelay);
config.loopDelay = transformValue(config.loopDelay as number);
}
if (config.hold) {
config.hold = transformValue(config.hold);

View File

@ -260,6 +260,10 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer)
label: "繁體中文",
handler: () => changeLocaleHandler("zh_TW")
},
{
label: "한국어",
handler: () => changeLocaleHandler("ko")
},
{
label: "Cancel",
handler: () => cancelHandler()

View File

@ -3,6 +3,8 @@ export const keysDown = new Map();
let lastTouchedId;
export function initTouchControls(buttonMap) {
const dpadDiv = document.querySelector("#dpad");
preventElementZoom(dpadDiv);
for (const button of document.querySelectorAll("[data-key]")) {
// @ts-ignore
bindKey(button, button.dataset.key, buttonMap);
@ -138,6 +140,3 @@ function preventElementZoom(element) {
event.target.click();
});
}
const dpadDiv = document.querySelector("#dpad");
preventElementZoom(dpadDiv);

View File

@ -6,17 +6,16 @@ import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
import {Setting, settingOptions} from "./system/settings";
import SettingsUiHandler from "./ui/settings-ui-handler";
import {Button} from "./enums/buttons";
import BattleScene from "./battle-scene";
export interface ActionKeys {
[key in Button]: () => void;
}
type ActionKeys = Record<Button, () => void>;
export class UiInputs {
private scene: Phaser.Scene;
private events: Phaser.Events;
private scene: BattleScene;
private events: Phaser.Events.EventEmitter;
private inputsController: InputsController;
constructor(scene: Phaser.Scene, inputsController: InputsController) {
constructor(scene: BattleScene, inputsController: InputsController) {
this.scene = scene;
this.inputsController = inputsController;
this.init();
@ -52,30 +51,48 @@ export class UiInputs {
}
getActionsKeyDown(): ActionKeys {
const actions = {};
actions[Button.UP] = () => this.buttonDirection(Button.UP);
actions[Button.DOWN] = () => this.buttonDirection(Button.DOWN);
actions[Button.LEFT] = () => this.buttonDirection(Button.LEFT);
actions[Button.RIGHT] = () => this.buttonDirection(Button.RIGHT);
actions[Button.SUBMIT] = () => this.buttonTouch();
actions[Button.ACTION] = () => this.buttonAb(Button.ACTION);
actions[Button.CANCEL] = () => this.buttonAb(Button.CANCEL);
actions[Button.MENU] = () => this.buttonMenu();
actions[Button.STATS] = () => this.buttonStats(true);
actions[Button.CYCLE_SHINY] = () => this.buttonCycleOption(Button.CYCLE_SHINY);
actions[Button.CYCLE_FORM] = () => this.buttonCycleOption(Button.CYCLE_FORM);
actions[Button.CYCLE_GENDER] = () => this.buttonCycleOption(Button.CYCLE_GENDER);
actions[Button.CYCLE_ABILITY] = () => this.buttonCycleOption(Button.CYCLE_ABILITY);
actions[Button.CYCLE_NATURE] = () => this.buttonCycleOption(Button.CYCLE_NATURE);
actions[Button.CYCLE_VARIANT] = () => this.buttonCycleOption(Button.CYCLE_VARIANT);
actions[Button.SPEED_UP] = () => this.buttonSpeedChange();
actions[Button.SLOW_DOWN] = () => this.buttonSpeedChange(false);
const actions: ActionKeys = {
[Button.UP]: () => this.buttonDirection(Button.UP),
[Button.DOWN]: () => this.buttonDirection(Button.DOWN),
[Button.LEFT]: () => this.buttonDirection(Button.LEFT),
[Button.RIGHT]: () => this.buttonDirection(Button.RIGHT),
[Button.SUBMIT]: () => this.buttonTouch(),
[Button.ACTION]: () => this.buttonAb(Button.ACTION),
[Button.CANCEL]: () => this.buttonAb(Button.CANCEL),
[Button.MENU]: () => this.buttonMenu(),
[Button.STATS]: () => this.buttonStats(true),
[Button.CYCLE_SHINY]: () => this.buttonCycleOption(Button.CYCLE_SHINY),
[Button.CYCLE_FORM]: () => this.buttonCycleOption(Button.CYCLE_FORM),
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
[Button.CYCLE_VARIANT]: () => this.buttonCycleOption(Button.CYCLE_VARIANT),
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
};
return actions;
}
getActionsKeyUp(): ActionKeys {
const actions = {};
actions[Button.STATS] = () => this.buttonStats(false);
const actions: ActionKeys = {
[Button.UP]: () => undefined,
[Button.DOWN]: () => undefined,
[Button.LEFT]: () => undefined,
[Button.RIGHT]: () => undefined,
[Button.SUBMIT]: () => undefined,
[Button.ACTION]: () => undefined,
[Button.CANCEL]: () => undefined,
[Button.MENU]: () => undefined,
[Button.STATS]: () => this.buttonStats(false),
[Button.CYCLE_SHINY]: () => undefined,
[Button.CYCLE_FORM]: () => undefined,
[Button.CYCLE_GENDER]: () => undefined,
[Button.CYCLE_ABILITY]: () => undefined,
[Button.CYCLE_NATURE]: () => undefined,
[Button.CYCLE_VARIANT]: () => undefined,
[Button.SPEED_UP]: () => undefined,
[Button.SLOW_DOWN]: () => undefined,
};
return actions;
}

View File

@ -4,6 +4,9 @@ import { Voucher } from "../system/voucher";
import { TextStyle, addTextObject } from "./text";
export default class AchvBar extends Phaser.GameObjects.Container {
private defaultWidth: number;
private defaultHeight: number;
private bg: Phaser.GameObjects.NineSlice;
private icon: Phaser.GameObjects.Sprite;
private titleText: Phaser.GameObjects.Text;
@ -19,7 +22,10 @@ export default class AchvBar extends Phaser.GameObjects.Container {
}
setup(): void {
this.bg = this.scene.add.nineslice(0, 0, "achv_bar", null, 160, 40, 41, 6, 16, 4);
this.defaultWidth = 160;
this.defaultHeight = 40;
this.bg = this.scene.add.nineslice(0, 0, "achv_bar", null, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4);
this.bg.setOrigin(0, 0);
this.add(this.bg);
@ -66,11 +72,22 @@ export default class AchvBar extends Phaser.GameObjects.Container {
this.scoreText.setText(`+${(achv as Achv).score}pt`);
}
// Take the width of the default interface or the title if longest
this.bg.width = Math.max(this.defaultWidth, this.icon.displayWidth + this.titleText.displayWidth + this.scoreText.displayWidth + 16);
this.scoreText.x = this.bg.width - 2;
this.descriptionText.width = this.bg.width - this.icon.displayWidth - 16;
this.descriptionText.setWordWrapWidth(this.descriptionText.width * 6);
// Take the height of the default interface or the description if longest
this.bg.height = Math.max(this.defaultHeight, this.titleText.displayHeight + this.descriptionText.displayHeight + 8);
this.icon.y = (this.bg.height / 2) - (this.icon.height / 2);
(this.scene as BattleScene).playSound("achv");
this.scene.tweens.add({
targets: this,
x: (this.scene.game.canvas.width / 6) - 76,
x: (this.scene.game.canvas.width / 6) - (this.bg.width / 2),
duration: 500,
ease: "Sine.easeOut"
});

View File

@ -0,0 +1,54 @@
import BattleScene from "#app/battle-scene.js";
import { addWindow } from "./ui-theme";
import { addTextObject, TextStyle } from "./text";
/**
* A container that displays the count of hatching eggs.
* Extends Phaser.GameObjects.Container.
*/
export default class EggsToHatchCountContainer extends Phaser.GameObjects.Container {
private readonly WINDOW_DEFAULT_WIDTH = 37;
private readonly WINDOW_MEDIUM_WIDTH = 42;
private readonly WINDOW_HEIGHT = 26;
private eggsToHatchCount: integer;
private eggsToHatchCountWindow: Phaser.GameObjects.NineSlice;
public eggCountText: Phaser.GameObjects.Text;
/**
* @param {BattleScene} scene - The scene to which this container belongs.
* @param {number} eggsToHatchCount - The number of eggs to hatch.
*/
constructor(scene: BattleScene, eggsToHatchCount: integer) {
super(scene, 0, 0);
this.eggsToHatchCount = eggsToHatchCount;
}
/**
* Sets up the container, creating the window, egg sprite, and egg count text.
*/
setup(): void {
const windowWidth = this.eggsToHatchCount > 9 ? this.WINDOW_MEDIUM_WIDTH : this.WINDOW_DEFAULT_WIDTH;
this.eggsToHatchCountWindow = addWindow(this.scene as BattleScene, 5, 5, windowWidth, this.WINDOW_HEIGHT);
this.setVisible(this.eggsToHatchCount > 1);
this.add(this.eggsToHatchCountWindow);
const eggSprite = this.scene.add.sprite(19, 18, "egg", "egg_0");
eggSprite.setScale(0.32);
this.eggCountText = addTextObject(this.scene, 28, 13, `${this.eggsToHatchCount}`, TextStyle.MESSAGE, { fontSize: "66px" });
this.add(eggSprite);
this.add(this.eggCountText);
}
/**
* Resets the window size to the default width and height.
*/
setWindowToDefaultSize(): void {
this.eggsToHatchCountWindow.setSize(this.WINDOW_DEFAULT_WIDTH, this.WINDOW_HEIGHT);
}
}

View File

@ -81,6 +81,10 @@ const languageSettings: { [key: string]: LanguageSetting } = {
instructionTextSize: "42px",
starterInfoXPos: 33,
},
"ko":{
starterInfoTextSize: "52px",
instructionTextSize: "38px",
}
};
const starterCandyCosts: { passive: integer, costReduction: [integer, integer] }[] = [

View File

@ -897,7 +897,7 @@ export default class SummaryUiHandler extends UiHandler {
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
this.extraMoveRowContainer.setVisible(true);
const newMoveTypeIcon = this.scene.add.sprite(0, 0, "types", Type[this.newMove.type].toLowerCase());
const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, Type[this.newMove.type].toLowerCase());
newMoveTypeIcon.setOrigin(0, 1);
this.extraMoveRowContainer.add(newMoveTypeIcon);

View File

@ -38,7 +38,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
this.titleContainer.add(this.dailyRunScoreboard);
this.playerCountLabel = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 90, `? ${i18next.t("menu:playersOnline")}`, TextStyle.MESSAGE, { fontSize: "54px" });
this.playerCountLabel = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 109, `? ${i18next.t("menu:playersOnline")}`, TextStyle.MESSAGE, { fontSize: "54px" });
this.playerCountLabel.setOrigin(1, 0);
this.titleContainer.add(this.playerCountLabel);