Refactor code structure for improved readability and maintainability
Gitea Auto Deploy / Deploy-Container (push) Successful in 41s
Gitea Auto Deploy / Deploy-Container (push) Successful in 41s
This commit is contained in:
Binary file not shown.
+18
-2
@@ -11,18 +11,34 @@ import MultiCharLineChart from "@/components/chart/damageLineForAll";
|
|||||||
import DamagePerCycleForAll from "@/components/chart/damagePerCycleForAll";
|
import DamagePerCycleForAll from "@/components/chart/damagePerCycleForAll";
|
||||||
import DamagePercentChartForAll from "@/components/chart/damagePercentForAll";
|
import DamagePercentChartForAll from "@/components/chart/damagePercentForAll";
|
||||||
import EnemyBar from "@/components/enemybar";
|
import EnemyBar from "@/components/enemybar";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { CharacterBasic, MonsterBasic } from "@/types";
|
import { CharacterBasic, MonsterBasic } from "@/types";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
const { setListAvatar, setMapAvatar, setListEnemy, setMapEnemy } = useAvatarDataStore();
|
const { setListAvatar, setMapAvatar, setListEnemy, setMapEnemy } = useAvatarDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
setListAvatar: state.setListAvatar,
|
||||||
|
setMapAvatar: state.setMapAvatar,
|
||||||
|
setListEnemy: state.setListEnemy,
|
||||||
|
setMapEnemy: state.setMapEnemy,
|
||||||
|
}))
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
totalAV,
|
totalAV,
|
||||||
totalDamage,
|
totalDamage,
|
||||||
damagePerAV,
|
damagePerAV,
|
||||||
turnHistory,
|
turnHistory,
|
||||||
enemyDetail
|
enemyDetail
|
||||||
} = useBattleDataStore();
|
} = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
totalAV: state.totalAV,
|
||||||
|
totalDamage: state.totalDamage,
|
||||||
|
damagePerAV: state.damagePerAV,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
enemyDetail: state.enemyDetail,
|
||||||
|
}))
|
||||||
|
);
|
||||||
const [expandedCharts, setExpandedCharts] = useState<string[]>([]);
|
const [expandedCharts, setExpandedCharts] = useState<string[]>([]);
|
||||||
|
|
||||||
const toggleExpand = (chartId: string) => {
|
const toggleExpand = (chartId: string) => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useEffect, useState, useRef } from "react";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { getNameChar } from "@/helper";
|
import { getNameChar } from "@/helper";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import NameAvatar from "../nameAvatar";
|
import NameAvatar from "../nameAvatar";
|
||||||
|
|
||||||
@@ -15,9 +16,17 @@ export default function ActionBar() {
|
|||||||
const [selectTurn, setSelectTurn] = useState<SkillBattleInfo | null>(null);
|
const [selectTurn, setSelectTurn] = useState<SkillBattleInfo | null>(null);
|
||||||
const [selectAvatar, setSelectAvatar] = useState<CharacterBasic | null>(null);
|
const [selectAvatar, setSelectAvatar] = useState<CharacterBasic | null>(null);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const { skillHistory, turnHistory, cycleIndex, waveIndex, maxWave } = useBattleDataStore();
|
const { skillHistory, turnHistory, cycleIndex, waveIndex, maxWave } = useBattleDataStore(
|
||||||
const { listAvatar } = useAvatarDataStore();
|
useShallow(state => ({
|
||||||
const { locale } = useLocaleStore();
|
skillHistory: state.skillHistory,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
cycleIndex: state.cycleIndex,
|
||||||
|
waveIndex: state.waveIndex,
|
||||||
|
maxWave: state.maxWave,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const listAvatar = useAvatarDataStore(state => state.listAvatar);
|
||||||
|
const locale = useLocaleStore(state => state.locale);
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
const turnListRef = useRef<HTMLDivElement>(null);
|
const turnListRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -39,7 +48,6 @@ export default function ActionBar() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
setSelectAvatar(null);
|
setSelectAvatar(null);
|
||||||
@@ -49,7 +57,6 @@ export default function ActionBar() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle ESC key to close modal
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape' && isModalOpen) {
|
if (event.key === 'Escape' && isModalOpen) {
|
||||||
@@ -61,7 +68,6 @@ export default function ActionBar() {
|
|||||||
return () => window.removeEventListener('keydown', handleEscKey);
|
return () => window.removeEventListener('keydown', handleEscKey);
|
||||||
}, [isModalOpen]);
|
}, [isModalOpen]);
|
||||||
|
|
||||||
// Scroll to the bottom when new turns are added
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (turnListRef.current && skillHistory.length > 0) {
|
if (turnListRef.current && skillHistory.length > 0) {
|
||||||
turnListRef.current.scrollTop = turnListRef.current.scrollHeight;
|
turnListRef.current.scrollTop = turnListRef.current.scrollHeight;
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ interface CharacterCardProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CharacterCard({ data }: CharacterCardProps) {
|
export default function CharacterCard({ data }: CharacterCardProps) {
|
||||||
const { locale } = useLocaleStore();
|
const locale = useLocaleStore(state => state.locale);
|
||||||
const { avatarDetail } = useBattleDataStore()
|
const avatarDetail = useBattleDataStore(state => state.avatarDetail);
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
const text = getNameChar(locale, transI18n, data)
|
const text = getNameChar(locale, transI18n, data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="z-10 flex flex-col w-28 items-center p-1 rounded-md shadow-lg bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300 hover:scale-105 m-1">
|
<li className="z-10 flex flex-col w-28 items-center p-1 rounded-md shadow-lg bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300 hover:scale-105 m-1">
|
||||||
@@ -64,33 +64,41 @@ export default function CharacterCard({ data }: CharacterCardProps) {
|
|||||||
className="mt-2 text-center text-base font-normal leading-tight"
|
className="mt-2 text-center text-base font-normal leading-tight"
|
||||||
/>
|
/>
|
||||||
{avatarDetail && (
|
{avatarDetail && (
|
||||||
<div className="space-y-2">
|
(() => {
|
||||||
<div className="flex justify-between items-center">
|
const stats = avatarDetail?.[Number(data.id)]?.stats;
|
||||||
<span className="text-xs text-base-content/70 mx-1">HP:</span>
|
const hp = stats?.CurrentHP ?? stats?.HP ?? 0;
|
||||||
<span className="text-xs font-medium">
|
const maxHp = stats?.MaxHP ?? 100;
|
||||||
<span className="text-error">
|
const hpPercent = Math.round((hp / maxHp) * 100);
|
||||||
{Number(avatarDetail?.[Number(data.id)]?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
|
||||||
</span>
|
|
||||||
<span className="text-base-content/50">/</span>
|
|
||||||
<span className="text-base-content/70">
|
|
||||||
{Number(avatarDetail?.[Number(data.id)]?.stats?.MaxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative w-full bg-base-300 rounded-full h-2.5">
|
return (
|
||||||
<div
|
<div className="space-y-2">
|
||||||
className="bg-error h-2.5 rounded-full transition-all duration-300"
|
<div className="flex justify-between items-center">
|
||||||
style={{
|
<span className="text-xs text-base-content/70 mx-1">HP:</span>
|
||||||
width: `${Math.max(0, Math.min(100, ((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100))}%`
|
<span className="text-xs font-medium">
|
||||||
}}
|
<span className="text-error">
|
||||||
/>
|
{Number(hp).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
<span className="absolute inset-0 flex items-center justify-center text-xs text-white font-medium">
|
</span>
|
||||||
{Math.round(((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100)}%
|
<span className="text-base-content/50">/</span>
|
||||||
</span>
|
<span className="text-base-content/70">
|
||||||
</div>
|
{Number(maxHp).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div className="relative w-full bg-base-300 rounded-full h-2.5">
|
||||||
|
<div
|
||||||
|
className="bg-error h-2.5 rounded-full transition-all duration-300"
|
||||||
|
style={{
|
||||||
|
width: `${Math.max(0, Math.min(100, (hp / maxHp) * 100))}%`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="absolute inset-0 flex items-center justify-center text-xs text-white font-medium">
|
||||||
|
{hpPercent}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -24,24 +24,24 @@ export default function MultiCharLineChart() {
|
|||||||
const [mode, setMode] = useState<1 | 2>(2);
|
const [mode, setMode] = useState<1 | 2>(2);
|
||||||
const dataByAvatar = useDamageLinesForAll(mode);
|
const dataByAvatar = useDamageLinesForAll(mode);
|
||||||
const avatarIds = Object.keys(dataByAvatar).map(Number);
|
const avatarIds = Object.keys(dataByAvatar).map(Number);
|
||||||
const { mapAvatar } = useAvatarDataStore()
|
const mapAvatar = useAvatarDataStore(state => state.mapAvatar);
|
||||||
const { locale } = useLocaleStore();
|
const locale = useLocaleStore(state => state.locale);
|
||||||
const transI18n = useTranslations("DataAnalysisPage")
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
datasets: avatarIds.map((id, idx) => ({
|
datasets: avatarIds
|
||||||
label: getNameChar(locale, transI18n, mapAvatar?.[id.toString()]),
|
.filter(id => id > 0 && mapAvatar?.[id.toString()])
|
||||||
data: dataByAvatar[id].map(({ x, y }: { x: number; y: number }) => {
|
.map((id, idx) => ({
|
||||||
return {
|
label: getNameChar(locale, transI18n, mapAvatar?.[id.toString()]),
|
||||||
x: x.toFixed(2),
|
data: dataByAvatar[id].map(({ x, y }: { x: number; y: number }) => ({
|
||||||
y: y.toFixed(2)
|
x: Number(x.toFixed(2)),
|
||||||
}
|
y: Number(y.toFixed(2))
|
||||||
}),
|
})),
|
||||||
borderColor: colors[idx % colors.length],
|
borderColor: colors[idx % colors.length],
|
||||||
backgroundColor: colors[idx % colors.length],
|
backgroundColor: colors[idx % colors.length],
|
||||||
fill: false,
|
fill: false,
|
||||||
tension: 0.3,
|
tension: 0.3,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import { Bar } from 'react-chartjs-2';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import {attackTypeToString} from "@/types";
|
import {attackTypeToString} from "@/types";
|
||||||
|
|
||||||
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend);
|
ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend);
|
||||||
|
|
||||||
const colorPalette = [
|
const colorPalette = [
|
||||||
@@ -47,9 +49,14 @@ const borderPalette = colorPalette.map((c) => c.replace('0.6', '1'));
|
|||||||
|
|
||||||
export default function DamagePerAvatarForAll() {
|
export default function DamagePerAvatarForAll() {
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
const { lineup, skillHistory } = useBattleDataStore();
|
const { lineup, skillHistory } = useBattleDataStore(
|
||||||
const { mapAvatar } = useAvatarDataStore();
|
useShallow(state => ({
|
||||||
const { locale } = useLocaleStore();
|
lineup: state.lineup,
|
||||||
|
skillHistory: state.skillHistory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const mapAvatar = useAvatarDataStore(state => state.mapAvatar);
|
||||||
|
const locale = useLocaleStore(state => state.locale);
|
||||||
|
|
||||||
const [mode, setMode] = useState<number>(2);
|
const [mode, setMode] = useState<number>(2);
|
||||||
|
|
||||||
@@ -93,7 +100,11 @@ export default function DamagePerAvatarForAll() {
|
|||||||
const damageTypesSet = new Set<string>();
|
const damageTypesSet = new Set<string>();
|
||||||
avatarMap.forEach(({ avatarId }) => {
|
avatarMap.forEach(({ avatarId }) => {
|
||||||
skillHistory.filter(s => s.avatarId === avatarId).forEach(s =>
|
skillHistory.filter(s => s.avatarId === avatarId).forEach(s =>
|
||||||
s.damageDetail?.forEach(d => d.damage_type && damageTypesSet.add(transI18n(attackTypeToString(d?.damage_type).toLowerCase())))
|
s.damageDetail?.forEach(d => {
|
||||||
|
if (d?.damage_type !== undefined) {
|
||||||
|
damageTypesSet.add(transI18n(attackTypeToString(d.damage_type).toLowerCase()));
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const damageTypes = Array.from(damageTypesSet);
|
const damageTypes = Array.from(damageTypesSet);
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
import { Bar } from "react-chartjs-2";
|
import { Bar } from "react-chartjs-2";
|
||||||
import { ChartOptions } from "chart.js";
|
import { ChartOptions } from "chart.js";
|
||||||
import { useDamagePerCycleForAll } from "@/hooks/useDamagePerCycle";
|
import { useDamagePerCycleForAll } from "@/hooks/useDamagePerCycle";
|
||||||
import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js";
|
import { Chart as ChartJS, BarElement, CategoryScale, LinearScale, Tooltip, Legend } from "chart.js";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
ChartJS.register(BarElement, ArcElement, RadialLinearScale);
|
ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend);
|
||||||
export default function DamagePerCycleForAll() {
|
export default function DamagePerCycleForAll() {
|
||||||
const [mode, setMode] = useState<0 | 1 | 2>(0);
|
const [mode, setMode] = useState<0 | 1 | 2>(0);
|
||||||
const dataRaw = useDamagePerCycleForAll(mode);
|
const dataRaw = useDamagePerCycleForAll(mode);
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { Bar } from "react-chartjs-2";
|
|||||||
import { ChartOptions } from "chart.js";
|
import { ChartOptions } from "chart.js";
|
||||||
import { useDamagePerCycleForOne } from "@/hooks/useDamagePerCycle";
|
import { useDamagePerCycleForOne } from "@/hooks/useDamagePerCycle";
|
||||||
|
|
||||||
import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js";
|
import { Chart as ChartJS, BarElement, CategoryScale, LinearScale, Tooltip, Legend } from "chart.js";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
ChartJS.register(BarElement, ArcElement, RadialLinearScale);
|
ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend);
|
||||||
export function DamagePerCycleForOne({
|
export function DamagePerCycleForOne({
|
||||||
avatarId,
|
avatarId,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -27,13 +27,15 @@ export default function DamagePercentChartForAll() {
|
|||||||
const [mode, setMode] = useState<1 | 2>(1);
|
const [mode, setMode] = useState<1 | 2>(1);
|
||||||
const damageByAvatar = useDamagePercentPerAvatar();
|
const damageByAvatar = useDamagePercentPerAvatar();
|
||||||
const damageByType = useDamagePercentByType();
|
const damageByType = useDamagePercentByType();
|
||||||
const { mapAvatar } = useAvatarDataStore();
|
const mapAvatar = useAvatarDataStore(state => state.mapAvatar);
|
||||||
const { locale } = useLocaleStore();
|
const locale = useLocaleStore(state => state.locale);
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
|
|
||||||
|
const filteredDamageByAvatar = damageByAvatar.filter(d => d.avatarId > 0 && mapAvatar?.[d.avatarId.toString()]);
|
||||||
|
|
||||||
const chartData = {
|
const chartData = {
|
||||||
labels: (mode === 1
|
labels: (mode === 1
|
||||||
? damageByAvatar.map(d =>
|
? filteredDamageByAvatar.map(d =>
|
||||||
getNameChar(locale, transI18n, mapAvatar?.[d.avatarId.toString()])
|
getNameChar(locale, transI18n, mapAvatar?.[d.avatarId.toString()])
|
||||||
)
|
)
|
||||||
: damageByType.map(d => transI18n(d.type.toLowerCase()))
|
: damageByType.map(d => transI18n(d.type.toLowerCase()))
|
||||||
@@ -42,8 +44,8 @@ export default function DamagePercentChartForAll() {
|
|||||||
{
|
{
|
||||||
label: '% ' + transI18n("damage"),
|
label: '% ' + transI18n("damage"),
|
||||||
data: (mode === 1
|
data: (mode === 1
|
||||||
? damageByAvatar.map(d => d.percent.toFixed(2))
|
? filteredDamageByAvatar.map(d => Number(d.percent.toFixed(2)))
|
||||||
: damageByType.map(d => d.percent.toFixed(2))
|
: damageByType.map(d => Number(d.percent.toFixed(2)))
|
||||||
),
|
),
|
||||||
backgroundColor: colors,
|
backgroundColor: colors,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
|||||||
@@ -9,15 +9,22 @@ import useLocaleStore from "@/stores/localeStore";
|
|||||||
import NameAvatar from "../nameAvatar";
|
import NameAvatar from "../nameAvatar";
|
||||||
|
|
||||||
export default function EnemyBar() {
|
export default function EnemyBar() {
|
||||||
const { enemyDetail } = useBattleDataStore()
|
const enemyDetail = useBattleDataStore(state => state.enemyDetail);
|
||||||
const { mapEnemy } = useAvatarDataStore()
|
const mapEnemy = useAvatarDataStore(state => state.mapEnemy);
|
||||||
const { locale } = useLocaleStore()
|
const locale = useLocaleStore(state => state.locale);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-3 w-full">
|
<div className="p-3 w-full">
|
||||||
<div className="flex gap-3 overflow-x-auto pb-2">
|
<div className="flex gap-3 overflow-x-auto pb-2">
|
||||||
{enemyDetail && Object.values(enemyDetail).filter((enemy) => (
|
{enemyDetail && Object.values(enemyDetail).filter((enemy) => {
|
||||||
enemy.stats.HP <= enemy.maxHP)).map((enemy, uid) => (
|
const hp = enemy.stats?.CurrentHP ?? enemy.stats?.HP ?? 0;
|
||||||
|
return hp <= enemy.maxHP;
|
||||||
|
}).map((enemy, uid) => {
|
||||||
|
const hp = enemy.stats?.CurrentHP ?? enemy.stats?.HP ?? 0;
|
||||||
|
const maxHp = enemy.maxHP || 100;
|
||||||
|
const hpPercent = Math.round((hp / maxHp) * 100);
|
||||||
|
|
||||||
|
return (
|
||||||
<div key={uid} className="bg-base-200 rounded-lg p-3 border border-gray-700 w-52 shrink-0">
|
<div key={uid} className="bg-base-200 rounded-lg p-3 border border-gray-700 w-52 shrink-0">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -49,11 +56,11 @@ export default function EnemyBar() {
|
|||||||
<div className="text-xs text-base-content/70">HP:</div>
|
<div className="text-xs text-base-content/70">HP:</div>
|
||||||
<div className="flex text-xs font-medium">
|
<div className="flex text-xs font-medium">
|
||||||
<div className="text-error">
|
<div className="text-error">
|
||||||
{Number(enemy?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
{Number(hp).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-base-content/50 mx-1">/</div>
|
<div className="text-base-content/50 mx-1">/</div>
|
||||||
<div className="text-base-content/70">
|
<div className="text-base-content/70">
|
||||||
{Number(enemy?.maxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
{Number(maxHp).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,17 +69,18 @@ export default function EnemyBar() {
|
|||||||
<div
|
<div
|
||||||
className="bg-error h-2.5 rounded-full transition-all duration-300"
|
className="bg-error h-2.5 rounded-full transition-all duration-300"
|
||||||
style={{
|
style={{
|
||||||
width: `${Math.max(0, Math.min(100, ((enemy.stats?.HP || 0) / (enemy.maxHP || 100)) * 100))}%`
|
width: `${Math.max(0, Math.min(100, (hp / maxHp) * 100))}%`
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-xs text-white font-medium">
|
<div className="absolute inset-0 flex items-center justify-center text-xs text-white font-medium">
|
||||||
{Math.round(((enemy.stats?.HP || 0) / (enemy.maxHP || 100)) * 100)}%
|
{hpPercent}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Link from "next/link";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
|
|
||||||
const themes = [
|
const themes = [
|
||||||
{ label: "Winter" },
|
{ label: "Winter" },
|
||||||
@@ -22,12 +23,33 @@ const themes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const { changeTheme } = useChangeTheme()
|
const { changeTheme } = useChangeTheme();
|
||||||
const { locale, setLocale } = useLocaleStore()
|
const { locale, setLocale } = useLocaleStore(
|
||||||
const { loadBattleDataFromJSON, version } = useBattleDataStore()
|
useShallow(state => ({
|
||||||
const router = useRouter()
|
locale: state.locale,
|
||||||
const transI18n = useTranslations("DataAnalysisPage")
|
setLocale: state.setLocale,
|
||||||
const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore();
|
}))
|
||||||
|
);
|
||||||
|
const { loadBattleDataFromJSON, version } = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
loadBattleDataFromJSON: state.loadBattleDataFromJSON,
|
||||||
|
version: state.version,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const router = useRouter();
|
||||||
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
|
const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
host: state.host,
|
||||||
|
port: state.port,
|
||||||
|
status: state.status,
|
||||||
|
connectionType: state.connectionType,
|
||||||
|
setHost: state.setHost,
|
||||||
|
setPort: state.setPort,
|
||||||
|
setStatus: state.setStatus,
|
||||||
|
setConnectionType: state.setConnectionType,
|
||||||
|
}))
|
||||||
|
);
|
||||||
const [message, setMessage] = useState({ text: '', type: '' });
|
const [message, setMessage] = useState({ text: '', type: '' });
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
@@ -131,7 +153,6 @@ export default function Header() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
@@ -140,7 +161,6 @@ export default function Header() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle ESC key to close modal
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape' && isModalOpen) {
|
if (event.key === 'Escape' && isModalOpen) {
|
||||||
|
|||||||
@@ -13,20 +13,31 @@ import { motion } from "framer-motion";
|
|||||||
import { DamageLineForOne } from "../chart/damageLineForOne";
|
import { DamageLineForOne } from "../chart/damageLineForOne";
|
||||||
import { DamagePerCycleForOne } from "../chart/damagePerCycleForOne";
|
import { DamagePerCycleForOne } from "../chart/damagePerCycleForOne";
|
||||||
import { useCalcTotalDmgAvatar, useCalcTotalTurnAvatar } from "@/hooks/useCalcAvatarData";
|
import { useCalcTotalDmgAvatar, useCalcTotalTurnAvatar } from "@/hooks/useCalcAvatarData";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import NameAvatar from "../nameAvatar";
|
import NameAvatar from "../nameAvatar";
|
||||||
// import ShowCaseInfo from "../card/showCaseCard";
|
|
||||||
|
|
||||||
export default function LineupBar() {
|
export default function LineupBar() {
|
||||||
const [selectedCharacter, setSelectedCharacter] = useState<CharacterBasic | undefined>(undefined);
|
const [selectedCharacter, setSelectedCharacter] = useState<CharacterBasic | undefined>(undefined);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
const { lineup, turnHistory, dataAvatar } = useBattleDataStore();
|
const { lineup, turnHistory, dataAvatar } = useBattleDataStore(
|
||||||
const { listAvatar, mapAvatar } = useAvatarDataStore();
|
useShallow(state => ({
|
||||||
const { locale } = useLocaleStore();
|
lineup: state.lineup,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
dataAvatar: state.dataAvatar,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const { listAvatar, mapAvatar } = useAvatarDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
listAvatar: state.listAvatar,
|
||||||
|
mapAvatar: state.mapAvatar,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const locale = useLocaleStore(state => state.locale);
|
||||||
const totalDamage = useCalcTotalDmgAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0);
|
const totalDamage = useCalcTotalDmgAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0);
|
||||||
const totalTurn = useCalcTotalTurnAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0)
|
const totalTurn = useCalcTotalTurnAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0);
|
||||||
|
|
||||||
|
|
||||||
const lineupAvatars = listAvatar?.filter(item =>
|
const lineupAvatars = listAvatar?.filter(item =>
|
||||||
@@ -42,7 +53,6 @@ export default function LineupBar() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
setSelectedCharacter(undefined);
|
setSelectedCharacter(undefined);
|
||||||
@@ -52,7 +62,6 @@ export default function LineupBar() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle ESC key to close modal
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape' && isModalOpen) {
|
if (event.key === 'Escape' && isModalOpen) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import useBattleDataStore from "@/stores/battleDataStore";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useCalcTotalDmgAvatar(avatarId: number) {
|
export function useCalcTotalDmgAvatar(avatarId: number) {
|
||||||
const { skillHistory } = useBattleDataStore.getState();
|
const skillHistory = useBattleDataStore(state => state.skillHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return skillHistory
|
return skillHistory
|
||||||
@@ -13,16 +13,9 @@ export function useCalcTotalDmgAvatar(avatarId: number) {
|
|||||||
|
|
||||||
|
|
||||||
export function useCalcTotalTurnAvatar(avatarId: number) {
|
export function useCalcTotalTurnAvatar(avatarId: number) {
|
||||||
const { turnHistory } = useBattleDataStore.getState();
|
const turnHistory = useBattleDataStore(state => state.turnHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const uniqueActionValues = new Set<number>();
|
return turnHistory.filter(turn => turn.avatarId === avatarId).length;
|
||||||
turnHistory.forEach(turn => {
|
|
||||||
if (turn.avatarId === avatarId) {
|
|
||||||
uniqueActionValues.add(turn.actionValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return uniqueActionValues.size;
|
|
||||||
}, [avatarId, turnHistory]);
|
}, [avatarId, turnHistory]);
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
import useBattleDataStore from "@/stores/battleDataStore";
|
import useBattleDataStore from "@/stores/battleDataStore";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useDamageLineForOne(avatarId: number, mode: 0 | 1 = 0) {
|
export function useDamageLineForOne(avatarId: number, mode: 0 | 1 = 0) {
|
||||||
const { skillHistory, turnHistory } = useBattleDataStore.getState();
|
const { skillHistory, turnHistory } = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
skillHistory: state.skillHistory,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const map = new Map<number, number>();
|
const map = new Map<number, number>();
|
||||||
|
|
||||||
for (const skill of skillHistory) {
|
for (const skill of skillHistory) {
|
||||||
if (skill.avatarId !== avatarId) continue;
|
if (skill.avatarId !== avatarId) continue;
|
||||||
const actionValue = turnHistory[skill.turnBattleId].actionValue
|
const turn = turnHistory[skill.turnBattleId];
|
||||||
|
if (!turn) continue;
|
||||||
|
const actionValue = turn.actionValue;
|
||||||
const prev = map.get(actionValue) || 0;
|
const prev = map.get(actionValue) || 0;
|
||||||
map.set(actionValue, prev + skill.totalDamage);
|
map.set(actionValue, prev + skill.totalDamage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
import useBattleDataStore from "@/stores/battleDataStore";
|
import useBattleDataStore from "@/stores/battleDataStore";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useDamageLinesForAll(mode: 1 | 2 = 1) {
|
export function useDamageLinesForAll(mode: 1 | 2 = 1) {
|
||||||
const { turnHistory, skillHistory } = useBattleDataStore.getState();
|
const { turnHistory, skillHistory } = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
skillHistory: state.skillHistory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const avatarMap = new Map<number, Map<number, number>>();
|
const avatarMap = new Map<number, Map<number, number>>();
|
||||||
|
|
||||||
for (const skill of skillHistory) {
|
for (const skill of skillHistory) {
|
||||||
|
if (!skill.avatarId || skill.avatarId <= 0) continue;
|
||||||
|
|
||||||
if (!avatarMap.has(skill.avatarId)) {
|
if (!avatarMap.has(skill.avatarId)) {
|
||||||
avatarMap.set(skill.avatarId, new Map());
|
avatarMap.set(skill.avatarId, new Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const turn = turnHistory[skill.turnBattleId];
|
||||||
|
if (!turn) continue;
|
||||||
|
|
||||||
const charMap = avatarMap.get(skill.avatarId)!;
|
const charMap = avatarMap.get(skill.avatarId)!;
|
||||||
const actionValue = turnHistory[skill.turnBattleId].actionValue
|
const actionValue = turn.actionValue;
|
||||||
const prev = charMap.get(actionValue) || 0;
|
const prev = charMap.get(actionValue) || 0;
|
||||||
charMap.set(actionValue, prev + skill.totalDamage);
|
charMap.set(actionValue, prev + skill.totalDamage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import useBattleDataStore from "@/stores/battleDataStore";
|
import useBattleDataStore from "@/stores/battleDataStore";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
@@ -6,7 +7,12 @@ import { useMemo } from "react";
|
|||||||
type Mode = 0 | 1 | 2;
|
type Mode = 0 | 1 | 2;
|
||||||
|
|
||||||
export function useDamagePerCycleForOne(avatarId: number, mode: Mode) {
|
export function useDamagePerCycleForOne(avatarId: number, mode: Mode) {
|
||||||
const { skillHistory, turnHistory } = useBattleDataStore.getState();
|
const { skillHistory, turnHistory } = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
skillHistory: state.skillHistory,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const damageMap = new Map<string, number>();
|
const damageMap = new Map<string, number>();
|
||||||
@@ -39,7 +45,12 @@ export function useDamagePerCycleForOne(avatarId: number, mode: Mode) {
|
|||||||
|
|
||||||
|
|
||||||
export function useDamagePerCycleForAll(mode: Mode) {
|
export function useDamagePerCycleForAll(mode: Mode) {
|
||||||
const { skillHistory, turnHistory } = useBattleDataStore.getState();
|
const { skillHistory, turnHistory } = useBattleDataStore(
|
||||||
|
useShallow(state => ({
|
||||||
|
skillHistory: state.skillHistory,
|
||||||
|
turnHistory: state.turnHistory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const damageMap = new Map<string, number>();
|
const damageMap = new Map<string, number>();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import useBattleDataStore from "@/stores/battleDataStore";
|
|||||||
import {attackTypeToString} from "@/types";
|
import {attackTypeToString} from "@/types";
|
||||||
|
|
||||||
export function useDamagePercentByType() {
|
export function useDamagePercentByType() {
|
||||||
const { skillHistory } = useBattleDataStore.getState();
|
const skillHistory = useBattleDataStore(state => state.skillHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const dmgByType = new Map<string, number>();
|
const dmgByType = new Map<string, number>();
|
||||||
@@ -11,7 +11,7 @@ export function useDamagePercentByType() {
|
|||||||
skillHistory.forEach(skill => {
|
skillHistory.forEach(skill => {
|
||||||
skill.damageDetail.forEach(detail => {
|
skill.damageDetail.forEach(detail => {
|
||||||
const type = detail.damage_type;
|
const type = detail.damage_type;
|
||||||
if (!type) return;
|
if (type === undefined) return;
|
||||||
dmgByType.set(attackTypeToString(type), (dmgByType.get(attackTypeToString(type)) || 0) + detail.damage);
|
dmgByType.set(attackTypeToString(type), (dmgByType.get(attackTypeToString(type)) || 0) + detail.damage);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import useBattleDataStore from "@/stores/battleDataStore";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useDamagePercentPerAvatar() {
|
export function useDamagePercentPerAvatar() {
|
||||||
const { skillHistory } = useBattleDataStore.getState();
|
const skillHistory = useBattleDataStore(state => state.skillHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const dmgByAvatar = new Map<number, number>();
|
const dmgByAvatar = new Map<number, number>();
|
||||||
|
|
||||||
skillHistory.forEach(t => {
|
skillHistory.forEach(t => {
|
||||||
|
if (!t.avatarId || t.avatarId <= 0) return;
|
||||||
dmgByAvatar.set(t.avatarId, (dmgByAvatar.get(t.avatarId) || 0) + t.totalDamage);
|
dmgByAvatar.set(t.avatarId, (dmgByAvatar.get(t.avatarId) || 0) + t.totalDamage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import useBattleDataStore from "@/stores/battleDataStore";
|
|||||||
import {attackTypeToString} from "@/types";
|
import {attackTypeToString} from "@/types";
|
||||||
|
|
||||||
export function useDamageByTypeForAvatar(avatarId: number) {
|
export function useDamageByTypeForAvatar(avatarId: number) {
|
||||||
const { skillHistory } = useBattleDataStore.getState();
|
const skillHistory = useBattleDataStore(state => state.skillHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const dmgMap = new Map<string, number>();
|
const dmgMap = new Map<string, number>();
|
||||||
@@ -14,6 +14,7 @@ export function useDamageByTypeForAvatar(avatarId: number) {
|
|||||||
|
|
||||||
for (const damage of skill.damageDetail) {
|
for (const damage of skill.damageDetail) {
|
||||||
const type = attackTypeToString(damage?.damage_type)?.toLowerCase() || "";
|
const type = attackTypeToString(damage?.damage_type)?.toLowerCase() || "";
|
||||||
|
if (!type) continue;
|
||||||
dmgMap.set(type, (dmgMap.get(type) || 0) + damage.damage);
|
dmgMap.set(type, (dmgMap.get(type) || 0) + damage.damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import useBattleDataStore from "@/stores/battleDataStore";
|
|||||||
import {attackTypeToString} from "@/types";
|
import {attackTypeToString} from "@/types";
|
||||||
|
|
||||||
export function useSkillDamageForAvatar(avatarId: number) {
|
export function useSkillDamageForAvatar(avatarId: number) {
|
||||||
const { skillHistory } = useBattleDataStore.getState();
|
const skillHistory = useBattleDataStore(state => state.skillHistory);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const dmgMap = new Map<string, number>();
|
const dmgMap = new Map<string, number>();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DamageType, AvatarAnalysisJson, UseSkillType, BattleBeginType, BattleEndType, DamageDetailType, EntityDefeatedType, SetBattleLineupType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType, VersionType, StatChangeType, UpdateTeamFormationType, Team, ParseAttackType } from '@/types';
|
import { DamageType, AvatarAnalysisJson, UseSkillType, BattleBeginType, BattleEndType, DamageDetailType, EntityDefeatedType, SetBattleLineupType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType, VersionType, StatChangeType, UpdateTeamFormationType, Team, ParseAttackType, AttackType } from '@/types';
|
||||||
import { InitializeEnemyType } from '@/types/enemy';
|
import { InitializeEnemyType } from '@/types/enemy';
|
||||||
import { AvatarBattleInfo, AvatarInfo, BattleDataStateJson, EnemyInfo, SkillBattleInfo, TurnBattleInfo } from '@/types/mics';
|
import { AvatarBattleInfo, AvatarInfo, BattleDataStateJson, EnemyInfo, SkillBattleInfo, TurnBattleInfo } from '@/types/mics';
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
@@ -54,27 +54,33 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
avatarDetail: undefined,
|
avatarDetail: undefined,
|
||||||
enemyDetail: undefined,
|
enemyDetail: undefined,
|
||||||
loadBattleDataFromJSON: (data: BattleDataStateJson) => {
|
loadBattleDataFromJSON: (data: BattleDataStateJson) => {
|
||||||
const skillHistory = data.skillHistory.map(it => {
|
const skillHistory = (data.skillHistory || []).map(it => {
|
||||||
it.damageDetail = it.damageDetail.map(it => {
|
const rawDetails = it.damageDetail || [];
|
||||||
|
const damageDetail = rawDetails.map(d => {
|
||||||
|
const rawType = d.damage_type !== undefined ? d.damage_type : (d as { type?: AttackType | string }).type;
|
||||||
return {
|
return {
|
||||||
...it,
|
...d,
|
||||||
damage_type: ParseAttackType(it.damage_type)
|
damage_type: ParseAttackType(rawType)
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
return it
|
return {
|
||||||
})
|
...it,
|
||||||
|
skillType: ParseAttackType(it.skillType),
|
||||||
|
damageDetail
|
||||||
|
};
|
||||||
|
});
|
||||||
set({
|
set({
|
||||||
lineup: data.lineup,
|
lineup: data.lineup || [],
|
||||||
turnHistory: data.turnHistory,
|
turnHistory: data.turnHistory || [],
|
||||||
skillHistory: skillHistory,
|
skillHistory: skillHistory,
|
||||||
dataAvatar: data.dataAvatar,
|
dataAvatar: data.dataAvatar || [],
|
||||||
totalAV: data.totalAV,
|
totalAV: data.totalAV || 0,
|
||||||
totalDamage: data.totalDamage,
|
totalDamage: data.totalDamage || 0,
|
||||||
damagePerAV: data.damagePerAV,
|
damagePerAV: data.damagePerAV || 0,
|
||||||
cycleIndex: data.cycleIndex,
|
cycleIndex: data.cycleIndex || 0,
|
||||||
waveIndex: data.waveIndex,
|
waveIndex: data.waveIndex || 1,
|
||||||
maxWave: data.maxWave,
|
maxWave: data.maxWave ?? Infinity,
|
||||||
maxCycle: data.maxCycle,
|
maxCycle: data.maxCycle ?? Infinity,
|
||||||
version: data.version,
|
version: data.version,
|
||||||
avatarDetail: data.avatarDetail,
|
avatarDetail: data.avatarDetail,
|
||||||
enemyDetail: data.enemyDetail
|
enemyDetail: data.enemyDetail
|
||||||
|
|||||||
+23
-11
@@ -12,6 +12,7 @@ export interface DamageDetailType {
|
|||||||
damage: number;
|
damage: number;
|
||||||
overkill_damage?: number;
|
overkill_damage?: number;
|
||||||
damage_type?: AttackType;
|
damage_type?: AttackType;
|
||||||
|
type?: AttackType | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -35,23 +36,32 @@ export enum AttackType {
|
|||||||
|
|
||||||
|
|
||||||
const attackTypeMap: Record<string, AttackType> = {
|
const attackTypeMap: Record<string, AttackType> = {
|
||||||
Talent: AttackType.Unknown,
|
Unknown: AttackType.Unknown,
|
||||||
Basic: AttackType.Normal,
|
Normal: AttackType.Normal,
|
||||||
Skill: AttackType.BPSkill,
|
BPSkill: AttackType.BPSkill,
|
||||||
Ultimate: AttackType.Ultra,
|
Ultra: AttackType.Ultra,
|
||||||
QTE: AttackType.QTE,
|
QTE: AttackType.QTE,
|
||||||
DOT: AttackType.DOT,
|
DOT: AttackType.DOT,
|
||||||
DoT: AttackType.DOT,
|
DoT: AttackType.DOT,
|
||||||
Pursued: AttackType.Pursued,
|
Pursued: AttackType.Pursued,
|
||||||
|
Maze: AttackType.Maze,
|
||||||
|
MazeNormal: AttackType.MazeNormal,
|
||||||
|
Insert: AttackType.Insert,
|
||||||
|
ElementDamage: AttackType.ElementDamage,
|
||||||
|
Level: AttackType.Level,
|
||||||
|
Servant: AttackType.Servant,
|
||||||
|
TrueDamage: AttackType.TrueDamage,
|
||||||
|
ElationDamage: AttackType.ElationDamage,
|
||||||
|
Talent: AttackType.Unknown,
|
||||||
|
Basic: AttackType.Normal,
|
||||||
|
Skill: AttackType.BPSkill,
|
||||||
|
Ultimate: AttackType.Ultra,
|
||||||
Additional: AttackType.Pursued,
|
Additional: AttackType.Pursued,
|
||||||
Technique: AttackType.Maze,
|
Technique: AttackType.Maze,
|
||||||
MazeNormal: AttackType.MazeNormal,
|
|
||||||
"Follow-up": AttackType.Insert,
|
"Follow-up": AttackType.Insert,
|
||||||
"Follow-Up": AttackType.Insert,
|
"Follow-Up": AttackType.Insert,
|
||||||
"Elemental Damage": AttackType.ElementDamage,
|
"Elemental Damage": AttackType.ElementDamage,
|
||||||
Break: AttackType.ElementDamage,
|
Break: AttackType.ElementDamage,
|
||||||
Level: AttackType.Level,
|
|
||||||
Servant: AttackType.Servant,
|
|
||||||
"True Damage": AttackType.TrueDamage,
|
"True Damage": AttackType.TrueDamage,
|
||||||
True: AttackType.TrueDamage,
|
True: AttackType.TrueDamage,
|
||||||
"Elation Damage": AttackType.ElationDamage,
|
"Elation Damage": AttackType.ElationDamage,
|
||||||
@@ -75,12 +85,14 @@ export function ParseAttackType(type: AttackType | string | undefined): AttackTy
|
|||||||
return attackTypeMap[type] ?? AttackType.Unknown;
|
return attackTypeMap[type] ?? AttackType.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attackTypeToString(type: AttackType | undefined): string {
|
export function attackTypeToString(type: AttackType | string | undefined): string {
|
||||||
if (type === undefined) {
|
if (type === undefined || type === null) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
const parsed = typeof type === "string" ? ParseAttackType(type) : type;
|
||||||
|
|
||||||
|
switch (parsed) {
|
||||||
case AttackType.Unknown: return "Talent";
|
case AttackType.Unknown: return "Talent";
|
||||||
case AttackType.Normal: return "Basic";
|
case AttackType.Normal: return "Basic";
|
||||||
case AttackType.BPSkill: return "Skill";
|
case AttackType.BPSkill: return "Skill";
|
||||||
|
|||||||
+7775
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user