[Localization] Modification some code about battle stat for localization (#1985)

* [Localization] Modification some code about battle stat for localization.

* Corrections to failed generating typedoc docs.

* [Localization] Modification some code about battle stat for localization.

* Corrections to failed generating typedoc docs.

* Corrections to change localized key for battle stat and level change description.

* Deleted battle-stat.ts file in Deutsch locales.

* Fixed build failure

* Move unit test file into localisation folder from battle folder.

* Fixed localization key

* Fixed build failure

* Fix merge conflict

* Fix merge conflict again

---------

Co-authored-by: Tempoanon <163687446+Tempo-anon@users.noreply.github.com>
Co-authored-by: Temps Ray <temps.ray@gmail.com>
This commit is contained in:
Lee ByungHoon 2024-06-16 11:25:18 +09:00 committed by GitHub
parent bf7eb52f85
commit 9b8f6f312b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 354 additions and 34 deletions

View File

@ -14,21 +14,21 @@ export enum BattleStat {
export function getBattleStatName(stat: BattleStat) {
switch (stat) {
case BattleStat.ATK:
return i18next.t("modifierType:TempBattleStatBoosterStatName.ATK");
return i18next.t("pokemonInfo:Stat.ATK");
case BattleStat.DEF:
return i18next.t("modifierType:TempBattleStatBoosterStatName.DEF");
return i18next.t("pokemonInfo:Stat.DEF");
case BattleStat.SPATK:
return i18next.t("modifierType:TempBattleStatBoosterStatName.SPATK");
return i18next.t("pokemonInfo:Stat.SPATK");
case BattleStat.SPDEF:
return i18next.t("modifierType:TempBattleStatBoosterStatName.SPDEF");
return i18next.t("pokemonInfo:Stat.SPDEF");
case BattleStat.SPD:
return i18next.t("modifierType:TempBattleStatBoosterStatName.SPD");
return i18next.t("pokemonInfo:Stat.SPD");
case BattleStat.ACC:
return i18next.t("modifierType:TempBattleStatBoosterStatName.ACC");
return i18next.t("pokemonInfo:Stat.ACC");
case BattleStat.EVA:
return i18next.t("modifierType:TempBattleStatBoosterStatName.EVA");
return i18next.t("pokemonInfo:Stat.EVA");
default:
return i18next.t("modifierType:TempBattleStatBoosterStatName.DEFAULT");
return "???";
}
}
@ -36,30 +36,30 @@ export function getBattleStatLevelChangeDescription(levels: integer, up: boolean
if (up) {
switch (levels) {
case 1:
return "rose";
return i18next.t("battle:statRose");
case 2:
return "sharply rose";
return i18next.t("battle:statSharplyRose");
case 3:
case 4:
case 5:
case 6:
return "rose drastically";
return i18next.t("battle:statRoseDrastically");
default:
return "won't go any higher";
return i18next.t("battle:statWontGoAnyHigher");
}
} else {
switch (levels) {
case 1:
return "fell";
return i18next.t("battle:statFell");
case 2:
return "harshly fell";
return i18next.t("battle:statHarshlyFell");
case 3:
case 4:
case 5:
case 6:
return "severely fell";
return i18next.t("battle:statSeverelyFell");
default:
return "won't go any lower";
return i18next.t("battle:statWontGoAnyLower");
}
}
}

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} setzt {{moveName}} ein!",
"drainMessage": "{{pokemonName}} wurde Energie abgesaugt",
"regainHealth": "KP von {{pokemonName}} wurden wieder aufgefrischt!",
"fainted": "{{pokemonNameWithAffix}} wurde besiegt!"
"fainted": "{{pokemonNameWithAffix}} wurde besiegt!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -14,6 +14,8 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEFshortened": "SpVert",
"SPD": "Initiative",
"SPDshortened": "Init",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} used {{moveName}}!",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!",
"fainted": "{{pokemonNameWithAffix}} fainted!"
"fainted": "{{pokemonNameWithAffix}} fainted!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "Sp. Def",
"SPDEFshortened": "SpDef",
"SPD": "Speed",
"SPDshortened": "Spd"
"SPDshortened": "Spd",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "¡{{pokemonNameWithAffix}} usó {{moveName}}!",
"drainMessage": "¡{{pokemonName}} tuvo su\nenergía absorbida!",
"regainHealth": "¡{{pokemonName}} recuperó\nPS!",
"fainted": "¡{{pokemonNameWithAffix}} se debilitó!"
"fainted": "¡{{pokemonNameWithAffix}} se debilitó!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "Def. Esp.",
"SPDEFshortened": "DefEsp",
"SPD": "Velocidad",
"SPDshortened": "Veloc."
"SPDshortened": "Veloc.",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} utilise\n{{moveName}} !",
"drainMessage": "Lénergie de {{pokemonName}}\nest drainée !",
"regainHealth": "{{pokemonName}} récupère\ndes PV !",
"fainted": "{{pokemonNameWithAffix}}\nest K.O. !"
"fainted": "{{pokemonNameWithAffix}}\nest K.O. !",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "Déf. Spé.",
"SPDEFshortened": "DéfSp",
"SPD": "Vitesse",
"SPDshortened": "Vit"
"SPDshortened": "Vit",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} usa {{moveName}}!",
"drainMessage": "Viene prelevata energia\n da{{pokemonName}}!",
"regainHealth": "{{pokemonName}} ha rigenerato\npunti salute!",
"fainted": "{{pokemonNameWithAffix}} non è più in\ngrado di combattere!"
"fainted": "{{pokemonNameWithAffix}} non è più in\ngrado di combattere!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "Dif. Sp.",
"SPDEFshortened": "DifSp",
"SPD": "Velocità",
"SPDshortened": "Vel"
"SPDshortened": "Vel",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -62,4 +62,12 @@ export const battle: SimpleTranslationEntries = {
"drainMessage": "{{pokemonName}}[[로]]부터\n체력을 흡수했다!",
"regainHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!",
"fainted": "{{pokemonNameWithAffix}}[[는]] 쓰러졌다!",
"statRose": "상승했다",
"statSharplyRose": "약간 상승했다",
"statRoseDrastically": "대폭 상승했다",
"statWontGoAnyHigher": "더 이상 상승할 수 없다",
"statFell": "떨어졌다",
"statHarshlyFell": "약간 떨어졌다",
"statSeverelyFell": "대폭 떨어졌다",
"statWontGoAnyLower": "더 이상 떨어질 수 없다",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "특수방어",
"SPDEFshortened": "특방",
"SPD": "스피드",
"SPDshortened": "스피드"
"SPDshortened": "스피드",
"ACC": "명중률",
"EVA": "회피율"
},
Type: {

View File

@ -62,4 +62,12 @@ export const battle: SimpleTranslationEntries = {
"drainMessage": "{{pokemonName}} teve sua\nenergia drenada!",
"regainHealth": "{{pokemonName}} recuperou\npontos de saúde!",
"fainted": "{{pokemonNameWithAffix}} desmaiou!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -27,7 +27,6 @@ import { menuUiHandler } from "./menu-ui-handler";
import { modifierType } from "./modifier-type";
import { move } from "./move";
import { nature } from "./nature";
import { partyUiHandler } from "./party-ui-handler";
import { pokeball } from "./pokeball";
import { pokemon } from "./pokemon";
import { pokemonInfo } from "./pokemon-info";
@ -39,6 +38,7 @@ import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial";
import { voucher } from "./voucher";
import { weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler";
export const ptBrConfig = {
ability: ability,
@ -82,5 +82,5 @@ export const ptBrConfig = {
trainerNames: trainerNames,
tutorial: tutorial,
voucher: voucher,
weather: weather,
weather: weather
};

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "Def. Esp.",
"SPDEFshortened": "DefEsp",
"SPD": "Veloc.",
"SPDshortened": "Veloc."
"SPDshortened": "Veloc.",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -61,5 +61,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} used {{moveName}}!",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!",
"fainted": "{{pokemonNameWithAffix}} fainted!"
"fainted": "{{pokemonNameWithAffix}} fainted!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "特防",
"SPDEFshortened": "特防",
"SPD": "速度",
"SPDshortened": "速度"
"SPDshortened": "速度",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {
@ -38,4 +40,4 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"FAIRY": "妖精",
"STELLAR": "星晶",
},
} as const;
} as const;

View File

@ -58,5 +58,13 @@ export const battle: SimpleTranslationEntries = {
"useMove": "{{pokemonNameWithAffix}} used {{moveName}}!",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!",
"fainted": "{{pokemonNameWithAffix}} fainted!"
"fainted": "{{pokemonNameWithAffix}} fainted!",
"statRose": "rose",
"statSharplyRose": "sharply rose",
"statRoseDrastically": "rose drastically",
"statWontGoAnyHigher": "won't go any higher",
"statFell": "fell",
"statHarshlyFell": "harshly fell",
"statSeverelyFell": "severely fell",
"statWontGoAnyLower": "won't go any lower",
} as const;

View File

@ -13,7 +13,9 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
"SPDEF": "特殊防禦",
"SPDEFshortened": "特防",
"SPD": "速度",
"SPDshortened": "速度"
"SPDshortened": "速度",
"ACC": "Accuracy",
"EVA": "Evasiveness"
},
Type: {

View File

@ -223,6 +223,7 @@ declare module "i18next" {
tutorial: SimpleTranslationEntries;
voucher: SimpleTranslationEntries;
weather: SimpleTranslationEntries;
battleStat: SimpleTranslationEntries;
};
}
}

