diff --git a/src/components/importBar/enka.tsx b/src/components/importBar/enka.tsx
index e7b8019..c9cfc37 100644
--- a/src/components/importBar/enka.tsx
+++ b/src/components/importBar/enka.tsx
@@ -120,7 +120,7 @@ export default function EnkaImport() {
}
const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length)
if (newProfile) {
- newAvatar.profileList.push()
+ newAvatar.profileList.push(newProfile)
newAvatar.profileSelect = newAvatar.profileList.length - 1
}
setAvatar(newAvatar)
diff --git a/src/components/relicBar/index.tsx b/src/components/relicBar/index.tsx
index e39de4c..29fb8cd 100644
--- a/src/components/relicBar/index.tsx
+++ b/src/components/relicBar/index.tsx
@@ -6,7 +6,7 @@ import React, { useEffect, useMemo } from 'react';
import SelectCustomImage from '../select/customSelectImage';
import { calcAffixBonus, randomPartition, randomStep, replaceByParam } from '@/helper';
import useAffixStore from '@/stores/affixStore';
-import { mapingStats } from '@/lib/constant';
+import { mappingStats } from '@/constant/constant';
import useAvatarStore from '@/stores/avatarStore';
import useModelStore from '@/stores/modelStore';
import useRelicMakerStore from '@/stores/relicMakerStore';
@@ -118,7 +118,7 @@ export default function RelicMaker() {
const data = affixSet[selectedMainStat];
if (!data) return 0;
- const stat = mapingStats?.[data.property];
+ const stat = mappingStats?.[data.property];
if (!stat) return 0;
const value = data.base + data.step * selectedRelicLevel;
@@ -257,8 +257,8 @@ export default function RelicMaker() {
({
value: key,
- label: mapingStats[value.property].name + " " + mapingStats[value.property].unit,
- imageUrl: mapingStats[value.property].icon
+ label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
+ imageUrl: mappingStats[value.property].icon
}))}
excludeSet={[]}
selectedCustomSet={selectedMainStat}
@@ -361,13 +361,13 @@ export default function RelicMaker() {
({
value: key,
- label: mapingStats[value.property].name + " " + mapingStats[value.property].unit,
- imageUrl: mapingStats[value.property].icon
+ label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
+ imageUrl: mappingStats[value.property].icon
}))}
excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({
value: key,
- label: mapingStats[value.property].name + " " + mapingStats[value.property].unit,
- imageUrl: mapingStats[value.property].icon
+ label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
+ imageUrl: mappingStats[value.property].icon
}))}
selectedCustomSet={v.affixId}
placeholder={transI18n("selectASubStat")}
@@ -378,7 +378,7 @@ export default function RelicMaker() {
{/* Current Value */}
+{ }
-
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mapingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
+
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
{/* Roll Values */}
@@ -387,19 +387,19 @@ export default function RelicMaker() {
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
className="btn btn-sm bg-white text-slate-800 hover:bg-gray-200 border-0"
>
- {calcAffixBonus(subAffixOptions[v.affixId], 0 , v.rollCount + 1)}{mapingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
+ {calcAffixBonus(subAffixOptions[v.affixId], 0 , v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
diff --git a/src/components/skillsInfo/index.tsx b/src/components/skillsInfo/index.tsx
new file mode 100644
index 0000000..889562f
--- /dev/null
+++ b/src/components/skillsInfo/index.tsx
@@ -0,0 +1,326 @@
+"use client"
+
+import { useTranslations } from "next-intl";
+import useAvatarStore from "@/stores/avatarStore";
+import { useCallback, useMemo } from "react";
+import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
+import useUserDataStore from "@/stores/userDataStore";
+import useLocaleStore from "@/stores/localeStore";
+import Image from "next/image";
+import { replaceByParam } from "@/helper";
+import { mappingStats } from "@/constant/constant";
+import { StatusAddType } from "@/types";
+import cloneDeep from "lodash/cloneDeep";
+import { toast } from "react-toastify";
+
+export default function SkillsInfo() {
+ const transI18n = useTranslations("DataPage")
+ const { theme } = useLocaleStore()
+ const { avatarSelected, mapAvatarInfo, skillSelected, setSkillSelected } = useAvatarStore()
+ const { avatars, setAvatar } = useUserDataStore()
+
+ const traceButtons = useMemo(() => {
+ if (!avatarSelected) return
+ return traceButtonsInfo[avatarSelected.baseType]
+ }, [avatarSelected])
+
+ const avatarInfo = useMemo(() => {
+ if (!avatarSelected) return
+ return mapAvatarInfo[avatarSelected.id]
+ }, [avatarSelected, mapAvatarInfo])
+
+
+
+ const avatarData = useMemo(() => {
+ if (!avatarSelected) return
+ return avatars[avatarSelected.id]
+ }, [avatarSelected, avatars])
+
+ const avatarSkillTree = useMemo(() => {
+ if (!avatarSelected || !avatars[avatarSelected.id]) return {}
+ if (avatars[avatarSelected.id].enhanced) {
+ return avatarInfo?.Enhanced[avatars[avatarSelected.id].enhanced.toString()].SkillTrees || {}
+ }
+ return avatarInfo?.SkillTrees || {}
+ }, [avatarSelected, avatarInfo, avatars])
+
+ const skillInfo = useMemo(() => {
+ if (!avatarSelected || !skillSelected) return
+ return avatarSkillTree?.[skillSelected || ""]?.["1"]
+ }, [avatarSelected, avatarSkillTree, skillSelected])
+
+ const getImageSkill = useCallback((icon: string | undefined, status: StatusAddType | undefined) => {
+ if (!icon) return
+ if (icon.startsWith("SkillIcon")) {
+ if (Number(avatarSelected?.id) > 8000 && Number(avatarSelected?.id) % 2 === 0) {
+ return `https://homdgcat.wiki/images/skillicons/avatar/${Number(avatarSelected?.id) - 1}/${icon.replaceAll(avatarSelected?.id || "", (Number(avatarSelected?.id) - 1).toString())}`
+ }
+ return `https://homdgcat.wiki/images/skillicons/avatar/${avatarSelected?.id}/${icon}`
+ } else if (status && mappingStats[status.PropertyType]) {
+ return mappingStats[status.PropertyType].icon
+ }
+ else if (icon.startsWith("Icon")) {
+ return `https://api.hakush.in/hsr/UI/trace/${icon.replace(".png", ".webp")}`
+ }
+ }, [avatarSelected])
+
+ const getTraceBuffDisplay = useCallback((status: StatusAddType) => {
+ const dataDisplay = mappingStats[status.PropertyType]
+ if (!dataDisplay) return ""
+ if (dataDisplay.unit === "%") {
+ return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}`
+ }
+ if (dataDisplay.name === "SPD") {
+ return `${status.Value.toFixed(1)}${dataDisplay.unit}`
+ }
+ return `${status.Value.toFixed(0)}${dataDisplay.unit}`
+ }, [])
+
+ const dataLevelUpSkill = useMemo(() => {
+ const skillIds: number[] = skillInfo?.LevelUpSkillID || []
+ if (!avatarSelected || !avatarInfo || !avatarData) return
+ let result = Object.values(avatarInfo.Skills || {})?.filter((skill) => skillIds.includes(skill.Id))
+ if (avatarData.enhanced) {
+ result = Object.values(avatarInfo.Enhanced[avatarData.enhanced.toString()].Skills || {})?.filter((skill) => skillIds.includes(skill.Id))
+ }
+ if (result && result.length > 0) {
+ return {
+ isServant: false,
+ data: result,
+ servantData: null,
+ }
+ }
+ const resultServant = Object.entries(avatarInfo.Memosprite?.Skills || {})
+ ?.filter(([skillId]) => skillIds.includes(Number(skillId)))
+ ?.map(([skillId, skillData]) => ({
+ Id: Number(skillId),
+ ...skillData,
+ }))
+ if (resultServant && resultServant.length > 0) {
+ return {
+ isServant: true,
+ data: resultServant,
+ servantData: avatarInfo.Memosprite,
+ }
+ }
+ return undefined
+ }, [skillInfo?.LevelUpSkillID, avatarSelected, avatarInfo, avatarData])
+
+
+ const handlerMaxAll = () => {
+ if (!avatarInfo || !avatarData || !avatarSkillTree) {
+ toast.error(transI18n("maxAllFailed"))
+ return
+ }
+ const newData = cloneDeep(avatarData)
+ newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => {
+ const firstEntry = Object.values(dataPointEntry)[0];
+ if (firstEntry) {
+ acc[firstEntry.PointID] = firstEntry.MaxLevel;
+ }
+ return acc;
+ }, {} as Record
)
+ toast.success(transI18n("maxAllSuccess"))
+ setAvatar(newData)
+ }
+
+ const handlerChangeStatusTrace = (status: boolean) => {
+ if (!avatarData || !skillInfo) return
+ const newData = cloneDeep(avatarData)
+ newData.data.skills[skillInfo?.PointID] = status ? 1 : 0
+
+ if (!status && traceLink?.[avatarSelected?.baseType || ""]?.[skillSelected || ""]) {
+ traceLink[avatarSelected?.baseType || ""][skillSelected || ""].forEach((pointId) => {
+ if (avatarSkillTree?.[pointId]?.["1"]) {
+ console.log(avatarSkillTree?.[pointId]?.["1"].PointID)
+ newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0
+ }
+ })
+ }
+ setAvatar(newData)
+ }
+
+ return (
+
+
+
+
+
+ {transI18n("skills")}
+
+
+
+ {traceButtons && avatarInfo && (
+
+
+ {traceButtons.map((btn, index) => (
+
{
+ setSkillSelected(btn.id === skillSelected ? null : btn.id)
+ }}
+ style={{
+ left: `calc(${btn.left} - var(--size-${btn.size}) / 2)`,
+ top: `calc(${btn.top} - var(--size-${btn.size}) / 2)`,
+ }}
+ >
+
+ {btn.size === "big" && (
+
{`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`}
+ )}
+
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ {transI18n("details")}
+
+ {skillSelected && avatarInfo?.SkillTrees && avatarData && (
+
+ {skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? (
+
+
{transI18n("level")}
+
+
{
+ const newData = cloneDeep(avatarData)
+ newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value)
+ setAvatar(newData)
+ }}
+ className="range range-success"
+ step="1" />
+
+ {Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => (
+ {index}
+ ))}
+
+
+
+ ) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillSelected)?.size !== "big" ? (
+
+
{
+ handlerChangeStatusTrace(e.target.checked)
+ }}
+ />
+
+ {avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")}
+
+
+ ) : (
+ null
+ )}
+
+ {((skillInfo?.PointName && skillInfo?.PointDesc) ||
+ (skillInfo?.PointName && skillInfo?.StatusAddList.length > 0))
+ && (
+
+ {skillInfo.PointName}
+ {skillInfo.StatusAddList.length > 0 && (
+
+ {skillInfo.StatusAddList.map((status, index) => (
+
+
{getTraceBuffDisplay(status)}
+
+ ))}
+
+ )}
+
+ )}
+
+ {skillInfo?.PointDesc && (
+
+ )}
+
+ {skillInfo?.LevelUpSkillID
+ && skillInfo?.LevelUpSkillID.length > 0
+ && dataLevelUpSkill
+ && (
+
+
+ {dataLevelUpSkill?.data?.map((skill, index) => (
+
+
+
+ {transI18n(dataLevelUpSkill.isServant ? `${skill?.Type ? "severaltalent" : "servantskill"}` : `${skill?.Type ? skill?.Type.toLowerCase() : "talent"}`)}
+ {` (${transI18n(skill.Tag.toLowerCase())})`}
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/themeController/themeContext.tsx b/src/components/themeController/themeContext.tsx
index 18d3ee9..025a191 100644
--- a/src/components/themeController/themeContext.tsx
+++ b/src/components/themeController/themeContext.tsx
@@ -1,5 +1,6 @@
"use client";
-import { createContext, PropsWithChildren, useEffect, useState } from "react";
+import { createContext, PropsWithChildren, useEffect } from "react";
+import useLocaleStore from "@/stores/localeStore";
interface ThemeContextType {
theme?: string;
@@ -8,7 +9,8 @@ interface ThemeContextType {
export const ThemeContext = createContext({});
export const ThemeProvider = ({ children }: PropsWithChildren) => {
- const [theme, setTheme] = useState("light");
+
+ const { theme, setTheme } = useLocaleStore()
useEffect(() => {
if (typeof window !== "undefined") {
@@ -19,14 +21,13 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const changeTheme = (nextTheme: string | null) => {
- console.log(nextTheme)
if (nextTheme) {
setTheme(nextTheme);
if (typeof window !== "undefined") {
localStorage.setItem("theme", nextTheme);
}
} else {
- setTheme((prev) => (prev === "light" ? "night" : "light"));
+ setTheme(theme === "winter" ? "night" : "winter");
if (typeof window !== "undefined") {
localStorage.setItem("theme", theme);
}
diff --git a/src/lib/constant.ts b/src/constant/constant.ts
similarity index 96%
rename from src/lib/constant.ts
rename to src/constant/constant.ts
index f762509..47842db 100644
--- a/src/lib/constant.ts
+++ b/src/constant/constant.ts
@@ -14,7 +14,7 @@ export const listCurrentLanguageApi : Record = {
zh: "cn"
};
-export const mapingStats = > {
+export const mappingStats = > {
"HPDelta": {
name:"HP",
icon:"/icon/hp.webp",
@@ -125,4 +125,6 @@ export const mapingStats = = {
+ Knight: [
+ { id: 'Point03', size: 'big', left: '51%', top: '52%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '35%' },
+ { id: 'Point02', size: 'big', left: '67%', top: '55%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '69.5%' },
+ { id: 'Point01', size: 'big', left: '33%', top: '55%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '21.5%' },
+ { id: 'Point07', size: 'medium', left: '71%', top: '86%' },
+ { id: 'Point06', size: 'medium', left: '29%', top: '86%' },
+ { id: 'Point16', size: 'small', left: '50%', top: '9%' },
+ { id: 'Point18', size: 'small', left: '66%', top: '13%' },
+ { id: 'Point17', size: 'small', left: '34%', top: '13%' },
+ { id: 'Point15', size: 'small', left: '79.5%', top: '44.5%' },
+ { id: 'Point12', size: 'small', left: '20.5%', top: '44.5%' },
+ { id: 'Point09', size: 'small', left: '50%', top: '82.5%' },
+ { id: 'Point13', size: 'small', left: '81%', top: '78%' },
+ { id: 'Point10', size: 'small', left: '19.5%', top: '78.5%' },
+ { id: 'Point14', size: 'small', left: '89%', top: '70%' },
+ { id: 'Point11', size: 'small', left: '11%', top: '70%' }
+ ],
+ Mage: [
+ { id: 'Point03', size: 'big', left: '50.5%', top: '53.5%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '32%' },
+ { id: 'Point02', size: 'big', left: '68.5%', top: '53.5%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '84%' },
+ { id: 'Point01', size: 'big', left: '33.5%', top: '53.5%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '14.5%' },
+ { id: 'Point07', size: 'medium', left: '80.5%', top: '53%' },
+ { id: 'Point06', size: 'medium', left: '19.5%', top: '53%' },
+ { id: 'Point17', size: 'small', left: '66%', top: '17.5%' },
+ { id: 'Point16', size: 'small', left: '34%', top: '17.5%' },
+ { id: 'Point18', size: 'small', left: '66%', top: '79%' },
+ { id: 'Point09', size: 'small', left: '34%', top: '79%' },
+ { id: 'Point13', size: 'small', left: '93%', top: '52%' },
+ { id: 'Point14', size: 'small', left: '89%', top: '63%' },
+ { id: 'Point15', size: 'small', left: '89%', top: '41.5%' },
+ { id: 'Point10', size: 'small', left: '7%', top: '52%' },
+ { id: 'Point11', size: 'small', left: '11.5%', top: '63%' },
+ { id: 'Point12', size: 'small', left: '12%', top: '41.5%' }
+ ],
+ Priest: [
+ { id: 'Point03', size: 'big', left: '51%', top: '49%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '31%' },
+ { id: 'Point02', size: 'big', left: '68.5%', top: '46%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '66%' },
+ { id: 'Point01', size: 'big', left: '33.5%', top: '46%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '13%' },
+ { id: 'Point07', size: 'medium', left: '68%', top: '79%' },
+ { id: 'Point06', size: 'medium', left: '33%', top: '79%' },
+ { id: 'Point16', size: 'small', left: '65%', top: '18%' },
+ { id: 'Point17', size: 'small', left: '35%', top: '18%' },
+ { id: 'Point09', size: 'small', left: '57%', top: '89%' },
+ { id: 'Point18', size: 'small', left: '43%', top: '89%' },
+ { id: 'Point10', size: 'small', left: '80.5%', top: '66%' },
+ { id: 'Point11', size: 'small', left: '93%', top: '52%' },
+ { id: 'Point12', size: 'small', left: '81%', top: '39%' },
+ { id: 'Point13', size: 'small', left: '20.5%', top: '66%' },
+ { id: 'Point14', size: 'small', left: '7%', top: '52%' },
+ { id: 'Point15', size: 'small', left: '20%', top: '39%' }
+ ],
+ Rogue: [
+ { id: 'Point03', size: 'big', left: '51%', top: '53%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '36%' },
+ { id: 'Point02', size: 'big', left: '68.5%', top: '46%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '70%' },
+ { id: 'Point01', size: 'big', left: '33.5%', top: '46%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '22%' },
+ { id: 'Point07', size: 'medium', left: '68%', top: '71%' },
+ { id: 'Point06', size: 'medium', left: '32%', top: '71%' },
+ { id: 'Point16', size: 'small', left: '50%', top: '9%' },
+ { id: 'Point18', size: 'small', left: '66%', top: '14%' },
+ { id: 'Point17', size: 'small', left: '34%', top: '14%' },
+ { id: 'Point09', size: 'small', left: '50%', top: '87%' },
+ { id: 'Point15', size: 'small', left: '81%', top: '35%' },
+ { id: 'Point12', size: 'small', left: '19%', top: '35%' },
+ { id: 'Point13', size: 'small', left: '81%', top: '57%' },
+ { id: 'Point10', size: 'small', left: '20%', top: '57%' },
+ { id: 'Point14', size: 'small', left: '93%', top: '44%' },
+ { id: 'Point11', size: 'small', left: '7%', top: '44%' }
+ ],
+ Shaman: [
+ { id: 'Point03', size: 'big', left: '51%', top: '57%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '36%' },
+ { id: 'Point02', size: 'big', left: '68.5%', top: '41%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '75%' },
+ { id: 'Point01', size: 'big', left: '33.5%', top: '41%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '22%' },
+ { id: 'Point07', size: 'medium', left: '89.5%', top: '56.5%' },
+ { id: 'Point06', size: 'medium', left: '17.5%', top: '56.5%' },
+ { id: 'Point16', size: 'small', left: '50%', top: '10%' },
+ { id: 'Point18', size: 'small', left: '66%', top: '14%' },
+ { id: 'Point17', size: 'small', left: '34%', top: '14%' },
+ { id: 'Point09', size: 'small', left: '50%', top: '87%' },
+ { id: 'Point15', size: 'small', left: '62%', top: '83%' },
+ { id: 'Point12', size: 'small', left: '38%', top: '83%' },
+ { id: 'Point13', size: 'small', left: '77%', top: '70%' },
+ { id: 'Point14', size: 'small', left: '67%', top: '61%' },
+ { id: 'Point10', size: 'small', left: '7%', top: '44%' },
+ { id: 'Point11', size: 'small', left: '20%', top: '31%' }
+ ],
+ Warlock: [
+ { id: 'Point03', size: 'big', left: '51%', top: '44%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '25%' },
+ { id: 'Point02', size: 'big', left: '68.5%', top: '47%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '62%' },
+ { id: 'Point01', size: 'big', left: '33.5%', top: '47%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '8%' },
+ { id: 'Point07', size: 'medium', left: '81.5%', top: '37%' },
+ { id: 'Point06', size: 'medium', left: '20.5%', top: '37%' },
+ { id: 'Point17', size: 'small', left: '66%', top: '14%' },
+ { id: 'Point16', size: 'small', left: '34%', top: '14%' },
+ { id: 'Point09', size: 'small', left: '50%', top: '74%' },
+ { id: 'Point18', size: 'small', left: '50%', top: '87%' },
+ { id: 'Point13', size: 'small', left: '94%', top: '48%' },
+ { id: 'Point14', size: 'small', left: '81%', top: '61%' },
+ { id: 'Point15', size: 'small', left: '68%', top: '74%' },
+ { id: 'Point10', size: 'small', left: '6%', top: '48%' },
+ { id: 'Point11', size: 'small', left: '20%', top: '61%' },
+ { id: 'Point12', size: 'small', left: '33%', top: '74%' }
+ ],
+ Warrior: [
+ { id: 'Point03', size: 'big', left: '51%', top: '53%' },
+ { id: 'Point04', size: 'big', left: '51%', top: '36%' },
+ { id: 'Point02', size: 'big', left: '69%', top: '48%' },
+ { id: 'Point05', size: 'big', left: '51%', top: '70.5%' },
+ { id: 'Point01', size: 'big', left: '33%', top: '48%' },
+ { id: 'Point08', size: 'medium', left: '50%', top: '22%' },
+ { id: 'Point07', size: 'medium', left: '67%', top: '83%' },
+ { id: 'Point06', size: 'medium', left: '33%', top: '83%' },
+ { id: 'Point16', size: 'small', left: '50%', top: '9%' },
+ { id: 'Point18', size: 'small', left: '66%', top: '14%' },
+ { id: 'Point17', size: 'small', left: '34%', top: '14%' },
+ { id: 'Point09', size: 'small', left: '50%', top: '87%' },
+ { id: 'Point15', size: 'small', left: '81%', top: '43.5%' },
+ { id: 'Point12', size: 'small', left: '19%', top: '43.5%' },
+ { id: 'Point13', size: 'small', left: '81%', top: '70%' },
+ { id: 'Point10', size: 'small', left: '19%', top: '70%' },
+ { id: 'Point14', size: 'small', left: '93%', top: '56.5%' },
+ { id: 'Point11', size: 'small', left: '7%', top: '56.5%' }
+ ],
+ Memory: [
+ { id: 'Point03', size: 'big', left: '51%', top: '72%' },
+ { id: 'Point04', size: 'big', left: '75%', top: '53%' },
+ { id: 'Point02', size: 'big', left: '67%', top: '67%' },
+ { id: 'Point05', size: 'big', left: '27%', top: '53%' },
+ { id: 'Point01', size: 'big', left: '35%', top: '67%' },
+ { id: 'Point08', size: 'medium', left: '34%', top: '34%' },
+ { id: 'Point07', size: 'medium', left: '50%', top: '87%' },
+ { id: 'Point06', size: 'medium', left: '91%', top: '51%' },
+ { id: 'Point16', size: 'small', left: '27.5%', top: '22%' },
+ { id: 'Point17', size: 'small', left: '43%', top: '14%' },
+ { id: 'Point18', size: 'small', left: '59%', top: '14%' },
+ { id: 'Point19', size: 'big', left: '51%', top: '50%' },
+ { id: 'Point20', size: 'big', left: '51%', top: '28%' },
+ { id: 'Point12', size: 'small', left: '86%', top: '40%' },
+ { id: 'Point13', size: 'small', left: '86%', top: '63%' },
+ { id: 'Point14', size: 'small', left: '35%', top: '82%' },
+ { id: 'Point15', size: 'small', left: '65%', top: '82%' },
+ { id: 'Point09', size: 'small', left: '9%', top: '51%' },
+ { id: 'Point10', size: 'small', left: '13%', top: '40%' },
+ { id: 'Point11', size: 'small', left: '13%', top: '63%' }
+ ]
+}
+
+export const traceLink : Record> = {
+ Knight: {
+ Point06: ["Point10", "Point11"],
+ Point07: ["Point13", "Point14"],
+ Point08: ["Point16", "Point17", "Point18"],
+ Point09: ["Point07", "Point06", "Point10", "Point11", "Point13", "Point14"],
+ Point10: ["Point11"],
+ Point16: ["Point17", "Point18"],
+ Point13: ["Point14"],
+ },
+ Mage: {
+ Point08: ["Point16", "Point17"],
+ Point07: ["Point13", "Point14", "Point15"],
+ Point13: ["Point14", "Point15"],
+ Point06: ["Point10", "Point11", "Point12"],
+ Point10: ["Point11", "Point12"],
+ },
+ Priest: {
+ Point08: ["Point16", "Point17"],
+ Point07: ["Point10", "Point11", "Point12"],
+ Point10: ["Point11", "Point12"],
+ Point11: ["Point12"],
+ Point06: ["Point13", "Point14", "Point15"],
+ Point13: ["Point14", "Point15"],
+ Point14: ["Point15"],
+ },
+ Rogue: {
+ Point08: ["Point16", "Point17", "Point18"],
+ Point16: ["Point17", "Point18"],
+ Point07: ["Point13", "Point14"],
+ Point13: ["Point14"],
+ Point06: ["Point10", "Point11"],
+ Point10: ["Point11"],
+ },
+ Shaman: {
+ Point08: ["Point16", "Point17", "Point18"],
+ Point16: ["Point17", "Point18"],
+ Point07: ["Point13", "Point14"],
+ Point13: ["Point14"],
+ Point06: ["Point10", "Point11"],
+ Point10: ["Point11"],
+ Point09: ["Point15", "Point12"],
+ },
+ Warlock: {
+ Point08: ["Point16", "Point17"],
+ Point07: ["Point13", "Point14", "Point15"],
+ Point13: ["Point14", "Point15"],
+ Point14: ["Point15"],
+ Point06: ["Point10", "Point11", "Point12"],
+ Point10: ["Point11", "Point12"],
+ Point11: ["Point12"],
+ Point09: ["Point18"],
+ },
+ Warrior: {
+ Point08: ["Point16", "Point17", "Point18"],
+ Point16: ["Point17", "Point18"],
+ Point07: ["Point13", "Point14", "Point15"],
+ Point13: ["Point14", "Point15"],
+ Point14: ["Point15"],
+ Point06: ["Point10", "Point11", "Point12"],
+ Point10: ["Point11", "Point12"],
+ Point11: ["Point12"],
+ },
+ Memory: {
+ Point16: ["Point17", "Point18"],
+ Point17: ["Point18"],
+ Point09: ["Point10", "Point11"],
+ Point07: ["Point14", "Point15"],
+ Point06: ["Point12", "Point13"],
+ }
+}
\ No newline at end of file
diff --git a/src/helper/calcData.ts b/src/helper/calcData.ts
index 91d83ed..28b4ec9 100644
--- a/src/helper/calcData.ts
+++ b/src/helper/calcData.ts
@@ -1,4 +1,4 @@
-import { mapingStats } from "@/lib/constant"
+import { mappingStats } from "@/constant/constant"
import { AffixDetail } from "@/types"
export function calcPromotion(level: number) {
@@ -40,10 +40,10 @@ export function calcRarity(rarity: string) {
export const calcAffixBonus = (affix: AffixDetail, stepCount: number, rollCount: number) => {
const data = affix;
if (!data) return 0;
- if (mapingStats?.[data.property].unit === "%") {
+ if (mappingStats?.[data.property].unit === "%") {
return ((data.base * rollCount + data.step * stepCount) * 100).toFixed(1);
}
- if (mapingStats?.[data.property].name === "SPD") {
+ if (mappingStats?.[data.property].name === "SPD") {
return (data.base * rollCount + data.step * stepCount).toFixed(1);
}
return (data.base * rollCount + data.step * stepCount).toFixed(0);
diff --git a/src/helper/getName.ts b/src/helper/getName.ts
index 6bee159..c740c37 100644
--- a/src/helper/getName.ts
+++ b/src/helper/getName.ts
@@ -1,4 +1,4 @@
-import { listCurrentLanguage } from "@/lib/constant";
+import { listCurrentLanguage } from "@/constant/constant";
import { CharacterBasic, EventBasic, LightConeBasic, MonsterBasic } from "@/types";
diff --git a/src/helper/getSkillTree.ts b/src/helper/getSkillTree.ts
index 1c6d01c..d583629 100644
--- a/src/helper/getSkillTree.ts
+++ b/src/helper/getSkillTree.ts
@@ -1,4 +1,4 @@
-import useAvatarStore from "@/stores/avatarStore"
+import useAvatarStore from "@/stores/avatarStore";
export function getSkillTree(enhanced: string) {
const { avatarSelected, mapAvatarInfo } = useAvatarStore.getState()
@@ -20,3 +20,4 @@ export function getSkillTree(enhanced: string) {
return acc;
}, {} as Record);
}
+
diff --git a/src/helper/json.ts b/src/helper/json.ts
index 8eff3ac..c484958 100644
--- a/src/helper/json.ts
+++ b/src/helper/json.ts
@@ -1,3 +1,4 @@
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function downloadJson(fileName: string, data: any) {
const json = JSON.stringify(data, null, 2)
const blob = new Blob([json], { type: 'application/json' })
diff --git a/src/helper/replaceByParam.ts b/src/helper/replaceByParam.ts
index bb44d2c..35d0099 100644
--- a/src/helper/replaceByParam.ts
+++ b/src/helper/replaceByParam.ts
@@ -1,26 +1,52 @@
export function replaceByParam(desc: string, params: number[]): string {
- desc = desc.replace(/(.*?)<\/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>/g, "$1");
+ function formatParam(
+ indexStr: string,
+ format: string,
+ floatDigits: string | undefined,
+ percent: string | undefined
+ ): string {
+ const i: number = parseInt(indexStr, 10) - 1;
+ const value: number | undefined = params[i];
+ if (value === undefined) return "";
+
+ if (format.startsWith("f")) {
+ const digits: number = parseInt(floatDigits || "1", 10);
+ const num: number = percent ? value * 100 : value;
+ return `${num.toFixed(digits)}${percent ? "%" : ""}`;
+ }
+
+ if (format === "i") {
+ return percent ? `${(value * 100).toFixed(0)}%` : `${Math.round(value)}`;
+ }
+
+ return `${value}`;
+ }
+
+ const desc1 = desc.replace(/(.*?)<\/color>/g, (match: string, inner: string): string => {
+ const colorCode: string = match.match(/#[0-9a-fA-F]{8}/)?.[0] ?? "#ffffff";
+ const processed: string = inner
+ .replace(/#(\d+)\[(f(\d+)|i)\](%)?/g, (
+ _: string,
+ index: string,
+ format: string,
+ floatDigits: string | undefined,
+ percent: string | undefined
+ ): string => formatParam(index, format, floatDigits, percent))
+ .replace(/(.*?)<\/unbreak>/g, "$1");
return `${processed}`;
});
- desc = desc.replace(/#(\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}`;
- });
+ const desc2 = desc1.replace(/#(\d+)\[(f(\d+)|i)\](%)?<\/unbreak>/g, (
+ _: string,
+ index: string,
+ format: string,
+ floatDigits: string | undefined,
+ percent: string | undefined
+ ): string => formatParam(index, format, floatDigits, percent));
- desc = desc.replace(/(\d+)<\/unbreak>/g, (_, number) => number);
+ const desc3 = desc2.replace(/(\d+)<\/unbreak>/g, (_: string, number: string): string => number);
- desc = desc.replaceAll("\\n", "
");
-
- return desc;
+ const desc4 = desc3.replaceAll("\\n", "
");
+ return desc4;
}
diff --git a/src/hooks/useFetchASData.ts b/src/hooks/useFetchASData.ts
index 18de3de..89bf422 100644
--- a/src/hooks/useFetchASData.ts
+++ b/src/hooks/useFetchASData.ts
@@ -2,7 +2,7 @@
import { useQuery } from '@tanstack/react-query'
import { fetchASByIdsNative, getASEventListApi } from '@/lib/api'
import { useEffect } from 'react'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import { toast } from 'react-toastify'
import useEventStore from '@/stores/eventStore'
diff --git a/src/hooks/useFetchAvatarData.ts b/src/hooks/useFetchAvatarData.ts
index 3ddb293..88d490a 100644
--- a/src/hooks/useFetchAvatarData.ts
+++ b/src/hooks/useFetchAvatarData.ts
@@ -4,7 +4,7 @@ import { fetchCharactersByIdsNative, getCharacterListApi } from '@/lib/api'
import { useEffect } from 'react'
import { toast } from 'react-toastify'
import useAvatarStore from '@/stores/avatarStore'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import useUserDataStore from '@/stores/userDataStore'
import { converterToAvatarStore } from '@/helper'
@@ -12,7 +12,7 @@ import { CharacterDetail } from '@/types'
export const useFetchAvatarData = () => {
const { setAvatars, avatars } = useUserDataStore()
- const { setListAvatar, setAllMapAvatarInfo, mapAvatarInfo, setAvatarSelected } = useAvatarStore()
+ const { setListAvatar, setAllMapAvatarInfo, mapAvatarInfo, setAvatarSelected, avatarSelected } = useAvatarStore()
const { locale } = useLocaleStore()
const { data: dataAvatar, error: errorAvatar } = useQuery({
queryKey: ['avatarData'],
@@ -57,12 +57,15 @@ export const useFetchAvatarData = () => {
useEffect(() => {
if (dataAvatar && !errorAvatar) {
setListAvatar(dataAvatar)
- setAvatarSelected(dataAvatar[0])
+ if (!avatarSelected) {
+ setAvatarSelected(dataAvatar[0])
+ }
} else if (errorAvatar) {
toast.error("Failed to load avatar data")
}
- }, [dataAvatar, errorAvatar, setAvatarSelected, setAvatars, setListAvatar, avatars])
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [dataAvatar, errorAvatar])
useEffect(() => {
if (dataAvatarInfo && !errorAvatarInfo) {
@@ -70,5 +73,6 @@ export const useFetchAvatarData = () => {
} else if (errorAvatarInfo) {
toast.error("Failed to load avatar info data")
}
- }, [dataAvatarInfo, errorAvatarInfo, setAllMapAvatarInfo, setAvatars])
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [dataAvatarInfo, errorAvatarInfo])
}
diff --git a/src/hooks/useFetchLightconeData.ts b/src/hooks/useFetchLightconeData.ts
index a592efd..6dc1d23 100644
--- a/src/hooks/useFetchLightconeData.ts
+++ b/src/hooks/useFetchLightconeData.ts
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import { fetchLightconesByIdsNative, getLightconeListApi } from '@/lib/api'
import { useEffect } from 'react'
import useLightconeStore from '@/stores/lightconeStore'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import { toast } from 'react-toastify'
diff --git a/src/hooks/useFetchMOCData.ts b/src/hooks/useFetchMOCData.ts
index 583cd22..5db84ee 100644
--- a/src/hooks/useFetchMOCData.ts
+++ b/src/hooks/useFetchMOCData.ts
@@ -2,7 +2,7 @@
import { useQuery } from '@tanstack/react-query'
import { fetchMOCByIdsNative, getMOCEventListApi } from '@/lib/api'
import { useEffect } from 'react'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import { toast } from 'react-toastify'
import useEventStore from '@/stores/eventStore'
diff --git a/src/hooks/useFetchPFData.ts b/src/hooks/useFetchPFData.ts
index f53da1c..0b12726 100644
--- a/src/hooks/useFetchPFData.ts
+++ b/src/hooks/useFetchPFData.ts
@@ -2,7 +2,7 @@
import { useQuery } from '@tanstack/react-query'
import { fetchPFByIdsNative, getPFEventListApi } from '@/lib/api'
import { useEffect } from 'react'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import { toast } from 'react-toastify'
import useEventStore from '@/stores/eventStore'
diff --git a/src/hooks/useFetchRelicData.ts b/src/hooks/useFetchRelicData.ts
index b0bad47..57041f7 100644
--- a/src/hooks/useFetchRelicData.ts
+++ b/src/hooks/useFetchRelicData.ts
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import { fetchRelicsByIdsNative, getRelicSetListApi } from '@/lib/api'
import { useEffect } from 'react'
import useRelicStore from '@/stores/relicStore'
-import { listCurrentLanguageApi } from '@/lib/constant'
+import { listCurrentLanguageApi } from '@/constant/constant'
import useLocaleStore from '@/stores/localeStore'
import { toast } from 'react-toastify'
diff --git a/src/stores/avatarStore.ts b/src/stores/avatarStore.ts
index 8839cef..ff2460a 100644
--- a/src/stores/avatarStore.ts
+++ b/src/stores/avatarStore.ts
@@ -8,11 +8,13 @@ interface AvatarState {
filter: FilterAvatarType;
avatarSelected: CharacterBasic | null;
mapAvatarInfo: Record;
+ skillSelected: string | null;
setListAvatar: (newListAvatar: CharacterBasic[]) => void;
setAvatarSelected: (newAvatarSelected: CharacterBasic) => void;
setFilter: (newFilter: FilterAvatarType) => void;
setMapAvatarInfo: (avatarId: string, newCharacter: CharacterDetail) => void;
setAllMapAvatarInfo: (newCharacter: Record) => void;
+ setSkillSelected: (newSkillSelected: string | null) => void;
}
const useAvatarStore = create((set, get) => ({
@@ -26,7 +28,9 @@ const useAvatarStore = create((set, get) => ({
locale: "",
},
avatarSelected: null,
+ skillSelected: null,
mapAvatarInfo: {},
+ setSkillSelected: (newSkillSelected: string | null) => set({ skillSelected: newSkillSelected }),
setListAvatar: (newListAvatar: CharacterBasic[]) => set({ listAvatar: newListAvatar, listRawAvatar: newListAvatar }),
setAvatarSelected: (newAvatarSelected: CharacterBasic) => set({ avatarSelected: newAvatarSelected }),
setFilter: (newFilter: FilterAvatarType) => {
diff --git a/src/stores/localeStore.ts b/src/stores/localeStore.ts
index d4e1189..6c5fb01 100644
--- a/src/stores/localeStore.ts
+++ b/src/stores/localeStore.ts
@@ -4,6 +4,8 @@ import { createJSONStorage, persist } from 'zustand/middleware';
interface LocaleState {
locale: string;
+ theme: string;
+ setTheme: (newTheme: string) => void;
setLocale: (newLocale: string) => void;
}
@@ -11,6 +13,8 @@ const useLocaleStore = create()(
persist(
(set) => ({
locale: "en",
+ theme: "night",
+ setTheme: (newTheme: string) => set({ theme: newTheme }),
setLocale: (newLocale: string) => set({ locale: newLocale }),
}),
{
diff --git a/src/types/characterDetail.ts b/src/types/characterDetail.ts
index 2925d06..f26502d 100644
--- a/src/types/characterDetail.ts
+++ b/src/types/characterDetail.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
export interface CharacterDetail {
Name: string;
Desc: string;
@@ -50,7 +51,8 @@ export interface RankType {
export interface SkillType {
Id: number;
Name: string;
- Desc: string;
+ Desc: string | null;
+ SimpleDesc: string;
Type: string;
Tag: string;
SPBase: number | null;
@@ -66,6 +68,13 @@ export interface LevelParams {
ParamList: number[];
}
+export type StatusAddType = {
+ $type: string;
+ PropertyType: string;
+ Value: number;
+ Name: string;
+};
+
export interface SkillTreePoint {
Anchor: string;
AvatarPromotionLimit: number | null;
@@ -75,14 +84,14 @@ export interface SkillTreePoint {
LevelUpSkillID: number[];
MaterialList: ItemConfigRow[];
MaxLevel: number;
- ParamList: any[];
+ ParamList: number[];
PointID: number;
PointName: string | null;
PointDesc: string | null;
PointTriggerKey: number;
PointType: number;
PrePoint: string[];
- StatusAddList: any[];
+ StatusAddList: StatusAddType[];
}
export interface ItemConfigRow {
@@ -109,6 +118,7 @@ export interface Memosprite {
export interface SpriteSkill {
Name: string;
Desc: string | null;
+ SimpleDesc: string;
Type: string | null;
Tag: string;
SPBase: number | null;
@@ -116,6 +126,7 @@ export interface SpriteSkill {
BPAdd: number | null;
ShowStanceList: number[];
SkillComboValueDelta: number | null;
+ Extra: Record;
Level: Record;
}
@@ -124,6 +135,13 @@ export interface UniqueAbility {
Name: string;
Desc: string;
Param: number[];
+ Extra: Record;
+}
+
+export interface Extra {
+ name: string;
+ desc: string;
+ param: number[];
}
export interface Stat {
diff --git a/tsconfig.json b/tsconfig.json
index eba91ff..6592720 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,6 +10,7 @@
"skipLibCheck": true,
"strict": true,
"noEmit": true,
+ "baseUrl": ".",
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,