This commit is contained in:
50
src/helper/calcData.ts
Normal file
50
src/helper/calcData.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { mapingStats } from "@/lib/constant"
|
||||
import { AffixDetail } from "@/types"
|
||||
|
||||
export function calcPromotion(level: number) {
|
||||
if (level < 20) {
|
||||
return 0
|
||||
}
|
||||
if (level < 30) {
|
||||
return 1
|
||||
}
|
||||
if (level < 40) {
|
||||
return 2
|
||||
}
|
||||
if (level < 50) {
|
||||
return 3
|
||||
}
|
||||
if (level < 60) {
|
||||
return 4
|
||||
}
|
||||
if (level < 70) {
|
||||
return 5
|
||||
}
|
||||
return 6
|
||||
}
|
||||
|
||||
|
||||
export function calcRarity(rarity: string) {
|
||||
if (rarity.includes("5")) {
|
||||
return 5
|
||||
}
|
||||
if (rarity.includes("4")) {
|
||||
return 4
|
||||
}
|
||||
if (rarity.includes("3")) {
|
||||
return 3
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
export const calcAffixBonus = (affix: AffixDetail, stepCount: number, rollCount: number) => {
|
||||
const data = affix;
|
||||
if (!data) return 0;
|
||||
if (mapingStats?.[data.property].unit === "%") {
|
||||
return ((data.base * rollCount + data.step * stepCount) * 100).toFixed(1);
|
||||
}
|
||||
if (mapingStats?.[data.property].name === "SPD") {
|
||||
return (data.base * rollCount + data.step * stepCount).toFixed(1);
|
||||
}
|
||||
return (data.base * rollCount + data.step * stepCount).toFixed(0);
|
||||
}
|
||||
87
src/helper/connect.ts
Normal file
87
src/helper/connect.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
"use client"
|
||||
import { SendDataThroughProxy, SendDataToServer } from "@/lib/api"
|
||||
import useConnectStore from "@/stores/connectStore"
|
||||
import useUserDataStore from "@/stores/userDataStore"
|
||||
import { converterToFreeSRJson } from "./converterToFreeSRJson"
|
||||
import { psResponseSchema } from "@/zod"
|
||||
|
||||
export const connectToPS = async (): Promise<{ success: boolean, message: string }> => {
|
||||
const {
|
||||
connectionType,
|
||||
privateType,
|
||||
serverUrl,
|
||||
username,
|
||||
password
|
||||
} = useConnectStore.getState()
|
||||
|
||||
let urlQuery = serverUrl
|
||||
if (!urlQuery.startsWith("http://") && !urlQuery.startsWith("https://")) {
|
||||
urlQuery = `http://${urlQuery}`
|
||||
}
|
||||
if (connectionType === "FireflyGo") {
|
||||
urlQuery = "http://localhost:21000/sync"
|
||||
} else if (connectionType === "Other" && privateType === "Server") {
|
||||
const response = await SendDataThroughProxy({data: {username, password, serverUrl, data: null, method: "POST"}})
|
||||
if (response instanceof Error) {
|
||||
return { success: false, message: response.message }
|
||||
} else if (response.error) {
|
||||
return { success: false, message: response.error }
|
||||
} else {
|
||||
const parsed = psResponseSchema.safeParse(response.data)
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Invalid response schema" }
|
||||
}
|
||||
return { success: true, message: "" }
|
||||
}
|
||||
}
|
||||
const response = await SendDataToServer(username, password, urlQuery, null)
|
||||
if (typeof response === "string") {
|
||||
return { success: false, message: response }
|
||||
} else if (response.status != 200) {
|
||||
return { success: false, message: response.message }
|
||||
} else {
|
||||
return { success: true, message: "" }
|
||||
}
|
||||
}
|
||||
|
||||
export const syncDataToPS = async (): Promise<{ success: boolean, message: string }> => {
|
||||
const {
|
||||
connectionType,
|
||||
privateType,
|
||||
serverUrl,
|
||||
username,
|
||||
password
|
||||
} = useConnectStore.getState()
|
||||
|
||||
const {avatars, battle_config} = useUserDataStore.getState()
|
||||
const data = converterToFreeSRJson(avatars, battle_config)
|
||||
|
||||
let urlQuery = serverUrl
|
||||
if (!urlQuery.startsWith("http://") && !urlQuery.startsWith("https://")) {
|
||||
urlQuery = `http://${urlQuery}`
|
||||
}
|
||||
if (connectionType === "FireflyGo") {
|
||||
urlQuery = "http://localhost:21000/sync"
|
||||
} else if (connectionType === "Other" && privateType === "Server") {
|
||||
const response = await SendDataThroughProxy({data: {username, password, serverUrl, data, method: "POST"}})
|
||||
if (response instanceof Error) {
|
||||
return { success: false, message: response.message }
|
||||
} else if (response.error) {
|
||||
return { success: false, message: response.error }
|
||||
} else {
|
||||
const parsed = psResponseSchema.safeParse(response.data)
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Invalid response schema" }
|
||||
}
|
||||
return { success: true, message: "" }
|
||||
}
|
||||
}
|
||||
const response = await SendDataToServer(username, password, urlQuery, data)
|
||||
if (typeof response === "string") {
|
||||
return { success: false, message: response }
|
||||
} else if (response.status != 200) {
|
||||
return { success: false, message: response.message }
|
||||
} else {
|
||||
return { success: true, message: "" }
|
||||
}
|
||||
}
|
||||
89
src/helper/convertData.ts
Normal file
89
src/helper/convertData.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { CharacterBasic, CharacterBasicRaw, LightConeBasic, LightConeBasicRaw, RelicBasic, RelicBasicEffect, RelicBasicRaw } from "@/types";
|
||||
|
||||
export function convertRelicSet(id: string, item: RelicBasicRaw): RelicBasic {
|
||||
let lang = new Map<string, string>([
|
||||
['en', item.en],
|
||||
['kr', item.kr],
|
||||
['cn', item.cn],
|
||||
['jp', item.jp]
|
||||
]);
|
||||
|
||||
const setRelic = new Map<string, RelicBasicEffect>();
|
||||
|
||||
Object.entries(item.set).forEach(([key, value]) => {
|
||||
setRelic.set(key, {
|
||||
ParamList: value.ParamList,
|
||||
lang: new Map<string, string>([
|
||||
['en', value.en],
|
||||
['kr', value.kr],
|
||||
['cn', value.cn],
|
||||
['jp', value.jp]
|
||||
])
|
||||
});
|
||||
});
|
||||
|
||||
const result: RelicBasic = {
|
||||
icon: item.icon,
|
||||
lang: lang,
|
||||
id: id,
|
||||
set: setRelic
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function convertLightcone(id: string, item: LightConeBasicRaw): LightConeBasic {
|
||||
|
||||
let lang = new Map<string, string>([
|
||||
['en', item.en],
|
||||
['kr', item.kr],
|
||||
['cn', item.cn],
|
||||
['jp', item.jp]
|
||||
]);
|
||||
const result: LightConeBasic = {
|
||||
rank: item.rank,
|
||||
baseType: item.baseType,
|
||||
desc: item.desc,
|
||||
lang: lang,
|
||||
id: id
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export function convertAvatar(id: string, item: CharacterBasicRaw): CharacterBasic {
|
||||
|
||||
let lang = new Map<string, string>([
|
||||
['en', item.en],
|
||||
['kr', item.kr],
|
||||
['cn', item.cn],
|
||||
['jp', item.jp]
|
||||
]);
|
||||
let text = ""
|
||||
if (Number(id) % 2 === 0 && Number(id) > 8000) {
|
||||
text = `Female ${item.damageType} MC`
|
||||
} else if (Number(id) > 8000) {
|
||||
text = `Male ${item.damageType} MC`
|
||||
}
|
||||
if (text !== "") {
|
||||
lang.set("en", text)
|
||||
lang.set("kr", text)
|
||||
lang.set("cn", text)
|
||||
lang.set("jp", text)
|
||||
}
|
||||
const result: CharacterBasic = {
|
||||
release: item.release,
|
||||
icon: item.icon,
|
||||
rank: item.rank,
|
||||
baseType: item.baseType,
|
||||
damageType: item.damageType,
|
||||
desc: item.desc,
|
||||
lang: lang,
|
||||
id: id
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
100
src/helper/converterToAvatarStore.ts
Normal file
100
src/helper/converterToAvatarStore.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, CharacterDetail, EnkaResponse, FreeSRJson, RelicStore } from "@/types";
|
||||
|
||||
function safeNumber(val: any, fallback = 0): number {
|
||||
const num = Number(val);
|
||||
return Number.isFinite(num) && num !== 0 ? num : fallback;
|
||||
}
|
||||
|
||||
export function converterToAvatarStore(data: Record<string, CharacterDetail>): { [key: string]: AvatarStore } {
|
||||
return Object.fromEntries(
|
||||
Object.entries(data).map(([key, value]) => [
|
||||
key,
|
||||
{
|
||||
owner_uid: 0,
|
||||
avatar_id: Number(key),
|
||||
data: {
|
||||
rank: 0,
|
||||
skills: Object.entries(value.SkillTrees).reduce((acc, [pointName, dataPointEntry]) => {
|
||||
const firstEntry = Object.values(dataPointEntry)[0];
|
||||
if (firstEntry) {
|
||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, number>)
|
||||
},
|
||||
level: 80,
|
||||
promotion: 6,
|
||||
techniques: [],
|
||||
sp_max: safeNumber(value.SPNeed, 100),
|
||||
can_change_sp: safeNumber(value.SPNeed) !== 0,
|
||||
sp_value: Math.ceil(safeNumber(value.SPNeed) / 2),
|
||||
profileSelect: 0,
|
||||
enhanced: "",
|
||||
profileList: [{
|
||||
profile_name: "Default",
|
||||
lightcone: null,
|
||||
relics: {} as Record<string, RelicStore>
|
||||
} as AvatarProfileStore]
|
||||
}
|
||||
])
|
||||
) as { [key: string]: AvatarStore }
|
||||
}
|
||||
|
||||
export function converterOneEnkaDataToAvatarStore(data: AvatarEnkaDetail, count: number): AvatarProfileStore | null {
|
||||
if (!data.equipment && (!data.relicList || data.relicList.length === 0)) return null
|
||||
const profile: AvatarProfileStore = {
|
||||
profile_name: `Enka Profile ${count}`,
|
||||
lightcone: {
|
||||
level: data.equipment.level,
|
||||
item_id: data.equipment.tid,
|
||||
rank: data.equipment.rank,
|
||||
promotion: data.equipment.promotion,
|
||||
},
|
||||
relics: Object.fromEntries(data.relicList.map((relic) => [relic.tid.toString()[relic.tid.toString().length - 1], {
|
||||
level: relic.level,
|
||||
relic_id: relic.tid,
|
||||
relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10),
|
||||
main_affix_id: relic.mainAffixId,
|
||||
sub_affixes: relic.subAffixList.map((subAffix) => ({
|
||||
sub_affix_id: subAffix.affixId,
|
||||
count: subAffix.cnt,
|
||||
step: subAffix.step ?? 0
|
||||
}))
|
||||
}]))
|
||||
}
|
||||
return profile
|
||||
}
|
||||
|
||||
|
||||
export function converterOneFreeSRDataToAvatarStore(data: FreeSRJson, count: number , avatar_id: number): AvatarProfileStore | null {
|
||||
const lightcone = data.lightcones.find((lightcone) => lightcone.equip_avatar === avatar_id)
|
||||
const relics = data.relics.filter((relic) => relic.equip_avatar === avatar_id)
|
||||
if (!lightcone && (!relics || relics.length === 0)) return null
|
||||
const relicsMap = {} as Record<string, RelicStore>
|
||||
|
||||
relics.forEach((relic) => {
|
||||
relicsMap[relic.relic_id.toString()[relic.relic_id.toString().length - 1]] = {
|
||||
level: relic.level,
|
||||
relic_id: relic.relic_id,
|
||||
relic_set_id: relic.relic_set_id,
|
||||
main_affix_id: relic.main_affix_id,
|
||||
sub_affixes: relic.sub_affixes.map((subAffix) => ({
|
||||
sub_affix_id: subAffix.sub_affix_id,
|
||||
count: subAffix.count,
|
||||
step: subAffix.step ?? 0
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
const profile: AvatarProfileStore = {
|
||||
profile_name: `FreeSR Profile ${count}`,
|
||||
lightcone: {
|
||||
level: lightcone?.level ?? 0,
|
||||
item_id: lightcone?.item_id ?? 0,
|
||||
rank: lightcone?.rank ?? 0,
|
||||
promotion: lightcone?.promotion ?? 0,
|
||||
},
|
||||
relics: relicsMap
|
||||
}
|
||||
return profile
|
||||
}
|
||||
71
src/helper/converterToFreeSRJson.ts
Normal file
71
src/helper/converterToFreeSRJson.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { AvatarJson, AvatarStore, BattleConfigJson, BattleConfigStore, FreeSRJson, LightconeJson, RelicJson } from "@/types";
|
||||
import useUserDataStore from "@/stores/userDataStore";
|
||||
|
||||
export function converterToFreeSRJson(avatars: Record<string, AvatarStore>, battle_config: BattleConfigStore): FreeSRJson {
|
||||
const lightcones: LightconeJson[] = []
|
||||
const relics: RelicJson[] = []
|
||||
const battleJson: BattleConfigJson = {
|
||||
battle_type: battle_config.battle_type,
|
||||
blessings: battle_config.blessings,
|
||||
custom_stats: battle_config.custom_stats,
|
||||
cycle_count: battle_config.cycle_count,
|
||||
stage_id: battle_config.stage_id,
|
||||
path_resonance_id: battle_config.path_resonance_id,
|
||||
monsters: battle_config.monsters,
|
||||
}
|
||||
const avatarsJson: { [key: string]: AvatarJson } = {}
|
||||
let internalUidLightcone = 0
|
||||
let internalUidRelic = 0
|
||||
Object.entries(avatars).forEach(([avatarId, avatar]) => {
|
||||
avatarsJson[avatarId] = {
|
||||
owner_uid: avatar.owner_uid,
|
||||
avatar_id: avatar.avatar_id,
|
||||
data: avatar.data,
|
||||
level: avatar.level,
|
||||
promotion: avatar.promotion,
|
||||
techniques: avatar.techniques,
|
||||
sp_value: avatar.sp_value,
|
||||
sp_max: avatar.sp_max,
|
||||
}
|
||||
const currentProfile = avatar.profileList[avatar.profileSelect]
|
||||
if (currentProfile.lightcone && currentProfile.lightcone.item_id !== 0) {
|
||||
const newLightcone: LightconeJson = {
|
||||
level: currentProfile.lightcone.level,
|
||||
item_id: currentProfile.lightcone.item_id,
|
||||
rank: currentProfile.lightcone.rank,
|
||||
promotion: currentProfile.lightcone.promotion,
|
||||
internal_uid: internalUidLightcone,
|
||||
equip_avatar: avatar.avatar_id,
|
||||
}
|
||||
internalUidLightcone++
|
||||
lightcones.push(newLightcone)
|
||||
}
|
||||
|
||||
if (currentProfile.relics) {
|
||||
["1", "2", "3", "4", "5", "6"].forEach(slot => {
|
||||
const relic = currentProfile.relics[slot]
|
||||
if (relic && relic.relic_id !== 0) {
|
||||
const newRelic: RelicJson = {
|
||||
level: relic.level,
|
||||
relic_id: relic.relic_id,
|
||||
relic_set_id: relic.relic_set_id,
|
||||
main_affix_id: relic.main_affix_id,
|
||||
sub_affixes: relic.sub_affixes,
|
||||
internal_uid: internalUidRelic,
|
||||
equip_avatar: avatar.avatar_id,
|
||||
}
|
||||
internalUidRelic++
|
||||
relics.push(newRelic)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return {
|
||||
lightcones,
|
||||
relics,
|
||||
avatars: avatarsJson,
|
||||
battle_config: battleJson,
|
||||
}
|
||||
}
|
||||
15
src/helper/getAvatarNotExist.ts
Normal file
15
src/helper/getAvatarNotExist.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
import useUserDataStore from "@/stores/userDataStore";
|
||||
import useAvatarStore from "@/stores/avatarStore";
|
||||
import { CharacterDetail } from "@/types";
|
||||
|
||||
export function getAvatarNotExist(): Record<string, CharacterDetail> {
|
||||
const { avatars } = useUserDataStore.getState()
|
||||
const { mapAvatarInfo } = useAvatarStore.getState()
|
||||
const listAvatarId = Object.keys(avatars)
|
||||
const listAvatarNotExist = Object.keys(mapAvatarInfo).filter((avatarId) => !listAvatarId.includes(avatarId))
|
||||
return listAvatarNotExist.reduce((acc, avatarId) => {
|
||||
acc[avatarId] = mapAvatarInfo[avatarId]
|
||||
return acc
|
||||
}, {} as Record<string, CharacterDetail>)
|
||||
}
|
||||
45
src/helper/getName.ts
Normal file
45
src/helper/getName.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { listCurrentLanguage } from "@/lib/constant";
|
||||
import { CharacterBasic, LightConeBasic } from "@/types";
|
||||
|
||||
|
||||
export function getNameChar(locale: string, data: CharacterBasic | undefined): string {
|
||||
if (!data) {
|
||||
return ""
|
||||
}
|
||||
if (!listCurrentLanguage.hasOwnProperty(locale)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
let text = data.lang.get(listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase()) ?? "";
|
||||
if (!text) {
|
||||
text = data.lang.get("en") ?? "";
|
||||
}
|
||||
if (Number(data.id) % 2 === 0 && Number(data.id) > 8000) {
|
||||
text = `Female ${data.damageType} MC`
|
||||
} else if (Number(data.id) > 8000) {
|
||||
text = `Male ${data.damageType} MC`
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
export function getNameLightcone(locale: string, data: LightConeBasic | undefined): string {
|
||||
if (!data) {
|
||||
return ""
|
||||
}
|
||||
if (!listCurrentLanguage.hasOwnProperty(locale)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
let text = data.lang.get(listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase()) ?? "";
|
||||
if (!text) {
|
||||
text = data.lang.get("en") ?? "";
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
export function parseRuby(text: string): string {
|
||||
const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs;
|
||||
return text.replace(rubyRegex, (_match, furigana, kanji) => {
|
||||
return `<ruby>${kanji}<rt>${furigana}</rt></ruby>`;
|
||||
});
|
||||
}
|
||||
23
src/helper/getSkillTree.ts
Normal file
23
src/helper/getSkillTree.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import useAvatarStore from "@/stores/avatarStore"
|
||||
import useUserDataStore from "@/stores/userDataStore"
|
||||
|
||||
export function getSkillTree(enhanced: string) {
|
||||
const { avatarSelected, mapAvatarInfo } = useAvatarStore.getState()
|
||||
|
||||
if (!avatarSelected) return null;
|
||||
if (enhanced != "") return Object.entries(mapAvatarInfo[avatarSelected.id || ""]?.Enhanced[enhanced].SkillTrees || {}).reduce((acc, [pointName, dataPointEntry]) => {
|
||||
const firstEntry = Object.values(dataPointEntry)[0];
|
||||
if (firstEntry) {
|
||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, number>)
|
||||
|
||||
return Object.entries(mapAvatarInfo[avatarSelected.id || ""]?.SkillTrees).reduce((acc, [pointName, dataPointEntry]) => {
|
||||
const firstEntry = Object.values(dataPointEntry)[0];
|
||||
if (firstEntry) {
|
||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
}
|
||||
9
src/helper/index.ts
Normal file
9
src/helper/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export * from "./getName"
|
||||
export * from "./replaceByParam"
|
||||
export * from "./converterToAvatarStore"
|
||||
export * from "./getAvatarNotExist"
|
||||
export * from "./calcData"
|
||||
export * from "./random"
|
||||
export * from "./json"
|
||||
export * from "./convertData"
|
||||
export * from "./connect"
|
||||
14
src/helper/json.ts
Normal file
14
src/helper/json.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function downloadJson(fileName: string, data: any) {
|
||||
const json = JSON.stringify(data, null, 2)
|
||||
const blob = new Blob([json], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `${fileName}.json`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
27
src/helper/random.ts
Normal file
27
src/helper/random.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export function randomPartition(sum: number, parts: number): number[] {
|
||||
let raw = Array.from({ length: parts }, () => Math.random());
|
||||
let total = raw.reduce((a, b) => a + b, 0);
|
||||
let result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1);
|
||||
let diff = sum - result.reduce((a, b) => a + b, 0);
|
||||
while (diff !== 0) {
|
||||
for (let i = 0; i < result.length && diff !== 0; i++) {
|
||||
if (diff > 0) {
|
||||
result[i]++;
|
||||
diff--;
|
||||
} else if (result[i] > 1) {
|
||||
result[i]--;
|
||||
diff++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function randomStep(x: number): number {
|
||||
let total = 0;
|
||||
for (let i = 0; i < x; i++) {
|
||||
total += Math.floor(Math.random() * 3);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
26
src/helper/replaceByParam.ts
Normal file
26
src/helper/replaceByParam.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export function replaceByParam(desc: string, params: number[]): string {
|
||||
desc = desc.replace(/<color=#[0-9a-fA-F]{8}>(.*?)<\/color>/g, (match, inner) => {
|
||||
const colorCode = match.match(/#[0-9a-fA-F]{8}/)?.[0] ?? "#ffffff";
|
||||
const processed = inner.replace(/#(\d+)\[i\](%)?/g, (_: string, index: string, percent: string | undefined) => {
|
||||
const i = parseInt(index, 10) - 1;
|
||||
const value = params[i];
|
||||
if (value === undefined) return "";
|
||||
return percent ? `${(value * 100).toFixed(0)}%` : `${value}`;
|
||||
}).replace(/<unbreak>(.*?)<\/unbreak>/g, "$1");
|
||||
|
||||
return `<span style="color:${colorCode}">${processed}</span>`;
|
||||
});
|
||||
|
||||
desc = desc.replace(/<unbreak>#(\d+)\[i\](%)?<\/unbreak>/g, (_: string, index: string, percent: string | undefined) => {
|
||||
const i = parseInt(index, 10) - 1;
|
||||
const value = params[i];
|
||||
if (value === undefined) return "";
|
||||
return percent ? `${(value * 100).toFixed(0)}%` : `${value}`;
|
||||
});
|
||||
|
||||
desc = desc.replace(/<unbreak>(\d+)<\/unbreak>/g, (_, number) => number);
|
||||
|
||||
desc = desc.replaceAll("\\n", "<br></br>");
|
||||
|
||||
return desc;
|
||||
}
|
||||
Reference in New Issue
Block a user