View File

@ -0,0 +1,229 @@
import {beforeAll, describe, expect, it} from "vitest";
import { getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js";
import { BattleStat } from "#app/data/battle-stat.js";
import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info.js";
import { battle as enBattleStat } from "#app/locales/en/battle.js";
import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info.js";
import { battle as deBattleStat } from "#app/locales/de/battle.js";
import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info.js";
import { battle as esBattleStat } from "#app/locales/es/battle.js";
import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info.js";
import { battle as frBattleStat } from "#app/locales/fr/battle.js";
import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info.js";
import { battle as itBattleStat } from "#app/locales/it/battle.js";
import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info.js";
import { battle as koBattleStat } from "#app/locales/ko/battle.js";
import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info.js";
import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle.js";
import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info.js";
import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle.js";
import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info.js";
import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle.js";
import i18next, {initI18n} from "#app/plugins/i18n";
interface BattleStatTestUnit {
stat: BattleStat,
key: string
}
interface BattleStatLevelTestUnit {
levels: integer,
up: boolean,
key: string
}
function testBattleStatName(stat: BattleStat, expectMessage: string) {
const message = getBattleStatName(stat);
console.log(`message ${message}, expected ${expectMessage}`);
expect(message).toBe(expectMessage);
}
function testBattleStatLevelChangeDescription(levels: integer, up: boolean, expectMessage: string) {
const message = getBattleStatLevelChangeDescription(levels, up);
console.log(`message ${message}, expected ${expectMessage}`);
expect(message).toBe(expectMessage);
}
describe("Test for BattleStat Localization", () => {
const battleStatUnits: BattleStatTestUnit[] = [];
const battleStatLevelUnits: BattleStatLevelTestUnit[] = [];
beforeAll(() => {
const fontFaceSetMock = {
add: jest.fn(),
load: jest.fn().mockResolvedValue([]),
check: jest.fn().mockReturnValue(true),
ready: Promise.resolve(),
};
const proxyHandler = {
get: (target, prop) => {
if (prop in target) {
return target[prop];
} else {
return document.fonts[prop];
}
}
};
const fontsProxy = new Proxy(fontFaceSetMock, proxyHandler);
Object.defineProperty(document, "fonts", {
value: fontsProxy,
configurable: true,
});
initI18n();
battleStatUnits.push({ stat: BattleStat.ATK, key: "Stat.ATK" });
battleStatUnits.push({ stat: BattleStat.DEF, key: "Stat.DEF" });
battleStatUnits.push({ stat: BattleStat.SPATK, key: "Stat.SPATK" });
battleStatUnits.push({ stat: BattleStat.SPDEF, key: "Stat.SPDEF" });
battleStatUnits.push({ stat: BattleStat.SPD, key: "Stat.SPD" });
battleStatUnits.push({ stat: BattleStat.ACC, key: "Stat.ACC" });
battleStatUnits.push({ stat: BattleStat.EVA, key: "Stat.EVA" });
battleStatLevelUnits.push({ levels: 1, up: true, key: "statRose" });
battleStatLevelUnits.push({ levels: 2, up: true, key: "statSharplyRose" });
battleStatLevelUnits.push({ levels: 3, up: true, key: "statRoseDrastically" });
battleStatLevelUnits.push({ levels: 4, up: true, key: "statRoseDrastically" });
battleStatLevelUnits.push({ levels: 5, up: true, key: "statRoseDrastically" });
battleStatLevelUnits.push({ levels: 6, up: true, key: "statRoseDrastically" });
battleStatLevelUnits.push({ levels: 7, up: true, key: "statWontGoAnyHigher" });
battleStatLevelUnits.push({ levels: 1, up: false, key: "statFell" });
battleStatLevelUnits.push({ levels: 2, up: false, key: "statHarshlyFell" });
battleStatLevelUnits.push({ levels: 3, up: false, key: "statSeverelyFell" });
battleStatLevelUnits.push({ levels: 4, up: false, key: "statSeverelyFell" });
battleStatLevelUnits.push({ levels: 5, up: false, key: "statSeverelyFell" });
battleStatLevelUnits.push({ levels: 6, up: false, key: "statSeverelyFell" });
battleStatLevelUnits.push({ levels: 7, up: false, key: "statWontGoAnyLower" });
});
it("Test getBattleStatName() in English", async () => {
i18next.changeLanguage("en");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, enPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in English", async () => {
i18next.changeLanguage("en");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, enBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in Español", async () => {
i18next.changeLanguage("es");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, esPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in Español", async () => {
i18next.changeLanguage("es");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, esBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in Italiano", async () => {
i18next.changeLanguage("it");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, itPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in Italiano", async () => {
i18next.changeLanguage("it");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, itBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in Français", async () => {
i18next.changeLanguage("fr");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, frPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in Français", async () => {
i18next.changeLanguage("fr");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, frBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in Deutsch", async () => {
i18next.changeLanguage("de");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, dePokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in Deutsch", async () => {
i18next.changeLanguage("de");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, deBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in Português (BR)", async () => {
i18next.changeLanguage("pt-BR");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, ptBrPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in Português (BR)", async () => {
i18next.changeLanguage("pt-BR");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, ptBrBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in 简体中文", async () => {
i18next.changeLanguage("zh-CN");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, zhCnPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in 简体中文", async () => {
i18next.changeLanguage("zh-CN");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, zhCnBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in 繁體中文", async () => {
i18next.changeLanguage("zh-TW");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, zhTwPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in 繁體中文", async () => {
i18next.changeLanguage("zh-TW");
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, zhTwBattleStat[unit.key]);
});
});
it("Test getBattleStatName() in 한국어", async () => {
await i18next.changeLanguage("ko");
battleStatUnits.forEach(unit => {
testBattleStatName(unit.stat, koPokemonInfo[unit.key.split(".")[0]][unit.key.split(".")[1]]);
});
});
it("Test getBattleStatLevelChangeDescription() in 한국어", async () => {
i18next.changeLanguage("ko", () => {
battleStatLevelUnits.forEach(unit => {
testBattleStatLevelChangeDescription(unit.levels, unit.up, koBattleStat[unit.key]);
});
});
});
});