update for 3.3.1
This commit is contained in:
@@ -59,7 +59,7 @@
|
|||||||
"knight": "存护",
|
"knight": "存护",
|
||||||
"mage": "智识",
|
"mage": "智识",
|
||||||
"priest": "丰饶",
|
"priest": "丰饶",
|
||||||
"rouge": "巡猎",
|
"rogue": "巡猎",
|
||||||
"shaman": "同协",
|
"shaman": "同协",
|
||||||
"warlock": "虚无",
|
"warlock": "虚无",
|
||||||
"memory": "记忆",
|
"memory": "记忆",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"knight": "Preservation",
|
"knight": "Preservation",
|
||||||
"mage": "Erudition",
|
"mage": "Erudition",
|
||||||
"priest": "Abundance",
|
"priest": "Abundance",
|
||||||
"rouge": "The Hunt",
|
"rogue": "The Hunt",
|
||||||
"shaman": "Harmony",
|
"shaman": "Harmony",
|
||||||
"warlock": "Nihility",
|
"warlock": "Nihility",
|
||||||
"memory": "Remembrance",
|
"memory": "Remembrance",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"knight": "存護",
|
"knight": "存護",
|
||||||
"mage": "知恵",
|
"mage": "知恵",
|
||||||
"priest": "豊穣",
|
"priest": "豊穣",
|
||||||
"rouge": "巡狩",
|
"rogue": "巡狩",
|
||||||
"shaman": "調和",
|
"shaman": "調和",
|
||||||
"warlock": "虚無",
|
"warlock": "虚無",
|
||||||
"memory": "記憶",
|
"memory": "記憶",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"knight": "보존",
|
"knight": "보존",
|
||||||
"mage": "지혜",
|
"mage": "지혜",
|
||||||
"priest": "풍요",
|
"priest": "풍요",
|
||||||
"rouge": "사냥",
|
"rogue": "사냥",
|
||||||
"shaman": "조화",
|
"shaman": "조화",
|
||||||
"warlock": "허무",
|
"warlock": "허무",
|
||||||
"memory": "기억",
|
"memory": "기억",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"actionValue": "Giá trị hành động",
|
"actionValue": "Giá trị hành động",
|
||||||
"character": "Nhân vật",
|
"character": "Nhân vật",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "Đường dẫn",
|
"path": "Vận mệnh",
|
||||||
"rarity": "Số sao",
|
"rarity": "Số sao",
|
||||||
"element": "Nguyên tố",
|
"element": "Nguyên tố",
|
||||||
"totalTurn": "Tổng lượt",
|
"totalTurn": "Tổng lượt",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"knight": "Bảo Hộ",
|
"knight": "Bảo Hộ",
|
||||||
"mage": "Tri Thức",
|
"mage": "Tri Thức",
|
||||||
"priest": "Phong Phú",
|
"priest": "Phong Phú",
|
||||||
"rouge": "Săn Bắn",
|
"rogue": "Săn Bắn",
|
||||||
"shaman": "Hài Hòa",
|
"shaman": "Hài Hòa",
|
||||||
"warlock": "Hư Vô",
|
"warlock": "Hư Vô",
|
||||||
"memory": "Ký Ức",
|
"memory": "Ký Ức",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"knight": "存护",
|
"knight": "存护",
|
||||||
"mage": "智识",
|
"mage": "智识",
|
||||||
"priest": "丰饶",
|
"priest": "丰饶",
|
||||||
"rouge": "巡猎",
|
"rogue": "巡猎",
|
||||||
"shaman": "同协",
|
"shaman": "同协",
|
||||||
"warlock": "虚无",
|
"warlock": "虚无",
|
||||||
"memory": "记忆",
|
"memory": "记忆",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import DamagePerAvatarForAll from "@/components/chart/damagePerAvatarForAll";
|
|||||||
import MultiCharLineChart from "@/components/chart/damageLineForAll";
|
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";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const transI18n = useTranslations("DataAnalysisPage");
|
const transI18n = useTranslations("DataAnalysisPage");
|
||||||
@@ -18,7 +19,8 @@ export default function Home() {
|
|||||||
totalAV,
|
totalAV,
|
||||||
totalDamage,
|
totalDamage,
|
||||||
damagePerAV,
|
damagePerAV,
|
||||||
turnHistory
|
turnHistory,
|
||||||
|
enemyDetail
|
||||||
} = useBattleDataStore();
|
} = useBattleDataStore();
|
||||||
const [expandedCharts, setExpandedCharts] = useState<string[]>([]);
|
const [expandedCharts, setExpandedCharts] = useState<string[]>([]);
|
||||||
|
|
||||||
@@ -55,22 +57,22 @@ export default function Home() {
|
|||||||
<div className="grid grid-cols-2 gap-2 mb-3">
|
<div className="grid grid-cols-2 gap-2 mb-3">
|
||||||
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-primary text-primary-content text-center shadow-md">
|
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-primary text-primary-content text-center shadow-md">
|
||||||
{transI18n("totalDamage")}
|
{transI18n("totalDamage")}
|
||||||
<div>{Number(totalDamage).toFixed(2)}</div>
|
<div>{Number(totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-secondary text-secondary-content text-center shadow-md">
|
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-secondary text-secondary-content text-center shadow-md">
|
||||||
{transI18n("totalAV")}
|
{transI18n("totalAV")}
|
||||||
<div>{Number(totalAV).toFixed(2)}</div>
|
<div>{Number(totalAV).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-accent text-accent-content text-center shadow-md">
|
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-accent text-accent-content text-center shadow-md">
|
||||||
{transI18n("damagePerAV")}
|
{transI18n("damagePerAV")}
|
||||||
<div>{Number(damagePerAV).toFixed(2)}</div>
|
<div>{Number(damagePerAV).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-warning text-warning-content text-center shadow-md">
|
<div className="p-2 text-base lg:text-lg xl:text-xl rounded bg-warning text-warning-content text-center shadow-md">
|
||||||
{transI18n("totalTurn")}
|
{transI18n("totalTurn")}
|
||||||
<div>{turnHistory.filter(it => it.avatarId && it.avatarId != -1).length}</div>
|
<div>{turnHistory.filter(it => it.avatarId && it.avatarId != -1).length}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{enemyDetail && <EnemyBar />}
|
||||||
|
|
||||||
<div className="rounded-lg p-2 shadow-md flex-grow">
|
<div className="rounded-lg p-2 shadow-md flex-grow">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export default function ActionBar() {
|
|||||||
{`${transI18n("useSkill")}: ${transI18n(attackTypeToString(turn.skillType).toLowerCase())}`}
|
{`${transI18n("useSkill")}: ${transI18n(attackTypeToString(turn.skillType).toLowerCase())}`}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-primary text-xs max-w-full">
|
<div className="text-primary text-xs max-w-full">
|
||||||
{`${transI18n("totalDamage")}: ${turn.totalDamage.toFixed(2)}`}
|
{`${transI18n("totalDamage")}: ${Number(turn.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -234,11 +234,11 @@ export default function ActionBar() {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||||
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
||||||
<h4 className="text-lg font-semibold mb-2 text-cyan-500 border-b border-cyan-300/30 pb-1">{transI18n("actionValue")}</h4>
|
<h4 className="text-lg font-semibold mb-2 text-cyan-500 border-b border-cyan-300/30 pb-1">{transI18n("actionValue")}</h4>
|
||||||
<p className="mt-2">{turnHistory[selectTurn?.turnBattleId].actionValue.toFixed(2)}</p>
|
<p className="mt-2">{Number(turnHistory[selectTurn?.turnBattleId].actionValue).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
||||||
<h4 className="text-lg font-semibold mb-2 text-purple-500 border-b border-purple-300/30 pb-1">{transI18n("totalDamage")}</h4>
|
<h4 className="text-lg font-semibold mb-2 text-purple-500 border-b border-purple-300/30 pb-1">{transI18n("totalDamage")}</h4>
|
||||||
<p className="mt-2 font-bold text-lg">{selectTurn?.totalDamage.toFixed(2)}</p>
|
<p className="mt-2 font-bold text-lg">{Number(selectTurn?.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ export default function ActionBar() {
|
|||||||
className="flex flex-col items-start gap-1 p-3 rounded-lg shadow bg-base-200"
|
className="flex flex-col items-start gap-1 p-3 rounded-lg shadow bg-base-200"
|
||||||
>
|
>
|
||||||
<span className="text-lg font-semibold text-primary">
|
<span className="text-lg font-semibold text-primary">
|
||||||
{detail.damage.toFixed(2)}
|
{Number(detail.damage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs uppercase text-gray-500 tracking-wide">
|
<span className="text-xs uppercase text-gray-500 tracking-wide">
|
||||||
{transI18n(attackTypeToString(detail?.damage_type).toLowerCase())}
|
{transI18n(attackTypeToString(detail?.damage_type).toLowerCase())}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { getNameChar } from '@/helper';
|
|||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { AvatarHakushiType } from '@/types';
|
import { AvatarHakushiType } from '@/types';
|
||||||
import NameAvatar from '../nameAvatar';
|
import NameAvatar from '../nameAvatar';
|
||||||
|
import useBattleDataStore from '@/stores/battleDataStore';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { AvatarInfo } from '@/types/mics';
|
||||||
|
|
||||||
interface CharacterCardProps {
|
interface CharacterCardProps {
|
||||||
data: AvatarHakushiType
|
data: AvatarHakushiType
|
||||||
@@ -14,6 +17,8 @@ interface CharacterCardProps {
|
|||||||
export default function CharacterCard({ data }: CharacterCardProps) {
|
export default function CharacterCard({ data }: CharacterCardProps) {
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
const text = getNameChar(locale, data)
|
const text = getNameChar(locale, data)
|
||||||
|
const { avatarDetail } = useBattleDataStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="z-10 flex flex-col w-28 items-center p-1 rounded-md shadow-lg bg-gradient-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-gradient-to-b from-customStart to-customEnd transform transition-transform duration-300 hover:scale-105 m-1">
|
||||||
<div
|
<div
|
||||||
@@ -50,6 +55,36 @@ export default function CharacterCard({ data }: CharacterCardProps) {
|
|||||||
text={text}
|
text={text}
|
||||||
className="mt-2 text-center text-base font-normal leading-tight"
|
className="mt-2 text-center text-base font-normal leading-tight"
|
||||||
/>
|
/>
|
||||||
|
{avatarDetail && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-xs text-base-content/70 mx-1">HP:</span>
|
||||||
|
<span className="text-xs font-medium">
|
||||||
|
<span className="text-error">
|
||||||
|
{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">
|
||||||
|
<div
|
||||||
|
className="bg-error h-2.5 rounded-full transition-all duration-300"
|
||||||
|
style={{
|
||||||
|
width: `${Math.max(0, Math.min(100, ((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100))}%`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="absolute inset-0 flex items-center justify-center text-xs text-white font-medium">
|
||||||
|
{Math.round(((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
71
src/components/enemybar/index.tsx
Normal file
71
src/components/enemybar/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import useBattleDataStore from "@/stores/battleDataStore";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
function formatEnemyIdForURL(id?: number): string {
|
||||||
|
const n = id ?? 0;
|
||||||
|
const adjusted = n.toString().length === 9 ? n / 100 : n;
|
||||||
|
return adjusted.toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EnemyBar() {
|
||||||
|
const { enemyDetail } = useBattleDataStore()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-3 w-full">
|
||||||
|
<div className="flex gap-3 overflow-x-auto pb-2">
|
||||||
|
{enemyDetail && Object.values(enemyDetail).filter((enemy) => (enemy.stats?.AV > 0
|
||||||
|
&& enemy.stats.HP <= enemy.maxHP)).map((enemy, uid) => (
|
||||||
|
<div key={uid} className="bg-base-200 rounded-lg p-3 border border-gray-700 w-52 flex-shrink-0">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Image
|
||||||
|
src={`https://api.hakush.in/hsr/UI/monstermiddleicon/Monster_${formatEnemyIdForURL(enemy.id)}.webp`}
|
||||||
|
alt={enemy.name}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="object-cover w-10 h-10 rounded-lg"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="text-base font-semibold leading-tight truncate overflow-hidden" title={enemy.name}>
|
||||||
|
{enemy.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-base-content/70 text-xs">Level {enemy.level || 1}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="text-xs text-base-content/70">HP:</div>
|
||||||
|
<div className="text-xs font-medium">
|
||||||
|
<div className="text-error">
|
||||||
|
{Number(enemy?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
|
</div>
|
||||||
|
<div className="text-base-content/50 mx-1">/</div>
|
||||||
|
<div className="text-base-content/70">
|
||||||
|
{Number(enemy?.maxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||||
|
</div>
|
||||||
|
</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, ((enemy.stats?.HP || 0) / (enemy.maxHP || 100)) * 100))}%`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<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)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ 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 } = useBattleDataStore()
|
const { loadBattleDataFromJSON, version } = useBattleDataStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const transI18n = useTranslations("DataAnalysisPage")
|
const transI18n = useTranslations("DataAnalysisPage")
|
||||||
const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore();
|
const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore();
|
||||||
@@ -219,6 +219,16 @@ export default function Header() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-500">For Veritas</p>
|
<p className="text-sm text-gray-500">For Veritas</p>
|
||||||
</a>
|
</a>
|
||||||
|
{version && (
|
||||||
|
<div className="px-2">
|
||||||
|
<div className="inline-flex items-center space-x-2 px-3 py-1.5 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full shadow-md hover:shadow-lg transition-all duration-200">
|
||||||
|
<div className="w-1.5 h-1.5 bg-white rounded-full animate-pulse"></div>
|
||||||
|
<div className="text-xs font-semibold text-white">
|
||||||
|
{version}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export default function LineupBar() {
|
|||||||
<p>
|
<p>
|
||||||
{transI18n("eidolons")}: <span className="font-bold">{avatar?.data?.rank}</span>
|
{transI18n("eidolons")}: <span className="font-bold">{avatar?.data?.rank}</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span>{transI18n("lightcones")}:</span>
|
<span>{transI18n("lightcones")}:</span>
|
||||||
<Image
|
<Image
|
||||||
src={`https://api.hakush.in/hsr/UI/lightconemediumicon/${avatar?.Lightcone?.item_id}.webp`}
|
src={`https://api.hakush.in/hsr/UI/lightconemediumicon/${avatar?.Lightcone?.item_id}.webp`}
|
||||||
@@ -218,8 +218,8 @@ export default function LineupBar() {
|
|||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</p>
|
</div>
|
||||||
<p className="flex items-center space-x-2 w-full">
|
<div className="flex items-center space-x-2 w-full">
|
||||||
<span>{transI18n("relics")}:</span>
|
<span>{transI18n("relics")}:</span>
|
||||||
<div className="grid grid-cols-3 md:flex md:flex-row w-full">
|
<div className="grid grid-cols-3 md:flex md:flex-row w-full">
|
||||||
{relicIds.map(it => (
|
{relicIds.map(it => (
|
||||||
@@ -233,7 +233,7 @@ export default function LineupBar() {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
@@ -256,10 +256,10 @@ export default function LineupBar() {
|
|||||||
<ShowCaseInfo></ShowCaseInfo>
|
<ShowCaseInfo></ShowCaseInfo>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
||||||
<p className="mt-2 font-bold text-lg text-cyan-500">{transI18n("totalTurn")}: <span className="text-base-content">{totalTurn.toFixed(2)}</span></p>
|
<p className="mt-2 font-bold text-lg text-cyan-500">{transI18n("totalTurn")}: <span className="text-base-content">{Number(totalTurn).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</span></p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
||||||
<h4 className="text-lg font-semibold mb-2 text-purple-500">{transI18n("totalDamage")}: <span className="text-base-content">{totalDamage.toFixed(2)}</span></h4>
|
<h4 className="text-lg font-semibold mb-2 text-purple-500">{transI18n("totalDamage")}: <span className="text-base-content">{Number(totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</span></h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
<div className="bg-base-200 rounded-lg p-4 shadow-md">
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ export const exportBattleData = (
|
|||||||
waveIndex,
|
waveIndex,
|
||||||
dataAvatar,
|
dataAvatar,
|
||||||
maxWave,
|
maxWave,
|
||||||
maxCycle
|
maxCycle,
|
||||||
|
version,
|
||||||
|
avatarDetail,
|
||||||
|
enemyDetail
|
||||||
} = useBattleDataStore.getState();
|
} = useBattleDataStore.getState();
|
||||||
|
|
||||||
const data: BattleDataStateJson = {
|
const data: BattleDataStateJson = {
|
||||||
@@ -28,7 +31,10 @@ export const exportBattleData = (
|
|||||||
cycleIndex,
|
cycleIndex,
|
||||||
waveIndex,
|
waveIndex,
|
||||||
maxWave,
|
maxWave,
|
||||||
maxCycle
|
maxCycle,
|
||||||
|
version,
|
||||||
|
avatarDetail,
|
||||||
|
enemyDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataStr = JSON.stringify(data, null, 2);
|
const dataStr = JSON.stringify(data, null, 2);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import axios from 'axios';
|
|||||||
export async function checkConnectTcpApi(): Promise<boolean> {
|
export async function checkConnectTcpApi(): Promise<boolean> {
|
||||||
const { host, port, connectionType } = useSocketStore.getState()
|
const { host, port, connectionType } = useSocketStore.getState()
|
||||||
let url = `${host}:${port}/check-tcp`
|
let url = `${host}:${port}/check-tcp`
|
||||||
if (connectionType === "FireflyPSLocal") {
|
if (connectionType === "PS") {
|
||||||
url = "http://localhost:21000/check-tcp"
|
url = "http://localhost:21000/check-tcp"
|
||||||
}
|
}
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
|||||||
@@ -26,17 +26,21 @@ function safeParse(json: unknown | string) {
|
|||||||
export const connectSocket = (): Socket => {
|
export const connectSocket = (): Socket => {
|
||||||
const { host, port, connectionType, setStatus } = useSocketStore.getState();
|
const { host, port, connectionType, setStatus } = useSocketStore.getState();
|
||||||
const {
|
const {
|
||||||
onSetBattleLineupService,
|
onConnectedService,
|
||||||
onTurnEndService,
|
|
||||||
onUseSkillService,
|
|
||||||
onKillService,
|
|
||||||
onDamageService,
|
|
||||||
onBattleEndService,
|
|
||||||
onTurnBeginService,
|
|
||||||
onBattleBeginService,
|
onBattleBeginService,
|
||||||
onCreateBattleService,
|
onSetBattleLineupService,
|
||||||
|
onDamageService,
|
||||||
|
onTurnBeginService,
|
||||||
|
onTurnEndService,
|
||||||
|
onEntityDefeatedService,
|
||||||
|
onUseSkillService,
|
||||||
|
onUpdateWaveService,
|
||||||
onUpdateCycleService,
|
onUpdateCycleService,
|
||||||
OnUpdateWaveService
|
onStatChange,
|
||||||
|
onUpdateTeamFormation,
|
||||||
|
onInitializeEnemyService,
|
||||||
|
onBattleEndService,
|
||||||
|
onCreateBattleService,
|
||||||
} = useBattleDataStore.getState();
|
} = useBattleDataStore.getState();
|
||||||
|
|
||||||
let url = `${host}:${port}`;
|
let url = `${host}:${port}`;
|
||||||
@@ -86,68 +90,73 @@ export const connectSocket = (): Socket => {
|
|||||||
setStatus(true);
|
setStatus(true);
|
||||||
notify(`Kết nối thành công với Socket ID: ${socket?.id}`, 'success');
|
notify(`Kết nối thành công với Socket ID: ${socket?.id}`, 'success');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBattleBegin = (data: BattleBeginType) => {
|
const onBattleBegin = (data: BattleBeginType) => {
|
||||||
notify("Battle Started!", "info")
|
notify("Battle Started!", "info")
|
||||||
onBattleBeginService(data)
|
onBattleBeginService(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSocketConnected()) onConnect();
|
if (isSocketConnected()) onConnect();
|
||||||
|
socket.on("Connected", (json) => {
|
||||||
socket.on("OnSetBattleLineup", (json) => {
|
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onSetBattleLineupService(data);
|
if (data) onConnectedService(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("OnTurnEnd", (json) => {
|
|
||||||
const data = safeParse(json);
|
|
||||||
if (data) onTurnEndService(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("OnUseSkill", (json) => {
|
|
||||||
const data = safeParse(json);
|
|
||||||
if (data) onUseSkillService(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("OnKill", (json) => {
|
|
||||||
const data = safeParse(json);
|
|
||||||
if (data) onKillService(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("OnDamage", (json) => {
|
|
||||||
const data = safeParse(json);
|
|
||||||
if (data) onDamageService(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("OnBattleBegin", (json) => {
|
socket.on("OnBattleBegin", (json) => {
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onBattleBegin(data);
|
if (data) onBattleBegin(data);
|
||||||
});
|
});
|
||||||
|
socket.on("OnSetBattleLineup", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onSetBattleLineupService(data);
|
||||||
|
});
|
||||||
|
socket.on("OnDamage", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onDamageService(data);
|
||||||
|
});
|
||||||
socket.on("OnTurnBegin", (json) => {
|
socket.on("OnTurnBegin", (json) => {
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onTurnBeginService(data);
|
if (data) onTurnBeginService(data);
|
||||||
});
|
});
|
||||||
|
socket.on("OnTurnEnd", (json) => {
|
||||||
socket.on("OnBattleEnd", (json) => {
|
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onBattleEndService(data);
|
if (data) onTurnEndService(data);
|
||||||
|
});
|
||||||
|
socket.on("OnEntityDefeated", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onEntityDefeatedService(data);
|
||||||
|
});
|
||||||
|
socket.on("OnUseSkill", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onUseSkillService(data);
|
||||||
|
});
|
||||||
|
socket.on("OnUpdateWave", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onUpdateWaveService(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("OnUpdateCycle", (json) => {
|
socket.on("OnUpdateCycle", (json) => {
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onUpdateCycleService(data);
|
if (data) onUpdateCycleService(data);
|
||||||
});
|
});
|
||||||
|
socket.on("OnStatChange", (json) => {
|
||||||
socket.on("OnUpdateWave", (json) => {
|
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) OnUpdateWaveService(data);
|
if (data) onStatChange(data);
|
||||||
|
});
|
||||||
|
socket.on("OnUpdateTeamFormation", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onUpdateTeamFormation(data);
|
||||||
|
});
|
||||||
|
socket.on("OnInitializeEnemy", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onInitializeEnemyService(data);
|
||||||
|
});
|
||||||
|
socket.on("OnBattleEnd", (json) => {
|
||||||
|
const data = safeParse(json);
|
||||||
|
if (data) onBattleEndService(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("OnCreateBattle", (json) => {
|
socket.on("OnCreateBattle", (json) => {
|
||||||
const data = safeParse(json);
|
const data = safeParse(json);
|
||||||
if (data) onCreateBattleService(data);
|
if (data) onCreateBattleService(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("Error", (msg: string) => {
|
socket.on("Error", (msg: string) => {
|
||||||
console.error("Server Error:", msg);
|
console.error("Server Error:", msg);
|
||||||
});
|
});
|
||||||
@@ -157,34 +166,42 @@ export const connectSocket = (): Socket => {
|
|||||||
|
|
||||||
export const disconnectSocket = (): void => {
|
export const disconnectSocket = (): void => {
|
||||||
const {
|
const {
|
||||||
onSetBattleLineupService,
|
onConnectedService,
|
||||||
onTurnEndService,
|
|
||||||
onUseSkillService,
|
|
||||||
onKillService,
|
|
||||||
onDamageService,
|
|
||||||
onBattleEndService,
|
|
||||||
onTurnBeginService,
|
|
||||||
onBattleBeginService,
|
onBattleBeginService,
|
||||||
onCreateBattleService,
|
onSetBattleLineupService,
|
||||||
|
onDamageService,
|
||||||
|
onTurnBeginService,
|
||||||
|
onTurnEndService,
|
||||||
|
onEntityDefeatedService,
|
||||||
|
onUseSkillService,
|
||||||
|
onUpdateWaveService,
|
||||||
onUpdateCycleService,
|
onUpdateCycleService,
|
||||||
OnUpdateWaveService
|
onStatChange,
|
||||||
|
onUpdateTeamFormation,
|
||||||
|
onInitializeEnemyService,
|
||||||
|
onBattleEndService,
|
||||||
|
onCreateBattleService,
|
||||||
} = useBattleDataStore.getState();
|
} = useBattleDataStore.getState();
|
||||||
const onBattleBegin = (data: BattleBeginType) => {
|
const onBattleBegin = (data: BattleBeginType) => {
|
||||||
notify("Battle Started!", "info")
|
notify("Battle Started!", "info")
|
||||||
onBattleBeginService(data)
|
onBattleBeginService(data)
|
||||||
}
|
}
|
||||||
if (socket) {
|
if (socket) {
|
||||||
|
socket.off("Connected", (json) => onConnectedService(JSON.parse(json)));
|
||||||
|
socket.off("OnBattleBegin", (json) => onBattleBegin(JSON.parse(json)));
|
||||||
socket.off("OnSetBattleLineup", (json) => onSetBattleLineupService(JSON.parse(json)));
|
socket.off("OnSetBattleLineup", (json) => onSetBattleLineupService(JSON.parse(json)));
|
||||||
socket.off("OnTurnEnd", (json) => onTurnEndService(JSON.parse(json)));
|
socket.off("OnTurnEnd", (json) => onTurnEndService(JSON.parse(json)));
|
||||||
socket.off("OnUseSkill", (json) => onUseSkillService(JSON.parse(json)));
|
socket.off("OnUseSkill", (json) => onUseSkillService(JSON.parse(json)));
|
||||||
socket.off("OnKill", (json) => onKillService(JSON.parse(json)));
|
socket.off("OnEntityDefeated", (json) => onEntityDefeatedService(JSON.parse(json)));
|
||||||
socket.off("OnDamage", (json) => onDamageService(JSON.parse(json)));
|
socket.off("OnDamage", (json) => onDamageService(JSON.parse(json)));
|
||||||
socket.off('OnBattleBegin', (json) => onBattleBegin(JSON.parse(json)));
|
|
||||||
socket.off('OnTurnBegin', (json) => onTurnBeginService(JSON.parse(json)));
|
socket.off('OnTurnBegin', (json) => onTurnBeginService(JSON.parse(json)));
|
||||||
socket.off('OnBattleEnd', (json) => onBattleEndService(JSON.parse(json)));
|
socket.off('OnBattleEnd', (json) => onBattleEndService(JSON.parse(json)));
|
||||||
socket.off('OnUpdateCycle', (json) => onUpdateCycleService(JSON.parse(json)));
|
socket.off('OnUpdateCycle', (json) => onUpdateCycleService(JSON.parse(json)));
|
||||||
socket.off('OnUpdateWave', (json) => OnUpdateWaveService(JSON.parse(json)));
|
socket.off('OnUpdateWave', (json) => onUpdateWaveService(JSON.parse(json)));
|
||||||
socket.off('OnCreateBattle', (json) => onCreateBattleService(JSON.parse(json)));
|
socket.off('OnCreateBattle', (json) => onCreateBattleService(JSON.parse(json)));
|
||||||
|
socket.off('OnStatChange', (json) => onStatChange(JSON.parse(json)));
|
||||||
|
socket.off('OnUpdateTeamFormation', (json) => onUpdateTeamFormation(JSON.parse(json)));
|
||||||
|
socket.off('OnInitializeEnemy', (json) => onInitializeEnemyService(JSON.parse(json)));
|
||||||
socket.offAny();
|
socket.offAny();
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
useSocketStore.getState().setStatus(false);
|
useSocketStore.getState().setStatus(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { AttackResultType, AvatarAnalysisJson, AvatarSkillType, BattleBeginType, BattleEndType, DamageDetailType, KillType, LineUpType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType } from '@/types';
|
import { DamageType, AvatarAnalysisJson, UseSkillType, BattleBeginType, BattleEndType, DamageDetailType, EntityDefeatedType, SetBattleLineupType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType, VersionType, StatType, StatChangeType, UpdateTeamFormationType } from '@/types';
|
||||||
import { AvatarBattleInfo, BattleDataStateJson, SkillBattleInfo, TurnBattleInfo } from '@/types/mics';
|
import { InitializeEnemyType } from '@/types/enemy';
|
||||||
|
import { AvatarBattleInfo, AvatarInfo, BattleDataStateJson, EnemyInfo, SkillBattleInfo, TurnBattleInfo } from '@/types/mics';
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
|
||||||
|
|
||||||
@@ -14,19 +15,26 @@ interface BattleDataState {
|
|||||||
cycleIndex: number;
|
cycleIndex: number;
|
||||||
waveIndex: number;
|
waveIndex: number;
|
||||||
maxWave: number;
|
maxWave: number;
|
||||||
maxCycle: number
|
maxCycle: number;
|
||||||
|
version?: string;
|
||||||
|
avatarDetail?: Record<number, AvatarInfo>;
|
||||||
|
enemyDetail?: Record<number, EnemyInfo>;
|
||||||
|
|
||||||
onSetBattleLineupService: (data: LineUpType) => void;
|
onConnectedService: (data: VersionType) => void
|
||||||
onTurnEndService: (data: TurnEndType) => void;
|
|
||||||
onBattleEndService: (data: BattleEndType) => void;
|
|
||||||
onUseSkillService: (data: AvatarSkillType) => void;
|
|
||||||
onKillService: (data: KillType) => void
|
|
||||||
onDamageService: (data: AttackResultType) => void;
|
|
||||||
onBattleBeginService: (data: BattleBeginType) => void;
|
onBattleBeginService: (data: BattleBeginType) => void;
|
||||||
|
onSetBattleLineupService: (data: SetBattleLineupType) => void;
|
||||||
|
onDamageService: (data: DamageType) => void;
|
||||||
onTurnBeginService: (data: TurnBeginType) => void;
|
onTurnBeginService: (data: TurnBeginType) => void;
|
||||||
onCreateBattleService: (data: AvatarAnalysisJson[]) => void;
|
onTurnEndService: (data: TurnEndType) => void;
|
||||||
OnUpdateWaveService: (data: UpdateWaveType) => void;
|
onEntityDefeatedService: (data: EntityDefeatedType) => void;
|
||||||
|
onUseSkillService: (data: UseSkillType) => void;
|
||||||
|
onUpdateWaveService: (data: UpdateWaveType) => void;
|
||||||
onUpdateCycleService: (data: UpdateCycleType) => void;
|
onUpdateCycleService: (data: UpdateCycleType) => void;
|
||||||
|
onStatChange: (data: StatChangeType) => void;
|
||||||
|
onUpdateTeamFormation: (data: UpdateTeamFormationType) => void;
|
||||||
|
onInitializeEnemyService: (data: InitializeEnemyType) => void;
|
||||||
|
onBattleEndService: (data: BattleEndType) => void;
|
||||||
|
onCreateBattleService: (data: AvatarAnalysisJson[]) => void;
|
||||||
loadBattleDataFromJSON: (data: BattleDataStateJson) => void;
|
loadBattleDataFromJSON: (data: BattleDataStateJson) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +50,9 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
waveIndex: 1,
|
waveIndex: 1,
|
||||||
maxWave: Infinity,
|
maxWave: Infinity,
|
||||||
maxCycle: Infinity,
|
maxCycle: Infinity,
|
||||||
|
version: undefined,
|
||||||
|
avatarDetail: undefined,
|
||||||
|
enemyDetail: undefined,
|
||||||
loadBattleDataFromJSON: (data: BattleDataStateJson) => {
|
loadBattleDataFromJSON: (data: BattleDataStateJson) => {
|
||||||
set({
|
set({
|
||||||
lineup: data.lineup,
|
lineup: data.lineup,
|
||||||
@@ -55,7 +65,15 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
cycleIndex: data.cycleIndex,
|
cycleIndex: data.cycleIndex,
|
||||||
waveIndex: data.waveIndex,
|
waveIndex: data.waveIndex,
|
||||||
maxWave: data.maxWave,
|
maxWave: data.maxWave,
|
||||||
maxCycle: data.maxCycle
|
maxCycle: data.maxCycle,
|
||||||
|
version: data.version,
|
||||||
|
avatarDetail: data.avatarDetail,
|
||||||
|
enemyDetail: data.enemyDetail
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onConnectedService: (data: VersionType) => {
|
||||||
|
set({
|
||||||
|
version: data.version
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onCreateBattleService: (data: AvatarAnalysisJson[]) => {
|
onCreateBattleService: (data: AvatarAnalysisJson[]) => {
|
||||||
@@ -75,36 +93,7 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
turnHistory: updatedHistory
|
turnHistory: updatedHistory
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDamageService: (data: AttackResultType) => {
|
onSetBattleLineupService: (data: SetBattleLineupType) => {
|
||||||
const skillHistory = get().skillHistory
|
|
||||||
|
|
||||||
const skillIdx = skillHistory.findLastIndex(it => it.avatarId === data.attacker.id)
|
|
||||||
if (skillIdx === -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const newTh = [...skillHistory]
|
|
||||||
newTh[skillIdx].damageDetail.push({damage: data.damage, damage_type: data?.damage_type} as DamageDetailType)
|
|
||||||
newTh[skillIdx].totalDamage += data.damage
|
|
||||||
set({
|
|
||||||
skillHistory: newTh,
|
|
||||||
totalDamage: get().totalDamage + data.damage,
|
|
||||||
damagePerAV: (get().totalDamage + data.damage) / (get().totalAV === 0 ? 1 : get().totalAV)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
onKillService: (data: KillType) => {
|
|
||||||
const lineups = get().lineup
|
|
||||||
const avatarIdx = lineups.findIndex(it => it.avatarId === data.attacker.id)
|
|
||||||
if (avatarIdx === -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const newLn = [...lineups]
|
|
||||||
newLn[avatarIdx].isDie = true
|
|
||||||
set({
|
|
||||||
lineup: newLn
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onSetBattleLineupService: (data: LineUpType) => {
|
|
||||||
const lineups: AvatarBattleInfo[] = []
|
const lineups: AvatarBattleInfo[] = []
|
||||||
for (const avatar of data.avatars) {
|
for (const avatar of data.avatars) {
|
||||||
lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo)
|
lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo)
|
||||||
@@ -125,13 +114,28 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
waveIndex: 1,
|
waveIndex: 1,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
onTurnBeginService: (data: TurnBeginType) => {
|
onDamageService: (data: DamageType) => {
|
||||||
|
const skillHistory = get().skillHistory
|
||||||
|
|
||||||
|
const skillIdx = skillHistory.findLastIndex(it => it.avatarId === data.attacker.uid)
|
||||||
|
if (skillIdx === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newTh = [...skillHistory]
|
||||||
|
newTh[skillIdx].damageDetail.push({damage: data.damage, damage_type: data?.damage_type} as DamageDetailType)
|
||||||
|
newTh[skillIdx].totalDamage += data.damage
|
||||||
|
set({
|
||||||
|
skillHistory: newTh,
|
||||||
|
totalDamage: get().totalDamage + data.damage,
|
||||||
|
damagePerAV: (get().totalDamage + data.damage) / (get().totalAV === 0 ? 1 : get().totalAV)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onTurnBeginService: (data: TurnBeginType) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
totalAV: data.action_value,
|
totalAV: data.action_value,
|
||||||
damagePerAV: state.totalDamage / (data.action_value === 0 ? 1 : data.action_value),
|
damagePerAV: state.totalDamage / (data.action_value === 0 ? 1 : data.action_value),
|
||||||
turnHistory: [...state.turnHistory, {
|
turnHistory: [...state.turnHistory, {
|
||||||
avatarId: data?.turn_owner?.id,
|
avatarId: data?.turn_owner?.uid,
|
||||||
actionValue: data.action_value,
|
actionValue: data.action_value,
|
||||||
waveIndex: state.waveIndex,
|
waveIndex: state.waveIndex,
|
||||||
cycleIndex: state.cycleIndex
|
cycleIndex: state.cycleIndex
|
||||||
@@ -146,11 +150,35 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
/ (data.turn_info.action_value === 0 ? 1 : data.turn_info.action_value)
|
/ (data.turn_info.action_value === 0 ? 1 : data.turn_info.action_value)
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
onUseSkillService: (data: AvatarSkillType) => {
|
onEntityDefeatedService: (data: EntityDefeatedType) => {
|
||||||
|
let avatarDetail = get().avatarDetail
|
||||||
|
let enemyDetail = get().enemyDetail
|
||||||
|
if (!enemyDetail) {
|
||||||
|
enemyDetail = {} as Record<number, EnemyInfo>
|
||||||
|
}
|
||||||
|
if (!avatarDetail) {
|
||||||
|
avatarDetail = {} as Record<number, AvatarInfo>
|
||||||
|
}
|
||||||
|
if (data.killer.team === "Player" && enemyDetail[data.entity_defeated.uid]) {
|
||||||
|
enemyDetail[data.entity_defeated.uid].isDie = true
|
||||||
|
enemyDetail[data.entity_defeated.uid].killer_uid = data.killer.uid
|
||||||
|
} else if (data.killer.team === "Enemy" && avatarDetail[data.entity_defeated.uid]) {
|
||||||
|
avatarDetail[data.entity_defeated.uid].isDie = true
|
||||||
|
avatarDetail[data.entity_defeated.uid].killer_uid = data.killer.uid
|
||||||
|
} else {
|
||||||
|
console.error("onEntityDefeatedService", data)
|
||||||
|
console.error("onEntityDefeatedService", enemyDetail)
|
||||||
|
console.error("onEntityDefeatedService", avatarDetail)
|
||||||
|
}
|
||||||
|
set({
|
||||||
|
avatarDetail: avatarDetail,
|
||||||
|
enemyDetail: enemyDetail
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onUseSkillService: (data: UseSkillType) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
skillHistory: [...state.skillHistory, {
|
skillHistory: [...state.skillHistory, {
|
||||||
avatarId: data.avatar.id,
|
avatarId: data.avatar.uid,
|
||||||
damageDetail: [],
|
damageDetail: [],
|
||||||
totalDamage: 0,
|
totalDamage: 0,
|
||||||
skillType: data.skill.type,
|
skillType: data.skill.type,
|
||||||
@@ -159,6 +187,120 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
} as SkillBattleInfo]
|
} as SkillBattleInfo]
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
onUpdateWaveService: (data: UpdateWaveType) => {
|
||||||
|
set({
|
||||||
|
waveIndex: data.wave
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onUpdateCycleService: (data: UpdateCycleType) => {
|
||||||
|
set({
|
||||||
|
cycleIndex: data.cycle
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onStatChange: (data: StatChangeType) => {
|
||||||
|
let avatarDetail = get().avatarDetail
|
||||||
|
let enemyDetail = get().enemyDetail
|
||||||
|
if (!enemyDetail) {
|
||||||
|
enemyDetail = {} as Record<number, EnemyInfo>
|
||||||
|
}
|
||||||
|
if (!avatarDetail) {
|
||||||
|
avatarDetail = {} as Record<number, AvatarInfo>
|
||||||
|
}
|
||||||
|
if (data.entity.team === "Player") {
|
||||||
|
const [key, value] = Object.entries(data.stat)[0]
|
||||||
|
const uid = data.entity.uid;
|
||||||
|
|
||||||
|
if (!avatarDetail[uid]) {
|
||||||
|
avatarDetail[uid] = {
|
||||||
|
id: uid,
|
||||||
|
isDie: false,
|
||||||
|
killer_uid: -1,
|
||||||
|
stats: {},
|
||||||
|
statsHistory: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
avatarDetail[uid].stats[key] = value
|
||||||
|
avatarDetail[uid].statsHistory.push({
|
||||||
|
stats: data.stat,
|
||||||
|
turnBattleId: get().turnHistory.length-1
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const [key, value] = Object.entries(data.stat)[0]
|
||||||
|
const uid = data.entity.uid;
|
||||||
|
|
||||||
|
if (!enemyDetail[uid]) {
|
||||||
|
enemyDetail[uid] = {
|
||||||
|
id: uid,
|
||||||
|
isDie: false,
|
||||||
|
killer_uid: -1,
|
||||||
|
name: "",
|
||||||
|
positionIndex: Object.keys(get().enemyDetail || {}).length,
|
||||||
|
maxHP: 0,
|
||||||
|
waveIndex: 0,
|
||||||
|
level: 0,
|
||||||
|
stats: {},
|
||||||
|
statsHistory: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
enemyDetail[uid].stats[key] = value
|
||||||
|
enemyDetail[uid].statsHistory.push({
|
||||||
|
stats: data.stat,
|
||||||
|
turnBattleId: get().turnHistory.length-1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
set({
|
||||||
|
avatarDetail: avatarDetail,
|
||||||
|
enemyDetail: enemyDetail
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onUpdateTeamFormation: (data: UpdateTeamFormationType) => {
|
||||||
|
let avatarDetail = get().avatarDetail
|
||||||
|
let enemyDetail = get().enemyDetail
|
||||||
|
if (!avatarDetail) {
|
||||||
|
avatarDetail = {} as Record<number, AvatarInfo>
|
||||||
|
for (const entity of data.entities) {
|
||||||
|
if (entity.team === "Player" && avatarDetail[entity.uid]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!enemyDetail) {
|
||||||
|
enemyDetail = {} as Record<number, EnemyInfo>
|
||||||
|
for (let i = 0; i < data.entities.length; i++) {
|
||||||
|
const entity = data.entities[i];
|
||||||
|
if (entity.team === "Enemy" && enemyDetail[entity.uid]) {
|
||||||
|
enemyDetail[entity.uid].positionIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set({
|
||||||
|
avatarDetail: avatarDetail,
|
||||||
|
enemyDetail: enemyDetail
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onInitializeEnemyService: (data: InitializeEnemyType) => {
|
||||||
|
const enemyDetail = get().enemyDetail
|
||||||
|
if (!enemyDetail) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enemyDetail[data.enemy.uid] = {
|
||||||
|
id: data.enemy.id,
|
||||||
|
isDie: false,
|
||||||
|
killer_uid: -1,
|
||||||
|
positionIndex: enemyDetail[data.enemy.uid].positionIndex,
|
||||||
|
waveIndex: get().waveIndex,
|
||||||
|
name: data.enemy.name,
|
||||||
|
maxHP: data.enemy.base_stats.hp,
|
||||||
|
level: data.enemy.base_stats.level,
|
||||||
|
stats: {},
|
||||||
|
statsHistory: []
|
||||||
|
}
|
||||||
|
set({
|
||||||
|
enemyDetail: enemyDetail
|
||||||
|
})
|
||||||
|
},
|
||||||
onBattleEndService: (data: BattleEndType) => {
|
onBattleEndService: (data: BattleEndType) => {
|
||||||
const lineups: AvatarBattleInfo[] = []
|
const lineups: AvatarBattleInfo[] = []
|
||||||
for (const avatar of data.avatars) {
|
for (const avatar of data.avatars) {
|
||||||
@@ -171,16 +313,6 @@ const useBattleDataStore = create<BattleDataState>((set, get) => ({
|
|||||||
damagePerAV: data.total_damage / (data.action_value === 0 ? 1 : data.action_value)
|
damagePerAV: data.total_damage / (data.action_value === 0 ? 1 : data.action_value)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
OnUpdateWaveService: (data: UpdateWaveType) => {
|
|
||||||
set({
|
|
||||||
waveIndex: data.wave
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onUpdateCycleService: (data: UpdateCycleType) => {
|
|
||||||
set({
|
|
||||||
cycleIndex: data.cycle
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useBattleDataStore;
|
export default useBattleDataStore;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AvatarType } from "./lineup";
|
import { EntityType } from "./entity";
|
||||||
|
|
||||||
export interface AttackResultType {
|
export interface DamageType {
|
||||||
attacker: AvatarType;
|
attacker: EntityType;
|
||||||
damage: number;
|
damage: number;
|
||||||
damage_type?: AttackType
|
damage_type?: AttackType
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ export interface BattleEndType {
|
|||||||
action_value: number;
|
action_value: number;
|
||||||
stage_id: number;
|
stage_id: number;
|
||||||
}
|
}
|
||||||
export interface KillType {
|
|
||||||
attacker: AvatarType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BattleBeginType {
|
export interface BattleBeginType {
|
||||||
max_waves: number
|
max_waves: number
|
||||||
|
|||||||
12
src/types/enemy.ts
Normal file
12
src/types/enemy.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { StatsType } from "./stat";
|
||||||
|
|
||||||
|
export interface EnemyType {
|
||||||
|
id: number;
|
||||||
|
uid: number;
|
||||||
|
name: string;
|
||||||
|
base_stats: StatsType
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InitializeEnemyType {
|
||||||
|
enemy: EnemyType
|
||||||
|
}
|
||||||
9
src/types/entity.ts
Normal file
9
src/types/entity.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface EntityType {
|
||||||
|
uid: number;
|
||||||
|
team: "Player" | "Enemy";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EntityDefeatedType {
|
||||||
|
killer: EntityType,
|
||||||
|
entity_defeated: EntityType
|
||||||
|
}
|
||||||
3
src/types/error.ts
Normal file
3
src/types/error.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface ErrorType {
|
||||||
|
msg: string
|
||||||
|
}
|
||||||
@@ -6,3 +6,6 @@ export * from "./skill"
|
|||||||
export * from "./turn"
|
export * from "./turn"
|
||||||
export * from "./waveAndCycle"
|
export * from "./waveAndCycle"
|
||||||
export * from "./srtools"
|
export * from "./srtools"
|
||||||
|
export * from "./version"
|
||||||
|
export * from "./entity"
|
||||||
|
export * from "./stat"
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
|
import { EntityType } from "./entity";
|
||||||
|
|
||||||
export interface AvatarType{
|
export interface AvatarType{
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LineUpType {
|
export interface SetBattleLineupType {
|
||||||
avatars: AvatarType[];
|
avatars: AvatarType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface UpdateTeamFormationType {
|
||||||
|
entities: EntityType[],
|
||||||
|
team: "Player" | "Enemy"
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import {AttackType, DamageDetailType} from "./attack";
|
import {AttackType, DamageDetailType} from "./attack";
|
||||||
import { AvatarAnalysisJson } from "./srtools";
|
import { AvatarAnalysisJson } from "./srtools";
|
||||||
|
import { StatType } from "./stat";
|
||||||
|
|
||||||
|
|
||||||
export interface AvatarBattleInfo {
|
export interface AvatarBattleInfo {
|
||||||
avatarId: number;
|
avatarId: number;
|
||||||
isDie: boolean;
|
isDie?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SkillBattleInfo {
|
export interface SkillBattleInfo {
|
||||||
@@ -33,5 +35,34 @@ export interface BattleDataStateJson {
|
|||||||
maxWave: number;
|
maxWave: number;
|
||||||
cycleIndex: number,
|
cycleIndex: number,
|
||||||
waveIndex: number,
|
waveIndex: number,
|
||||||
maxCycle: number
|
maxCycle: number,
|
||||||
|
version?: string,
|
||||||
|
avatarDetail?: Record<number, AvatarInfo>;
|
||||||
|
enemyDetail?: Record<number, EnemyInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StatsHistoryType {
|
||||||
|
stats: StatType
|
||||||
|
turnBattleId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnemyInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
maxHP: number;
|
||||||
|
level: number;
|
||||||
|
isDie: boolean;
|
||||||
|
positionIndex: number;
|
||||||
|
waveIndex: number;
|
||||||
|
killer_uid: number;
|
||||||
|
stats: Record<string, number>;
|
||||||
|
statsHistory: StatsHistoryType[];
|
||||||
|
}
|
||||||
|
export interface AvatarInfo {
|
||||||
|
id: number;
|
||||||
|
isDie: boolean;
|
||||||
|
killer_uid: number;
|
||||||
|
stats: Record<string, number>;
|
||||||
|
statsHistory: StatsHistoryType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { AvatarType } from "./lineup";
|
|
||||||
import { AttackType } from "@/types/attack";
|
import { AttackType } from "@/types/attack";
|
||||||
|
import { EntityType } from "./entity";
|
||||||
|
|
||||||
export interface SkillInfo {
|
export interface SkillInfo {
|
||||||
name: string;
|
name: string;
|
||||||
type: AttackType;
|
type: AttackType;
|
||||||
|
skill_config_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AvatarSkillType {
|
export interface UseSkillType {
|
||||||
avatar: AvatarType;
|
avatar: EntityType;
|
||||||
skill: SkillInfo;
|
skill: SkillInfo;
|
||||||
}
|
}
|
||||||
13
src/types/stat.ts
Normal file
13
src/types/stat.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { EntityType } from "./entity";
|
||||||
|
|
||||||
|
export type StatType = Record<string, number>
|
||||||
|
|
||||||
|
export interface StatsType {
|
||||||
|
level: number;
|
||||||
|
hp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatChangeType {
|
||||||
|
entity: EntityType,
|
||||||
|
stat: StatType,
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { AvatarType } from "./lineup";
|
import { EntityType } from "./entity";
|
||||||
|
|
||||||
|
|
||||||
export interface TurnInfoType {
|
export interface TurnInfoType {
|
||||||
avatars_turn_damage: number[];
|
avatars_turn_damage: number[];
|
||||||
@@ -11,10 +10,9 @@ export interface TurnInfoType {
|
|||||||
|
|
||||||
export interface TurnBeginType {
|
export interface TurnBeginType {
|
||||||
action_value: number;
|
action_value: number;
|
||||||
turn_owner?: AvatarType
|
turn_owner?: EntityType | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TurnEndType {
|
export interface TurnEndType {
|
||||||
avatars: AvatarType[];
|
|
||||||
turn_info: TurnInfoType
|
turn_info: TurnInfoType
|
||||||
}
|
}
|
||||||
3
src/types/version.ts
Normal file
3
src/types/version.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface VersionType {
|
||||||
|
version: string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user