From 44a8f1d665cca636b0866219c713142f8f67c6ac Mon Sep 17 00:00:00 2001 From: Godlander Date: Sun, 9 Mar 2025 11:56:43 -0700 Subject: [PATCH] Somnia Bot: add talent scalings to archive char command (#2775) * add debug command * Update apps/somnia/src/commands/debug.ts Co-authored-by: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> * add links to github * fix hyperlink * Update debug.ts Co-authored-by: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> * Update debug.ts Co-authored-by: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> * user installed archive command * Update events.ts * fix asset url * fix formatting * add talent scalings * fix command name * mini ci * cleanup * update gen * Update archive.ts * Update char.ts --------- Co-authored-by: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> --- apps/somnia/src/commands/archive.ts | 44 +-- apps/somnia/src/commands/archive/char.ts | 275 +++++++++++++----- apps/somnia/src/commands/archive/weapon.ts | 6 +- apps/somnia/src/commands/go/calculator.ts | 15 +- libs/gi/consts/src/character.ts | 6 + .../assets/locales/chs/charNames_gen.json | 4 + .../assets/locales/cht/charNames_gen.json | 4 + .../assets/locales/de/charNames_gen.json | 4 + .../assets/locales/en/charNames_gen.json | 4 + .../assets/locales/es/charNames_gen.json | 4 + .../assets/locales/fr/charNames_gen.json | 4 + .../assets/locales/id/charNames_gen.json | 4 + .../assets/locales/it/charNames_gen.json | 4 + .../assets/locales/ja/charNames_gen.json | 4 + .../assets/locales/ko/charNames_gen.json | 4 + .../assets/locales/pt/charNames_gen.json | 4 + .../assets/locales/ru/charNames_gen.json | 4 + .../assets/locales/th/charNames_gen.json | 4 + .../assets/locales/tr/charNames_gen.json | 4 + .../assets/locales/vi/charNames_gen.json | 4 + .../src/executors/gen-locale/executor.ts | 9 +- libs/gi/i18n-node/src/i18n.ts | 4 +- libs/gi/stats/src/index.ts | 31 +- package.json | 1 + 24 files changed, 335 insertions(+), 116 deletions(-) diff --git a/apps/somnia/src/commands/archive.ts b/apps/somnia/src/commands/archive.ts index 72b0dc1e51..ab80342011 100644 --- a/apps/somnia/src/commands/archive.ts +++ b/apps/somnia/src/commands/archive.ts @@ -17,10 +17,10 @@ import { error } from '../lib/message' import { allArtifactSetKeys, - allCharacterKeys, + allCharacterSheetKeys, allWeaponKeys, type ArtifactSetKey, - type CharacterKey, + type CharacterSheetKey, type WeaponKey, } from '@genshin-optimizer/gi/consts' import { i18nInstance } from '@genshin-optimizer/gi/i18n-node' @@ -106,7 +106,7 @@ export const slashcommand = new SlashCommandBuilder() type ArchiveSubcommand = 'char' | 'weapon' | 'artifact' const archive = { - char: allCharacterKeys, + char: allCharacterSheetKeys, weapon: allWeaponKeys, artifact: allArtifactSetKeys, } @@ -114,11 +114,13 @@ function translate( namespace: string, key: string, lang = 'en', - object = false + object = false, + options?: { [key: string]: any } ): any { return i18nInstance.t(`${namespace}:${key}`, { returnObjects: object, lng: lang, + ...options, }) } export { translate } @@ -135,15 +137,20 @@ export const talentlist = { //clean tags from input //discord has no colored text, so just bold everything instead export function clean(s: string) { - //keep italic tags - s = s.replaceAll(/()+/g, '-# *') + //trim whitespace + s = s.trim() + //italics become small + s = s.replace(/([\s\S]*)\n([\s\S]*<\/i>)/g, (m) => + m.replace(/\n/g, '\n-# ') + ) + s = s.replaceAll(/()+/g, '*-# ') s = s.replaceAll(/(<\/i>)+/g, '*') //turn rest into bold s = s.replaceAll(/(<\/?\w+>)+/g, '**') //ignore
tags s = s.replaceAll(/<\w+\/>/g, '') //remove extra whitespace - return s.trim() + return s } export async function autocomplete(interaction: AutocompleteInteraction) { @@ -187,25 +194,25 @@ export async function autocomplete(interaction: AutocompleteInteraction) { async function archiveMessage( subcommand: ArchiveSubcommand, id: string, - lang: string, - arg: string + arg: string, + lang: string ) { //character archive if (subcommand === 'char') { - if (!archive[subcommand].includes(id as CharacterKey)) - throw 'invalid character name' - return await charArchive(id as CharacterKey, lang, arg) + if (!archive[subcommand].includes(id as CharacterSheetKey)) + throw 'Invalid character name' + return await charArchive(id as CharacterSheetKey, arg, lang) } //weapons archive else if (subcommand === 'weapon') { if (!archive[subcommand].includes(id as WeaponKey)) - throw 'invalid weapon name' - return await weaponArchive(id as WeaponKey, lang, arg) + throw 'Invalid weapon name' + return await weaponArchive(id as WeaponKey, arg, lang) } //artifacts archive else if (subcommand === 'artifact') { if (!archive[subcommand].includes(id as ArtifactSetKey)) - throw 'invalid artifact name' + throw 'Invalid artifact name' return await artifactArchive(id as ArtifactSetKey, lang) } else throw 'Invalid selection' } @@ -222,7 +229,7 @@ export async function run(interaction: ChatInputCommandInteraction) { arg = interaction.options.getString('refine', false) ?? '' try { - interaction.reply(await archiveMessage(subcommand, id, lang, arg)) + interaction.reply(await archiveMessage(subcommand, id, arg, lang)) } catch (e) { error(interaction, e) } @@ -234,11 +241,12 @@ export async function selectmenu( ) { const subcommand = args[1] as ArchiveSubcommand const id = args[2] - const lang = args[3] + //const oldarg = args[3] + const lang = args[4] const arg = interaction.values[0] try { - interaction.update(await archiveMessage(subcommand, id, lang, arg)) + interaction.update(await archiveMessage(subcommand, id, arg, lang)) } catch (e) { error(interaction, e) } diff --git a/apps/somnia/src/commands/archive/char.ts b/apps/somnia/src/commands/archive/char.ts index cbde89fdc8..4d0891537b 100644 --- a/apps/somnia/src/commands/archive/char.ts +++ b/apps/somnia/src/commands/archive/char.ts @@ -1,13 +1,17 @@ import { getUnitStr, valueString } from '@genshin-optimizer/common/util' import { AssetData, CommonAssetData } from '@genshin-optimizer/gi/assets-data' -import type { - CharacterKey, - LocationGenderedCharacterKey, - TravelerKey, +import { + sheetKeyToCharKey, + type CharacterSheetKey, + type LocationGenderedCharacterKey, } from '@genshin-optimizer/gi/consts' import { i18nInstance } from '@genshin-optimizer/gi/i18n-node' -import { getCharEle, getCharStat } from '@genshin-optimizer/gi/stats' -import type { MessageReaction } from 'discord.js' +import { + getCharEle, + getCharParam, + getCharStat, +} from '@genshin-optimizer/gi/stats' +import type { AnyComponentBuilder, MessageReaction } from 'discord.js' import { ActionRowBuilder, EmbedBuilder, @@ -16,43 +20,92 @@ import { } from 'discord.js' import { elementColors } from '../../assets/assets' import { giURL } from '../../lib/util' -import { clean, talentlist, translate } from '../archive' +import { clean, slashcommand, talentlist, translate } from '../archive' import { baseCharStats, getFixed } from '../go/calculator' function getEmbed( - id: CharacterKey, + id: CharacterSheetKey, namespace: string, - lang: string, - arg: string + arg: string, + lang: string ) { + const res: { embed: EmbedBuilder; components: AnyComponentBuilder[] } = { + embed: {} as EmbedBuilder, + components: [], + } + //parse level + let level = arg.length > 1 ? parseInt(arg.substring(1)) : NaN + + //talent level dropdown + if ('neq'.includes(arg[0])) { + if (isNaN(level)) level = 10 + + //create dropdown menu + const options = [] + for (const tl of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) { + const menu = new StringSelectMenuOptionBuilder() + .setLabel('Talent Level ' + tl) + .setValue(arg[0] + tl) + if (tl === level) menu.setDefault(true) + options.push(menu) + } + res.components = [ + new StringSelectMenuBuilder() + .setCustomId(`${slashcommand.name} char ${id} ${arg} ${lang} 1`) + .setPlaceholder('Talent Level ' + level) + .addOptions(options), + ] + } + //character profile - if (arg === 'p') return profileEmbed(id, namespace, lang) + if (arg === 'p') res.embed = profileEmbed(id, namespace, lang) //normal/charged/plunging attacks - else if (arg === 'n') return normalsEmbed(id, namespace, lang) + else if (arg[0] === 'n') res.embed = normalsEmbed(id, namespace, level, lang) //elemental skill - else if (arg === 'e') return skillEmbed(id, namespace, lang) + else if (arg[0] === 'e') res.embed = skillEmbed(id, namespace, level, lang) //elemental burst - else if (arg === 'q') return burstEmbed(id, namespace, lang) + else if (arg[0] === 'q') res.embed = burstEmbed(id, namespace, level, lang) //passives - else if (arg.match(/^a[14]?$/)) return passivesEmbed(id, namespace, lang, arg) + else if (arg[0] === 'a') res.embed = passivesEmbed(id, namespace, level, lang) //constellations - else if (arg.match(/^c[123456]?$/)) - return constellationsEmbed(id, namespace, lang, arg) - else throw 'Invalid talent name.' + else if (arg[0] === 'c') { + res.embed = constellationsEmbed(id, namespace, level, lang) + + //create dropdown menu + const options = [] + for (const cl of [0, 1, 2, 3, 4, 5, 6]) { + const label = cl ? 'Constellation ' + cl : 'All Constellations' + const value = cl ? 'c' + cl : 'c' + const menu = new StringSelectMenuOptionBuilder() + .setLabel(label) + .setValue(value) + if (value === arg) menu.setDefault(true) + options.push(menu) + } + const label = level ? 'Constellation ' + level : 'All Constellations' + res.components = [ + new StringSelectMenuBuilder() + .setCustomId(`${slashcommand.name} char ${id} ${arg} ${lang} 1`) + .setPlaceholder(label) + .addOptions(options), + ] + } else throw 'Invalid talent name.' + + return res } -function getAssets(id: CharacterKey) { - let genderedId: LocationGenderedCharacterKey | TravelerKey = id - if (id.includes('Traveler')) genderedId = 'TravelerM' - return AssetData.chars[genderedId] +function getAssets(id: CharacterSheetKey) { + if (id.includes('Traveler')) { + id = 'Traveler' + id.charAt(id.length - 1) + } + return AssetData.chars[id as LocationGenderedCharacterKey] } -function getName(id: CharacterKey, lang: string) { - if (id.includes('Traveler')) return id.replace('Traveler', 'Traveler (') + ')' - else return translate(`char_${id}_gen`, 'name', lang) +function getName(id: CharacterSheetKey, lang: string) { + return translate(`charNames_gen`, id, lang) } -function baseEmbed(id: CharacterKey, lang: string) { +function baseEmbed(id: CharacterSheetKey, lang: string) { const element = getCharEle(id) let icon = getAssets(id).icon if (!icon) icon = CommonAssetData.elemIcons[element] @@ -67,7 +120,47 @@ function baseEmbed(id: CharacterKey, lang: string) { .setColor(elementColors[element]) } -function profileEmbed(id: CharacterKey, namespace: string, lang: string) { +//get scalings +function talentFields( + namespace: string, + skill: string, + params: number[][], + level: number, + lang: string +) { + const scalings = Object.fromEntries( + params.map((hit, i) => [i, hit[level - 1]]) + ) + let text = '' + let val = '' + for (const index in scalings) { + text += translate(namespace, `${skill}.skillParams.${index}`, lang) + '\n' + val += + translate( + namespace, + `${skill}.skillParamsEncoding.${index}`, + lang, + false, + scalings + ) + '\n' + } + + //format to inline embed fields + return [ + { + name: ' ', + value: clean(text), + inline: true, + }, + { + name: ' ', + value: clean(val), + inline: true, + }, + ] +} + +function profileEmbed(id: CharacterSheetKey, namespace: string, lang: string) { const element = getCharEle(id) let text = '' //base stats @@ -79,14 +172,17 @@ function profileEmbed(id: CharacterKey, namespace: string, lang: string) { }) //description text += - '\n-# *' + - i18nInstance.t( - [ - `${namespace}:description`, - 'A traveler from another world who had their only kin taken away, forcing them to embark on a journey to find The Seven.', - ], - { lng: lang } - ) + + '\n*-# ' + + i18nInstance + .t( + [ + `${namespace}:description`, + 'A traveler from another world who had their only kin taken away, forcing them to embark on a journey to find The Seven.', + ], + { lng: lang } + ) + .trim() + .replaceAll('\n', '\n-# ') + '*' //make embed const embed = baseEmbed(id, lang) @@ -103,9 +199,22 @@ function profileEmbed(id: CharacterKey, namespace: string, lang: string) { return embed } -function normalsEmbed(id: CharacterKey, namespace: string, lang: string) { - const weapon = getCharStat(id).weaponType +function normalsEmbed( + id: CharacterSheetKey, + namespace: string, + level: number, + lang: string +) { const auto = translate(namespace, 'auto', lang, true) + const weapon = getCharStat(sheetKeyToCharKey(id)).weaponType + const scalings = talentFields( + namespace, + 'auto', + getCharParam(id).auto, + level, + lang + ) + //make embed return baseEmbed(id, lang) .setTitle(auto.name) .setDescription( @@ -119,46 +228,74 @@ function normalsEmbed(id: CharacterKey, namespace: string, lang: string) { ) ) .setThumbnail(giURL(CommonAssetData.normalIcons[weapon])) + .addFields(scalings) } -function skillEmbed(id: CharacterKey, namespace: string, lang: string) { +function skillEmbed( + id: CharacterSheetKey, + namespace: string, + level: number, + lang: string +) { const skill = translate(namespace, 'skill', lang, true) + const scalings = talentFields( + namespace, + 'skill', + getCharParam(id).skill, + level, + lang + ) const embed = baseEmbed(id, lang) .setTitle(skill.name) .setDescription(clean(Object.values(skill.description).flat().join('\n'))) + .addFields(scalings) const thumbnail = getAssets(id).skill if (thumbnail) embed.setThumbnail(giURL(thumbnail)) + return embed } -function burstEmbed(id: CharacterKey, namespace: string, lang: string) { +function burstEmbed( + id: CharacterSheetKey, + namespace: string, + level: number, + lang: string +) { const burst = translate(namespace, 'burst', lang, true) + const scalings = talentFields( + namespace, + 'burst', + getCharParam(id).burst, + level, + lang + ) const embed = baseEmbed(id, lang) .setTitle(burst.name) .setDescription(clean(Object.values(burst.description).flat().join('\n'))) + .addFields(scalings) const thumbnail = getAssets(id).burst if (thumbnail) embed.setThumbnail(giURL(thumbnail)) return embed } type Passives = 'passive1' | 'passive2' | 'passive3' | 'passive' -function selectPassive(arg: string): Passives[] { - if (arg.length > 1) { - if (arg[1] === '1') return ['passive1'] - if (arg[1] === '4') return ['passive2'] +function selectPassive(p: number): Passives[] { + if (p) { + if (p === 1) return ['passive1'] + if (p === 4) return ['passive2'] } return ['passive1', 'passive2', 'passive3', 'passive'] } function passivesEmbed( - id: CharacterKey, + id: CharacterSheetKey, namespace: string, - lang: string, - arg: string + level: number, + lang: string ) { let text = '' //select passives - const showPassives = selectPassive(arg) + const showPassives = selectPassive(level) //make embed for (const passiveId of showPassives) { const passive = translate(namespace, passiveId, lang, true) @@ -179,16 +316,16 @@ function passivesEmbed( } function constellationsEmbed( - id: CharacterKey, + id: CharacterSheetKey, namespace: string, - lang: string, - arg: string + level: number, + lang: string ) { let text = '' //select constellations const allCons = ['1', '2', '3', '4', '5', '6'] as const - const showCons = - arg.length > 1 ? allCons.filter((e) => e.includes(arg[1])) : allCons + if (level < 1 || level > 6) throw 'Invalid constellation.' + const showCons = level ? [allCons[level - 1]] : allCons for (const constellationId of showCons) { const constellation = translate( namespace, @@ -212,15 +349,13 @@ function constellationsEmbed( } export async function charArchive( - id: CharacterKey, - lang: string, - args: string + id: CharacterSheetKey, + arg: string, + lang: string ) { - const namespace = id.includes('Traveler') - ? `char_${id}M_gen` - : `char_${id}_gen` + const namespace = `char_${id}_gen` await i18nInstance.loadNamespaces(namespace) - const embed = getEmbed(id, namespace, lang, args) + const res = getEmbed(id, namespace, arg, lang) //create dropdown menu const options = [] @@ -228,20 +363,24 @@ export async function charArchive( const menu = new StringSelectMenuOptionBuilder() .setLabel(talent.name) .setValue(talent.value) - if (talent.value === args) menu.setDefault(true) + if (talent.value === arg[0]) menu.setDefault(true) options.push(menu) } - const components = new ActionRowBuilder().addComponents( - new StringSelectMenuBuilder() - .setCustomId(`archive char ${id} ${lang} ${args}`) - .setPlaceholder(talentlist[args[0] as keyof typeof talentlist].name) - .addOptions(options) - ) + const components = [ + new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder() + .setCustomId(`${slashcommand.name} char ${id} ${arg} ${lang} 0`) + .setPlaceholder(talentlist[arg[0] as keyof typeof talentlist].name) + .addOptions(options) + ), + ] + for (const component of res.components) + components.push(new ActionRowBuilder().addComponents(component)) return { content: '', - embeds: [embed], - components: [components], + embeds: [res.embed], + components: components, } } diff --git a/apps/somnia/src/commands/archive/weapon.ts b/apps/somnia/src/commands/archive/weapon.ts index 1542621e87..8bfc5992a1 100644 --- a/apps/somnia/src/commands/archive/weapon.ts +++ b/apps/somnia/src/commands/archive/weapon.ts @@ -15,7 +15,7 @@ import { } from 'discord.js' import { rarityColors } from '../../assets/assets' import { giURL } from '../../lib/util' -import { clean, translate } from '../archive' +import { clean, slashcommand, translate } from '../archive' import { getFixed } from '../go/calculator' const refinedisplay: Record = { @@ -39,14 +39,14 @@ function getDropdown(id: string, lang: string, refine: string) { return [ new ActionRowBuilder().addComponents( new StringSelectMenuBuilder() - .setCustomId(`archive weapon ${id} ${lang} ${refine}`) + .setCustomId(`${slashcommand.name} weapon ${id} ${lang} ${refine}`) .setPlaceholder(`Refinement ${refine}`) .addOptions(options) ), ] } -export async function weaponArchive(id: WeaponKey, lang: string, args: string) { +export async function weaponArchive(id: WeaponKey, args: string, lang: string) { const namespace = `weapon_${id}_gen` await i18nInstance.loadNamespaces(namespace) const msg: any = {} diff --git a/apps/somnia/src/commands/go/calculator.ts b/apps/somnia/src/commands/go/calculator.ts index a26f6438f1..334edd5040 100644 --- a/apps/somnia/src/commands/go/calculator.ts +++ b/apps/somnia/src/commands/go/calculator.ts @@ -1,8 +1,9 @@ -import type { - AscensionKey, - CharacterKey, - RefinementKey, - WeaponKey, +import { + sheetKeyToCharKey, + type AscensionKey, + type CharacterSheetKey, + type RefinementKey, + type WeaponKey, } from '@genshin-optimizer/gi/consts' import type { StatKey } from '@genshin-optimizer/gi/dm' import type { TagMapNodeEntries } from '@genshin-optimizer/gi/formula' @@ -22,7 +23,7 @@ export function getFixed(key: StatKey) { } export function baseCharStats( - id: CharacterKey, + id: CharacterSheetKey, level = 90, ascension: AscensionKey = 6 ) { @@ -31,7 +32,7 @@ export function baseCharStats( ...withMember( '0', ...charData({ - key: id, + key: sheetKeyToCharKey(id), level: level, ascension: ascension, constellation: 0, diff --git a/libs/gi/consts/src/character.ts b/libs/gi/consts/src/character.ts index 9540b0f377..323f822c08 100644 --- a/libs/gi/consts/src/character.ts +++ b/libs/gi/consts/src/character.ts @@ -247,6 +247,12 @@ export function locCharKeyToCharKey( return locKey as CharacterKey } +export function sheetKeyToCharKey(sheetKey: CharacterSheetKey): CharacterKey { + if (sheetKey.includes('Traveler')) + return sheetKey.slice(0, -1) as CharacterKey + else return sheetKey as CharacterKey +} + export function travelerElement(element: TravelerElementKey): TravelerKey { return ('Traveler' + element.toUpperCase().slice(0, 1) + diff --git a/libs/gi/dm-localization/assets/locales/chs/charNames_gen.json b/libs/gi/dm-localization/assets/locales/chs/charNames_gen.json index 75d4678b17..9f156ca86e 100644 --- a/libs/gi/dm-localization/assets/locales/chs/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/chs/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "荧 (岩元素)", "TravelerElectroF": "荧 (雷)", "TravelerDendroF": "荧 (草元素)", + "TravelerHydroF": "荧 (水元素)", + "TravelerPyroF": "荧 (火)", "TravelerAnemoM": "空 (风)", "TravelerGeoM": "空 (岩元素)", "TravelerElectroM": "空 (雷)", "TravelerDendroM": "空 (草元素)", + "TravelerHydroM": "空 (水元素)", + "TravelerPyroM": "空 (火)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/cht/charNames_gen.json b/libs/gi/dm-localization/assets/locales/cht/charNames_gen.json index 8990dcf631..4e0e3cc784 100644 --- a/libs/gi/dm-localization/assets/locales/cht/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/cht/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "熒 (岩元素)", "TravelerElectroF": "熒 (雷)", "TravelerDendroF": "熒 (草元素)", + "TravelerHydroF": "熒 (水元素)", + "TravelerPyroF": "熒 (火)", "TravelerAnemoM": "空 (風)", "TravelerGeoM": "空 (岩元素)", "TravelerElectroM": "空 (雷)", "TravelerDendroM": "空 (草元素)", + "TravelerHydroM": "空 (水元素)", + "TravelerPyroM": "空 (火)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/de/charNames_gen.json b/libs/gi/dm-localization/assets/locales/de/charNames_gen.json index 66251e9cee..76d1fc5a9d 100644 --- a/libs/gi/dm-localization/assets/locales/de/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/de/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Geo)", "TravelerElectroF": "Lumine (Elektro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Leer (Anemo)", "TravelerGeoM": "Leer (Geo)", "TravelerElectroM": "Leer (Elektro)", "TravelerDendroM": "Leer (Dendro)", + "TravelerHydroM": "Leer (Hydro)", + "TravelerPyroM": "Leer (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/en/charNames_gen.json b/libs/gi/dm-localization/assets/locales/en/charNames_gen.json index da49589bcb..20274c0834 100644 --- a/libs/gi/dm-localization/assets/locales/en/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/en/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Geo)", "TravelerElectroF": "Lumine (Electro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Aether (Anemo)", "TravelerGeoM": "Aether (Geo)", "TravelerElectroM": "Aether (Electro)", "TravelerDendroM": "Aether (Dendro)", + "TravelerHydroM": "Aether (Hydro)", + "TravelerPyroM": "Aether (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/es/charNames_gen.json b/libs/gi/dm-localization/assets/locales/es/charNames_gen.json index 1799a2b2ed..4bc38fe6ab 100644 --- a/libs/gi/dm-localization/assets/locales/es/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/es/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumina (Geo)", "TravelerElectroF": "Lumina (Electro)", "TravelerDendroF": "Lumina (Dendro)", + "TravelerHydroF": "Lumina (Hydro)", + "TravelerPyroF": "Lumina (Pyro)", "TravelerAnemoM": "Éter (Anemo)", "TravelerGeoM": "Éter (Geo)", "TravelerElectroM": "Éter (Electro)", "TravelerDendroM": "Éter (Dendro)", + "TravelerHydroM": "Éter (Hydro)", + "TravelerPyroM": "Éter (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/fr/charNames_gen.json b/libs/gi/dm-localization/assets/locales/fr/charNames_gen.json index 43ec93e0db..48226dd993 100644 --- a/libs/gi/dm-localization/assets/locales/fr/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/fr/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Géo)", "TravelerElectroF": "Lumine (Électro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Aether (Anémo)", "TravelerGeoM": "Aether (Géo)", "TravelerElectroM": "Aether (Électro)", "TravelerDendroM": "Aether (Dendro)", + "TravelerHydroM": "Aether (Hydro)", + "TravelerPyroM": "Aether (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/id/charNames_gen.json b/libs/gi/dm-localization/assets/locales/id/charNames_gen.json index da49589bcb..20274c0834 100644 --- a/libs/gi/dm-localization/assets/locales/id/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/id/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Geo)", "TravelerElectroF": "Lumine (Electro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Aether (Anemo)", "TravelerGeoM": "Aether (Geo)", "TravelerElectroM": "Aether (Electro)", "TravelerDendroM": "Aether (Dendro)", + "TravelerHydroM": "Aether (Hydro)", + "TravelerPyroM": "Aether (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/it/charNames_gen.json b/libs/gi/dm-localization/assets/locales/it/charNames_gen.json index 1dd699c213..6575c3f606 100644 --- a/libs/gi/dm-localization/assets/locales/it/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/it/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Geo)", "TravelerElectroF": "Lumine (Electro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Aether (Anemo)", "TravelerGeoM": "Aether (Geo)", "TravelerElectroM": "Aether (Electro)", "TravelerDendroM": "Aether (Dendro)", + "TravelerHydroM": "Aether (Hydro)", + "TravelerPyroM": "Aether (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/ja/charNames_gen.json b/libs/gi/dm-localization/assets/locales/ja/charNames_gen.json index 24d17cdcc9..cdf49374db 100644 --- a/libs/gi/dm-localization/assets/locales/ja/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/ja/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "蛍 (岩元素)", "TravelerElectroF": "蛍 (雷)", "TravelerDendroF": "蛍 (草元素)", + "TravelerHydroF": "蛍 (水元素)", + "TravelerPyroF": "蛍 (炎)", "TravelerAnemoM": "空 (風)", "TravelerGeoM": "空 (岩元素)", "TravelerElectroM": "空 (雷)", "TravelerDendroM": "空 (草元素)", + "TravelerHydroM": "空 (水元素)", + "TravelerPyroM": "空 (炎)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/ko/charNames_gen.json b/libs/gi/dm-localization/assets/locales/ko/charNames_gen.json index c97b92de22..8c6175cd5d 100644 --- a/libs/gi/dm-localization/assets/locales/ko/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/ko/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "루미네 (바위 원소)", "TravelerElectroF": "루미네 (번개)", "TravelerDendroF": "루미네 (풀 원소)", + "TravelerHydroF": "루미네 (물 원소)", + "TravelerPyroF": "루미네 (불)", "TravelerAnemoM": "아이테르 (바람)", "TravelerGeoM": "아이테르 (바위 원소)", "TravelerElectroM": "아이테르 (번개)", "TravelerDendroM": "아이테르 (풀 원소)", + "TravelerHydroM": "아이테르 (물 원소)", + "TravelerPyroM": "아이테르 (불)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/pt/charNames_gen.json b/libs/gi/dm-localization/assets/locales/pt/charNames_gen.json index f1766363ab..a5716d302b 100644 --- a/libs/gi/dm-localization/assets/locales/pt/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/pt/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Geo)", "TravelerElectroF": "Lumine (Electro)", "TravelerDendroF": "Lumine (Dendro)", + "TravelerHydroF": "Lumine (Hydro)", + "TravelerPyroF": "Lumine (Pyro)", "TravelerAnemoM": "Aether (Anemo)", "TravelerGeoM": "Aether (Geo)", "TravelerElectroM": "Aether (Electro)", "TravelerDendroM": "Aether (Dendro)", + "TravelerHydroM": "Aether (Hydro)", + "TravelerPyroM": "Aether (Pyro)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/ru/charNames_gen.json b/libs/gi/dm-localization/assets/locales/ru/charNames_gen.json index 25ff75dd6e..2261828be1 100644 --- a/libs/gi/dm-localization/assets/locales/ru/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/ru/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Люмин (Гео)", "TravelerElectroF": "Люмин (Электро)", "TravelerDendroF": "Люмин (Дендро)", + "TravelerHydroF": "Люмин (Гидро)", + "TravelerPyroF": "Люмин (Пиро)", "TravelerAnemoM": "Итэр (Анемо)", "TravelerGeoM": "Итэр (Гео)", "TravelerElectroM": "Итэр (Электро)", "TravelerDendroM": "Итэр (Дендро)", + "TravelerHydroM": "Итэр (Гидро)", + "TravelerPyroM": "Итэр (Пиро)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/th/charNames_gen.json b/libs/gi/dm-localization/assets/locales/th/charNames_gen.json index 7fef09ff03..37cef74de5 100644 --- a/libs/gi/dm-localization/assets/locales/th/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/th/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (หิน)", "TravelerElectroF": "Lumine (ไฟฟ้า)", "TravelerDendroF": "Lumine (ไม้)", + "TravelerHydroF": "Lumine (น้ำ)", + "TravelerPyroF": "Lumine (ไฟ)", "TravelerAnemoM": "Aether (ลม)", "TravelerGeoM": "Aether (หิน)", "TravelerElectroM": "Aether (ไฟฟ้า)", "TravelerDendroM": "Aether (ไม้)", + "TravelerHydroM": "Aether (น้ำ)", + "TravelerPyroM": "Aether (ไฟ)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/tr/charNames_gen.json b/libs/gi/dm-localization/assets/locales/tr/charNames_gen.json index 887d77e4b2..58461ac8a3 100644 --- a/libs/gi/dm-localization/assets/locales/tr/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/tr/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Toprak)", "TravelerElectroF": "Lumine (Elektrik)", "TravelerDendroF": "Lumine (Doğa)", + "TravelerHydroF": "Lumine (Su)", + "TravelerPyroF": "Lumine (Ateş)", "TravelerAnemoM": "Aether (Rüzgar)", "TravelerGeoM": "Aether (Toprak)", "TravelerElectroM": "Aether (Elektrik)", "TravelerDendroM": "Aether (Doğa)", + "TravelerHydroM": "Aether (Su)", + "TravelerPyroM": "Aether (Ateş)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/assets/locales/vi/charNames_gen.json b/libs/gi/dm-localization/assets/locales/vi/charNames_gen.json index 7880f96f68..fdc2365962 100644 --- a/libs/gi/dm-localization/assets/locales/vi/charNames_gen.json +++ b/libs/gi/dm-localization/assets/locales/vi/charNames_gen.json @@ -99,9 +99,13 @@ "TravelerGeoF": "Lumine (Nham)", "TravelerElectroF": "Lumine (Lôi)", "TravelerDendroF": "Lumine (Thảo)", + "TravelerHydroF": "Lumine (Thủy)", + "TravelerPyroF": "Lumine (Hỏa)", "TravelerAnemoM": "Aether (Phong)", "TravelerGeoM": "Aether (Nham)", "TravelerElectroM": "Aether (Lôi)", "TravelerDendroM": "Aether (Thảo)", + "TravelerHydroM": "Aether (Thủy)", + "TravelerPyroM": "Aether (Hỏa)", "Somnia": "Somnia" } \ No newline at end of file diff --git a/libs/gi/dm-localization/src/executors/gen-locale/executor.ts b/libs/gi/dm-localization/src/executors/gen-locale/executor.ts index 6747be3165..d517a14670 100644 --- a/libs/gi/dm-localization/src/executors/gen-locale/executor.ts +++ b/libs/gi/dm-localization/src/executors/gen-locale/executor.ts @@ -417,7 +417,14 @@ export default async function runExecutor(_options: GenLocaleExecutorSchema) { // Add the traveler variants to charNames_gen allGenderKeys.forEach((gender) => { - const keys = ['Anemo', 'Geo', 'Electro', 'Dendro'] as const + const keys = [ + 'Anemo', + 'Geo', + 'Electro', + 'Dendro', + 'Hydro', + 'Pyro', + ] as const keys.forEach((ele) => { const transLocGenKey = languageData[lang as Language].charNames[ diff --git a/libs/gi/i18n-node/src/i18n.ts b/libs/gi/i18n-node/src/i18n.ts index a2532e700f..114c0cb5f6 100644 --- a/libs/gi/i18n-node/src/i18n.ts +++ b/libs/gi/i18n-node/src/i18n.ts @@ -46,10 +46,10 @@ i18nInstance.init({ escapeValue: false, //react does interlopation already }, }) -i18n.services.formatter?.add('percent', (value, _lng, options) => { +i18nInstance.services.formatter?.add('percent', (value, _lng, options) => { return (value * 100).toFixed(options.fixed) }) -i18n.services.formatter?.add('fixed', (value, _lng, options) => { +i18nInstance.services.formatter?.add('fixed', (value, _lng, options) => { return value.toFixed(options.fixed) }) diff --git a/libs/gi/stats/src/index.ts b/libs/gi/stats/src/index.ts index 01843bcb87..51a29e3b1a 100644 --- a/libs/gi/stats/src/index.ts +++ b/libs/gi/stats/src/index.ts @@ -1,7 +1,9 @@ import type { ArtifactSetKey, CharacterKey, + CharacterSheetKey, ElementKey, + NonTravelerCharacterKey, WeaponKey, } from '@genshin-optimizer/gi/consts' import { charKeyToLocCharKey } from '@genshin-optimizer/gi/consts' @@ -19,23 +21,14 @@ const allStats = allStat_gen as AllStats export { allStats } -export function getCharEle(ck: CharacterKey): ElementKey { - switch (ck) { - case 'TravelerAnemo': - return 'anemo' - case 'TravelerGeo': - return 'geo' - case 'TravelerElectro': - return 'electro' - case 'TravelerDendro': - return 'dendro' - case 'TravelerHydro': - return 'hydro' - case 'TravelerPyro': - return 'pyro' - default: - return allStats.char.data[ck].ele! - } +export function getCharEle(ck: CharacterSheetKey | CharacterKey): ElementKey { + if (ck.startsWith('TravelerAnemo')) return 'anemo' + else if (ck.startsWith('TravelerGeo')) return 'geo' + else if (ck.startsWith('TravelerElectro')) return 'electro' + else if (ck.startsWith('TravelerDendro')) return 'dendro' + else if (ck.startsWith('TravelerHydro')) return 'hydro' + else if (ck.startsWith('TravelerPyro')) return 'pyro' + else return allStats.char.data[ck as NonTravelerCharacterKey].ele! } // Omit the ele, should use getCharEle to get char element. @@ -44,6 +37,10 @@ export function getCharStat(ck: CharacterKey): Omit { return allStats.char.data[locCharKey] } +export function getCharParam(ck: CharacterSheetKey) { + return allStats.char.skillParam[ck] +} + export function isCharMelee(ck: CharacterKey) { const charStat = getCharStat(ck) const weaponTypeKey = charStat.weaponType diff --git a/package.json b/package.json index 9da23f0099..beb706d7fe 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "private": true, "scripts": { "frontend": "nx serve frontend", + "somnia": "nx serve somnia", "build-all": "nx run-many -t build", "mini-ci": "nx format:write && nx affected -t typecheck && nx affected -t eslint:lint --max-warnings=0 && CI=true nx affected -t test", "reload-dm": "git submodule update --init",