Files
gcsim-translater/lib/translator/final-stats.ts
T
gunshiz 73efb513d2
CI / verify (push) Successful in 42s
CI / build-and-deploy (push) Successful in 1m57s
feat : add import from gcsim and roation translater
2026-09-13 15:22:31 +07:00

77 lines
3.7 KiB
TypeScript

import { calculateSubstats, MAIN_STATS } from "./generate";
import type { Build, CatalogBaseStat } from "./types";
export interface FinalStat {
key: string;
value: number;
percent?: boolean;
}
type Bonuses = Record<string, number>;
function addCatalogStats(stats: CatalogBaseStat[], bonuses: Bonuses) {
for (const stat of stats) {
const value = Number(stat.value);
if (!Number.isFinite(value)) continue;
const key = stat.key.toLowerCase().replaceAll(" ", "");
if (key === "em") bonuses.em += value;
else if (key === "er" || key === "energyrecharge%") bonuses.er += value / 100;
else if (key === "cr" || key === "critrate%") bonuses.cr += value / 100;
else if (key === "cd" || key === "critdmg%") bonuses.cd += value / 100;
else if (key === "hp%") bonuses["hp%"] += value / 100;
else if (key === "atk%") bonuses["atk%"] += value / 100;
else if (key === "def%") bonuses["def%"] += value / 100;
else if (key.endsWith("%")) {
const bonusKey = key.slice(0, -1);
bonuses[bonusKey] = (bonuses[bonusKey] ?? 0) + value / 100;
}
}
}
function baseValue(stats: CatalogBaseStat[], key: string): number {
return Number(stats.find((stat) => stat.key.toLowerCase() === key)?.value ?? 0);
}
export function calculateFinalStats(build: Build, characterStats: CatalogBaseStat[], weaponStats: CatalogBaseStat[]): FinalStat[] {
const bonuses: Bonuses = { em: 0, er: 1, cr: 0.05, cd: 0.5, "hp%": 0, "atk%": 0, "def%": 0 };
addCatalogStats(characterStats.filter((stat) => !["hp", "atk", "def"].includes(stat.key.toLowerCase())), bonuses);
addCatalogStats(weaponStats.filter((stat) => stat.key.toLowerCase() !== "atk"), bonuses);
const artifactStats: Bonuses = { hp: 4780, atk: 311, def: 0, em: 0, er: 0, cr: 0, cd: 0, "hp%": 0, "atk%": 0, "def%": 0 };
for (const key of build.importedStats ? [] : [build.sands, build.goblet, build.circlet]) {
const bonusKey = key.endsWith("%") && !["hp%", "atk%", "def%"].includes(key) ? key.slice(0, -1) : key;
artifactStats[bonusKey] = (artifactStats[bonusKey] ?? 0) + MAIN_STATS[key];
}
const substats = build.importedStats ?? calculateSubstats(build);
if (build.importedStats) { artifactStats.hp = 0; artifactStats.atk = 0; }
for (const [key, value] of Object.entries(substats)) {
const bonusKey = key.endsWith("%") && !["hp%", "atk%", "def%"].includes(key) ? key.slice(0, -1) : key;
artifactStats[bonusKey] = (artifactStats[bonusKey] ?? 0) + value;
}
for (const [key, value] of Object.entries(artifactStats)) bonuses[key] = (bonuses[key] ?? 0) + value;
const characterAtk = baseValue(characterStats, "atk");
const weaponAtk = baseValue(weaponStats, "atk");
const core: FinalStat[] = [
{ key: "HP", value: baseValue(characterStats, "hp") * (1 + bonuses["hp%"]) + bonuses.hp },
{ key: "ATK", value: (characterAtk + weaponAtk) * (1 + bonuses["atk%"]) + bonuses.atk },
{ key: "DEF", value: baseValue(characterStats, "def") * (1 + bonuses["def%"]) + bonuses.def },
{ key: "EM", value: bonuses.em },
{ key: "ER", value: bonuses.er, percent: true },
{ key: "CR", value: bonuses.cr, percent: true },
{ key: "CD", value: bonuses.cd, percent: true },
];
const known = new Set(["hp", "atk", "def", "em", "er", "cr", "cd", "hp%", "atk%", "def%"]);
const extras = Object.entries(bonuses)
.filter(([key, value]) => !known.has(key) && value !== 0)
.map(([key, value]) => ({ key, value, percent: true }));
return [...core, ...extras];
}
export function formatFinalStat(stat: FinalStat): string {
const value = stat.percent
? `${(stat.value * 100).toLocaleString("en-US", { maximumFractionDigits: 2 })}%`
: Math.round(stat.value).toLocaleString("en-US");
return `${stat.key.toUpperCase()} ${value}`;
}