From cf9553d343d769555f9b081dc6e5956536ead449 Mon Sep 17 00:00:00 2001 From: AzenKain Date: Thu, 27 Aug 2026 18:23:17 +0700 Subject: [PATCH] Refactor TypeScript files for improved readability and consistency - Cleaned up imports and formatting in localeStore.ts and socketSettingStore.ts. - Standardized interface definitions and formatting in attack.ts, avatar.ts, battle.ts, enemy.ts, entity.ts, error.ts, index.ts, lineup.ts, mics.ts, skill.ts, srtools.ts, team.ts, turn.ts, version.ts, and waveAndCycle.ts. - Ensured consistent use of semicolons and spacing across all modified files. - Updated tsconfig.json for consistency in formatting. --- .gitea/workflows/build.yml | 28 +- Dockerfile | 112 +- data/avatar_basic.json.br | Bin 4139 -> 8039 bytes data/monster_basic.json.br | Bin 49192 -> 137425 bytes docker-compose.yml | 30 +- eslint.config.mjs | 26 +- i18n/request.ts | 20 +- messages/cn.json | 222 ++-- messages/en.json | 222 ++-- messages/ja.json | 222 ++-- messages/ko.json | 222 ++-- messages/vi.json | 222 ++-- messages/zh.json | 222 ++-- src/app/api/data/[name]/route.ts | 48 +- src/components/actionbar/index.tsx | 564 +++++----- src/components/card/characterCard.tsx | 198 ++-- src/components/chart/damageLineForAll.tsx | 168 +-- src/components/chart/damageLineForOne.tsx | 160 +-- .../chart/damagePerAvatarForAll.tsx | 354 +++---- src/components/chart/damagePerCycleForAll.tsx | 120 +-- src/components/chart/damagePerCycleForOne.tsx | 128 +-- src/components/chart/damagePercentForAll.tsx | 182 ++-- src/components/chart/skillBarChart.tsx | 140 +-- src/components/chart/skillPieChart.tsx | 150 +-- src/components/enemybar/index.tsx | 156 +-- src/components/footer/index.tsx | 22 +- src/components/header/index.tsx | 996 +++++++++--------- src/components/lineupbar/index.tsx | 612 +++++------ src/components/nameAvatar/index.tsx | 30 +- .../themeController/clientThemeWrapper.tsx | 14 +- src/components/themeController/index.ts | 4 +- .../themeController/themeContext.tsx | 52 +- src/global.d.ts | 8 +- src/helper/exportDataBattle.ts | 94 +- src/helper/getNameChar.ts | 124 +-- src/helper/importDataBattle.ts | 40 +- src/helper/index.ts | 4 +- src/hooks/useCalcAvatarData.ts | 54 +- src/hooks/useChangeTheme.ts | 6 +- src/hooks/useDamageLineForOne.ts | 62 +- src/hooks/useDamageLinesForAll.ts | 88 +- src/hooks/useDamagePerCycle.ts | 136 +-- src/hooks/useDamagePercentForDamgeType.ts | 52 +- src/hooks/useDamagePercentPerAvatar.ts | 42 +- src/hooks/useDamageTypeForAvatar.ts | 52 +- src/hooks/useSkillDamageForAvatar.ts | 48 +- src/lib/api.ts | 102 +- src/lib/cache/cache.ts | 74 +- src/lib/constant.ts | 12 +- src/lib/socket.ts | 510 ++++----- src/stores/avatarDataStore.ts | 58 +- src/stores/battleDataStore.ts | 736 ++++++------- src/stores/localeStore.ts | 48 +- src/stores/socketSettingStore.ts | 66 +- src/types/attack.ts | 202 ++-- src/types/avatar.ts | 16 +- src/types/battle.ts | 34 +- src/types/enemy.ts | 52 +- src/types/entity.ts | 20 +- src/types/error.ts | 4 +- src/types/index.ts | 26 +- src/types/lineup.ts | 32 +- src/types/mics.ts | 136 +-- src/types/skill.ts | 26 +- src/types/srtools.ts | 84 +- src/types/team.ts | 6 +- src/types/turn.ts | 34 +- src/types/version.ts | 4 +- src/types/waveAndCycle.ts | 12 +- tsconfig.json | 2 +- 70 files changed, 4376 insertions(+), 4376 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 0f1b04d..6dac2c7 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,15 +1,15 @@ -name: Gitea Auto Deploy -run-name: ${{ gitea.actor }} pushed code 🚀 - -on: [push] - -jobs: - Deploy-Container: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Deploy to Container - run: | +name: Gitea Auto Deploy +run-name: ${{ gitea.actor }} pushed code 🚀 + +on: [push] + +jobs: + Deploy-Container: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Deploy to Container + run: | docker compose up -d --build --remove-orphans \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 120a327..0eea4aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,56 @@ -FROM oven/bun:canary-alpine AS base - -# Install dependencies only when needed -FROM base AS deps - -WORKDIR /app - -# Install dependencies -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Disable telemetry during the build -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN bun run build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV=production - -# Disable telemetry -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder /app/public ./public -COPY --from=builder /app/messages ./messages -# Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:bun .next - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:bun /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static - -USER nextjs - -EXPOSE 3000 - -ENV PORT=3000 - -# Set hostname to localhost -ENV HOSTNAME=0.0.0.0 - -CMD ["bun", "server.js"] +FROM oven/bun:canary-alpine AS base + +# Install dependencies only when needed +FROM base AS deps + +WORKDIR /app + +# Install dependencies +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Disable telemetry during the build +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN bun run build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# Disable telemetry +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder /app/messages ./messages +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:bun .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:bun /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +# Set hostname to localhost +ENV HOSTNAME=0.0.0.0 + +CMD ["bun", "server.js"] diff --git a/data/avatar_basic.json.br b/data/avatar_basic.json.br index ea2eeeec77e6958a89f92ed3ba2fc2bc2b8d1808..922797f56e500365a14d107bcdee25885437d745 100644 GIT binary patch literal 8039 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 literal 4139 zcmV+`5Y+D*4srmjiwB2jvI+<8jny8Fh6+bCWDLgl!3ara)&Gkk-8Znbej*2U?+EKM zgqxqXoutE>>+$}|&~UW%(!@f=v2tISl=IzBvrz-y_0Qr(0qzZqE?PIm^EFFTXtL<^ zmB^l%T3WRnxqrU)D_T2Azmjm|jwnC}5*N|wlJ({PzfH5sIp1;!QEr1btPQ#ja;5ot z($~(&C@ml=X#4Ar-&hMX3tWv|NkLOUy8-ICEY@XGQQ26jb_42U6hCYP92f2M_oLEh zPA5q_J$0jB1VnU?Nq?sjXQxoN0rY#PPG6r=!S8SO)62wN|MA47q{#KUQs_y^5$Leb z@om5Fzp&w*0NW*aZXe0$|HhKLe~D7O#+uh-hrQQnml^y*!eO0skHvCb5Y9Xhq{LaK zNdA-kkSWo(vQ*ziPDyaGS+pFFk@NM|@{M2EquEBjCMCA=?^fC=V6@&- zt0mw2lPxm+`6AuttgstQlZf@aB)CKYr=byW%cYS_J?E}{YpfQ`e4Qp%z|h1+GmKCI zd8cqjZ#f`iEFw@C-O8JEye!zp(iP8Tb4Ko~aqhBNR~UfGYBr~M>Hr9uuaT*#3b$;f z^dKs1f9=M!g_rslU){7o2V(5TFK~WZ@JOt88hk2zOxVk6(eKs}arPp$e8#UXt~?BB2!V<=_HK4PHgaSpyND8Sv9mOzu+l@@%{ z`W&>tVtVVZr#j6!djK_jiMvtlC|*Qq$vFkk*v7$1q#jDQTnM~EyBtUL?v(eL8wz$P zBUS}}j19hMOzR!JlGT4l1WbpjXW=4oizFpOU+t6VbVinJUADYzAYSS`x1Jf=@jT=L zRZ3#K=8Y?v7AXRZ74Q+V6@;Q!RJ5k$Rh9Ni36zu1^u=67MEp4o=?e4UI64SRAr(6H z0^M`~C94>NCAMIRCq&*{)t+jag)%nQu{kmy%}C22^GcAF35$5EEtEilXi@`RF=S?; zPUr}Hm%$4A4fQo|=up>`g0gUp;#5?j1{F|TJ;>x>`E83gnblDm1;wZ;_@RyWN3`+Cvw(pk)}F(6F7c z+`z-!y1eOktVB%>c^H^F9&=EIzaFPlQ#%fWZJag9qxg6Zw7>@0HfB^Ul9d;;7@GnC zdkK_#tk1@N*k?+IXYTJRFdRAsup1(#m~ATW67N9OjOQW;2sO0|;C#Sjh;Ou-KB8eJ*7RRKcEDXO?&2HJN3_j8nCkW<<&cCzJ?qFaKUAK#j2fNE%>_x zPz=~cN`q19K||wu<3ORnD%|UdL(1Zzb%Wn-({Ban`JCZB0%;;`86y_IZ(|5C$M@dQ z2Q`mSsC_1CIPsh0HI~Y3StjZ70pACIP~F%ul-ZWIERHm;;9c{y-|i46+E*2>`<|qN7hDQ zwgteOCW%86ge^uKOuEt&8ep2jzdV>%;~g>-M8#P~xpp@7_=0O<9gkOwnq>s$4bci# z5^2h(p$1*x9=yC9B682$Vl&O+>q&~HijVVx#cq#r{DmS&nqmUCRP2q5<@UN_kc_k$91^0}peD*dS1d5XYo1Xt7 z_)WRrF1~fo@O{h%y^`{2q3DrVEHSadP!=zTEWA@q(SF3jTnqUE7F6&eqWwz=;aN}{ z^^rx!pKOQzRC;w1(HRsWPSvp>!Dp@-c)UgPB;I%Eu%zo1(&>}gV2xT)E^2COTquCx zoJ%pO4t$+M>1bmrD?TXBp*e%ELs4lbJ!gP(408tOPD)(Vpac>~qd(Hu?W;tSypj^f zq#c0Pq_x~0H0(i{ct}e8mv;B7L=G(bwjvZl20e2$E0%6|5|`92$wn zDWL`$ZM|ark~r=uGT5P>&@VfTX)tDWCej!L&_c`|xNOmlAgitRAPTmZnRL&p#SXQD zNloykVQ-VUHWDd?K?6*H(cW0}e6tW?x06dXDO3C@h3Pf?_}#x`aaaj^9cORbrUmE$ zFZi2sPtx1;i9GYqq(FE8?D_&VzZ~JJVbdhQ=A^_bXn;TqET~Khk=eVINAu`%8CZ^; z*w<~~mIZezC5LJAx=Fu_4k`p5iW${cQ=Q7y}R@EFKN&B8Z=eih0$c%UzP#IRn za9(_$l-6ZiXl2ttQ45!>J&S5P*dEL=)F=kabdC!Q!zDG(ioKApsRc#H&F%6^C!D{?1JnU9zzC2>&S3oL ze5`63&d>m}uSH&V?q{^`xSsXXcgcG{y9Bw2107|sH1(dVgcZ_>=PinBZ`FF#z(=_= zq7=w0U+bh7q=*_Te=sqIHNe|{S_it3Wgk3}Z@(xq$O&u;P)p@BJKO%@-OL8JXVpn;VAXB- z4_!Rbofork7U=nSB(`teimhJ3QpBxhJ>wkpMm!Xf+!hJcPfbdeNkt(;16pA3%yNt- zc%8l#nTe+8N?IF+bV4W8+sw(szMo0@(+Fk-eZUvsV<84&NNcE zY5k&H8{rVU*5k}k?7GNlfiTm)bMU0wkD^Gtg8NMR%v(}lR=x7p0R<3X@OyCTWK_O| zwXsz<;S7++=ZuUxnC#rQ%9}5FElG0k4%J>2#wd@cth2R!G78dk;Y;L=QD%sJoK9$f z@mOhOEF$m%JDTC|le+4W))Ch?+{Rh@VpCoSH6g-{B z*)s!L*D-%k*xNT-%v^sS$V|qS*mTo;yf9DKK&LalIh?MPT7BpvN(1R&qOM**t`OKZ zhOi}*edxzmYX&8kCPG>GW<;xKlp383p|sY5uQ593nO8G%$5)V$15T-}7fBeQ0xBZ5 zUKg-6Sbi7Ql4`8N$&Ju19Sc7Z3E6!I7%!!gCK}S8G+3eb-D7;}Dmy67mx?eZS;>#P zQ8-4Q>(&A;XBqSi>w-h$V`w4JxV)>6n2mfRU zJ>W&o%heOys=7>C3aY6CXcxLrCmPxbhYDQ}mXz_yvd&_aVM7b7PZj*`8I;%ym8*-^w8HUEPv8~1zpi|v?1j6^3v>^a_`P;m!gp= zR6s?<-6>5@(wA+;cW7HOiaXepa^-%(t^Rs{$%5z5-C~`!rxuWhl1VR~yz9N`sSjQI z^$IMZ2HMLRSZW1EY9trW1jB3E$C1!G5Mi}p&T!G#hj#7!e_{g-F!-oGT2Pj@k~($E zxfz?9emv%+WvdNOVh~-fj5HdRX|tMLp(v@x4C*NZ$})V#D`E14WTmRp0t@<+6Uj{e z>=)njXFB(_!4eB>{t68+sE9WsNy#7Xhl%kH-Gr5^4P>9qI*Lt0;wH*5_ef?qhvG2n zwFc%u9#8`fGLt){d-O=sT^%ZIAwGmz+crC&AOg3%&5T))F5lV0+~)EL4LoRo2{IJs z+KbT6ldBaVBQ{4`yVrJ7ErUs5j>GnQ<>auD4>A|J>YRJ zgQ=!{u@fq*d^n=@4YDQTAMMn3Xzi43)=|Y%jO|$DO|8m|y9Nk1)RyEBR)spZ255j0 zQG}iPu22HWE1UUECT3@vIMR^2J5_uDUfEc_HFB}%P%%|5#K>yQ)h2P$EfwuqdN2H$*ggdGv;Bd#!l3Mine7Z0d`_S#J2xg!}nRH`$Y!Gi`E zA45ni7meEgA?QO_4KCEhAdgKO*Rl)`Bl$3A3tV)xpR|VT^rBEOQxLQV*KCiemeNC? zkB@MJen!3Fi9jqdvBQi=4AX)IuBr%ttYxW|Z59)^zUlz=5BltI@yT)97Nd^44EXqg z-FK!|;0s|2UR#X->6n|#$sXK5*|cxPdSy*O0Kk1^7%Wzx{jBceZS6)GK<@lQpFb3a zaDL1LGytzLfTcIgY3#km>~h1}%XG8Fi$m$DX|&R?!JG!I;^dxRe-bU@ZWaPtF)xiV pX~?o(*Ww}NT_>HLedPBM>D+i#)^j^mOowui^T^E-Xn+C6{s%3G{O14w diff --git a/data/monster_basic.json.br b/data/monster_basic.json.br index e61c65d3af255f75be149a3c826b83fb48c5aebc..f24e4fe7d507499513ec56af9d121883dd10d26f 100644 GIT binary patch literal 137425 zcmV(nK=QwvW8oW7LdONZB#jF9}s`hqf8x) zUdNHsZ4NFLl4{~~XwCo`a!b?w00>DU$y`zm+ah)uB!k3NwuxQpzxCNZ~Wrl z&n&Ipa%1;j5&zmM*0r>W9>-8(<ACXh1eG zqzE+SI?{g!RhJ|4tMv+!b{BafPHntFC_^}!TK;+eM3f6uD>Uh-K!p#3$p(*)Q#0#- zpMD}b*-@O605%KEvL0SvE~{&NtNYRK z+$+10Y~&K`;sS+vB7Jip%leRw38ie^S$#YMvnE$UDun!G=^8H(;s3uc)vBt;8vtz~ zWI9PI?Ju@fMJ#IZw6z!bmRZr8ZSBUrSe;4@g#|t7N5FsK0fCYd=5&(e|0fH9C zfB*ac<3|95A^?yEDcMO%N*5^6Hb_ZMQp$9fNu}87NjZ*<+AqM-VMu!|Nq}tr8G56NYnrtK*R2qDtaz4AtRtw9hKK!r6t38 zgaV|_+vnWZQ$%{Henfg2=r~{l4F3P9x{#F6@c;bYs!y6@EMi08Hx)HTDyBiWr#gN= z$pi8k_&3&B`?SkQxr|~%xR3y*=RUdYLS2pIf4{G)q&ZzPJ0yamsfwSoRTLA!f?d(x zeA5J+C*Qx5h10o>eg8s)`J>09^G9~#S ztjW;ygua(-=Jc>HW#eV^tGcBb@)8bf6si`zQrfGNJD3JwY1vk;LW()+5T1*TMov?! z3#j)0)%spR1O&a%Uo&)(bu7uUe2X<%dy&2REnS+X>u2|_B?gQwIRx-ulHEZKYpVTP zXCZ;RN~U7IEUSN+P9Nv&KN1qau9A&pXozgsRIO?j!&aSpv(EF~=a>(=3xZLLCM0+& zek;FygkJ@UVvucR_J9^qvpT1(Cwr6Je`2dp6i78nVgW#ae#L$|q}nF^&P*!*WA8MG zf^h7C>MVqTKnNpax9gJ+ab)gd_Q9A0X^T(qA5{F&GW{&suxKEz*;eH^uLH1(`Kpu9%dBgV#ad(JK2`5?~CN$*bMo3D91>% zFS=DG^g?#BmR3E>%zB(NSNdIllk0KGhjB|2#HoMomg-Ba!ChdXZ_M(tj?vR0`?L~v z*SMkE{F2u9U58gT>&6kvVF7pSbljI*%n)?gi%a}1*kmG8etEk?aJ#?LgS_;X{asOA z1AkinfVf90dLdX?MwG4*voOB|4TaE-620+%-~S>X$Kj{^!7BDbB_+sWfh0XC zH2angUkoqKhkr{GuNXz}rWohy>sj>y902o>afli2b0O|$(F;f;irXI2XBE1Xn&>a0 zJ7pNxft(NrO=udfVKP6hGYl{E&(h7pLBCe80Dbln+zY3Or5%yA^} z$i`U&X^o|uLpn8)&SbhkF+~Z9ux?JVG9-G~S2AGHg5*WiAh7b2}!f%3+^x4iua zw|=3>>q@7Pr>J7hP!a&^vqwn!(aRaiqym--4`V9ii>uS;zK|Nu`!dQ~b1<;R_O6M1 zAYLEFyf&Re$T7#u`k&Ku7N!46ORgmQM77{oFP9to3vhq~eIsx`Zkvb6Lsz&bra2vR zT84H;cH)V|hbw3BNlh2UCc&bl?WBps9hrY+*>Ly9MK9jK0kDFxunOTe}2$A zhwnm7??G#VtKb0x1#;*I<#%2W0-(#EfFObbI5;CpaXw(k|!MiYgIF3ABnPzHr zXPH&H?y4*d#M1_FbDD^yjvNL$PhP9L%Xwv543ffPx5>E~+7ObV7m;OxPmz2WJPoSl*m zgZA6A_~11U;7a0aCy>{IIeBEHcysV^HRF<(sxCx}ePd?9J96?R>cQuZReTWK@#E91 z$1GR@R__o!&+%@7c7;y*@HuaBcYgK9v&7JzVQuoYpBo-^yR!7;vw_Z-V=WfQ>w9{f zR-N;Ccyoep41GO+Ep<3F~az< z1^ASO!COmA_PW?pF@?KCe?qJC@BIzUYYBR6Zlqc(3>Ar-E47wA5F`Prg+s=3g?eQ0 z3`{&Z|BUeQ>#_hDGH7M1$q{=9F5>%>OkzE}d}&2djdE=r#93s>m4j<^1b|aSRO%-ccAN#x;`Mf^|Ltd#{^)hyLi`mfw1d-zTO` zCO!{zv@n?Vr!(&%z*7}Z0#j=GU0UVT(=fB&4uDF7zjRA0$%f4vZff9$ReaLr5L-@) zRhpAl;L2kos%3_z(&UqytD2go%OYfRQet^NG7oh5!7)GLwkKJ^VY&S)UMPqv4jPST z)uB*0ZXNYCC2l-LKHf2@j5L{ic+kPYv#u-aOG++!D9zXQQe**dyirr4^oUrDmmFW) zqtNW8ZZPHZ(cLd{ci_{(P6>{j)5kdFB6FLFmRzGL^nM9&3-5;=N%Dqce&jgapj(RI zhvPRt2<^)((P%vS8~3fDX?(L8m;WDqaO14!QR7X^PhsoiGC1qaGCg0N>dwu3W4?#& zLYHB7SBM`rRpi4?mcg?dRr)9u$|W^FP@*LJ&oojd3SNUMX^Rxi^(m=j_U7`Rn;uSW z#)oezvkcwAS=u?%mJQT;Uf_7?oH?B%tu1!YM{3tFZ%hfM^XSPQ+O<)Q$zSe+?$Tv0 zmA0z5i#XkUoaywI^JV%k!OefN^Ns*l1y(UObZqkQ;er2}y1?QuEY&5hcD&?~n)*QS z)MEAKNiP=5B%KV40pkH}sjZWqe7JmR@-e2LQW@dQn*qJ0A`py1;r_+3Gf)eH??U+2 z!U(hJQds5>p7203srO@+(t-zq!Q@?_kA;bk(uURcXj~@CZTu_!`~IK&@So&2|6t>} z(dr;=aq9R`eZo*{($P)d?C+ZBjRul>95vQ)zGiY^{4oW?_$t3UKb|7UK3! zr=ccH9TLNmVBmhQb7f5ZR495bAOwxZTsgePn;>vaL)=w{Nty-VRYjB|6I4yA;_QF3 zUZ93mZ!KHNY}FZ*K)8Gq6^g5K+ z7?>2v#)trFb3|=76mUK*W3(1fYTA9RJ(Rc23!Nc>2s*x_yk)` zQKpUlzV1W8cTT*JXKSHEd(D4v)(Po&?4%MNjk`9E4CjVDA+`ETCJN+AKDnjMXuiP* zk|s2ASzTLe+{mEVHHl02cCM_tR~uX0k<#yVPZ(e0H*0adDUY}x_AGPeO4|1-p}^|# zx{tKEbI_^&gUm{;o`s}~xSlZR$U+&~EfAWoYF$+~uB^_7d_#n=H8=NQ!A=k>Z(cdM zbReoUJf(x1Fm)W&DI()upQKc?NGrF6{%+0M3-*;-^h%qkAtL^Wxm#POKaqFE57#Ar zGT+vo{5v~Qo{;N_m+0^}-#Fd|;g)Ws9?B;we|@$CZ)sRUw7wU~sAR04ou`_jHw6N= z)QeEbGakBwQ8z9&ar|f2zhHtrc+qYbEf2M#OB>Tvcgna9iD|*Dq53;G;9`0~P1bhM*sS z2ytkw=(Yt!G;x&xrA4kp=k1fJ`H$gn+-#6O7zK5Wg2j_AYqkJ6UBZx=d$e-XfTX#O z4@Tq3j-~g)(Wfx-QM+*3g0mZ$awbLN<+8^1rg~U|DFULtCIK>2wZ1e$-Q>nDM>oeM zS%W%BFj1ZJqo&`h25ph&q5<8Je9;=tAQ)!QG&XZPuf!!#}$t@D>*1oZiB*-Sq()!u57drOXxdqtB)%(ZpcniPN*pYW$4kB&;g z8dP`CZ$D|u({y-+bxH2bky#In8?T+%ALE@+54>Q?Z3~kEY+s+x(m|+Y1mg2+(U?SP zyXPu@KE-Q+1}0OWXQ`L*0KwdREVp!6-A3w|93^toF~k?AbhW2!yXmvJ2>dnYf3`iw zX850M0QW526ub)8E(#Imk|&Gi+$9E&AdfCj1CJ_eULHYgisPkE>@xDRuZ!JHl~&rD zmrw{Em!Li9Z-Wz2^C<5Uk=pIW> zc9`-b@XxL02OS~s>`E(@azuXC@r1p1;SLA-;(`4Bl@8M8QHAn90MQCFjan_0qIniQ%CxM zNu(EU>WdrBG{F#`pn}aDlpat5xgzeg`7PP8|tj{AO}EFAqw><)a}Eh%;S8DmO( zO%xHud*9Fa9j8~*F-v^*)w7wzYbSVU4D&b*=3Kk^|Anhg8y*L%D8gbs^k0gKsrF-h ztOXF9 zfddRhrJiRdC(|~`@hA1QP!PT8Itq>K#INi_ zy1TTCx&!+fd&Y$R%|HP$=7o3WvaVofdWHX9a4Y=GMd4WcxULu*; z=u9k9TA-w@^o9`pQ|p6fQNktnm1k(>kwM>=Tb{Lypx)AL8NDva?bvUwI$Q1^KfQ|~ zcjSDWmTZA_6#9M0EQGY;z|S|#T#|<=tnKmf9IHou;^8OLGo1&k!WTRYKpyuxbt2>J z(q=NvCD3@%56?^>2)k;OU|5rkY1JTq3m=Oo+}cjhB^?Th#FArNWuXwOEm=Kt+wq17 z;rM7NFycO5y63yW1rI!p93HWJ?Ra=}#_ln!V|*?XT>fweLQq9cJJ5v9$`8iXtAQ`~ zwGS2#lS-LKpK(I1ZsiLD;uB#D;R3j9kTipQ@z}F3Nj}mD(P2&Uw<>!n8>bFn?DN|L z_jV*)*}Q@Gdo{aw&xox06@&j&Bb?vi^JQ^7*2Vj`&orR&({FSN4*`1$2=S+4K7UFt z(8XHrLI!tV(90bofH@&tj7VKsPa~_?gNH_db4;pA#Qfc_-(FE@s=QKbnu?VI{Q5;E zKH_}4AQ3brLxr560W`8f@GMOr6U)~ej9*0MgMackNhpQpIT9z%ADxuM=R_ zlPct>f+t)|;?aNG+;;h=${X{dIw~_?cTkO;mB)3ybMkodGblzOeKFnfbJK5*otFU( zMHZ(=<*08&(z49C_6Blll~VlQtXl9Q69s=RJY2F-9&%@|Q=}!si<4%m4w)H9$MHiX zY?2hhDlP~?ur7w@y5{cn(C@r2-J;_2O9V#Fu-Mpf67zEhOg6>WX{CN6ofvZC^`k4|oW8Tq9szo<`LEO!h=TQ45EX3V!bXu9q$ zh1_^yC!o{6v6NziUNTW4LYoJ7>*2XOIip{o+40t5>DHyfC3OEVf^ycsfbdj{FCQ%h zAc4I$o7D3{A8UFbw}WW9p&@*IT&V0+3LNSSYJz6X&Ys8V4v1ZZNVI>(OG#7STs{a( zC}nT+Jyy1DlWu+I*!nc_yMOyR!QF+8Zdb}K>}}Gb74`}V0Z+YOd!FwaAMqY%(;?_5 z=ujLe+;6Y2*Z)5BKb$DRUCuzl@d%5^G1|fm)`hr|%7u8zYYf`UL}) z++=ZUHyC*%VQ}?Oz+U4W?I*XGwr9vAO`-R+Gc9#xSi(e7F#QoQJ?)HTw8VKS{`Slq z*+;>Ib{W>DZj92AX%D?1`Z1;-Tdlww4R#qLj{8F%yS01zLluH$!dQ#!?z22CSU#zn z@}JZXWNcS^Xa2yQ-N-3SJK-F!&|H7%R}&tbiGpi-_eLI zN(jGgoZd#AAK_oH130VB!LC)rsewQeHAN0imbLRLE*+Q^z#ud^j!h>x%Pcnmj@FA3goXhUlaw{nK14>Ip z7vOkEl^};>3k#aX$k)^NiRMflNZ*V8c%MN^A%(jNlwXuPuKIoA7l627u5(<}2)hza zUEPLVSpgoz{*Ez6<9Y77N41`LLAAKZmfVW-&NFl_ll-YAI0GX)=tmvOfs$}F7qj(C z``?=DC*(3B_*hEUxt#jtlU%kJBUh(O+D5D3u?)tH9ui+U#mLF!8#a1RbwU0OcxZ$7 z1=^#?gSvY8^>cIHQl$gc3(8l>6&o%h?q`1?S%BX`68w{ScC^7aLye{$oK(|pfpKH7R1kUeT2 z{3C%iqdhrz_Lo^6MICnis4*!f$Gbj%lkr49XiWAiJvRFs4UFXwS@5HHdh5o(LVkYy z2QNtiPY?F5&XF{X8G*g1<@`A7dCl|U2;v0D4e_3LwAvBaKf&5w9}iG4V(2#XBkIh< z@<rIr9jeZv#+Vs; z(m+^LPhO1H-kH`@=Rex56J5y@(E{bgP|2{B*ZJ_Y;Uu^*POz8v?%9qzCf@zevuJ4} z;=ntUu6uvGl4f2(9j#lB-1ka)fb0oUEUU|_GgpGC?fd3=xu{ADWuL4ZYHLUN(p)a( zkJa^f&8M@XH8cFhQRkEttYm9lkw_>Bv>K_&R0*jP4s$yZ9njy;e3zs^XIm3&zw$Ny z)@Yjyb@_=bk&Xqg^C8a%X2R!QgWx#Hje#a#!}6OTt?k-YG#@DaPBo2DG9{vv1^esy zVUf3X(L;ohqSeN^(F+HvQ#k9~go`IatR|hosey4e*s6+hZCJX3&_~{>d3xvB_9-+o zsg6g!Z42r%WgA{o`JVZ!>V~g(C%#-t1RiW(#?eoOXI*oGe1|Ho$oUv@_F*j-81kue zV|bCRF?Z^0kX`uU>|Z)h+Pgh~?9*3(nO=QGJltUO3KXR;sx0rD>u)^ILi>8{m-Po* z&p-J+w7+c#il%i|vwZuSNu1^@@v_jqVPcbT`2Y5~g3|5kbih`Ap-*pB#RBbFc`hiL zwD;0YaPKb+pB7C$Z+g!-+sjpkA!Skw9gdJniaH9!i!sx}CfY=o^IHLtrSx)!|Bs8p zk)#8nv=IOfnQlKjkr9Q0@sLgbcy{{tOMj;CLK)&}v%d(9lfGq@K$F_ZGhh7m-CdYwV|+P@;A?2fZG;+z1*?SEm5q1KKDjjb zb=Aqn2l3P77pqVvxloGCBprU_rMqVWpS(`vdn5{=bJ528G(qw!Dv+Fnnzv|mAv)Vs zm%hpoLr0YIiCY?`??COQ@8@RSh1rXK?@JI(en=~m@rKY<1{K;RGT&y{SVyRitigvi zppJ6hHkP2Nh|vwrw*tB)^NoUUq1gsz?wPdQuGxZS7^6>h{qT^IAf_QupUC4QZo`lQ z@ub;V;Mv3~vMvcr8TSLGj5jb3v&$2cKzt(4Mi#l7&B8X$?W9vQ!7(OgL%8rrnRfEJs0?C%oci2CAVKCe*5!b{Tzajs((L;`jQ~B!XHq`l5v1aEDoy;Yh^g?9WKP2Gy z8O2fTnsYfcFmU;?WOCe`QKiN15yj?qGCvwQUkQ&^J_Ef=U%5TB#rHJHl$`}fziBsR zSi$+QewXKKBK6qkwfm*9H1t))?nTzmQ0K>7GOg2nc8{8)@*eIq4$F2T7^Anh>!*yT zG}1Cw7++Uy=jJh;&QYFI})($lP}5 zx=+ZUt&OOkcZUn(rSG;Tor6n3-X_QYg|7YY%UvHMGLI@vqfxJGQL~lK4X$tJUC2)R z!i;w~Fijtiy-L>I?0K-}ofnZCZx_8P$9v2b{bT#b9}|K~Pk@c}0d#b6L{U2H*_NQe zS6(Z$Qseeb^%eF1^6hQGT#WY3EQ>vDC7J?vK7av&jq1AqXti<`aH>%J=;@qZe zK7!Ow^M&AIJ`v;Geese?|g! zaP{(4rBT7r>HE3)vt6T-hnaaxw+-_*v(NX+ROKeE75hj)Ki~pK#5>D3?9D0A;pgXLX{5Y+7QqV&y)+cpwDDIgQa=rg+$-H^1aHZ`jcYmwRfB z;`~*7i(`1lD<1e$r|%3$Jztx$fxu4OSFjH6B}90kOby(xJ)$}9P5Lpjj}lQb?T1jS z-#FZS;8e**2m#-DclG_vmEY%u+@DRx*P2&o%I<@R+atI$N}du$yS`E%Z?K`fsm6^s zcrWC#=KB*L1ehfE?V=tw0E@vtc z;-3pQ#wk{cEglWTnSRlk>jkdr0 zE*xjec%kO5_f`QX8w$Y}RJ*@s9Rmr;8}-_pgdd!56) zR+`^L*fbUUkk0jkI$wseFLinEm!26~$Y{;S z3(G{4GCwck`*5LZOK|~OLSdNZlAKb?GBI-qV1DPBCv5R7Q^nuB7ql>`1t^4wJxumB0L5Pt20x3f&)L+^hjSPghj_&T1CD(p(_y8U!U^t;hX5 zm$<$P?PaCy*7aW2YkK&ZsM~T*;bf^L5HWGT0>-g3sJQ1lpr;tF(}is?sC0eG#8aLA*IChTUtrZX zD_{ai#Ti?4an#}8VE|0XGHSQqGws;$yvhxdA{&&0>M}u`AuWPv(2lcNOw>Lj@PjQ| zAEwD$StYxM3F(&yuM~knmNV5s!N#~e?*LM0#;L07St51BZdHo;9ox2)z2cn_tPE0t z!61P;Te^Ztf*Ky;8nu-YkiCQaz7c$Hkl{WM{Y^n~1%yRj4)`6J1gze6^hjbost4m( z&booeu;W9J1yP-8Je49+Zqdp*l+0JnD24tF?vc%B+?>FdSOY(7>7CzN-9T5x=dy8m zD~kKaq*Hw?&T2e*>Yi|r%FkY9h=n+VpTK0LGq`f&u&sN?ZL0#HXg1q{p}0x%w^(@@6Dx)soQ zVY1C#B+gWTR4d-in}}UAAFNODf9Tj{M1zfAUFy6~7b>tJ(m_@OB0c6OIy;6^Q*S0c zmFK}4c(Rk0(5A+|9^sG4VX{ykeVvXjm8IXCc(gI%7|dx}oNx1A>2WLQT@?m(%Y^LX&&f#GoCxLt6BGkwK?>ZDSYP!==qM&Iz%*^xlDNC;p_cE9iEy}cZ*|uMPd~m&+)7>& z5>Ir~yMFe-Y+@Oz1^NIbDyZ8{&IaF)SqIx@qiMZoXQ_4Yiat=5=Geztqse^?`Z~go zW_@Z&%He)2bPK8wk7{y(F>HOhsm8K^3MkY=pr~iQ4>h@A4!$h(72}%A5(WlKbzQdu zE-+XtV76_T-k30x%68hgIC(8h_uH^;FT@RSi z^;MZW;F5n~eTK+exby8|LtWoNSCObERgEtM+m?=X79#ftx!^IB{hq>|zSq{dK5P4m z_X3hI2NjR1(?%D+Q~BXY)*$o2cOqGo!GmSc;9SQ11RYzf z4zS+(CC;VZbg|rF+Oy`J$#L$3X)n2ohv&?v$IdJtV@<|W&)O|&owJq6cP;%XrC&p) z1{b6|W8Is|e16tQ#{P7`A2$8!HHMXpp2^z-m}*xx=x1HJ+Zs&f%H=9B+3ftmP(34i zJ`RO2+L_Xa;**@Z4hWRbR%I?ZdX)rPu6zMWlUC5ATyKF-GBFdotOCgwz>^VuE2p;7 zLQCd;q3hYc1fQOFAaXnvuIkp1bqn$1(t9HMQ1vj6r*&Z~EzpLwfEPbeUhn>#Q*^xasyT?I`G8XTP&_uBd>$}9=LA%LYuo{!Ad?;N!If%2r1A7F>d%p5mumn+Jd}@LgG`qjj=$)OdpLNMD-MDP zc+Ln$>vR02u!}xI9{5<+ncDq+B&Yhgh;)8-A+VIvnPnme*TRqdHvcfx#T_~3`u zOt52?pY<$RDIay2h~3Euo~{yhZTMS=kXN7b8nkNbf?omlf3&TL9y)!#Gj@w+G1h>k z5%=@b-!<1XFZP2&gL3H3u(kWZZ}OmvaQR1bloXBToalx-fQo{FV@p3y^iB%tkh`X;=k45e^(hz*zbt0Z zIEXFyd|5+yRo16t@5I!6R9A;c?@6d2jEz5IMaA&RV7bPqT-60fXl3<1wixfDVX|T! zSpF&J`<)kFlyR1GrwZ!|9Q1;^&fZt(% zo!fDT$t1>rd10AY`3tKyy4;cr4Zq#1ONSL?{h+BVhb;*aCX7@xH{oKp;`|LX{B#Qe zW$W$)nO5|hAGY)JrC{F7`&0ciH+vDBbM1XN;^Z%H`-fdRQH5M;pcWWqWSC#Xs>}t- zkhIBiZOtZY(GPsra%Ic!C%K2?I%6}+eLAZhQf9@C|F9p<3l7&VjGj^EB5ZYSa~KMU z>7H;`#>)P$b7VL|s#?wvd%(11FE7~`ILpAI^)lu$ll~-mDpQoVEKY^|Q-;z)_2(u$ zJXN}hQ$DQhx+3EfoINaZd4urw<$NAKZn7^i*`0EsRM`Q9EK-ps@(j)DkKeUIP^Av! zFc2z<**&pJrs+`E$@y0&If*7wJ)}JBt#My$Z#Y&Mt`BrYk?s7u%;)Cgs`2bjLB0*y z>$===Dd7Xmr-_Qusf1d1spIj)z_LWmAmyy@gOo|(3(LpaJp1hQD6%gLt;a{`$mjJe z6TfE-&)Ix`zR2~Ioc^&{(5;7b!yH1hP9$*@b3a8xZUu}Zqgbosbv(TRhbi6c3=GN_ z$Z$}@>f{hla$o>=tx7nQ(wYN=RYV1*w3XfXX)=kv%>`>*CGww^sKrX>ZL39~_6Thf zEOBGhX2a72 zy%*N$<=5#W5kW=B@jQ6?^@)pRGuuf=PR@seCC-sm(xA;@dR zaq{a{ha&eg-SNUAXCgVvm*-y9c!p$#+p8e+z~ob2tk1LSpW+EjI5MreT@K&P#z~1f zdEx|zxHK9bx)RlM-=4)pN?-|;^6Gn?b-q?q!dTJg0QZXIffR+7wb4c95QOm31=u_9 za`}fk3>p+VTC&nI$g-m`Oz%YgJznHx>48d$Je86lDs|V$NQv;OHrD}H!+_#ax57)4 ziI125j=>Pu@|9lV7R%=3{UCY4k>Q_*zSK^WS9> z2(&(3fEd6%X3igfL$fC2w*O!KkTR-4M!vt^ZQ#Y0`tydn|JjF>F)_jp_t)45`Anzc zP4-ob>`R(|`hC$LF8P(z-TW=*4?LMUCm%*Juo-!-BHe0=i#Mno;+7sXt#8m;Xt8Ael#(IfD8{l$6-4)YhM8S#N% z^kc1Nj8GYYMirNACnMtRU0SVWX4xHRx*I!Q3lrJewGU&QNeLT0X#BBfGH8^p-&4+l zM<0hc{=(FB=lHmk*#CleP&s5zyC(?aBm&Wa8Ed|20sy+`)c6=ya#MOc_#If*tY>fJ zm&bE-PwKR`y+!8oikPKKJu?VinEInXq)Z@I?E@V&`wcfk_kA@s2Ve^KM1DE2K5ROp z+kQ-7CMNj$aqRB1~4ZYOe~Z^fMTu{+%^nhLHJ2bGEN^H3^D={790x-cV{=v`@W zvd~snIusToqx}j0UEzEQE4ZZ_#~Z%Joi{Wtp{6r9i z=9tpuGxt7Ra1G&Gx+-~4$-9FwfeXYt0)qc$Pml217@V>zd_srRw67oP$os4?gX*+% z&w2wa2?E{jiRsd$?U+H%OT~N_HiZ~o#iOAPQHdMxsU+n`gtwJ9=m1iiu`CgU$f&wapK(!qfLkMH4lcOaZ z@<3XJk^$vJ*=zii@`it?#C=@EF<;0T)>DWYoR z%{H)%h&UbHdUQa#(sZ(*_-|N1@}!F)oi^DzCpdKGtrl;*7lW-$=&FN$l#@3(m=Hm& zCChJ(>Af+{-Rg$lHRVhVok}(-zGk3OygoE>5Q8n$4e9dZOEhq;`eH( zMYj$GhBQlFyj=PWwB=5LhldFVHn(J5v=-0U#l~$h=utCo?a{ocRpQ2MDvAR@zg9V? zu&lB7QEyyG$*+3D{f94Z$3m|@$iP^L4rDqZ7b9N}9DUAx!J}dXWLFve_=^c=9{D48 z1S2uE7~s8o$?t8LUW4Z^YUZ`m=EUgSH~5smI>)SLSPESaAK$c2k1RDQp0SR)Hxu$N zSCJ6>Yp|Q0U~x?3C5x^mod;x#9KDvV_&x&5rEpvB37tp43&EzxMIEA+u@@q zM$cEfsvkTNEu#-cFp3-*f*e z-h1k=VVLb-P8ZF(VNJKet@HMRt-g$evLe8`%ZYa|T*{#d^ zin{4OaM@YxlZ~(kUegzzyoI=UohGO^`_J*k)ZY=gXE$GAhY6PBIp(1>-@md+IuPo# z#tL^TD@7esRSPgWGTVSV<;W(y7f@y94Z3hMurv&Xz=He!V&nJJr9bbHMwZF(`waYv zw7XO)pM3D3ap4^6g{+Y4j2%;Qnt6;1TFL_}efEM)TpibNoKbilAfW--irx3aduQ>R=1k6`htsEa;s%#O4Wo)aC(EI4kmulgySIwi>I=?go%c#+yS*J9hsi}I{MDy?*Gl12`o1FVG+Q-v?3@TI&j{+g2qJ~qxOO`Mo- z+hM8pdi+wMOz7Th*8w~i(Xz*o~*Hl`4Bi7xheeatryih6=qe1!p&KC zh~Vzf9@fRv+PH6DGud)+>Z5b)_W2Y1TZ`(@`Nc>+V4@Y$X>Pj2O@wip$V<)HskYvP z6erX26kd?-%@eF<8gpxl*OVLid<8^s0iTbZ&km-b59Y*s0lC&(Px1fZV0m=gy&?M% ztd7K?3Z=zvi|mcmJF`Tyo-|@cKcg6bNZCf#jpLY{jta>8AHbYCCsP`^`0|-NZk9dQ z6bc-)R=I=RnKw75+M833d(6K#xS}X?BPoCa{GxL*;)=aJ6K(s#>=l)R?YVWK{49(6 zEmw0CSeRmlD{;+^V-1l?b7A`8)Ds7U4)%O~7ys(><9p%WNCU@dJJkg+)hjdJGRgaasg5+ieAU4h4K>kTL(bk>V z^?X6D{j$cYq^|y~zVlrnW;$L3?1L)m#zqxgLgdwQFO8>lSEuqmJh*7)UdY4BSSuZD z{s<8>qS-#GQ0~kE|NkQj1AP4CDX@R(=UbD<`sIj6iHcYq26NP?SbQEl{E5GzIhWT9 zhtZPl%_<@&{`qx`BxhWF=6BPp7j!fvavIM5c~cb3-o~;!IU~DLHqqjl@zJHsfm+Dy zIg&7HVl<8Al1Oeb`Ns8UdmjAT*2yJ@Z*|}kr^TorjECDwZ;IwJh>i$8Ug z%kH;hxV{w9r|Z#W1U-|Jozfj!@63&>MxoK(FB|iy`S_vW;%BFB8`u|glW*pu#67Z^Dc{UKs$OiW&FR)%Ni-DjR({bkprozB z&dZ%8MV^*4gLr-7NGbl{7{C__DG5ciYF!(c8GSb7vfOv?AG|` zdfGfm_l&$7ebV?(rtOq=j-iDesrz7vJpORxMhJDSt?GU)aD}qb)oN+!$$j=nr=XN_ z*ZkiL^9g<6gK2xd(^Oj$M*7~aM?bCp5-K0SqA?Y-d0ZGY?V;T&`84fTCH`GJvZM{DaXNkKfMoQFo*ZaeDgkZuqI)HKeURO$6iYwJ9*sltRhhn8q#PKhGyU0kSg7Xm{#~n z{^4B=(OB`x`Fh%l)2GV`5Vp~7ot|dT}EM0<~37{ou znz@cSP&yg)1SM3MPi31KI(KGz4o=)H)LT6^b@|jvW=tQwL6t*$l}KszRdZjsdbI3o z_`evYFp8^Wc|TpY7`$y#V6m!q{6QWv2o?QI$<^zz=$=lWB zcokvfHO8%?Lf^0bbJL%&h`Pz^Qe}XKe|w83kT{e7xuG3%u*mdoLMEG}?cBkx+=pQ(Bv6@esB-JaRr{0>t}r&~!G;^J20sJd6%>444kLb44Aa&|u`s`i*b#4vX`5v0 z(-2(Z>X@=A1Gk~oyP5i>OvO08FbyN$5@B4|MAgDtI9Cv=XWzh5 zRvn+h-@@3TS9wPEv`&)~_j2AFDcc&^hEaOk@0h9`X{|NArMj~~#=T?HoM?>m#s*;+ zcA45aJ)dFefg7Z9pM9s}#Ut0v4fGn%mvzaI7S5n+H)hq{o4bF|MlMFTrBxupm`rwR z_oQt|NhjNGiMa4u#h}-eY~k$RZ8hPF7~?Riq?`OWHs|1j*3ME+qOZ{Pl%8!|vZK)Tz8Im)OUQ`% z&XMQMQWl~u5?FpR{X#QU7Xm!gc065jvXY|2fKEyuYYusT#t9=*-b)vaicSM5rcMb@ zB#+RIm0MZ}JE$mF35-W6?r!bINR}N;F%nm&WuELTqPjC!XCmXzhRnVN7s=en!SeC| zj|Aq1?y$fRlJbe}TTCEj=F^gQuXEda7pPyi~ zUlw-Zt%}9v+_Qi+kx2bYD{S2kKqgvI=XIlN^|+h7J1Mj~nk@$@jCL^mwh~oHK%}Sn z*~#BlZVD#(muDKGnFlov_if1H?7R1fQ@uY(Z|2)z>ZcJu8#7EBGo0||PO>7%LE9NM zha`0EgTKl6MNBPT*NfjwNM{re9e;lw;GM7+AGuHC03Wtb&pv%h=Vy{}oX*+DvVQHu zf@Bj!UckAAkdUTMklo>i5%_d(>ViavABiPEi%B5U*(ZTXw`KX4dvI)Y{nIA=?GGe0aiA;1u zLAAst7RUG`5H4i2dnD2dHx9Y=Qp_VNYZ;?c5l8ksq@N%wEi1n50S1?p+&LZOS4q*( ze7sEgW#ip4`;SEH$J4wI_8T%c%)IGjG*u`e)o`^FsgA91Saw8*`*4zi7osbCl!;RN+X16%pfwSSr(2eLh? zp7p1!c~jNBDGoEjZ(RLWF-fxF<79Yd41ez}e9@h-ugtP&dfvVW_q#drseW%*i%u`h z3s%E^bNi}n_DM6%{GIaKi~B#z`S~p`oMY`kT&g>UKKr1c-UuQQ+dQ~x8$&ejn& ze2XahR5`kmlT?)G<={Vh>i-iI`h($aBH`t)D>w-PShPVPpE5BXZ*Y6n>e#avboS{q zcC0NTRyo%-3$Wt~8Qux`uGqk0yS6Mp=V=l-hq-6^yY(yKvf*E&>6-Vnri*pd1h<7c zUq#HMhH=*7@jJX}CYjeQ)akp>%N$MdrI!_(#d-V&w5a8|~_?uySRCt)hfh4QW=buf|k#%=KNg62(^q--B|EfRaa42S)QG&H~PGAPB z#RfaORVQ&uE&IR->md&@cdAvkw!79zUd&w8iggBOu6gz_L%rs@1T~G&l~MdYs+akg z)1?{m;R4!j3t8Kf-r{GDWb|2Mcl*Vn;&$>d`Th&VVi<8Fue|48NtZR4j{v&zRu4~Cb%+cV*5U>~d+Er?o^2ul%sy=tr? zcJ*L7eB6&cK54&SkO03*@!-r=SMEzJ)VeF`a#k^KWCai>pY6BmHJ&l@;~VR}9b)9{ z?Zudf?=5X68_FAcA^(ppBjQ7^wy3{M#6m$JaH-C2z%`jsgMwLAP3%* z^48=5PBZyRJPuFu=pus>4!^o1wE4jiC9D(QUr5v5xxZ8paT>w>BnOShDl?Qy#ES$w zC=ZOwh;1ea9nl9JA!7CsclQ1L;!cL1?llt@&6<5~mc4O|NQ00mFiu8b6baW8_@GbH z81miCkoDMdijJwnP5YC9$wPel__okGHaqA~aLyV!GJ78s!^44CqQ?W+2YEnQ5AN*_ zpPbQOE38sY<3B$XJqaBSocFc-(2;*ZamD4#wHYEm2l4aipNxf|>VUC8mq*nt*9tXP zwacRh9`!udNC0{1N`eZ@Spyp<16FeITb}dSNodH$&zFG5pOGke)x%)J6eKarGaqZk zH1AsW@zMAQ>;-Ecaa0L-RQOrKBO&eqc<#6!*3q(0A8KgdWB>3(S1;|R%UE;eA&Qy* zvXx`-V|qV*ale8E49WZ z%oi84y*d*bX?+N>H6p*JbTk~dQjdZ^39-;TPBy-FsxH2BU7x(I{md$dnB*6uPP(M+ zR+va5>6Qn*D=!CoJ^}Eo9`|0FtA_f_*C8HPQ%M^>KXDZp4n4Xg-cmxMEx!K4x8q_h zN}gF`O$?axg*3h7ZD|T3uBScuK0+SZJOoHQ<Q_uzRn-AJRA9CL4VX=ySJY+W&_=O27VCbgI>FunD3^m zd9gTpGO2)f&`Y&9e5(@&T2m%rVZrLH+Gc>y+v&K zv;C^{^#`h~dJ(zH4e9bxsmF@4h!T8LCFSgf8HH)o)74YWbgJ!+*0;-)c5ziXQVhfpNqQGa@C}F zU$Z50dKIqv#8cA+JvxrO7rW?l)uQrN|1<;-d{7B%lFMfQ*M`e)n>t>l%wKBow;1)~ zuNl}l9#YRkQ-9^^4vklZwRFmHm*`?5t`kxVTe>r^gL>L>SzHa7QA zTZY+&N3F3b*o<~BF|}3H!KDjqRAwj zs8WuVtcC?`bs2aOBpGE+s>efboKPQ$jM!>=Q0n!k{`3hy=enn{wf zg!jDcGd*(5U+64cpLTPiQUXhZWQs0OWDn5+<+&^It7^k32Jm?4v&0rn>w2ES3Rr*w z2u}vHa8mIOMWY(O;RCjvx?>#Oudfoudm@K_8z1j}=;6{CLTt1q_l9-#5u5YctA~=u zXuOR}6i7a%bj9xpv?-hC_yGLCB`kcm9jfoJwet50n+hH^$^t6QJS;NU8j|r|02DAP z3TG_>swjsNoaDl|t-VN@VrhM~ODdD=;}qqs#)^n*WF8;gM^KdoM(!{WS3sEtQe42K z!?Kn@Apg!}Q~06?KQ@$=ZbRQwJ#hslWYmNdiKffEMJ+T~1e9k`F;5ni;84xNOH13A z?iVOYFas94#qJ^Gr4e5t@dp@Qwyl>s`ShUoM2GS_2dZ{tZvpl>Cae1%v0ZMS`4AZ|<)JGNjOnj$WGck8 ze@njm?hpR-uAqt}CB}F{{Iwp(Rap?#KnHeftg-`^6>=GWls*jv1@c{T zCs(sBvxUCJd-Ms-!x$-{I>vw6L&)iqYZ^YU4{rWLQ;5y?qj$QgW;T7O2?m$6QQTC4 zOtrKI5VXX@?2UK0`z;F|eQJTmdPBc!)iFA&({l6g zNoh&<=G4#dE%X{(#!Hazdt8KoC-GgaBAc7 z{xk#^Ueked+C4AA1De*VUlq85IZ#+m^5(GeV&~Keri5TK5)$V=j0!Y{X4?;ohhRTP zpA$1@PFqd`bqf`6^z!|x1oBtKKEx#Y=J=u|xTD?01Sb_%r!zmuds0sJhr{hmEB0fD zIuOl}FSGVF*?^PBI2?!1_G5F!@~)_EFq-B>D1I{NP~f0BEdWhfq7amZ&4S)m+~MfVlum*EnD@Wc(=!MCi4}byl)3;h(i}jYA z84DOuD38H&4S39`T5TVny5J zrB)b^@E-=g_}=&R<&5m%9p1T@R2lMQuk8K=jxDxtIo>q$BCdEUMvC8LY3UT&BE#Y! z^N%7E=&^Nz13BNNlM1ta%l4drg`yYjArZzLsH&+9p|||-yRK79EtK>wO<GFIM zX}sHx!YRptVBJ*Hi{jYL0w+ij=xMhJcOdQz^zN*Ew;242f&bi$&xb-+4}|x-6Gw0% zan4ysgQQTLWq#pjYeawZ69O^0=r*Ch+4^^Du=|{Gyre7(`bL9@Fj3AT0)G{;{qxWf z!25T4Q<46r9jM+cQoHB2aL=9Q0QPm1wf#H&`^Y8REq(tu@UwRFL@+8{g%l5Q{xmUFj9)hKrPnyiTxoue{_~w|{pU{IfTXeCLq-6i{yQQslEX^G$$u50O znPtmad$PYDwEVWOr+3p!U8uO0{BqCi#A)4?o$$<`UCE4-nbV{KI!S8X(;qMdoUlE? zy-Ja&Xp}l=Z6Hb=xk!2fW~~LOxtMyxjc4pOI*2%U>?)5 zjURurS*OPGN2{i@%&0F*BGfwf(TVHlWds;C>7#lEcga%2Aaq^c{bKRNeJf>ACQ)kM z)23zSsjtUQ4aT?3x`N_VI{thR$BnW=$_puxrJVW2iMHu8v^=94l(mwi@#QH>`DD_k zxyhWbOgyxQp_az}Xq?b^*6G4Sz}GA!{4PNwNM3Qwn7Hv!6uW+(q0>nU7%cnsM3l0) zL{0}p@COt^=)nQ1)e{6dIDrg}afdsj$0Q&?-d(!TON4RztwfpazZWL+OrKa>IkHKD zcSTA)t>{8`MT)+mEK7O9(1Jq+HQ1-+_BSk%xu<<#k#rqVWJd>SU+My3=q_5ojB}Gk zyAC@284HouaEnEWJ=|w6sWogg0-P>3)kPv?DG14$Pq+_Xe3Tp0JNG=-8~l`B-$J_| zp@iWs1dM*A_xt4EKj&0UY(lb5K(d?*<<^TVrT6rVycx~r2rAhJ$+%3P!jma&f=ufp z!T$t3TvZ6?YCMb3^|RAj3wrfsUau0T{CE41hget;*BTO#6IALRMb1quWu(CDFPLdJ zsd73Oc5cwt7h|NK(<#UsWy(2GwkCpA(Oi!6RfTe1r^Hcb-qh56NnBLVu9vTw2rxh@1CeFMJJG%q$=;h(y62+ z5LA_k-B~UQW08}pc-vFC#@74l@iucCPXF$(`vld^VQzInhN%erEUNcbkMr=eh|89) zi(wHOhYD~y{`^>A&EY6MhY8eA&idxX(5tOjQmr*HC5q};-x`!3##mebhN8IrfyqI9 z@~qgIB-- zA~7e>DsSVYjeNtZb^4_x3idg`dwJy=mGj;NCf8X4)VU ztT!H!Du67TZ;%Fga*$^#d4teubWqrypAuU2>6bH(xab2SXl_zluTk%t=B9mArTF`E z3WIT;hbEtajv_nx4s@z%3swk0$_|^&-9r(0;LgTfwGQop&aZ{T)X*Ov8uF#MH7lke z8tb2mV=HCaIQa*z&gU>%O#wD{?7O+G$1gdT=qy{@gP9{Db1u{Uxt6`6*Y+J=i0uZ(C zD6h{7ufw8O_G$n)OGj3>)!`VkyKT$)NYvz_9Hwc#dyM3C;*5@0PjCl~742K|+DoB- ztpDXv^4xpdjw*ba*Y%u2l6MQ9?+Pp{*f$Q6EBx+x&5z?;s?h;ro=|x{v*_ya9DTc% zmi6qQ{65SN6ZzY&1pFBRw(qrfa$h$|{)POo51oBr&kwqSnlbOnjFZ1ShcWRnUQ-7Mo7HsXO*!kbNUA~gNB;)lf^xFyl&JAskT%RJB%0vO3_-Uev3bo3&(RgZ^} z&Oblpr7dF?cn5QTxcufAD2I&|$4QIHLBjLPQJ!i0^mv(h!L(%^CA9i)GB22I+gPdF zmbI7SO6;)_!Spyv*pp`Q-RrIJ;wi+5a3P33f3!OP!i@9xnqLMocuzg`Z}HJF+R0kK zlThJewKF9I8VoOhrY$cuIr%4XJ2)l=QDLDowz{RDY0K~R5U!*22YLAX-Gh7$+Ay3$ zQQ`v8^z)V%rglYVjx^MTqH%Jc((2ThkEYHc?lDR3NV5W0rZD|+2nGxTWUHN5pzlW| z5T-2^b_BQ-d6w&_6bdvfqzZ)y!-XlIQodm!8`l2e-cAJyEMDKajy&M^=zQY&hWY)z zUvu1>2@VL2acQctb(1w8o&N$&*2sBr`x9Ig0jb6Haz^3{(VSy@jezgzHDbyrpz8_-!3mKk=^L2=qop&(tZdrLOD`6|BScbpbH^qOh z9wo!d&h0%`ofR-G&%pt7$>_5S+?8wS6qK1wCr0=u8HTn&x3WCF-QzU-w|Rb1)3gqL z{XXY*YE>KRBIm8<@=)|VXWiXax=9X)p%oEV0$T3hEwix02@*Aw%uk|%NE8$AOT6t@ z1-r57lG{J~kj4pw-{MuTm!)euPSlFo*pns9QT28mE`%kvy8L8%JlaX{Z}T!6L9KQQ zrD(gS!COO!9(RlDO;(mv7?U01^jSDI9~V#fq-nur@rX$ZArMq+WhY9Q0FLnw;?lC$ zZxlTHocU2-hSGa+laBQ(s+%W1DDW{2t_xf1rZ_s3sQZ1 zF`_;qUZ^_ShI8qfl`*76$3fQ965N9+B@7Eibx=RdiPFy3xlFsoAxqv^llAZEVAO!8 zhO~?xCCG#TXW^y9{pWFU;pL$YeSvGd*quW<9w@v4ObTs6QfqRPZ&|fjNvT~-Y7K`H zYJ!t+8<=#rAIkQ&S9y`bXMy;}!B;7&Qv)j*B@o=tQU-}mBP7jjVACd1s`AMbrRnb_ zJo{lEauxCSR=%QfrpvRSxTw_}s1QAD0##ntJG*%~-t)(IdDL7rE;-l($%qFjIRg zso6Ln_VJ#wD2pp!*4D&LY15ljgPI7JlsV7>fm|2w#(rD`ILgRjn2*`s(<)R__O-($o0Tr{Iq6yf3x2sozK9& z>y9lVQrJgcmS%aIo)qHleUdprpATV2axeiAij@AZ)K_N-R`LJVNzo9%U>&_YejKSg ztYySwGVbRv&oyJlih_e+@PnQ;2Aj>rBCsx$E4q45(OT+tKt1sr6z|+(3N*lU_P@c= zRHE>4N{c%R7{v}Ega14PUTml+SN|o1KkNJshK%o3+|6%!xX6++Hl&YYPyk=b;1P6^ zUy_FVl1oe0N^w@_IdM;sr{-TU$ZH(>L^WeF5SX@?NtD~^KTvfGpCTm{{X)}UzsZj< zheCHt3MiOv5^Vji_}5^3+=jQx{I?v2z-paDl>lmm+h+YxX%{=!R~h!rXnNeG6;iH& ziDUg!Sp|nVR;u^}H9?_Dd62n`rqo&aTRRWUArcyi#{MM?t}h@;{e&K7G(F^B|3&mm z6s06=Mof&#W0sB#&p^ibrEwrhb}XA_3`oQk<{#6yAT$|NE){M7E;}9L?lpAr&VquPtlZ`0csc#d zFHQ^7|J{t6XpcSG+P_i{=H@j#Sm!*%YQrAO^Noyk_lg6b7N$5XdH>W1Pdi_C^8kKW zSW55h43VRsfl4y9h_+^#XXMdd9f#4M8&kg#>O*A@gYxqPG^oVIyhgd zX+~)AW#3ZW#E*Z3jYzo5^^4YwZw;CiDw;sBh9-Vs6{Q&2z}RGBg@@Zqd!&8**F^#+ zH-=p_pj>`d3Kf&3;$8m4bm#K2uv&CmKcz&$YM~>PR3X`(m|Hs0(}L%q@C~9MF}aR$2#*wUyzVSd@w1swid{lABZO?7!Wk? zAwd(cpcP8YLqc|gkBEKt`_X+jTp1P3fzHKl{jUz2nANfil?w6}+tG}bqGe#K;N)f@ zx}%L`5@OnR2idXFuh$LL+mR?z{8%lY>q?oW{1de<=_d z!2a1(A(p7X7CSuz&B+c7jVN*8Ty=UWXRlZJ7rgy^vBuI}z~w!IfHQ1^C%jrdY>dET zp@;*kO*$ho*@5;LFW#?mJ|BKtP75tKD!CBYRN+MAj&5>~ip=11kV%F_4ws?noL zy>~xhe<#}(_1jv(QEJEy$gal~9lRlPh!Z6)lNq`AP_VT(HlIc;ayrg7;64wGj z1v9INtw3-n^SA*j2jc&&imj<9`SpE5^>5ahapC$qSoaGbur$RfS5VA&)OnW9+REC= zQ_RK-#jhK)z~VYKUtbI1f!}O36x-AjEH|?;i@ts%Y&^W|$pl4cZDX?sOFY{|MyYW= zsh6`4tvtEBTm`pG`ITD~b{&S59FJHBCgg*3YY&Sv8H7#I2WZhArMFx?ySja8cw4Cc zc;IC8jVIXexV&T&8G9R$-!&d<;j4w?6R*ev3tI*9RRoqqiMi485LWFxCQ7m^tnsN? zxCbDw)f^yj&}`uFJgWf9V~OcdMt&Yj4F3}8n6@W_qYHHAO?csH++2S_aHwPzlzF&J znywj8g1i9C=pQTq9Vm{CllVS|%{iZ%K*kPz>{DU}Oio6544;fz{lJcs?{LLXGGA^ei^^KsP5Av87?3iSH^~Bz)C7*0jI(V2rI3>48jLDky4yo8~Vb4r8 z2!C&ZH;ax*Lm!>pu;w{Oosz2lt))bmiUi@-g&MCo;qif7+^;f@g8E#6HUr>PR%t)X zq=u)QdM-VopKSHk>md&bZm0uuKGQzyoWmMzG~!5hGg*x9^zp+Gl~*Az7w@%e-nYZ_ zATL}0CkiAr-~RpMEXRH30uylm5XJZL4eJ$LM+rN3KY-{Y-aU|apmf$m!);a9ghjgzW|*Xc%J3V_Y9BSuqUonS zCN(`USpB4m>PXGILED*bM#u7l#t?8hg6~W*Gh-<1kki+fm?1A)@S*9_AX|*cf7w#tbueobnyLMT*t7)W8v!u-4v3i zuoQbXIg&%ld>_>HC`OY>F`q6m)18h7TgW zb>Qg;{*ox4n1viN!(@jqi+!z@QU`^As}?t((|JHL>jZ3&A-~e;O;<$w%wT45 z1w7Tzpk>y`$1_K3M^{-)i@gl;3HG=RpUc((To~14TR-NiCm&NMiO#ZMZxkq#I1agvotB^b{kFqvsU#mEpSKYHHpOJ zmJc39PJ}*mQMh=~xsIg>g$*uG%y6oaQn1>NYX0spoywF+l^!W#vJ*Y)#8;4azBl_u zZdsAKlaDP;5Sdg*lL0y4GRx4- zk*G{!)YO3yct`=&;sFK`JIiF9RhPba@~o>pUf0)0s8 z?PN>}0FXlmA*gi*qN@W4)Hq4e($;^0hlZ8r*Au31M$^=v(n0+5ACccX|AQAWkMjK$>c# zi@FK7qEAjTBLbJ=11UtjmSt1Obx#Z)tB_)L0D2Z59X`M6DygE8SU^s)cG{E02S)cX z_}2vO`n}KMWE5qW_!PxDU;DSL+O^7?W}LH(MA(8Q8p?%sijxO}2gzPN`a)nNkOALx z=^xn7d0S8LiUw*L3?%mMqwmk9;4ub=VtRIhs5Ok$^i!5mEeUNp1iK}s>Wh=%fDH3W zSFY!NFEIaUX35|;8DW(%xEq;iD7w$QsPx8-tf@#r6>>VQ6=#g?kZFH8`t||*t9e-b zXxYPq&$fNZqcpKPYnok%8@Op|)|AZnxLcbm{oyJLyKsk0_)Pg%HfGpF!NAT{hhPa* zFhdNHKp1a7E?&KARpGd`Jxm&9m_deOK<-E>t|{!1ccC^Yc3Q7kNKlOs1@+^l^8nc| zRMON!*(QKbzfQOr^$aK>oHw8lQLJqk+&`go{q`!)ybyS`{#^JsvKLC2PhF;M1TI5c zl1?!gWwnh*kK=D3%*L;W50D8xnKi~7BYGN~!^dKeiJR!B9>5$*Tkhgfq?hhUaHnkdp<-;V*mpzQHg1x458cKI~`1ie} zdwB3=f4SA>WG}nlh{x9%z=?Qctw-yf(o44D5C67M;q`EikL?rtJ;r=>{-bXpMoXR2 zN_Mu;ui4-O+f7JL@2B}GrWxA#H=%OJ4EWNgEvxBR|R0ni@k4kkjeonLO zvS0ukYe_}bZ3X;w72(zRws6@jUc17p=n0{7z_w#Xm8e_g(2qn-&9AtPl6cCgmXLU4 zrBt6)#F}SWoz1bw#vsg}go;9w^Co}LK-%z< ziaj3I;vvB;eLs*3!XAaLO2YM>Uq+f-EsE`0O{|(T>aL~(c}M~EO7XyE(_GcbVdc4e zwntQFdY@fru${9y(AcJ^4roIv=`ta;3YJ!t-xNb@hm(>z_a$b|=%de?SrDiW8@H-j zowmr?Vx+Vb{b;?kKnMPnb~pSAeY$>k3nL|+Qw?mWmxazpre4^BsS`Xar0_8(j*{{D z2@#ua{a4b(ZGlRc%X<1S(~pwMN2Ro%6;pItMw-u%{57m`PpZ?^k=H+&t1u>i+K}g2 zJ|h(HrP{n&7^cgh5Z%@^3C;IQe%)Y>5Otyi;IZk~H|WF}N-&fa<5(uI{e54KJ*(&c zk$IV0kRY=s_eQA9s~m%!mXe_BA6yhH>ea06J`{D_b7+IlnL7h32L>#IK}X>`^1UtY zVE*k!=Btza`lXfB!ly^FqCA@h?d89#d_}l-!j|)8lN=HXMW&j*?6d{?B?35H7Sltf z@=4DkZkg@MtWswp-H48s5TAi_(I0g@hkxVH8cXBn0XzUDR!F5xW(6LZ-AdT2(nMPQ z8$~sC$Su9Q)U+- zR?oXGBd^Qy)1SgCndHDgXx;lv>%&SE?JX(*fvw?v7+TgJ`lYJ(Ix~#3kM#dKISLQa zm5@C5l2Kq8SI*gtP|0ywU}RqI^^iLerKY!gnwzfv8wq8dei-s`oir=wX9J;(6Q3`m za$DZa2AW-?^IlM!N6t7Tf)GnHhqQ!yblc>3uMD^cGWCpnRVC1aV~(dBmKJd)s) z)sdGP#OU*>uB5YU?(beQ!&Q}}7050B)D%>0PFnq%KwbQ{Wv@aqK;z7J#{Uthnyl{r z%qjz<<2bR5_$=c8|6>A{Lik9Fap-65>Ok`uP5e_MW;XkUQe(-WvT|xVc<;ky*NTmi z)pLfyknqn}u1kp!^5}m}Q_l-JE$&p!E^s>hqCs>}!cU6sGBJA2pC<`O zniPA$323tHy*)V{>XR?Oe>TQ7dcDiKLCrJy9H2BXe&hpEP^- zISL|PdFOAu?+~FSEo+{!!!z^huZK zR2@Cnlq-H_%`V)G{vJ7(+Z_F_;_NXvGDj(*3T{nINI7pC8{tJR5n-U2EY=>o%p39j z_q!(gA)vA95KeULY^^PUu8Owp(MB!{qk*~;LIAt@J4dgRx{w+hOB9)Os8j_h8|*XZ z#fEmoM;1GoD}n7F;^w#j{f=fo;)Bz?x^>dgZKIu9pVa_MQD-UKLeJt*#sy2oJ=jwAbT#*PW{R zriQ-$&Fr3cen?qz6~E+N!IZ}A8W@+wW*;QOFd)R~h!dGCaXT5g&L_5TYX}*rbUZmR zGDUalI`0OI7^zPwPy$ziG_|Xv@8BA6lp@NqbZeY5+dHr**@7*F|Y+I6x9S-Cj%Wqy@iS_-+fbJ4O&_WL1Py_vM! zvA-KC4brxfVeT+6%^H(q>r)R7Xe11jZ^ji1Q&E2Vrh{ zV=H))A=+kHjZNqt;{ztsqM*yaWr}TGcc9V)Ii|@ZR}OJge)7e+0LMN=J;Dy~hOR#o zvSZI5df^;-1MZCtRibC#F;{`y_6 z@OyN&i+Pxw3SpynZ~wRwVh@NZrK>@4P6KBpiT`CGtWMPWGhHZAcH z5~gjU<5NPR{9(Z^(`EK$T=2-Fgm8eeq<|Bh1dg@r+_z~?_rjY}I?}D*jNFB>o|CW= zeM6mb&JpOi)OUrFV;=Xo{1K|={oc@dLM_hwROkN&P^&DhF9BE5b8Ss#Ouu@O=*JBV1CuAd&-#n zW&g_kl-@v<{qoJA^Lmd8`@Y@@c_{=T#@Js|I3O`_23jzUuZpv;J95^kA{1S5Z?QTb zml<*tMmWW~lX?Vh;xBZIxr94M$Nehyx2}YqIfUHC^Udo>d!UB&j`b;^f5Lrha8tO_ zNao4u!(GdFix-Sfgnh$0x@8*_Q1NE$=k6m&=$aKAZDE$WnDZjz^Hj;KaELJTS9G^m zfXz~})D+@F%``gcky<;*n;4RN}66*GhJ12ZrgD2uDf$+)R3 zvL=D%5e|)xxTs-W>w${j0(RR?B`!?1u~Xb~)q~1#VDom01dVL@@Ge{MGpuHu{n8LJIQ>0QfqNY78Uz!L3ZaM$ zKf9w}&QZ!W3(*K#52FOBcUWAvV_-(cEVt`bFqQ0=8v1dHY+R zaV6-ev+!6fnij%4UEOozKp9Tn%G%>JzUNe|K|EYMW_OItJjgnM{z4elYUSbsSW?kJ#F%O_V)TuD|HX>TM{~7AQ0?cak zaDIV*=1;}2I_lihY1}x}1@$MMlJFyw!q!xMgfx_p&$LKf9t-??{- zcm2PYu9(TrMJ*0x2+ZQaK)DQez&zV|X?z|jXum!DD6tVM`BmPV4(yqv1jnF#1(D6i zU@;lz+7MH;+QEgip*cIlB*Bzk>&RuqZve`w4%6^%>4){-Z&=@#UB@!q?4Le`hBB1a z+gUD{>z+E-78$G52~tVn0laJg|;PmF@gZ6KcxVw~tGj2hRY* z+4PLE!%2@Tti_Kj#nG+RJX$04RNZxlV!v5Ixn7VE#Lm5WwOVKp83ck4n^(!VVCC<2 z>3EiC<1Qpb=*i=T$PZ!Ijv+)xK3ceguC`V{K=)aJ5mL#sB9hIeU#K|BbNYGRKS=KHk#Ru zVywM#1bRl1&0%0tERH>8_zBRn*AzEyMOpi zgYVq%8iKAB7uFKHS@NAsc5@b+{qB7crpSt#L|Xt=1W{OGmXVcV@)qQ1xQ^dt(Jwd~C0-2~p(l~q zQ^X11P*?%1prm%QRSW`R-K)I|?kx0rfGLa9(yXbI%N7vor%RQTW9U@QM%anXo$>Gkvv?^YbsLQ< zs1kGLb^(%kA=F7t#RB7}2FT+Az_YkXcjDJka0N`GR589nIj5Am3jK#G7b*hcX8EcX z;DL~}{k6v-u1SC6Ipb;sX$iFXC_!6g~BwF!?$6M9IWLaMiv}b(!3! z^D&Q|aK3cr5)*4nyfv<{`w7j5A_%OUNcFWZR)?qF-vtyhK=snb4e1Qv@)QHqo-xu{eYLYBZXp81yp_r|vMpl6JBX*pmPErU-1 zpd^xlbv;rkb&<*^lM66l7Brv4Aty@;^0H{~8ded6VW^Apx~4zy=F6l0K+Z{*t0l&e zOPXg}#e|Ny4pQHg>aS4s4VWG+& z(~&{hugh3FD}Zfeuwi^2=`6W@fIt7ZdWN{PUr0sVG{|%8Gzo9a>%s8gveEKP{X|R1 zZyqv#{oeeq>1Rc(lK#dSsNWYQT`dwB59f@qTdUCwE1zx5I3RuTG3?Hs$5fr$wH(#X zry<+sod!Qc8h#GUWE)%P{yFmE+dtH`Oz?&)rx%;B+U(^U`~8&*>`LD(uQ+1mvzb&> z{e*3z{GKb!g$}hDik9@VEvSQ~PzBR$iX?&54U1~eFY}E0b5F=(U-6{u-t>R}Ijx@& zKgDN+VS(^}>ndT8XjYl$?FlnSr11xd8OQ}+1M4rXZ*W2=rg2;(jDIvBaXd@9f09~r z41eD{6;?q$d8yoHw~uN5IoZCry+C?J)%`h7=*fq)!-?{fdkR>6xkMq3rzIn3dDwi# zWk}4#;Gt62+(TT_RS<*l`CCEbJ}QHl=L0LllAlQ_N=8LI&SYFy|L8oW&2;a~#^E!N z4B1m+t0fq7gy6Uh{dj?r^~{H};+l!bD<&C^ev z9+oi@MP+>+))%W#H3UtK-Csn`Z{boprNCR<0iIOED1I2JFs7yF;1VE{LSkd~h%;Vt zoEoHYt{e1MzXfHnu1+0V{T_7k;TPwEgXu^;$ArM+S=c3t!$q|_eYuFAUI@up>wnyf zaaG!(;$t8eayp#0ez22YKwm+*rrSe3rmI-pg&YN`gEzQ&nzD~aUkH1n{iYIMDU4T= zaI?Hb>9bZ0>*9EgC*%A3cNpMI;)MWT3;nM2 zsZDLwU4lM{s-u%aQZ{ zit+%Gy@Ywf2`xxY+Cl=^<56J|!)nO}aj%vQ+2M(gU?$ipo_wR&+c3#$2sVny{Y((U zmZf|Xk$zi$+yG`x8CWy6p7 z#3RD$^YAqUh72f^GoaIBSSwvinNfp$qUkl;?n@7-F>Nx9%$VxOn|3-<0UNXA; z5Ny^`A2fMBRWJy!;b<{RT?Qs_%q*V~e*mVWNe3+s#mS&!p4$`xEAZNb$1)7=x!W6N zl(pD{PAToaIs(|Ov62kPb37D)lQZqZKx|Qhag5(VKdO#Zlq3p!VDq;Z5A%}afC8@RnugRb%$($2+do0gj$Y;6e)sYJbO+3^``eUECDb|lN{kO4u zZ*q()5ZrS*9EQ`8F=N7b*m%R z)#Uhn%Em#vWG&Ca#7z`eZGtC3+?`1|MF_9pZa3trC8yS<;^%yiJ;B4EaH zZ`N%N35}RZ$nXxY8eh7i@yBBteU?2Y|HKhrNd;DZN@Zrh2yld zbr>8$QzrqPi2g;~z#MtQWFfQUMNc57=gT;LF;@T^K*i_!FI7UnUQ0ITD#BZwK78^= z)29W7yLC3tx;dq9)W&?g=Z$}DO>&V5qA7M-(>vxF+GtG@A!41NGA)7r=TrZP+!K>HCmZjD zmiJws`srSKJ!qqM;?q6#dMx2DrP|{Gl>A5nd4bI!H|r+8ufXzF$z>aOM`voNi3UP& z0kBytb5`M|)Mc_%D{iv9{1re)NhkkG*c-)S3EVRmP6xMMp?oB}aOMG#W4;VkBmOT; zp?XF9$4w;fO1d=Cp{K=i^jw8*SR5a7&glrT`r-htB+UHl_|_C=`H~qvupD;37b}v^ zMx~9(4?Js8glGL4Zr@+rlJAbi=txG2WqyZ^lnKi0>DZjlay?ik@v#TqVp?KdAKSMu z@pHcak@A=H(Gv2&gScHHePnE@k>Cn#F}riY@D6$L@tlbX&SlHq4X%UG;u zXZg#SX3X!`RoBTG=Afl#tuQ%6U`J062iS!(KAQ9R!i*sd4RAO}Z$(MJH~y&^zy59! zAV)gSwtkpz5r*lzyoC$4(`9`fvZOl)p#QkV=sB`P&_GYL?OUij%Lp6Y1~w!N+GB6sd7JQqN;pMUAqJ;E{4~6s?Xd7KAj)C@a|4dO5tdO5v3) z*J1@#);*mMRTK7!xwodsOtx5th1?~Vlrn71#qY|Nrv-cUQh27m)6f8y9avGtE-zzC zkdyqXD=)j#4N{z-Jojj^I>8&;wm32|OYs!j) zuQ?IdfbSj^w|xwz73r|K9K&pY_rc)GsT}9+dn-d*e3^$nhuEB{fQ>UE{1?01UhO?m z)Las9TIf(kHE1BR!sJXGX0I^38lDGeae0+zDk!P^unVv&v>03J7Z=YgkB#&->ZSsL z1%VKU?h8Q;`#}jWRiPLMf=Q@X_6RkGT+Bp-(wTg>Mqi=$o2kj-b?6P1Sts$-en!_V z0#+iHs@~f5LK;-pgbe*NR%l=oaiyt~BmV4#I&D?shfpV~rh(Cc5LBY#64L?cW)$_d zBGO-HzQgZ=KqOts8BNr@^rb6Z;sm#x?hFti&JY5+#SrPcQs3oA`pZI%r+LzG%4<#X ztB5GjJP3JAxJXyKtBD$N4r=ZO(9{bM!g)}0R-J-~i5Co^R}8xNhMcGD3&yWAWu=L9T4y=c-brq6Z)nhaEyzfrFuY~s?H`4meY!lnnGAH8Ml}DT zF;g93O_Nw~D9`>Xy3n+}ZcVr`kP(IsNUR$j^a41JUA`&19(Ssp#MP4KfNhMUt!aIT z=Z*=icl+5h!?qt*>~Vsz=rEYl=L*J&DafqqMAMBBeG#Ic&jFPEWJwX*q{rXIOuo3N z@qt=Bx*@innnVaUHUeE(Y4nqiX$;myV94ZpLl_-AzM~j*K7TJ!{8Fi=;nb zdJwYw&FA_na+QzB%wHnQSrg7$A;zeQ+sWUl=}FK>1P`g6nChX%N3l5VR(hEV@V!$#E)~MN*5I2YmEc$#R z7}xDwe*>E`k*`(QgMe=A#c%5J|E37tox`quy0fRj#)& zZ#iLlncmhV$0xLw-DYZ~Zhms%;5vNr<_#;!O@l)9+X`BkwE90_gI;qN5y4wJhp35c zt;mh29eNx*@k9!l7;#TSG;3ve71--)(nL%x+XX)_s_*RTAr)R?N=m_9cUM;9*S^&s zz^=0^imdk>bK!Ul2p5OM{J#3u@_B7n@tX76$c&F^L)8Y@iTYk;(T4Q*`nPh*(j~N5 zq-wI_Y#2#b*-1*F6;K+wrzt2AgWaN!rW96W8)m+1$>bu1$K0mNqpqlqXmmp93)JYh zjw$PPB)t7?Oqz`&F(Ec%5+ISD#)QH-sQFOWGZ8i?gNyM z7QZ)NeU-f|W)@FBGU|D(iFcU&UYTK_ts6XI-_32W5M8?Pwx$@Vt+COYvLt-r8ePN7 z*3W{P7XaUTlCWOhd+MfXC=9rq_5~qd8?tUarHS+*33lJotG_ji zJ9GE~JPUA;V7hWI=S@#fz627XgN&&==o_QpqTNkP@p7L-ICr zLg5JlOG3?9nQlyY#cF5U>IbjS45_?B>@K?Anr=)ql0(E&C8=jng^AAcH8KAe1hbl+ zd%Jnm%>mq9ecIijy{-Bo-qI~q33=7+tTxm;>eJADa|-N!#LQrCX=15p_Zr%KR^;2& za_Y%e^Gy-~zYdwfs1!Z|Rwjb^S!cchmgTO^YNim`ar0NAA4LR6`7BJ`_4o5O)_S4} zHU?{FnF$&1C8<{p%^QOT%7{^^dm3NCsj8<$zL8~q*UEAH8ddqv5338i`9q(Ke>siv z_0l06ir0>}ApfW|8I0)XIMaxkPhq}o^i)rE=(Wb(jn?6JtsKU*>37z>$NvkS60u&D zu{mS8$M!RtO^;$t^e=E4>_{TJf6D2CbsY68;2smamzFE36Zb#8b-hUU~jOgH&2-OJ-M_-u9jG~NlFtur_+x| zpO>Oc>VlsYkdtkAHjGR)j49_zna0$Bg|G;+AEco702ya6x>t0@P>{!Py}YL~r`6Gh zJ)^xTd>%KixBv{)Hok~Kd9brecS-kC{V>-^LQDk%666XS0#qeN7$o-mPul0(>|8SC zea?rcsPKq+!tiWh5`cu+<%_bXzGOy>uh|KsLi#fA?XTGhHZ2w93Mj?%6H^*;!N4l{ zSS7}O1TM9iUD~1{8@;7r+M!%}V=JT{d(w8qT&Y*l;2{wm|0)2zt)oO9W0Qu9 zWe+s`7F9Z8NL2x>W8*b&m?J%Ih7dSJCa%gnP0Nc1a^u7NVab{yVo&Z0$xqt9=hzKS54c{++2;41SRft9KE{_s{3U^Wn_n1~N z!XJq5+9w;F{1@C z7RU=Zc>KCB1?M&a5Db)Ko@}~&>|x=E^LEIQ%fi7+ICG&|es}B_x)%b2l=pyZL5{^( zmG>&YMw6(rl}y014_tA%%QWA`0IYWaJXT?NMz-{3F+AJEa1Clbi$_d6!2Q$+xDGsf z6Lj_LL(pmQ;e3^qKN=!&)7iF*i5hfmi3kVWu{?Sxrc^tP4PB|11A2ad^%@}Ow`n}m z`(#(u5U74%)%1+BTT`jo&m&-YP?xb^|NLzq?%>lTBD2-u?330S2-$BQ ztEjkFj`OpgjwyXOPw(6Nn!=|%`W`TdKYR#EW`9aWcy_S+xAa3BKaV3WbtlUxa;mrx-9Sir2O zgM%v4&tlZ4K+kRjlWFg^`Eviv8aQ|Rli{CP$G@BhaDhM82K!Rz8{sLQX?sVeI^lM? zCX2oND147GN+$O!2^qABug=K#?XNPX(wa-4Lyi5hQ?fm3HQDX(xp~8Pe20YcZ?yWO zbBk-CYi4VQMq}$rE`Rj()UibYh?cuC>=zs90OHh7XOJideGktFnuOjN*jC%lSVo!gbGn?8chGVBWT)wbfFqU#j4ov4?jEXC;#VCykJ0DDtEfGh>Fh&-0z%kqju zvx{ZvvoY`buXsJdcfd*(jMnt#D#>6>T9(v+Aun{C-!Sz$xeq3?$GLMhaA;yV$f{#^ zj^&$%3C}74aMMNtXN|v409hq(awxD2P*THcYmeX}ss!{}y={T7W(XNy)wtj?gm71) z#@f>^S2=}~6`Lh;cELwmal>qIx!&kIm`PGY7@~uIMK%o%s#uIWkpX&rKzLg4MismJo6I{2=DRt)eDVhCx`qeGE@!<5 zsEBNSc(d+D1nlB8zS{3epsnYP(0OVY@<^FKVTcuO0jyx4N(^d@bM$*P*l4~vWg^V{ zl!3a(;V`Y~wmGK@QH-=r{?>i$FyJe~mM^KNQdj_;?(GVm+SPBG9i0!CDe+&=}kE{{&-Tsi{M6cb3Pw1 zy-#dW$-Y;otd8 zSFOSpT5UKUSkvout>>IVWk*`8{AVD00O(r_2*FNv+p}fVs%4TY)!d=E%m{V~1`RcO zak*>Rs~LCYFI`7$IG2xqzB)&zf&76Fm=G=i$ls^`p((yBw@4cfXh*x67_NK(czx@w z{FpZ4oiQFM%+E5F&rQ2|60`GDjpJnF+~RhOm}hb#F__(kg}o#nl?;g|l4%P}_Rm>d zE1Zex@IE}UV8oMT6Yq#0587tHc+mH!bZg%DppHP8!1HbbX+ceRu2YEU#Z2WReKDx4 zAq`d|9lwh;PJ}ni$ypy6j_=jLRFC|{q?UzX#fGN{aoEQ+tHJzb!_G*8s)cZKMRHZg zXk_|kK78Rbop$07Z{#)_I|$L$z2o)C1nI$osTZTditAE%#;i&kvXvafjP7Wq-D74= zl=^2GNdM{rXRsrHMsS7h%Dec*5gaiyZI&$_u)(JVQYXTWju@Qc$233)UAPJ5Ek+^b zJzym=C^a^3Vg2jF{<`~J99g{PYff(e$j?wU$>Olre~&I9!D_wU@|CCm^aXuYH18|r?8G||mS$Su+iXex!f<_l%|FFOsQyt2$-|6Dx==fWwv2LjSqqs~l1jP2q z+7vy7%oo=+gBuYor|18Z7r;9=&~7;(AR@-I!;ee`+M%(A-y(?4K^eu@wq)M?U%(p! z=@?o*qdvHqLWCJOTTiu>BUn-K?$h$B2~N+I%?+36jZ&y97ctJW4aKJpOVfr4UiG$k zZoa}MDp9QjRX;6c(LZXFP6vgUW6`YY)6)r0Wpq`G%_Ws{+`1vBlz4HaxOKF|Z@P(A z_y{XgR)@aGxp~3uS0Q(%*kkrFIxHV zp5M}8cs+5aqEWZ7AdU`{PsVELz5o(^sV)$6uk6ZJG@kd?R^p+SA~h5XT{yteMImT; zcCesjK2k!_67#e%uTW~FN?+5?CoGjmKQuw!2^|`jd9I||*dq4ELI4?NIXNBo80?nD zLO#+nOCiyI(8aGIPvVeDf73Cx7d(Dm(`4a!u+JJd@MgY=?MtAX>?`?p#`R0V4xoBe*azYgn|NlYE|c>>ZT1H9k{Hc#UC?zDOjUE z{ma64m|o{cu-mstvZThO4jQ4Z-QlO|N-u{>ADTbb&V`H$D|Hh?jPy zYm+%<`?D!zHXmLd}en*tb{Q#+~0Q7utj{C`exgkF_ryMI+KtP`2` z2_jQS9b`4Zr9aX6rpc!GjBHQPoS4MkY)YbW_>MW(Bk~mM(nP|lWMdfqXu{~%;UzL> zGHMdgP}y%+04EmI$Df5E6?oF+Oi=Bd?H^^bVqbf*k1CyEnQkiQdSv5OL5JJ{)1;Mv zS5dBbe?riDm>@RzczI+Xv*-_RVt2p?6;}mo&0ro{TtG{bVJo79+`i(H8g79$2E-0! zFP~2axA9>hhhG2>Avy8n%#<93u94*cuS&d;Xm-kVplg_wA&yXg40y5U*}e2`qm1c9 z+^#4)_lNLCX_D@C1xvKGg;6<`VO|u1;xmhH^+t^r4S_h%b(-AOri?MmkWq3`alGX% z6B<5($`$osK1+j2!~5wp8!4yBWQ-#8OL4oku=yWYI%wGamQicL1G-La z%1}JyRGeoKML_Z~cm>_}^&)#Pm_|lTkPv_dnidfB2L-0!ss;As≻Xeb(y*kmtbR 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

tyu&KC;i*7heKyV=9zMhV~h#?7p~kdQD} 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 literal 49192 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 z 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 { - const locale = (await cookies()).get("MYNEXTAPP_LOCALE")?.value || "en"; - - return { - locale, - messages: (await import(`../messages/${locale}.json`)).default - } +import { getRequestConfig } from "next-intl/server"; +import { cookies } from "next/headers"; + +export default getRequestConfig( async () => { + const locale = (await cookies()).get("MYNEXTAPP_LOCALE")?.value || "en"; + + return { + locale, + messages: (await import(`../messages/${locale}.json`)).default + } }) \ No newline at end of file diff --git a/messages/cn.json b/messages/cn.json index 3da0d67..2372f3e 100644 --- a/messages/cn.json +++ b/messages/cn.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firefly 分析器", - "description": "Veritas 分析工具" - }, - "DataAnalysisPage": { - "useSkill": "使用技能", - "totalDamage": "总伤害", - "damagePerAV": "每行动值伤害", - "totalAV": "总行动值", - "skillType": "技能类型", - "skillName": "技能名称", - "actionValue": "行动值", - "character": "角色", - "id": "ID", - "path": "命途", - "rarity": "稀有度", - "damageByActionValue": "此行动值的伤害", - "element": "属性", - "totalTurn": "总回合", - "technique": "密技", - "talent": "天赋", - "basic": "普攻", - "skill": "战技", - "ultimate": "终结技", - "servant": "忆灵", - "skillDamageBreakdown": "技能类别", - "skillUsageDistribution": "技能使用分布", - "damageOverTime": "累计伤害", - "damage": "伤害", - "cumulativeDamage": "累计伤害量", - "characterInformation": "角色信息", - "turnDetail": "轮次详情", - "damageDetails": "伤害详情", - "cycleCount": "轮次", - "chartInfo": "图表信息", - "actionBar": "行动轴", - "lineupInfo": "阵容信息", - "loadData": "加载战斗数据", - "exportData": "导出战斗数据", - "connectSetting": "连接设置", - "connected": "已连接", - "unconnected": "未连接", - "socketConnection": "socket连接", - "connectionType": "连接类型", - "status": "状态", - "connect": "连接", - "checkGameConnect": "检查游戏连接", - "other": "其他", - "host": "主机", - "port": "端口", - "hostPlaceHolder": "输入主机地址", - "portPlaceHolder": "输入端口号", - "noDamageDetail": "没有可用的伤害详情", - "noCharactersInLineup": "队伍中没有角色", - "noTurns": "尚未有回合", - "type": "类型 ", - "warrior": "毁灭", - "knight": "存护", - "mage": "智识", - "priest": "丰饶", - "rogue": "巡猎", - "shaman": "同协", - "warlock": "虚无", - "memory": "记忆", - "elation": "欢愉", - "fire": "火", - "ice": "冰", - "imaginary": "虚数", - "physical": "物理", - "quantum": "量子", - "thunder": "雷", - "wind": "风", - "cycle": "回合", - "wave": "波次", - "hp": "生命值", - "atk": "攻击力", - "speed": "速度", - "critRate": "暴击率", - "critDmg": "暴击伤害", - "breakEffect": "击破特攻", - "effectRes": "效果抵抗", - "energyRegenerationRate": "能量回复效率", - "effectHitRate": "效果命中", - "outgoingHealingBoost": "治疗量加成", - "fireDmgBoost": "火属性伤害提高", - "iceDmgBoost": "冰属性伤害提高", - "imaginaryDmgBoost": "虚数属性伤害提高", - "physicalDmgBoost": "物理属性伤害提高", - "quantumDmgBoost": "量子属性伤害提高", - "thunderDmgBoost": "雷属性伤害提高", - "windDmgBoost": "风属性伤害提高", - "pursued": "附加伤害", - "true damage": "真实伤害", - "elation damage": "欢愉伤害", - "follow-up": "追加攻击", - "elemental damage": "击破与超击破伤害", - "dot": "持续伤害 ", - "damagePerCycle": "每轮伤害", - "damagePerCycleAndWave": "每轮/波伤害", - "damagePerWave": "每波伤害", - "lastTurn": "最后一回合", - "qte": "QTE技能", - "mazenormal": "普通迷宫", - "level": "等级", - "relics": "遗器", - "eidolons": "星魂", - "lightcones": "光锥", - "trailblazer": "开拓者" - } -} +{ + "TabTitle": { + "title": "Firefly 分析器", + "description": "Veritas 分析工具" + }, + "DataAnalysisPage": { + "useSkill": "使用技能", + "totalDamage": "总伤害", + "damagePerAV": "每行动值伤害", + "totalAV": "总行动值", + "skillType": "技能类型", + "skillName": "技能名称", + "actionValue": "行动值", + "character": "角色", + "id": "ID", + "path": "命途", + "rarity": "稀有度", + "damageByActionValue": "此行动值的伤害", + "element": "属性", + "totalTurn": "总回合", + "technique": "密技", + "talent": "天赋", + "basic": "普攻", + "skill": "战技", + "ultimate": "终结技", + "servant": "忆灵", + "skillDamageBreakdown": "技能类别", + "skillUsageDistribution": "技能使用分布", + "damageOverTime": "累计伤害", + "damage": "伤害", + "cumulativeDamage": "累计伤害量", + "characterInformation": "角色信息", + "turnDetail": "轮次详情", + "damageDetails": "伤害详情", + "cycleCount": "轮次", + "chartInfo": "图表信息", + "actionBar": "行动轴", + "lineupInfo": "阵容信息", + "loadData": "加载战斗数据", + "exportData": "导出战斗数据", + "connectSetting": "连接设置", + "connected": "已连接", + "unconnected": "未连接", + "socketConnection": "socket连接", + "connectionType": "连接类型", + "status": "状态", + "connect": "连接", + "checkGameConnect": "检查游戏连接", + "other": "其他", + "host": "主机", + "port": "端口", + "hostPlaceHolder": "输入主机地址", + "portPlaceHolder": "输入端口号", + "noDamageDetail": "没有可用的伤害详情", + "noCharactersInLineup": "队伍中没有角色", + "noTurns": "尚未有回合", + "type": "类型 ", + "warrior": "毁灭", + "knight": "存护", + "mage": "智识", + "priest": "丰饶", + "rogue": "巡猎", + "shaman": "同协", + "warlock": "虚无", + "memory": "记忆", + "elation": "欢愉", + "fire": "火", + "ice": "冰", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "风", + "cycle": "回合", + "wave": "波次", + "hp": "生命值", + "atk": "攻击力", + "speed": "速度", + "critRate": "暴击率", + "critDmg": "暴击伤害", + "breakEffect": "击破特攻", + "effectRes": "效果抵抗", + "energyRegenerationRate": "能量回复效率", + "effectHitRate": "效果命中", + "outgoingHealingBoost": "治疗量加成", + "fireDmgBoost": "火属性伤害提高", + "iceDmgBoost": "冰属性伤害提高", + "imaginaryDmgBoost": "虚数属性伤害提高", + "physicalDmgBoost": "物理属性伤害提高", + "quantumDmgBoost": "量子属性伤害提高", + "thunderDmgBoost": "雷属性伤害提高", + "windDmgBoost": "风属性伤害提高", + "pursued": "附加伤害", + "true damage": "真实伤害", + "elation damage": "欢愉伤害", + "follow-up": "追加攻击", + "elemental damage": "击破与超击破伤害", + "dot": "持续伤害 ", + "damagePerCycle": "每轮伤害", + "damagePerCycleAndWave": "每轮/波伤害", + "damagePerWave": "每波伤害", + "lastTurn": "最后一回合", + "qte": "QTE技能", + "mazenormal": "普通迷宫", + "level": "等级", + "relics": "遗器", + "eidolons": "星魂", + "lightcones": "光锥", + "trailblazer": "开拓者" + } +} diff --git a/messages/en.json b/messages/en.json index b49d056..f6e1590 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firely Analytics", - "description": "Analytics tool for Veritas" - }, - "DataAnalysisPage": { - "useSkill": "Use Ability", - "totalDamage": "Total damage", - "damagePerAV": "Damage/Action value", - "totalAV": "Total action value", - "skillType": "Skill Type", - "skillName": "Skill Name", - "actionValue": "Action Value", - "character": "Character", - "id": "Id", - "path": "Path", - "rarity": "Rarity", - "element": "Element", - "totalTurn": "Total Turn", - "technique": "Technique", - "talent": "Talent", - "basic": "Basic Attack", - "skill": "Skill", - "ultimate": "Ultimate", - "servant": "Servant", - "skillDamageBreakdown": "Skill Damage Breakdown", - "skillUsageDistribution": "Skill Usage Distribution", - "damageOverTime": "Damage Over Time", - "damage": "Damage", - "cumulativeDamage": "Cumulative Damage", - "damageByActionValue": "Damage by Action Value", - "characterInformation": "Character Information", - "turnDetail": "Turn Details", - "damageDetails": "Damage Details", - "cycleCount": "Cycle Count", - "chartInfo": "Chart info", - "actionBar": "Action time line", - "lineupInfo": "Lineup info", - "loadData": "Load data battle", - "exportData": "Export data battle", - "connectSetting": "Connection Setting", - "connected": "Connected", - "unconnected": "Unconnected", - "socketConnection": "Socket Connection", - "connectionType": "Connection Type", - "status": "Status", - "connect": "Connect", - "checkGameConnect": "Check game connection", - "other": "Other", - "host": "Host", - "port": "Port", - "hostPlaceHolder": "Enter Host", - "portPlaceHolder": "Enter port number", - "noDamageDetail": "No damage details available", - "noCharactersInLineup": "No characters in lineup", - "noTurns": "No turns yet", - "type": "Type", - "warrior": "The Destruction", - "knight": "The Preservation", - "mage": "The Erudition", - "priest": "The Abundance", - "rogue": "The Hunt", - "shaman": "The Harmony", - "warlock": "The Nihility", - "memory": "The Remembrance", - "elation": "The Elation", - "fire": "Fire", - "ice": "Ice", - "imaginary": "Imaginary", - "physical": "Physical", - "quantum": "Quantum", - "thunder": "Thunder", - "wind": "Wind", - "cycle": "Cycle", - "wave": "Wave", - "hp": "Hp", - "atk": "Atk", - "speed": "Speed", - "critRate": "Crit Rate", - "critDmg": "Crit Dmg", - "breakEffect": "Break Effect", - "effectRes": "Effect Res", - "energyRegenerationRate": "Energy Regeneration Rate", - "effectHitRate": "Effect Hit Rate", - "outgoingHealingBoost": "Outgoing Healing Boost", - "fireDmgBoost": "Fire damage boost", - "iceDmgBoost": "Ice damage Boost", - "imaginaryDmgBoost": "Imaginary damage boost", - "physicalDmgBoost": "Physical damage boost", - "quantumDmgBoost": "Quantum damage boost", - "thunderDmgBoost": "Thunder damage boost", - "windDmgBoost": "Wind damage boost", - "pursued": "Additional damage", - "true damage": "True damage", - "elation damage": "Elation Damage", - "follow-up": "Follow-up Damage", - "elemental damage": "Break and Super break damage", - "dot": "Damage over time ", - "damagePerCycle": "Damage per Cycle", - "damagePerCycleAndWave": "Damage per Cycle and Wave", - "damagePerWave": "Damage per Wave", - "lastTurn": "Last turn", - "qte": "QTE Skill", - "mazenormal": "MazeNormal", - "level": "Level", - "relics": "Relics", - "eidolons": "Eidolons", - "lightcones": "Lightcones", - "trailblazer": "Trailblazer" - } -} +{ + "TabTitle": { + "title": "Firely Analytics", + "description": "Analytics tool for Veritas" + }, + "DataAnalysisPage": { + "useSkill": "Use Ability", + "totalDamage": "Total damage", + "damagePerAV": "Damage/Action value", + "totalAV": "Total action value", + "skillType": "Skill Type", + "skillName": "Skill Name", + "actionValue": "Action Value", + "character": "Character", + "id": "Id", + "path": "Path", + "rarity": "Rarity", + "element": "Element", + "totalTurn": "Total Turn", + "technique": "Technique", + "talent": "Talent", + "basic": "Basic Attack", + "skill": "Skill", + "ultimate": "Ultimate", + "servant": "Servant", + "skillDamageBreakdown": "Skill Damage Breakdown", + "skillUsageDistribution": "Skill Usage Distribution", + "damageOverTime": "Damage Over Time", + "damage": "Damage", + "cumulativeDamage": "Cumulative Damage", + "damageByActionValue": "Damage by Action Value", + "characterInformation": "Character Information", + "turnDetail": "Turn Details", + "damageDetails": "Damage Details", + "cycleCount": "Cycle Count", + "chartInfo": "Chart info", + "actionBar": "Action time line", + "lineupInfo": "Lineup info", + "loadData": "Load data battle", + "exportData": "Export data battle", + "connectSetting": "Connection Setting", + "connected": "Connected", + "unconnected": "Unconnected", + "socketConnection": "Socket Connection", + "connectionType": "Connection Type", + "status": "Status", + "connect": "Connect", + "checkGameConnect": "Check game connection", + "other": "Other", + "host": "Host", + "port": "Port", + "hostPlaceHolder": "Enter Host", + "portPlaceHolder": "Enter port number", + "noDamageDetail": "No damage details available", + "noCharactersInLineup": "No characters in lineup", + "noTurns": "No turns yet", + "type": "Type", + "warrior": "The Destruction", + "knight": "The Preservation", + "mage": "The Erudition", + "priest": "The Abundance", + "rogue": "The Hunt", + "shaman": "The Harmony", + "warlock": "The Nihility", + "memory": "The Remembrance", + "elation": "The Elation", + "fire": "Fire", + "ice": "Ice", + "imaginary": "Imaginary", + "physical": "Physical", + "quantum": "Quantum", + "thunder": "Thunder", + "wind": "Wind", + "cycle": "Cycle", + "wave": "Wave", + "hp": "Hp", + "atk": "Atk", + "speed": "Speed", + "critRate": "Crit Rate", + "critDmg": "Crit Dmg", + "breakEffect": "Break Effect", + "effectRes": "Effect Res", + "energyRegenerationRate": "Energy Regeneration Rate", + "effectHitRate": "Effect Hit Rate", + "outgoingHealingBoost": "Outgoing Healing Boost", + "fireDmgBoost": "Fire damage boost", + "iceDmgBoost": "Ice damage Boost", + "imaginaryDmgBoost": "Imaginary damage boost", + "physicalDmgBoost": "Physical damage boost", + "quantumDmgBoost": "Quantum damage boost", + "thunderDmgBoost": "Thunder damage boost", + "windDmgBoost": "Wind damage boost", + "pursued": "Additional damage", + "true damage": "True damage", + "elation damage": "Elation Damage", + "follow-up": "Follow-up Damage", + "elemental damage": "Break and Super break damage", + "dot": "Damage over time ", + "damagePerCycle": "Damage per Cycle", + "damagePerCycleAndWave": "Damage per Cycle and Wave", + "damagePerWave": "Damage per Wave", + "lastTurn": "Last turn", + "qte": "QTE Skill", + "mazenormal": "MazeNormal", + "level": "Level", + "relics": "Relics", + "eidolons": "Eidolons", + "lightcones": "Lightcones", + "trailblazer": "Trailblazer" + } +} diff --git a/messages/ja.json b/messages/ja.json index a9c3224..91fea0b 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firefly Analytics", - "description": "Veritasの分析ツール" - }, - "DataAnalysisPage": { - "useSkill": "スキルを使用", - "totalDamage": "総ダメージ", - "damagePerAV": "ダメージ/行動値", - "totalAV": "総行動値", - "skillType": "スキルタイプ", - "skillName": "スキル名", - "actionValue": "行動値", - "character": "キャラクター", - "id": "ID", - "path": "運命", - "rarity": "レアリティ", - "element": "属性", - "totalTurn": "総ターン数", - "technique": "秘技", - "talent": "天賦", - "basic": "通常攻撃", - "skill": "スキル", - "ultimate": "必殺技", - "servant": "召喚", - "skillDamageBreakdown": "スキルダメージ内訳", - "skillUsageDistribution": "スキル使用分布", - "damageOverTime": "継続ダメージ", - "damage": "ダメージ", - "cumulativeDamage": "累積ダメージ", - "damageByActionValue": "行動値別ダメージ", - "characterInformation": "キャラクター情報", - "turnDetail": "ターン詳細", - "damageDetails": "ダメージ詳細", - "cycleCount": "サイクル数", - "chartInfo": "チャート情報", - "actionBar": "アクションタイムライン", - "lineupInfo": "編成情報", - "loadData": "戦闘データを読み込む", - "exportData": "戦闘データをエクスポート", - "connectSetting": "接続設定", - "connected": "接続済み", - "unconnected": "未接続", - "socketConnection": "ソケット接続", - "connectionType": "接続タイプ", - "status": "ステータス", - "connect": "接続する", - "checkGameConnect": "ゲーム接続を確認", - "other": "その他", - "host": "ホスト", - "port": "ポート", - "hostPlaceHolder": "ホストを入力", - "portPlaceHolder": "ポート番号を入力", - "noDamageDetail": "ダメージ詳細がありません", - "noCharactersInLineup": "編成にキャラクターがいません", - "noTurns": "まだターンがありません", - "type": "タイプ", - "warrior": "壊滅", - "knight": "存護", - "mage": "知恵", - "priest": "豊穣", - "rogue": "巡狩", - "shaman": "調和", - "warlock": "虚無", - "memory": "記憶", - "elation": "愉悦", - "fire": "炎", - "ice": "氷", - "imaginary": "虚数", - "physical": "物理", - "quantum": "量子", - "thunder": "雷", - "wind": "風", - "cycle": "サイクル", - "wave": "ウェーブ", - "hp": "HP", - "atk": "攻撃力", - "speed": "速度", - "critRate": "会心率", - "critDmg": "会心ダメージ", - "breakEffect": "撃破特効", - "effectRes": "効果抵抗", - "energyRegenerationRate": "エネルギー回復効率", - "effectHitRate": "効果命中率", - "outgoingHealingBoost": "治癒量上昇", - "fireDmgBoost": "炎属性ダメージ上昇", - "iceDmgBoost": "氷属性ダメージ上昇", - "imaginaryDmgBoost": "虚数属性ダメージ上昇", - "physicalDmgBoost": "物理属性ダメージ上昇", - "quantumDmgBoost": "量子属性ダメージ上昇", - "thunderDmgBoost": "雷属性ダメージ上昇", - "windDmgBoost": "風属性ダメージ上昇", - "pursued": "追加ダメージ", - "true damage": "貫通ダメージ", - "elation damage": "愉悦ダメージ", - "follow-up": "追撃ダメージ", - "elemental damage": "撃破・超撃破ダメージ", - "dot": "継続ダメージ", - "damagePerCycle": "サイクル毎のダメージ", - "damagePerCycleAndWave": "サイクル・ウェーブ毎のダメージ", - "damagePerWave": "ウェーブ毎のダメージ", - "lastTurn": "最後のターン", - "qte": "QTEスキル", - "mazenormal": "通常迷宮", - "level": "レベル", - "relics": "遺物", - "eidolons": "星魂", - "lightcones": "光円錐", - "trailblazer": "開拓者" - } -} +{ + "TabTitle": { + "title": "Firefly Analytics", + "description": "Veritasの分析ツール" + }, + "DataAnalysisPage": { + "useSkill": "スキルを使用", + "totalDamage": "総ダメージ", + "damagePerAV": "ダメージ/行動値", + "totalAV": "総行動値", + "skillType": "スキルタイプ", + "skillName": "スキル名", + "actionValue": "行動値", + "character": "キャラクター", + "id": "ID", + "path": "運命", + "rarity": "レアリティ", + "element": "属性", + "totalTurn": "総ターン数", + "technique": "秘技", + "talent": "天賦", + "basic": "通常攻撃", + "skill": "スキル", + "ultimate": "必殺技", + "servant": "召喚", + "skillDamageBreakdown": "スキルダメージ内訳", + "skillUsageDistribution": "スキル使用分布", + "damageOverTime": "継続ダメージ", + "damage": "ダメージ", + "cumulativeDamage": "累積ダメージ", + "damageByActionValue": "行動値別ダメージ", + "characterInformation": "キャラクター情報", + "turnDetail": "ターン詳細", + "damageDetails": "ダメージ詳細", + "cycleCount": "サイクル数", + "chartInfo": "チャート情報", + "actionBar": "アクションタイムライン", + "lineupInfo": "編成情報", + "loadData": "戦闘データを読み込む", + "exportData": "戦闘データをエクスポート", + "connectSetting": "接続設定", + "connected": "接続済み", + "unconnected": "未接続", + "socketConnection": "ソケット接続", + "connectionType": "接続タイプ", + "status": "ステータス", + "connect": "接続する", + "checkGameConnect": "ゲーム接続を確認", + "other": "その他", + "host": "ホスト", + "port": "ポート", + "hostPlaceHolder": "ホストを入力", + "portPlaceHolder": "ポート番号を入力", + "noDamageDetail": "ダメージ詳細がありません", + "noCharactersInLineup": "編成にキャラクターがいません", + "noTurns": "まだターンがありません", + "type": "タイプ", + "warrior": "壊滅", + "knight": "存護", + "mage": "知恵", + "priest": "豊穣", + "rogue": "巡狩", + "shaman": "調和", + "warlock": "虚無", + "memory": "記憶", + "elation": "愉悦", + "fire": "炎", + "ice": "氷", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "風", + "cycle": "サイクル", + "wave": "ウェーブ", + "hp": "HP", + "atk": "攻撃力", + "speed": "速度", + "critRate": "会心率", + "critDmg": "会心ダメージ", + "breakEffect": "撃破特効", + "effectRes": "効果抵抗", + "energyRegenerationRate": "エネルギー回復効率", + "effectHitRate": "効果命中率", + "outgoingHealingBoost": "治癒量上昇", + "fireDmgBoost": "炎属性ダメージ上昇", + "iceDmgBoost": "氷属性ダメージ上昇", + "imaginaryDmgBoost": "虚数属性ダメージ上昇", + "physicalDmgBoost": "物理属性ダメージ上昇", + "quantumDmgBoost": "量子属性ダメージ上昇", + "thunderDmgBoost": "雷属性ダメージ上昇", + "windDmgBoost": "風属性ダメージ上昇", + "pursued": "追加ダメージ", + "true damage": "貫通ダメージ", + "elation damage": "愉悦ダメージ", + "follow-up": "追撃ダメージ", + "elemental damage": "撃破・超撃破ダメージ", + "dot": "継続ダメージ", + "damagePerCycle": "サイクル毎のダメージ", + "damagePerCycleAndWave": "サイクル・ウェーブ毎のダメージ", + "damagePerWave": "ウェーブ毎のダメージ", + "lastTurn": "最後のターン", + "qte": "QTEスキル", + "mazenormal": "通常迷宮", + "level": "レベル", + "relics": "遺物", + "eidolons": "星魂", + "lightcones": "光円錐", + "trailblazer": "開拓者" + } +} diff --git a/messages/ko.json b/messages/ko.json index 000f91f..15d4685 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firefly 분석", - "description": "Veritas의 분석 도구" - }, - "DataAnalysisPage": { - "useSkill": "스킬 사용", - "totalDamage": "총 피해량", - "damagePerAV": "피해량/행동값", - "totalAV": "총 행동값", - "skillType": "스킬 유형", - "skillName": "스킬 이름", - "actionValue": "행동값", - "character": "캐릭터", - "id": "ID", - "path": "운명", - "rarity": "희귀도", - "element": "속성", - "totalTurn": "총 턴 수", - "technique": "비전", - "talent": "재능", - "basic": "기본 공격", - "skill": "스킬", - "ultimate": "필살기", - "servant": "소환", - "skillDamageBreakdown": "스킬 피해 세부사항", - "skillUsageDistribution": "스킬 사용 분포", - "damageOverTime": "시간당 피해", - "damage": "피해", - "cumulativeDamage": "누적 피해", - "damageByActionValue": "행동값별 피해", - "characterInformation": "캐릭터 정보", - "turnDetail": "턴 상세 정보", - "damageDetails": "피해 세부사항", - "cycleCount": "사이클 수", - "chartInfo": "차트 정보", - "actionBar": "행동 타임라인", - "lineupInfo": "편성 정보", - "loadData": "전투 데이터 불러오기", - "exportData": "전투 데이터 내보내기", - "connectSetting": "연결 설정", - "connected": "연결됨", - "unconnected": "연결되지 않음", - "socketConnection": "소켓 연결", - "connectionType": "연결 유형", - "status": "상태", - "connect": "연결", - "checkGameConnect": "게임 연결 확인", - "other": "기타", - "host": "호스트", - "port": "포트", - "hostPlaceHolder": "호스트 입력", - "portPlaceHolder": "포트 번호 입력", - "noDamageDetail": "피해 세부사항 없음", - "noCharactersInLineup": "편성에 캐릭터 없음", - "noTurns": "아직 턴이 없습니다", - "type": "유형", - "warrior": "파괴", - "knight": "보존", - "mage": "지혜", - "priest": "풍요", - "rogue": "사냥", - "shaman": "조화", - "warlock": "허무", - "memory": "기억", - "elation": "환락", - "fire": "불", - "ice": "얼음", - "imaginary": "허수", - "physical": "물리", - "quantum": "양자", - "thunder": "번개", - "wind": "바람", - "cycle": "사이클", - "wave": "웨이브", - "hp": "HP", - "atk": "공격력", - "speed": "속도", - "critRate": "치명타 확률", - "critDmg": "치명타 피해", - "breakEffect": "파괴 효과", - "effectRes": "효과 저항", - "energyRegenerationRate": "에너지 회복 속도", - "effectHitRate": "효과 적중률", - "outgoingHealingBoost": "치유량 증가", - "fireDmgBoost": "불 속성 피해 증가", - "iceDmgBoost": "얼음 속성 피해 증가", - "imaginaryDmgBoost": "허수 속성 피해 증가", - "physicalDmgBoost": "물리 속성 피해 증가", - "quantumDmgBoost": "양자 속성 피해 증가", - "thunderDmgBoost": "번개 속성 피해 증가", - "windDmgBoost": "바람 속성 피해 증가", - "pursued": "추가 피해", - "true damage": "관통 피해", - "elation damage": "환락 피해", - "follow-up": "추가 공격 피해", - "elemental damage": "파괴 및 초파괴 피해", - "dot": "지속 피해", - "damagePerCycle": "사이클당 피해", - "damagePerCycleAndWave": "사이클 및 웨이브당 피해", - "damagePerWave": "웨이브당 피해", - "lastTurn": "마지막 턴", - "qte": "QTE 스킬", - "mazenormal": "일반 미궁", - "level": "레벨", - "relics": "유물", - "eidolons": "에이돌론", - "lightcones": "광추", - "trailblazer": "개척자" - } -} +{ + "TabTitle": { + "title": "Firefly 분석", + "description": "Veritas의 분석 도구" + }, + "DataAnalysisPage": { + "useSkill": "스킬 사용", + "totalDamage": "총 피해량", + "damagePerAV": "피해량/행동값", + "totalAV": "총 행동값", + "skillType": "스킬 유형", + "skillName": "스킬 이름", + "actionValue": "행동값", + "character": "캐릭터", + "id": "ID", + "path": "운명", + "rarity": "희귀도", + "element": "속성", + "totalTurn": "총 턴 수", + "technique": "비전", + "talent": "재능", + "basic": "기본 공격", + "skill": "스킬", + "ultimate": "필살기", + "servant": "소환", + "skillDamageBreakdown": "스킬 피해 세부사항", + "skillUsageDistribution": "스킬 사용 분포", + "damageOverTime": "시간당 피해", + "damage": "피해", + "cumulativeDamage": "누적 피해", + "damageByActionValue": "행동값별 피해", + "characterInformation": "캐릭터 정보", + "turnDetail": "턴 상세 정보", + "damageDetails": "피해 세부사항", + "cycleCount": "사이클 수", + "chartInfo": "차트 정보", + "actionBar": "행동 타임라인", + "lineupInfo": "편성 정보", + "loadData": "전투 데이터 불러오기", + "exportData": "전투 데이터 내보내기", + "connectSetting": "연결 설정", + "connected": "연결됨", + "unconnected": "연결되지 않음", + "socketConnection": "소켓 연결", + "connectionType": "연결 유형", + "status": "상태", + "connect": "연결", + "checkGameConnect": "게임 연결 확인", + "other": "기타", + "host": "호스트", + "port": "포트", + "hostPlaceHolder": "호스트 입력", + "portPlaceHolder": "포트 번호 입력", + "noDamageDetail": "피해 세부사항 없음", + "noCharactersInLineup": "편성에 캐릭터 없음", + "noTurns": "아직 턴이 없습니다", + "type": "유형", + "warrior": "파괴", + "knight": "보존", + "mage": "지혜", + "priest": "풍요", + "rogue": "사냥", + "shaman": "조화", + "warlock": "허무", + "memory": "기억", + "elation": "환락", + "fire": "불", + "ice": "얼음", + "imaginary": "허수", + "physical": "물리", + "quantum": "양자", + "thunder": "번개", + "wind": "바람", + "cycle": "사이클", + "wave": "웨이브", + "hp": "HP", + "atk": "공격력", + "speed": "속도", + "critRate": "치명타 확률", + "critDmg": "치명타 피해", + "breakEffect": "파괴 효과", + "effectRes": "효과 저항", + "energyRegenerationRate": "에너지 회복 속도", + "effectHitRate": "효과 적중률", + "outgoingHealingBoost": "치유량 증가", + "fireDmgBoost": "불 속성 피해 증가", + "iceDmgBoost": "얼음 속성 피해 증가", + "imaginaryDmgBoost": "허수 속성 피해 증가", + "physicalDmgBoost": "물리 속성 피해 증가", + "quantumDmgBoost": "양자 속성 피해 증가", + "thunderDmgBoost": "번개 속성 피해 증가", + "windDmgBoost": "바람 속성 피해 증가", + "pursued": "추가 피해", + "true damage": "관통 피해", + "elation damage": "환락 피해", + "follow-up": "추가 공격 피해", + "elemental damage": "파괴 및 초파괴 피해", + "dot": "지속 피해", + "damagePerCycle": "사이클당 피해", + "damagePerCycleAndWave": "사이클 및 웨이브당 피해", + "damagePerWave": "웨이브당 피해", + "lastTurn": "마지막 턴", + "qte": "QTE 스킬", + "mazenormal": "일반 미궁", + "level": "레벨", + "relics": "유물", + "eidolons": "에이돌론", + "lightcones": "광추", + "trailblazer": "개척자" + } +} diff --git a/messages/vi.json b/messages/vi.json index 5639767..b454d74 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firefly Analytics", - "description": "Công cụ phân tích cho Veritas" - }, - "DataAnalysisPage": { - "useSkill": "Sử dụng kỹ năng", - "totalDamage": "Tổng sát thương", - "damagePerAV": "Sát thương/giá trị hành động", - "totalAV": "Tổng giá trị hành động", - "skillType": "Loại kỹ năng", - "skillName": "Tên kỹ năng", - "actionValue": "Giá trị hành động", - "character": "Nhân vật", - "id": "ID", - "path": "Vận mệnh", - "rarity": "Số sao", - "element": "Nguyên tố", - "totalTurn": "Tổng lượt", - "damageByActionValue": "Sát thương theo giá trị hành động", - "technique": "Bĩ kỹ", - "talent": "Thiên phú", - "basic": "Tấn công thường", - "skill": "Chiến kỹ", - "ultimate": "Tuyệt Kĩ", - "servant": "Vật triệu hồi", - "skillDamageBreakdown": "Phân tích sát thương kỹ năng", - "skillUsageDistribution": "Phân phối sử dụng kỹ năng", - "damageOverTime": "Sát thương theo thời gian", - "damage": "Sát thương", - "cumulativeDamage": "Sát thương tích lũy", - "characterInformation": "Thông tin nhân vật", - "turnDetail": "Chi tiết lượt", - "damageDetails": "Chi tiết sát thương", - "cycleCount": "Số vòng", - "chartInfo": "Thông tin biểu đồ", - "actionBar": "Thanh hành động", - "lineupInfo": "Thông tin đội hình", - "loadData": "Tải dữ liệu trận đấu", - "exportData": "Xuất dữ liệu trận đấu", - "connectSetting": "Cài đặt kết nối", - "connected": "Đã kết nối", - "unconnected": "Chưa kết nối", - "socketConnection": "Kết nối socket", - "connectionType": "Loại kết nối", - "status": "Trạng thái", - "connect": "Kết nối", - "checkGameConnect": "Kiểm tra kết nối game", - "other": "Khác", - "host": "Địa chỉ kết nối", - "port": "Cổng kết nối", - "hostPlaceHolder": "Nhập địa chỉ kết nối", - "portPlaceHolder": "Nhâp cổng kết nối", - "noDamageDetail": "Không có chi tiết sát thương", - "noCharactersInLineup": "Không có nhân vật trong đội hình", - "noTurns": "Chưa có lượt hành động", - "type": "Loại", - "warrior": "Hủy Diệt", - "knight": "Bảo Hộ", - "mage": "Tri Thức", - "priest": "Phong Phú", - "rogue": "Săn Bắn", - "shaman": "Hài Hòa", - "warlock": "Hư Vô", - "memory": "Ký Ức", - "elation": "Vui vẻ", - "fire": "Lửa", - "ice": "Băng", - "imaginary": "Số ảo", - "physical": "Vật Lý", - "quantum": "Lượng Tử", - "thunder": "Lôi", - "wind": "Phong", - "cycle": "Vòng", - "wave": "Đợt quái", - "hp": "Máu", - "atk": "Tấn công", - "speed": "Tốc độ", - "critRate": "Tỉ lệ chí mạng", - "critDmg": "Sát thương chí mạng", - "breakEffect": "Tấn công kích phá", - "effectRes": "Kháng hiệu ứng", - "energyRegenerationRate": "Hiệu quả nạp", - "effectHitRate": "Chinh xác hiệu ứng", - "outgoingHealingBoost": "Hiệu quả trị liệu", - "fireDmgBoost": "Tăng sát thương hỏa", - "iceDmgBoost": "Tăng sát thương băng", - "imaginaryDmgBoost": "Tăng sát thương số ảo", - "physicalDmgBoost": "Tăng sát thương vật lý", - "quantumDmgBoost": "Tăng sát thương lượng tử", - "thunderDmgBoost": "Tăng sát thương lôi", - "windDmgBoost": "Tăng sát thương phong", - "pursued": "Sát thương kèm theo", - "true damage": "Sát thương chuẩn", - "elation damage": "Sát thương vui vẻ", - "follow-up": "Sát thương theo sau", - "elemental damage": "Sát thương phá vỡ và siêu phá vỡ", - "dot": "Sát Thương Duy Trì", - "damagePerCycle": "Sát thương mỗi vòng", - "damagePerCycleAndWave": "Sát thương mỗi vòng và đợt", - "damagePerWave": "Sát thương mỗi đợt", - "lastTurn": "Lượt cuối", - "qte": "Kỹ năng QTE", - "mazenormal": "Mê cung (Thường)", - "level": "Cấp độ", - "relics": "Di vật", - "eidolons": "Tinh hồn", - "lightcones": "Nón Ánh sáng", - "trailblazer": "Nhà khai phá" - } -} +{ + "TabTitle": { + "title": "Firefly Analytics", + "description": "Công cụ phân tích cho Veritas" + }, + "DataAnalysisPage": { + "useSkill": "Sử dụng kỹ năng", + "totalDamage": "Tổng sát thương", + "damagePerAV": "Sát thương/giá trị hành động", + "totalAV": "Tổng giá trị hành động", + "skillType": "Loại kỹ năng", + "skillName": "Tên kỹ năng", + "actionValue": "Giá trị hành động", + "character": "Nhân vật", + "id": "ID", + "path": "Vận mệnh", + "rarity": "Số sao", + "element": "Nguyên tố", + "totalTurn": "Tổng lượt", + "damageByActionValue": "Sát thương theo giá trị hành động", + "technique": "Bĩ kỹ", + "talent": "Thiên phú", + "basic": "Tấn công thường", + "skill": "Chiến kỹ", + "ultimate": "Tuyệt Kĩ", + "servant": "Vật triệu hồi", + "skillDamageBreakdown": "Phân tích sát thương kỹ năng", + "skillUsageDistribution": "Phân phối sử dụng kỹ năng", + "damageOverTime": "Sát thương theo thời gian", + "damage": "Sát thương", + "cumulativeDamage": "Sát thương tích lũy", + "characterInformation": "Thông tin nhân vật", + "turnDetail": "Chi tiết lượt", + "damageDetails": "Chi tiết sát thương", + "cycleCount": "Số vòng", + "chartInfo": "Thông tin biểu đồ", + "actionBar": "Thanh hành động", + "lineupInfo": "Thông tin đội hình", + "loadData": "Tải dữ liệu trận đấu", + "exportData": "Xuất dữ liệu trận đấu", + "connectSetting": "Cài đặt kết nối", + "connected": "Đã kết nối", + "unconnected": "Chưa kết nối", + "socketConnection": "Kết nối socket", + "connectionType": "Loại kết nối", + "status": "Trạng thái", + "connect": "Kết nối", + "checkGameConnect": "Kiểm tra kết nối game", + "other": "Khác", + "host": "Địa chỉ kết nối", + "port": "Cổng kết nối", + "hostPlaceHolder": "Nhập địa chỉ kết nối", + "portPlaceHolder": "Nhâp cổng kết nối", + "noDamageDetail": "Không có chi tiết sát thương", + "noCharactersInLineup": "Không có nhân vật trong đội hình", + "noTurns": "Chưa có lượt hành động", + "type": "Loại", + "warrior": "Hủy Diệt", + "knight": "Bảo Hộ", + "mage": "Tri Thức", + "priest": "Phong Phú", + "rogue": "Săn Bắn", + "shaman": "Hài Hòa", + "warlock": "Hư Vô", + "memory": "Ký Ức", + "elation": "Vui vẻ", + "fire": "Lửa", + "ice": "Băng", + "imaginary": "Số ảo", + "physical": "Vật Lý", + "quantum": "Lượng Tử", + "thunder": "Lôi", + "wind": "Phong", + "cycle": "Vòng", + "wave": "Đợt quái", + "hp": "Máu", + "atk": "Tấn công", + "speed": "Tốc độ", + "critRate": "Tỉ lệ chí mạng", + "critDmg": "Sát thương chí mạng", + "breakEffect": "Tấn công kích phá", + "effectRes": "Kháng hiệu ứng", + "energyRegenerationRate": "Hiệu quả nạp", + "effectHitRate": "Chinh xác hiệu ứng", + "outgoingHealingBoost": "Hiệu quả trị liệu", + "fireDmgBoost": "Tăng sát thương hỏa", + "iceDmgBoost": "Tăng sát thương băng", + "imaginaryDmgBoost": "Tăng sát thương số ảo", + "physicalDmgBoost": "Tăng sát thương vật lý", + "quantumDmgBoost": "Tăng sát thương lượng tử", + "thunderDmgBoost": "Tăng sát thương lôi", + "windDmgBoost": "Tăng sát thương phong", + "pursued": "Sát thương kèm theo", + "true damage": "Sát thương chuẩn", + "elation damage": "Sát thương vui vẻ", + "follow-up": "Sát thương theo sau", + "elemental damage": "Sát thương phá vỡ và siêu phá vỡ", + "dot": "Sát Thương Duy Trì", + "damagePerCycle": "Sát thương mỗi vòng", + "damagePerCycleAndWave": "Sát thương mỗi vòng và đợt", + "damagePerWave": "Sát thương mỗi đợt", + "lastTurn": "Lượt cuối", + "qte": "Kỹ năng QTE", + "mazenormal": "Mê cung (Thường)", + "level": "Cấp độ", + "relics": "Di vật", + "eidolons": "Tinh hồn", + "lightcones": "Nón Ánh sáng", + "trailblazer": "Nhà khai phá" + } +} diff --git a/messages/zh.json b/messages/zh.json index 3da0d67..2372f3e 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -1,111 +1,111 @@ -{ - "TabTitle": { - "title": "Firefly 分析器", - "description": "Veritas 分析工具" - }, - "DataAnalysisPage": { - "useSkill": "使用技能", - "totalDamage": "总伤害", - "damagePerAV": "每行动值伤害", - "totalAV": "总行动值", - "skillType": "技能类型", - "skillName": "技能名称", - "actionValue": "行动值", - "character": "角色", - "id": "ID", - "path": "命途", - "rarity": "稀有度", - "damageByActionValue": "此行动值的伤害", - "element": "属性", - "totalTurn": "总回合", - "technique": "密技", - "talent": "天赋", - "basic": "普攻", - "skill": "战技", - "ultimate": "终结技", - "servant": "忆灵", - "skillDamageBreakdown": "技能类别", - "skillUsageDistribution": "技能使用分布", - "damageOverTime": "累计伤害", - "damage": "伤害", - "cumulativeDamage": "累计伤害量", - "characterInformation": "角色信息", - "turnDetail": "轮次详情", - "damageDetails": "伤害详情", - "cycleCount": "轮次", - "chartInfo": "图表信息", - "actionBar": "行动轴", - "lineupInfo": "阵容信息", - "loadData": "加载战斗数据", - "exportData": "导出战斗数据", - "connectSetting": "连接设置", - "connected": "已连接", - "unconnected": "未连接", - "socketConnection": "socket连接", - "connectionType": "连接类型", - "status": "状态", - "connect": "连接", - "checkGameConnect": "检查游戏连接", - "other": "其他", - "host": "主机", - "port": "端口", - "hostPlaceHolder": "输入主机地址", - "portPlaceHolder": "输入端口号", - "noDamageDetail": "没有可用的伤害详情", - "noCharactersInLineup": "队伍中没有角色", - "noTurns": "尚未有回合", - "type": "类型 ", - "warrior": "毁灭", - "knight": "存护", - "mage": "智识", - "priest": "丰饶", - "rogue": "巡猎", - "shaman": "同协", - "warlock": "虚无", - "memory": "记忆", - "elation": "欢愉", - "fire": "火", - "ice": "冰", - "imaginary": "虚数", - "physical": "物理", - "quantum": "量子", - "thunder": "雷", - "wind": "风", - "cycle": "回合", - "wave": "波次", - "hp": "生命值", - "atk": "攻击力", - "speed": "速度", - "critRate": "暴击率", - "critDmg": "暴击伤害", - "breakEffect": "击破特攻", - "effectRes": "效果抵抗", - "energyRegenerationRate": "能量回复效率", - "effectHitRate": "效果命中", - "outgoingHealingBoost": "治疗量加成", - "fireDmgBoost": "火属性伤害提高", - "iceDmgBoost": "冰属性伤害提高", - "imaginaryDmgBoost": "虚数属性伤害提高", - "physicalDmgBoost": "物理属性伤害提高", - "quantumDmgBoost": "量子属性伤害提高", - "thunderDmgBoost": "雷属性伤害提高", - "windDmgBoost": "风属性伤害提高", - "pursued": "附加伤害", - "true damage": "真实伤害", - "elation damage": "欢愉伤害", - "follow-up": "追加攻击", - "elemental damage": "击破与超击破伤害", - "dot": "持续伤害 ", - "damagePerCycle": "每轮伤害", - "damagePerCycleAndWave": "每轮/波伤害", - "damagePerWave": "每波伤害", - "lastTurn": "最后一回合", - "qte": "QTE技能", - "mazenormal": "普通迷宫", - "level": "等级", - "relics": "遗器", - "eidolons": "星魂", - "lightcones": "光锥", - "trailblazer": "开拓者" - } -} +{ + "TabTitle": { + "title": "Firefly 分析器", + "description": "Veritas 分析工具" + }, + "DataAnalysisPage": { + "useSkill": "使用技能", + "totalDamage": "总伤害", + "damagePerAV": "每行动值伤害", + "totalAV": "总行动值", + "skillType": "技能类型", + "skillName": "技能名称", + "actionValue": "行动值", + "character": "角色", + "id": "ID", + "path": "命途", + "rarity": "稀有度", + "damageByActionValue": "此行动值的伤害", + "element": "属性", + "totalTurn": "总回合", + "technique": "密技", + "talent": "天赋", + "basic": "普攻", + "skill": "战技", + "ultimate": "终结技", + "servant": "忆灵", + "skillDamageBreakdown": "技能类别", + "skillUsageDistribution": "技能使用分布", + "damageOverTime": "累计伤害", + "damage": "伤害", + "cumulativeDamage": "累计伤害量", + "characterInformation": "角色信息", + "turnDetail": "轮次详情", + "damageDetails": "伤害详情", + "cycleCount": "轮次", + "chartInfo": "图表信息", + "actionBar": "行动轴", + "lineupInfo": "阵容信息", + "loadData": "加载战斗数据", + "exportData": "导出战斗数据", + "connectSetting": "连接设置", + "connected": "已连接", + "unconnected": "未连接", + "socketConnection": "socket连接", + "connectionType": "连接类型", + "status": "状态", + "connect": "连接", + "checkGameConnect": "检查游戏连接", + "other": "其他", + "host": "主机", + "port": "端口", + "hostPlaceHolder": "输入主机地址", + "portPlaceHolder": "输入端口号", + "noDamageDetail": "没有可用的伤害详情", + "noCharactersInLineup": "队伍中没有角色", + "noTurns": "尚未有回合", + "type": "类型 ", + "warrior": "毁灭", + "knight": "存护", + "mage": "智识", + "priest": "丰饶", + "rogue": "巡猎", + "shaman": "同协", + "warlock": "虚无", + "memory": "记忆", + "elation": "欢愉", + "fire": "火", + "ice": "冰", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "风", + "cycle": "回合", + "wave": "波次", + "hp": "生命值", + "atk": "攻击力", + "speed": "速度", + "critRate": "暴击率", + "critDmg": "暴击伤害", + "breakEffect": "击破特攻", + "effectRes": "效果抵抗", + "energyRegenerationRate": "能量回复效率", + "effectHitRate": "效果命中", + "outgoingHealingBoost": "治疗量加成", + "fireDmgBoost": "火属性伤害提高", + "iceDmgBoost": "冰属性伤害提高", + "imaginaryDmgBoost": "虚数属性伤害提高", + "physicalDmgBoost": "物理属性伤害提高", + "quantumDmgBoost": "量子属性伤害提高", + "thunderDmgBoost": "雷属性伤害提高", + "windDmgBoost": "风属性伤害提高", + "pursued": "附加伤害", + "true damage": "真实伤害", + "elation damage": "欢愉伤害", + "follow-up": "追加攻击", + "elemental damage": "击破与超击破伤害", + "dot": "持续伤害 ", + "damagePerCycle": "每轮伤害", + "damagePerCycleAndWave": "每轮/波伤害", + "damagePerWave": "每波伤害", + "lastTurn": "最后一回合", + "qte": "QTE技能", + "mazenormal": "普通迷宫", + "level": "等级", + "relics": "遗器", + "eidolons": "星魂", + "lightcones": "光锥", + "trailblazer": "开拓者" + } +} diff --git a/src/app/api/data/[name]/route.ts b/src/app/api/data/[name]/route.ts index c8b7583..25ee82a 100644 --- a/src/app/api/data/[name]/route.ts +++ b/src/app/api/data/[name]/route.ts @@ -1,25 +1,25 @@ -import { getDataCache } from "@/lib/cache/cache" - -export async function GET( - req: Request, - { params }: { params: Promise<{ name: string }> } -) { - const { name } = await params - - const item = getDataCache(name) - - if (!item) { - return new Response("Not found", { status: 404 }) - } - - const headers: Record = { - "Content-Type": "application/json", - "Cache-Control": "public, max-age=3600" - } - - if (item.type === "br") { - headers["Content-Encoding"] = "br" - } - - return new Response(new Uint8Array(item.buf), { headers }) +import { getDataCache } from "@/lib/cache/cache" + +export async function GET( + req: Request, + { params }: { params: Promise<{ name: string }> } +) { + const { name } = await params + + const item = getDataCache(name) + + if (!item) { + return new Response("Not found", { status: 404 }) + } + + const headers: Record = { + "Content-Type": "application/json", + "Cache-Control": "public, max-age=3600" + } + + if (item.type === "br") { + headers["Content-Encoding"] = "br" + } + + return new Response(new Uint8Array(item.buf), { headers }) } \ No newline at end of file diff --git a/src/components/actionbar/index.tsx b/src/components/actionbar/index.tsx index 57781d6..bbc187b 100644 --- a/src/components/actionbar/index.tsx +++ b/src/components/actionbar/index.tsx @@ -1,283 +1,283 @@ -"use client"; -import useAvatarDataStore from "@/stores/avatarDataStore"; -import useBattleDataStore from "@/stores/battleDataStore"; -import useLocaleStore from "@/stores/localeStore"; -import { attackTypeToString, CharacterBasic } from "@/types"; -import { SkillBattleInfo } from "@/types/mics"; -import { useEffect, useState, useRef } from "react"; -import { useTranslations } from "next-intl"; -import { motion } from "framer-motion"; -import { getNameChar } from "@/helper"; -import Image from "next/image"; -import NameAvatar from "../nameAvatar"; - -export default function ActionBar() { - const [selectTurn, setSelectTurn] = useState(null); - const [selectAvatar, setSelectAvatar] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); - const { skillHistory, turnHistory, cycleIndex, waveIndex, maxWave } = useBattleDataStore(); - const { listAvatar } = useAvatarDataStore(); - const { locale } = useLocaleStore(); - const transI18n = useTranslations("DataAnalysisPage"); - const turnListRef = useRef(null); - - - const contentStyle: React.CSSProperties = { - - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }; - - const handleShow = (modalId: string, avatar: CharacterBasic, turn: SkillBattleInfo) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - setSelectAvatar(avatar); - setSelectTurn(turn); - setIsModalOpen(true); - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - setIsModalOpen(false); - setSelectAvatar(null); - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close(); - } - }; - - // Handle ESC key to close modal - useEffect(() => { - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape' && isModalOpen) { - handleCloseModal("action_detail_modal"); - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isModalOpen]); - - // Scroll to the bottom when new turns are added - useEffect(() => { - if (turnListRef.current && skillHistory.length > 0) { - turnListRef.current.scrollTop = turnListRef.current.scrollHeight; - } - }, [skillHistory.length]); - - return ( -

- - {transI18n("actionBar")} - -
-
- - - - {waveIndex}/{maxWave} -
-
- - - - {cycleIndex} -
-
-
-
- {skillHistory.length === 0 ? ( -
-

{transI18n("noTurns")}

-
- ) : ( - skillHistory.map((turn, index) => { - const data = listAvatar.find(it => it.id === turn.avatarId.toString()); - if (!data) return null; - const text = getNameChar(locale, transI18n, data); - - return ( -
-
handleShow("action_detail_modal", data, turn)} - className="h-full grid grid-cols-2 gap-2 border bg-base-100 w-full hover:bg-base-200 transition-colors duration-200 border-cyan-400 border-l-4 cursor-pointer min-w-50 sm:min-w-62.5 md:min-w-0" - > -
-
-
- - {text} -
-
- -
-
-
- {`${transI18n("useSkill")}: ${transI18n(attackTypeToString(turn.skillType).toLowerCase())}`} -
-
- {`${transI18n("totalDamage")}: ${Number(turn.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`} -
-
- {`${transI18n("actionValue")}: ${Number(turnHistory[turn.turnBattleId]?.actionValue || 0).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`} -
-
-
-
- ); - - }) - )} -
-
- - - {/* Character Detail Modal */} - -
-
- handleCloseModal("action_detail_modal")} - > - ✕ - -
- -
-

- {transI18n("turnDetail").toUpperCase()} -

-
- - {selectAvatar && selectTurn && ( - -
-

{transI18n("characterInformation")}

-
-
-
- {transI18n("id")}: - {selectAvatar.id} -
-
- {transI18n("character")}: - -
-
-
- {getNameChar(locale, -
-
-
- -
-
-

{transI18n("skillType")}

-

{transI18n(attackTypeToString(selectTurn?.skillType).toLowerCase())}

-
-
-

{transI18n("skillName")}

-

{selectTurn?.skillName}

-
-
- -
-
-

{transI18n("cycle")}

-

{turnHistory[selectTurn?.turnBattleId].cycleIndex}

-
-
-

{transI18n("wave")}

-

{turnHistory[selectTurn?.turnBattleId].waveIndex}

-
-
- -
-
-

{transI18n("actionValue")}

-

{Number(turnHistory[selectTurn?.turnBattleId].actionValue).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

-
-
-

{transI18n("totalDamage")}

-

{Number(selectTurn?.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

-
-
- -
-

{transI18n("damageDetails")}

- {selectTurn?.damageDetail?.length > 0 ? ( -
- {selectTurn.damageDetail.map((detail, idx) => ( -
- - {Number(detail.damage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })} - - - {transI18n(attackTypeToString(detail?.damage_type).toLowerCase())} - -
- ))} -
- ) : ( -

{transI18n("noDamageDetail")}

- )} - -
-
- )} - -
- -
-
- ); +"use client"; +import useAvatarDataStore from "@/stores/avatarDataStore"; +import useBattleDataStore from "@/stores/battleDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import { attackTypeToString, CharacterBasic } from "@/types"; +import { SkillBattleInfo } from "@/types/mics"; +import { useEffect, useState, useRef } from "react"; +import { useTranslations } from "next-intl"; +import { motion } from "framer-motion"; +import { getNameChar } from "@/helper"; +import Image from "next/image"; +import NameAvatar from "../nameAvatar"; + +export default function ActionBar() { + const [selectTurn, setSelectTurn] = useState(null); + const [selectAvatar, setSelectAvatar] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const { skillHistory, turnHistory, cycleIndex, waveIndex, maxWave } = useBattleDataStore(); + const { listAvatar } = useAvatarDataStore(); + const { locale } = useLocaleStore(); + const transI18n = useTranslations("DataAnalysisPage"); + const turnListRef = useRef(null); + + + const contentStyle: React.CSSProperties = { + + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }; + + const handleShow = (modalId: string, avatar: CharacterBasic, turn: SkillBattleInfo) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + setSelectAvatar(avatar); + setSelectTurn(turn); + setIsModalOpen(true); + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + setIsModalOpen(false); + setSelectAvatar(null); + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + // Handle ESC key to close modal + useEffect(() => { + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isModalOpen) { + handleCloseModal("action_detail_modal"); + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isModalOpen]); + + // Scroll to the bottom when new turns are added + useEffect(() => { + if (turnListRef.current && skillHistory.length > 0) { + turnListRef.current.scrollTop = turnListRef.current.scrollHeight; + } + }, [skillHistory.length]); + + return ( +
+ + {transI18n("actionBar")} + +
+
+ + + + {waveIndex}/{maxWave} +
+
+ + + + {cycleIndex} +
+
+
+
+ {skillHistory.length === 0 ? ( +
+

{transI18n("noTurns")}

+
+ ) : ( + skillHistory.map((turn, index) => { + const data = listAvatar.find(it => it.id === turn.avatarId.toString()); + if (!data) return null; + const text = getNameChar(locale, transI18n, data); + + return ( +
+
handleShow("action_detail_modal", data, turn)} + className="h-full grid grid-cols-2 gap-2 border bg-base-100 w-full hover:bg-base-200 transition-colors duration-200 border-cyan-400 border-l-4 cursor-pointer min-w-50 sm:min-w-62.5 md:min-w-0" + > +
+
+
+ + {text} +
+
+ +
+
+
+ {`${transI18n("useSkill")}: ${transI18n(attackTypeToString(turn.skillType).toLowerCase())}`} +
+
+ {`${transI18n("totalDamage")}: ${Number(turn.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`} +
+
+ {`${transI18n("actionValue")}: ${Number(turnHistory[turn.turnBattleId]?.actionValue || 0).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}`} +
+
+
+
+ ); + + }) + )} +
+
+ + + {/* Character Detail Modal */} + +
+
+ handleCloseModal("action_detail_modal")} + > + ✕ + +
+ +
+

+ {transI18n("turnDetail").toUpperCase()} +

+
+ + {selectAvatar && selectTurn && ( + +
+

{transI18n("characterInformation")}

+
+
+
+ {transI18n("id")}: + {selectAvatar.id} +
+
+ {transI18n("character")}: + +
+
+
+ {getNameChar(locale, +
+
+
+ +
+
+

{transI18n("skillType")}

+

{transI18n(attackTypeToString(selectTurn?.skillType).toLowerCase())}

+
+
+

{transI18n("skillName")}

+

{selectTurn?.skillName}

+
+
+ +
+
+

{transI18n("cycle")}

+

{turnHistory[selectTurn?.turnBattleId].cycleIndex}

+
+
+

{transI18n("wave")}

+

{turnHistory[selectTurn?.turnBattleId].waveIndex}

+
+
+ +
+
+

{transI18n("actionValue")}

+

{Number(turnHistory[selectTurn?.turnBattleId].actionValue).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

+
+
+

{transI18n("totalDamage")}

+

{Number(selectTurn?.totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

+
+
+ +
+

{transI18n("damageDetails")}

+ {selectTurn?.damageDetail?.length > 0 ? ( +
+ {selectTurn.damageDetail.map((detail, idx) => ( +
+ + {Number(detail.damage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })} + + + {transI18n(attackTypeToString(detail?.damage_type).toLowerCase())} + +
+ ))} +
+ ) : ( +

{transI18n("noDamageDetail")}

+ )} + +
+
+ )} + +
+ +
+
+ ); } \ No newline at end of file diff --git a/src/components/card/characterCard.tsx b/src/components/card/characterCard.tsx index e97aa86..5cca49b 100644 --- a/src/components/card/characterCard.tsx +++ b/src/components/card/characterCard.tsx @@ -1,99 +1,99 @@ -"use client"; - -import { getNameChar } from '@/helper'; -import useLocaleStore from '@/stores/localeStore'; -import { CharacterBasic } from '@/types'; -import NameAvatar from '../nameAvatar'; -import useBattleDataStore from '@/stores/battleDataStore'; -import Image from 'next/image'; -import { useTranslations } from 'next-intl'; - -interface CharacterCardProps { - data: CharacterBasic -} - -export default function CharacterCard({ data }: CharacterCardProps) { - const { locale } = useLocaleStore(); - const { avatarDetail } = useBattleDataStore() - const transI18n = useTranslations("DataAnalysisPage"); - const text = getNameChar(locale, transI18n, data) - - return ( -
  • -
    - -
    - ALT - {data?.damageType?.toLowerCase()} - {data.baseType.toLowerCase()} -
    -
    - - - {avatarDetail && ( -
    -
    - HP: - - - {Number(avatarDetail?.[Number(data.id)]?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })} - - / - - {Number(avatarDetail?.[Number(data.id)]?.stats?.MaxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })} - - -
    - -
    -
    - - {Math.round(((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100)}% - -
    - -
    - )} - -
  • - - ); -} +"use client"; + +import { getNameChar } from '@/helper'; +import useLocaleStore from '@/stores/localeStore'; +import { CharacterBasic } from '@/types'; +import NameAvatar from '../nameAvatar'; +import useBattleDataStore from '@/stores/battleDataStore'; +import Image from 'next/image'; +import { useTranslations } from 'next-intl'; + +interface CharacterCardProps { + data: CharacterBasic +} + +export default function CharacterCard({ data }: CharacterCardProps) { + const { locale } = useLocaleStore(); + const { avatarDetail } = useBattleDataStore() + const transI18n = useTranslations("DataAnalysisPage"); + const text = getNameChar(locale, transI18n, data) + + return ( +
  • +
    + +
    + ALT + {data?.damageType?.toLowerCase()} + {data.baseType.toLowerCase()} +
    +
    + + + {avatarDetail && ( +
    +
    + HP: + + + {Number(avatarDetail?.[Number(data.id)]?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })} + + / + + {Number(avatarDetail?.[Number(data.id)]?.stats?.MaxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })} + + +
    + +
    +
    + + {Math.round(((avatarDetail?.[Number(data.id)]?.stats?.HP || 0) / (avatarDetail?.[Number(data.id)]?.stats?.MaxHP || 100)) * 100)}% + +
    + +
    + )} + +
  • + + ); +} diff --git a/src/components/chart/damageLineForAll.tsx b/src/components/chart/damageLineForAll.tsx index 6a3593f..245b8ae 100644 --- a/src/components/chart/damageLineForAll.tsx +++ b/src/components/chart/damageLineForAll.tsx @@ -1,84 +1,84 @@ -'use client'; -import { - Chart as ChartJS, - LineElement, - CategoryScale, - LinearScale, - PointElement, - Tooltip, - Legend, -} from 'chart.js'; -import { Line } from 'react-chartjs-2'; -import { useDamageLinesForAll } from '@/hooks/useDamageLinesForAll'; -import useAvatarDataStore from '@/stores/avatarDataStore'; -import useLocaleStore from '@/stores/localeStore'; -import { getNameChar } from '@/helper'; -import { useState } from 'react'; -import { useTranslations } from 'next-intl'; - -ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement, Tooltip, Legend); - -const colors = ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c']; - -export default function MultiCharLineChart() { - const [mode, setMode] = useState<1 | 2>(2); - const dataByAvatar = useDamageLinesForAll(mode); - const avatarIds = Object.keys(dataByAvatar).map(Number); - const { mapAvatar } = useAvatarDataStore() - const { locale } = useLocaleStore(); - const transI18n = useTranslations("DataAnalysisPage") - - const data = { - datasets: avatarIds.map((id, idx) => ({ - label: getNameChar(locale, transI18n, mapAvatar?.[id.toString()]), - data: dataByAvatar[id].map(({ x, y }: { x: number; y: number }) => { - return { - x: x.toFixed(2), - y: y.toFixed(2) - } - }), - borderColor: colors[idx % colors.length], - backgroundColor: colors[idx % colors.length], - fill: false, - tension: 0.3, - })), - }; - - const options = { - responsive: true, - scales: { - x: { - type: 'linear' as const, - position: 'bottom' as "bottom" | "center" | "left" | "top" | "right", - ticks: { - stepSize: 1, - }, - }, - y: { - beginAtZero: true, - }, - }, - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - - -
    - - ) -} +'use client'; +import { + Chart as ChartJS, + LineElement, + CategoryScale, + LinearScale, + PointElement, + Tooltip, + Legend, +} from 'chart.js'; +import { Line } from 'react-chartjs-2'; +import { useDamageLinesForAll } from '@/hooks/useDamageLinesForAll'; +import useAvatarDataStore from '@/stores/avatarDataStore'; +import useLocaleStore from '@/stores/localeStore'; +import { getNameChar } from '@/helper'; +import { useState } from 'react'; +import { useTranslations } from 'next-intl'; + +ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement, Tooltip, Legend); + +const colors = ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c']; + +export default function MultiCharLineChart() { + const [mode, setMode] = useState<1 | 2>(2); + const dataByAvatar = useDamageLinesForAll(mode); + const avatarIds = Object.keys(dataByAvatar).map(Number); + const { mapAvatar } = useAvatarDataStore() + const { locale } = useLocaleStore(); + const transI18n = useTranslations("DataAnalysisPage") + + const data = { + datasets: avatarIds.map((id, idx) => ({ + label: getNameChar(locale, transI18n, mapAvatar?.[id.toString()]), + data: dataByAvatar[id].map(({ x, y }: { x: number; y: number }) => { + return { + x: x.toFixed(2), + y: y.toFixed(2) + } + }), + borderColor: colors[idx % colors.length], + backgroundColor: colors[idx % colors.length], + fill: false, + tension: 0.3, + })), + }; + + const options = { + responsive: true, + scales: { + x: { + type: 'linear' as const, + position: 'bottom' as "bottom" | "center" | "left" | "top" | "right", + ticks: { + stepSize: 1, + }, + }, + y: { + beginAtZero: true, + }, + }, + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + + +
    + + ) +} diff --git a/src/components/chart/damageLineForOne.tsx b/src/components/chart/damageLineForOne.tsx index 8f95719..0056a85 100644 --- a/src/components/chart/damageLineForOne.tsx +++ b/src/components/chart/damageLineForOne.tsx @@ -1,80 +1,80 @@ -"use client"; -import { Line } from "react-chartjs-2"; -import { ChartOptions } from "chart.js"; -import { useDamageLineForOne } from "@/hooks/useDamageLineForOne"; -import { - Chart as ChartJS, - LineElement, - PointElement, - CategoryScale, - LinearScale, - Title, - Tooltip, - Legend, - Filler, -} from "chart.js"; -import { useTranslations } from "next-intl"; -import { useState } from "react"; - -ChartJS.register( - LineElement, - PointElement, - CategoryScale, - LinearScale, - Title, - Tooltip, - Legend, - Filler -); - - -export function DamageLineForOne({ avatarId }: { avatarId: number; }) { - const [mode, setMode] = useState<0 | 1>(1); - const dataRaw = useDamageLineForOne(avatarId, mode); - const transI18n = useTranslations("DataAnalysisPage"); - const data = { - labels: dataRaw.map(d => Number(d.x.toFixed(2))), - datasets: [ - { - label: mode === 1 ? transI18n("cumulativeDamage") : transI18n("damageByActionValue"), - data: dataRaw.map(d => Number(d.y.toFixed(2))), - borderColor: "rgba(75,192,192,1)", - backgroundColor: "rgba(75,192,192,0.2)", - tension: 0.3, - fill: true, - }, - ], - }; - - const options: ChartOptions<"line"> = { - responsive: true, - plugins: { - legend: { display: true }, - }, - scales: { - x: { title: { display: true, text: transI18n("actionValue") } }, - y: { title: { display: true, text: transI18n("damage") }, beginAtZero: true }, - }, - - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - ) -} +"use client"; +import { Line } from "react-chartjs-2"; +import { ChartOptions } from "chart.js"; +import { useDamageLineForOne } from "@/hooks/useDamageLineForOne"; +import { + Chart as ChartJS, + LineElement, + PointElement, + CategoryScale, + LinearScale, + Title, + Tooltip, + Legend, + Filler, +} from "chart.js"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; + +ChartJS.register( + LineElement, + PointElement, + CategoryScale, + LinearScale, + Title, + Tooltip, + Legend, + Filler +); + + +export function DamageLineForOne({ avatarId }: { avatarId: number; }) { + const [mode, setMode] = useState<0 | 1>(1); + const dataRaw = useDamageLineForOne(avatarId, mode); + const transI18n = useTranslations("DataAnalysisPage"); + const data = { + labels: dataRaw.map(d => Number(d.x.toFixed(2))), + datasets: [ + { + label: mode === 1 ? transI18n("cumulativeDamage") : transI18n("damageByActionValue"), + data: dataRaw.map(d => Number(d.y.toFixed(2))), + borderColor: "rgba(75,192,192,1)", + backgroundColor: "rgba(75,192,192,0.2)", + tension: 0.3, + fill: true, + }, + ], + }; + + const options: ChartOptions<"line"> = { + responsive: true, + plugins: { + legend: { display: true }, + }, + scales: { + x: { title: { display: true, text: transI18n("actionValue") } }, + y: { title: { display: true, text: transI18n("damage") }, beginAtZero: true }, + }, + + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + ) +} diff --git a/src/components/chart/damagePerAvatarForAll.tsx b/src/components/chart/damagePerAvatarForAll.tsx index 84c4d4f..9533ff4 100644 --- a/src/components/chart/damagePerAvatarForAll.tsx +++ b/src/components/chart/damagePerAvatarForAll.tsx @@ -1,177 +1,177 @@ -'use client'; -import { useState } from 'react'; -import { getNameChar } from '@/helper'; -import useAvatarDataStore from '@/stores/avatarDataStore'; -import useBattleDataStore from '@/stores/battleDataStore'; -import useLocaleStore from '@/stores/localeStore'; -import { - Chart as ChartJS, - BarElement, - CategoryScale, - LinearScale, - Tooltip, - Legend -} from 'chart.js'; -import { Bar } from 'react-chartjs-2'; -import { useTranslations } from 'next-intl'; -import {attackTypeToString} from "@/types"; - -ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend); - -const colorPalette = [ - 'rgba(255, 99, 132, 0.6)', - 'rgba(54, 162, 235, 0.6)', - 'rgba(255, 206, 86, 0.6)', - 'rgba(75, 192, 192, 0.6)', - 'rgba(153, 102, 255, 0.6)', - 'rgba(255, 159, 64, 0.6)', - 'rgba(199, 199, 199, 0.6)', - 'rgba(255, 99, 255, 0.6)', - 'rgba(0, 200, 83, 0.6)', - 'rgba(255, 112, 67, 0.6)', - 'rgba(63, 81, 181, 0.6)', - 'rgba(0, 188, 212, 0.6)', - 'rgba(233, 30, 99, 0.6)', - 'rgba(124, 179, 66, 0.6)', - 'rgba(255, 235, 59, 0.6)', - 'rgba(158, 158, 158, 0.6)', - 'rgba(121, 85, 72, 0.6)', - 'rgba(96, 125, 139, 0.6)', - 'rgba(100, 181, 246, 0.6)', - 'rgba(255, 171, 145, 0.6)', - 'rgba(174, 213, 129, 0.6)', - 'rgba(255, 245, 157, 0.6)' - ]; - -const borderPalette = colorPalette.map((c) => c.replace('0.6', '1')); - -export default function DamagePerAvatarForAll() { - const transI18n = useTranslations("DataAnalysisPage"); - const { lineup, skillHistory } = useBattleDataStore(); - const { mapAvatar } = useAvatarDataStore(); - const { locale } = useLocaleStore(); - - const [mode, setMode] = useState(2); - - const avatarMap = lineup.map((avatar) => { - if (!mapAvatar?.[avatar.avatarId.toString()]) return undefined; - return { - avatarId: avatar.avatarId, - avatarName: getNameChar(locale, transI18n, mapAvatar?.[avatar.avatarId.toString()]) - }; - }).filter(Boolean) as { avatarId: number, avatarName: string }[]; - - const labels = avatarMap.map(a => a.avatarName); - - let datasets: Array<{ - label: string; - data: number[]; - backgroundColor: string | string[]; - borderColor: string | string[]; - borderWidth: number; - stack?: string; - }> = []; - - if (mode === 1) { - datasets = [ - { - label: transI18n("totalDamage"), - data: avatarMap.map(({ avatarId }) => { - const total = skillHistory - .filter(skill => skill.avatarId === avatarId) - .reduce((sum, s) => sum + s.totalDamage, 0); - return total; - }), - backgroundColor: avatarMap.map((_, i) => colorPalette[i % colorPalette.length]), - borderColor: avatarMap.map((_, i) => borderPalette[i % borderPalette.length]), - borderWidth: 1 - } - ]; - } - - if (mode === 2) { - const damageTypesSet = new Set(); - avatarMap.forEach(({ avatarId }) => { - skillHistory.filter(s => s.avatarId === avatarId).forEach(s => - s.damageDetail?.forEach(d => d.damage_type && damageTypesSet.add(transI18n(attackTypeToString(d?.damage_type).toLowerCase()))) - ); - }); - const damageTypes = Array.from(damageTypesSet); - - datasets = damageTypes.map((dt, i) => ({ - label: dt, - data: avatarMap.map(({ avatarId }) => { - const total = skillHistory - .filter(skill => skill.avatarId === avatarId) - .flatMap(s => s.damageDetail || []) - .filter(d => transI18n(attackTypeToString(d?.damage_type).toLowerCase()) === dt) - .reduce((sum, d) => sum + d.damage, 0); - return total; - }), - backgroundColor: colorPalette[i % colorPalette.length], - borderColor: borderPalette[i % borderPalette.length], - borderWidth: 1, - stack: transI18n('damage') - })); - } - - if (mode === 3) { - const skillTypesSet = new Set(); - skillHistory.forEach(s => skillTypesSet.add(transI18n(attackTypeToString(s?.skillType)?.toLowerCase()))); - const skillTypes = Array.from(skillTypesSet); - - datasets = skillTypes.map((st, i) => ({ - label: st, - data: avatarMap.map(({ avatarId }) => { - const total = skillHistory - .filter(skill => skill.avatarId === avatarId && transI18n(attackTypeToString(skill?.skillType).toLowerCase()) === st) - .reduce((sum, s) => sum + s.totalDamage, 0); - return total; - }), - backgroundColor: colorPalette[i % colorPalette.length], - borderColor: borderPalette[i % borderPalette.length], - borderWidth: 1, - stack: transI18n('skill') - })); - } - - const options = { - responsive: true, - scales: { - y: { - beginAtZero: true, - stacked: mode !== 1, - ticks: { - precision: 0, - }, - }, - x: { - stacked: mode !== 1, - }, - }, - - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - { mode: 3, label: `${transI18n("type")} 3`, className: "btn-accent" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - - -
    - - ); -} +'use client'; +import { useState } from 'react'; +import { getNameChar } from '@/helper'; +import useAvatarDataStore from '@/stores/avatarDataStore'; +import useBattleDataStore from '@/stores/battleDataStore'; +import useLocaleStore from '@/stores/localeStore'; +import { + Chart as ChartJS, + BarElement, + CategoryScale, + LinearScale, + Tooltip, + Legend +} from 'chart.js'; +import { Bar } from 'react-chartjs-2'; +import { useTranslations } from 'next-intl'; +import {attackTypeToString} from "@/types"; + +ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend); + +const colorPalette = [ + 'rgba(255, 99, 132, 0.6)', + 'rgba(54, 162, 235, 0.6)', + 'rgba(255, 206, 86, 0.6)', + 'rgba(75, 192, 192, 0.6)', + 'rgba(153, 102, 255, 0.6)', + 'rgba(255, 159, 64, 0.6)', + 'rgba(199, 199, 199, 0.6)', + 'rgba(255, 99, 255, 0.6)', + 'rgba(0, 200, 83, 0.6)', + 'rgba(255, 112, 67, 0.6)', + 'rgba(63, 81, 181, 0.6)', + 'rgba(0, 188, 212, 0.6)', + 'rgba(233, 30, 99, 0.6)', + 'rgba(124, 179, 66, 0.6)', + 'rgba(255, 235, 59, 0.6)', + 'rgba(158, 158, 158, 0.6)', + 'rgba(121, 85, 72, 0.6)', + 'rgba(96, 125, 139, 0.6)', + 'rgba(100, 181, 246, 0.6)', + 'rgba(255, 171, 145, 0.6)', + 'rgba(174, 213, 129, 0.6)', + 'rgba(255, 245, 157, 0.6)' + ]; + +const borderPalette = colorPalette.map((c) => c.replace('0.6', '1')); + +export default function DamagePerAvatarForAll() { + const transI18n = useTranslations("DataAnalysisPage"); + const { lineup, skillHistory } = useBattleDataStore(); + const { mapAvatar } = useAvatarDataStore(); + const { locale } = useLocaleStore(); + + const [mode, setMode] = useState(2); + + const avatarMap = lineup.map((avatar) => { + if (!mapAvatar?.[avatar.avatarId.toString()]) return undefined; + return { + avatarId: avatar.avatarId, + avatarName: getNameChar(locale, transI18n, mapAvatar?.[avatar.avatarId.toString()]) + }; + }).filter(Boolean) as { avatarId: number, avatarName: string }[]; + + const labels = avatarMap.map(a => a.avatarName); + + let datasets: Array<{ + label: string; + data: number[]; + backgroundColor: string | string[]; + borderColor: string | string[]; + borderWidth: number; + stack?: string; + }> = []; + + if (mode === 1) { + datasets = [ + { + label: transI18n("totalDamage"), + data: avatarMap.map(({ avatarId }) => { + const total = skillHistory + .filter(skill => skill.avatarId === avatarId) + .reduce((sum, s) => sum + s.totalDamage, 0); + return total; + }), + backgroundColor: avatarMap.map((_, i) => colorPalette[i % colorPalette.length]), + borderColor: avatarMap.map((_, i) => borderPalette[i % borderPalette.length]), + borderWidth: 1 + } + ]; + } + + if (mode === 2) { + const damageTypesSet = new Set(); + avatarMap.forEach(({ avatarId }) => { + skillHistory.filter(s => s.avatarId === avatarId).forEach(s => + s.damageDetail?.forEach(d => d.damage_type && damageTypesSet.add(transI18n(attackTypeToString(d?.damage_type).toLowerCase()))) + ); + }); + const damageTypes = Array.from(damageTypesSet); + + datasets = damageTypes.map((dt, i) => ({ + label: dt, + data: avatarMap.map(({ avatarId }) => { + const total = skillHistory + .filter(skill => skill.avatarId === avatarId) + .flatMap(s => s.damageDetail || []) + .filter(d => transI18n(attackTypeToString(d?.damage_type).toLowerCase()) === dt) + .reduce((sum, d) => sum + d.damage, 0); + return total; + }), + backgroundColor: colorPalette[i % colorPalette.length], + borderColor: borderPalette[i % borderPalette.length], + borderWidth: 1, + stack: transI18n('damage') + })); + } + + if (mode === 3) { + const skillTypesSet = new Set(); + skillHistory.forEach(s => skillTypesSet.add(transI18n(attackTypeToString(s?.skillType)?.toLowerCase()))); + const skillTypes = Array.from(skillTypesSet); + + datasets = skillTypes.map((st, i) => ({ + label: st, + data: avatarMap.map(({ avatarId }) => { + const total = skillHistory + .filter(skill => skill.avatarId === avatarId && transI18n(attackTypeToString(skill?.skillType).toLowerCase()) === st) + .reduce((sum, s) => sum + s.totalDamage, 0); + return total; + }), + backgroundColor: colorPalette[i % colorPalette.length], + borderColor: borderPalette[i % borderPalette.length], + borderWidth: 1, + stack: transI18n('skill') + })); + } + + const options = { + responsive: true, + scales: { + y: { + beginAtZero: true, + stacked: mode !== 1, + ticks: { + precision: 0, + }, + }, + x: { + stacked: mode !== 1, + }, + }, + + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + { mode: 3, label: `${transI18n("type")} 3`, className: "btn-accent" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + + +
    + + ); +} diff --git a/src/components/chart/damagePerCycleForAll.tsx b/src/components/chart/damagePerCycleForAll.tsx index f3767b9..75b0e6e 100644 --- a/src/components/chart/damagePerCycleForAll.tsx +++ b/src/components/chart/damagePerCycleForAll.tsx @@ -1,60 +1,60 @@ -"use client"; -import { Bar } from "react-chartjs-2"; -import { ChartOptions } from "chart.js"; -import { useDamagePerCycleForAll } from "@/hooks/useDamagePerCycle"; -import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js"; -import { useTranslations } from "next-intl"; -import { useState } from "react"; - -ChartJS.register(BarElement, ArcElement, RadialLinearScale); -export default function DamagePerCycleForAll() { - const [mode, setMode] = useState<0 | 1 | 2>(0); - const dataRaw = useDamagePerCycleForAll(mode); - const transI18n = useTranslations("DataAnalysisPage"); - const data = { - labels: dataRaw.map(d => d.x), - datasets: [ - { - label: mode === 0 ? `${transI18n("damagePerCycleAndWave")}` : mode === 1 ? `${transI18n("damagePerCycle")}` : `${transI18n("damagePerWave")}`, - data: dataRaw.map(d => d.y), - backgroundColor: dataRaw.map((_, i) => - ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c', '#f472b6'][i % 7] - ), - }, - ], - - }; - - const options: ChartOptions<"bar"> = { - responsive: true, - plugins: { - legend: { display: true }, - }, - scales: { - x: { title: { display: true, text: transI18n("cycleCount") } }, - y: { title: { display: true, text: transI18n("totalDamage") }, beginAtZero: true }, - }, - - }; - - return ( -
    -
    - {[ - { mode: 0, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 1, label: `${transI18n("type")} 2`, className: "btn-warning" }, - { mode: 2, label: `${transI18n("type")} 3`, className: "btn-accent" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - ) -} +"use client"; +import { Bar } from "react-chartjs-2"; +import { ChartOptions } from "chart.js"; +import { useDamagePerCycleForAll } from "@/hooks/useDamagePerCycle"; +import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; + +ChartJS.register(BarElement, ArcElement, RadialLinearScale); +export default function DamagePerCycleForAll() { + const [mode, setMode] = useState<0 | 1 | 2>(0); + const dataRaw = useDamagePerCycleForAll(mode); + const transI18n = useTranslations("DataAnalysisPage"); + const data = { + labels: dataRaw.map(d => d.x), + datasets: [ + { + label: mode === 0 ? `${transI18n("damagePerCycleAndWave")}` : mode === 1 ? `${transI18n("damagePerCycle")}` : `${transI18n("damagePerWave")}`, + data: dataRaw.map(d => d.y), + backgroundColor: dataRaw.map((_, i) => + ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c', '#f472b6'][i % 7] + ), + }, + ], + + }; + + const options: ChartOptions<"bar"> = { + responsive: true, + plugins: { + legend: { display: true }, + }, + scales: { + x: { title: { display: true, text: transI18n("cycleCount") } }, + y: { title: { display: true, text: transI18n("totalDamage") }, beginAtZero: true }, + }, + + }; + + return ( +
    +
    + {[ + { mode: 0, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 1, label: `${transI18n("type")} 2`, className: "btn-warning" }, + { mode: 2, label: `${transI18n("type")} 3`, className: "btn-accent" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + ) +} diff --git a/src/components/chart/damagePerCycleForOne.tsx b/src/components/chart/damagePerCycleForOne.tsx index 21f80a2..970e6ff 100644 --- a/src/components/chart/damagePerCycleForOne.tsx +++ b/src/components/chart/damagePerCycleForOne.tsx @@ -1,64 +1,64 @@ -"use client"; -import { Bar } from "react-chartjs-2"; -import { ChartOptions } from "chart.js"; -import { useDamagePerCycleForOne } from "@/hooks/useDamagePerCycle"; - -import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js"; -import { useTranslations } from "next-intl"; -import { useState } from "react"; - -ChartJS.register(BarElement, ArcElement, RadialLinearScale); -export function DamagePerCycleForOne({ - avatarId, -}: { - avatarId: number; -}) { - const [mode, setMode] = useState<0 | 1 | 2>(0); - const dataRaw = useDamagePerCycleForOne(avatarId, mode); - const transI18n = useTranslations("DataAnalysisPage"); - const data = { - labels: dataRaw.map(d => d.x), - datasets: [ - { - label: mode === 0 ? `${transI18n("damagePerCycleAndWave")}` : mode === 1 ? `${transI18n("damagePerCycle")}` : `${transI18n("damagePerWave")}`, - data: dataRaw.map(d => d.y), - backgroundColor: dataRaw.map((_, i) => - ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c', '#f472b6'][i % 7] - ), - }, - ], - }; - - const options: ChartOptions<"bar"> = { - responsive: true, - plugins: { - legend: { display: true }, - }, - scales: { - x: { title: { display: true, text: transI18n("cycleCount") } }, - y: { title: { display: true, text: transI18n("totalDamage") }, beginAtZero: true }, - }, - - }; - - return ( -
    -
    - {[ - { mode: 0, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 1, label: `${transI18n("type")} 2`, className: "btn-warning" }, - { mode: 2, label: `${transI18n("type")} 3`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - ) -} +"use client"; +import { Bar } from "react-chartjs-2"; +import { ChartOptions } from "chart.js"; +import { useDamagePerCycleForOne } from "@/hooks/useDamagePerCycle"; + +import { Chart as ChartJS, BarElement, ArcElement, RadialLinearScale } from "chart.js"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; + +ChartJS.register(BarElement, ArcElement, RadialLinearScale); +export function DamagePerCycleForOne({ + avatarId, +}: { + avatarId: number; +}) { + const [mode, setMode] = useState<0 | 1 | 2>(0); + const dataRaw = useDamagePerCycleForOne(avatarId, mode); + const transI18n = useTranslations("DataAnalysisPage"); + const data = { + labels: dataRaw.map(d => d.x), + datasets: [ + { + label: mode === 0 ? `${transI18n("damagePerCycleAndWave")}` : mode === 1 ? `${transI18n("damagePerCycle")}` : `${transI18n("damagePerWave")}`, + data: dataRaw.map(d => d.y), + backgroundColor: dataRaw.map((_, i) => + ['#f87171', '#34d399', '#60a5fa', '#facc15', '#a78bfa', '#fb923c', '#f472b6'][i % 7] + ), + }, + ], + }; + + const options: ChartOptions<"bar"> = { + responsive: true, + plugins: { + legend: { display: true }, + }, + scales: { + x: { title: { display: true, text: transI18n("cycleCount") } }, + y: { title: { display: true, text: transI18n("totalDamage") }, beginAtZero: true }, + }, + + }; + + return ( +
    +
    + {[ + { mode: 0, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 1, label: `${transI18n("type")} 2`, className: "btn-warning" }, + { mode: 2, label: `${transI18n("type")} 3`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + ) +} diff --git a/src/components/chart/damagePercentForAll.tsx b/src/components/chart/damagePercentForAll.tsx index d6f6572..2b2a356 100644 --- a/src/components/chart/damagePercentForAll.tsx +++ b/src/components/chart/damagePercentForAll.tsx @@ -1,91 +1,91 @@ -'use client'; -import { - Chart as ChartJS, - ArcElement, - Tooltip, - Legend -} from 'chart.js'; -import { Pie } from 'react-chartjs-2'; -import { useDamagePercentPerAvatar } from '@/hooks/useDamagePercentPerAvatar'; -import useAvatarDataStore from '@/stores/avatarDataStore'; -import useLocaleStore from '@/stores/localeStore'; -import { getNameChar } from '@/helper'; -import { useTranslations } from 'next-intl'; -import { useState } from 'react'; -import { useDamagePercentByType } from '@/hooks/useDamagePercentForDamgeType'; - -ChartJS.register(ArcElement, Tooltip, Legend); - -const colors = [ - '#f87171', '#34d399', '#60a5fa', '#facc15', - '#a78bfa', '#fb923c', '#f472b6', '#10b981', - '#fcd34d', '#c084fc', '#f97316', '#ec4899', - '#4ade80', '#fbbf24', '#8b5cf6', '#f43f5e' -]; - -export default function DamagePercentChartForAll() { - const [mode, setMode] = useState<1 | 2>(1); - const damageByAvatar = useDamagePercentPerAvatar(); - const damageByType = useDamagePercentByType(); - const { mapAvatar } = useAvatarDataStore(); - const { locale } = useLocaleStore(); - const transI18n = useTranslations("DataAnalysisPage"); - - const chartData = { - labels: (mode === 1 - ? damageByAvatar.map(d => - getNameChar(locale, transI18n, mapAvatar?.[d.avatarId.toString()]) - ) - : damageByType.map(d => transI18n(d.type.toLowerCase())) - ), - datasets: [ - { - label: '% ' + transI18n("damage"), - data: (mode === 1 - ? damageByAvatar.map(d => d.percent.toFixed(2)) - : damageByType.map(d => d.percent.toFixed(2)) - ), - backgroundColor: colors, - borderWidth: 1, - }, - ], - }; - - const options = { - responsive: true, - plugins: { - tooltip: { - callbacks: { - label: (ctx: import('chart.js').TooltipItem<'pie'>) => `${ctx.label}: ${ctx.raw}%`, - }, - }, - datalabels: { - display: false, - }, - }, - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - -
    -
    - ); - -} +'use client'; +import { + Chart as ChartJS, + ArcElement, + Tooltip, + Legend +} from 'chart.js'; +import { Pie } from 'react-chartjs-2'; +import { useDamagePercentPerAvatar } from '@/hooks/useDamagePercentPerAvatar'; +import useAvatarDataStore from '@/stores/avatarDataStore'; +import useLocaleStore from '@/stores/localeStore'; +import { getNameChar } from '@/helper'; +import { useTranslations } from 'next-intl'; +import { useState } from 'react'; +import { useDamagePercentByType } from '@/hooks/useDamagePercentForDamgeType'; + +ChartJS.register(ArcElement, Tooltip, Legend); + +const colors = [ + '#f87171', '#34d399', '#60a5fa', '#facc15', + '#a78bfa', '#fb923c', '#f472b6', '#10b981', + '#fcd34d', '#c084fc', '#f97316', '#ec4899', + '#4ade80', '#fbbf24', '#8b5cf6', '#f43f5e' +]; + +export default function DamagePercentChartForAll() { + const [mode, setMode] = useState<1 | 2>(1); + const damageByAvatar = useDamagePercentPerAvatar(); + const damageByType = useDamagePercentByType(); + const { mapAvatar } = useAvatarDataStore(); + const { locale } = useLocaleStore(); + const transI18n = useTranslations("DataAnalysisPage"); + + const chartData = { + labels: (mode === 1 + ? damageByAvatar.map(d => + getNameChar(locale, transI18n, mapAvatar?.[d.avatarId.toString()]) + ) + : damageByType.map(d => transI18n(d.type.toLowerCase())) + ), + datasets: [ + { + label: '% ' + transI18n("damage"), + data: (mode === 1 + ? damageByAvatar.map(d => d.percent.toFixed(2)) + : damageByType.map(d => d.percent.toFixed(2)) + ), + backgroundColor: colors, + borderWidth: 1, + }, + ], + }; + + const options = { + responsive: true, + plugins: { + tooltip: { + callbacks: { + label: (ctx: import('chart.js').TooltipItem<'pie'>) => `${ctx.label}: ${ctx.raw}%`, + }, + }, + datalabels: { + display: false, + }, + }, + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + +
    +
    + ); + +} diff --git a/src/components/chart/skillBarChart.tsx b/src/components/chart/skillBarChart.tsx index 240c70e..65f04fa 100644 --- a/src/components/chart/skillBarChart.tsx +++ b/src/components/chart/skillBarChart.tsx @@ -1,70 +1,70 @@ -'use client'; -import { useState } from 'react'; -import { Bar } from 'react-chartjs-2'; -import { Chart as ChartJS, BarElement, CategoryScale, LinearScale, Tooltip, Legend } from 'chart.js'; -import { useTranslations } from 'next-intl'; -import { useSkillDamageForAvatar } from '@/hooks/useSkillDamageForAvatar'; -import { useDamageByTypeForAvatar } from '@/hooks/useDamageTypeForAvatar'; - - -ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend); - -export default function SkillBarChart({ avatarId }: { avatarId: number }) { - const [mode, setMode] = useState<1 | 2>(2); - - const transI18n = useTranslations('DataAnalysisPage'); - const { labels: skillLabels, damageValues: skillDamageValues } = useSkillDamageForAvatar(avatarId); - const { labels: typeLabels, damageValues: typeDamageValues } = useDamageByTypeForAvatar(avatarId); - - const colors = ['#f87171', '#facc15', '#34d399', '#60a5fa', '#a78bfa', '#fb923c']; - - const data = { - labels: mode === 1 ? skillLabels.map(it => transI18n(it)) : typeLabels.map(it => transI18n(it)), - datasets: [ - { - label: mode === 1 - ? transI18n('skill') - : transI18n('damage'), - data: mode === 1 ? skillDamageValues : typeDamageValues, - backgroundColor: colors.slice(0, (mode === 1 ? skillLabels : typeLabels).length), - borderColor: '#111', - borderWidth: 1, - }, - ], - }; - - const options = { - responsive: true, - scales: { - y: { - beginAtZero: true, - ticks: { precision: 0 }, - }, - }, - plugins: { - legend: { - display: false, - }, - }, - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - ); -} +'use client'; +import { useState } from 'react'; +import { Bar } from 'react-chartjs-2'; +import { Chart as ChartJS, BarElement, CategoryScale, LinearScale, Tooltip, Legend } from 'chart.js'; +import { useTranslations } from 'next-intl'; +import { useSkillDamageForAvatar } from '@/hooks/useSkillDamageForAvatar'; +import { useDamageByTypeForAvatar } from '@/hooks/useDamageTypeForAvatar'; + + +ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend); + +export default function SkillBarChart({ avatarId }: { avatarId: number }) { + const [mode, setMode] = useState<1 | 2>(2); + + const transI18n = useTranslations('DataAnalysisPage'); + const { labels: skillLabels, damageValues: skillDamageValues } = useSkillDamageForAvatar(avatarId); + const { labels: typeLabels, damageValues: typeDamageValues } = useDamageByTypeForAvatar(avatarId); + + const colors = ['#f87171', '#facc15', '#34d399', '#60a5fa', '#a78bfa', '#fb923c']; + + const data = { + labels: mode === 1 ? skillLabels.map(it => transI18n(it)) : typeLabels.map(it => transI18n(it)), + datasets: [ + { + label: mode === 1 + ? transI18n('skill') + : transI18n('damage'), + data: mode === 1 ? skillDamageValues : typeDamageValues, + backgroundColor: colors.slice(0, (mode === 1 ? skillLabels : typeLabels).length), + borderColor: '#111', + borderWidth: 1, + }, + ], + }; + + const options = { + responsive: true, + scales: { + y: { + beginAtZero: true, + ticks: { precision: 0 }, + }, + }, + plugins: { + legend: { + display: false, + }, + }, + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + ); +} diff --git a/src/components/chart/skillPieChart.tsx b/src/components/chart/skillPieChart.tsx index 49bd61e..477a555 100644 --- a/src/components/chart/skillPieChart.tsx +++ b/src/components/chart/skillPieChart.tsx @@ -1,75 +1,75 @@ -'use client'; -import { useState } from 'react'; -import { Pie } from 'react-chartjs-2'; -import { - Chart as ChartJS, - ArcElement, - Tooltip, - Legend, -} from 'chart.js'; -import { useTranslations } from 'next-intl'; -import { useSkillDamageForAvatar } from '@/hooks/useSkillDamageForAvatar'; -import { useDamageByTypeForAvatar } from '@/hooks/useDamageTypeForAvatar'; - -ChartJS.register(ArcElement, Tooltip, Legend); - -const COLORS = [ - '#f87171', '#facc15', '#34d399', '#60a5fa', '#a78bfa', '#fb923c', - '#f472b6', '#38bdf8', '#a3e635', '#fdba74', '#c084fc', '#4ade80', - '#fcd34d', '#818cf8', '#f43f5e', '#14b8a6', '#e879f9', '#86efac', -]; - -export default function SkillPieChart({ avatarId }: { avatarId: number }) { - const [mode, setMode] = useState<1 | 2>(2); - const transI18n = useTranslations("DataAnalysisPage"); - - const skillData = useSkillDamageForAvatar(avatarId); - const typeData = useDamageByTypeForAvatar(avatarId); - - const { labels, damageValues } = mode === 1 ? skillData : typeData; - const total = damageValues.reduce((sum, val) => sum + val, 0); - - const labelsWithPercent = labels.map((label, index) => { - const value = damageValues[index]; - const percent = total ? ((value / total) * 100).toFixed(1) : '0.0'; - return `${transI18n(label.toLowerCase())} (${percent}%)`; - }); - - const data = { - labels: labelsWithPercent, - datasets: [ - { - data: damageValues, - backgroundColor: COLORS.slice(0, labels.length), - borderColor: '#fff', - borderWidth: 1, - }, - ], - }; - - return ( -
    -
    - {[ - { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, - { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, - ].map(({ mode: m, label, className }) => ( - - ))} -
    - -
    - ); -} +'use client'; +import { useState } from 'react'; +import { Pie } from 'react-chartjs-2'; +import { + Chart as ChartJS, + ArcElement, + Tooltip, + Legend, +} from 'chart.js'; +import { useTranslations } from 'next-intl'; +import { useSkillDamageForAvatar } from '@/hooks/useSkillDamageForAvatar'; +import { useDamageByTypeForAvatar } from '@/hooks/useDamageTypeForAvatar'; + +ChartJS.register(ArcElement, Tooltip, Legend); + +const COLORS = [ + '#f87171', '#facc15', '#34d399', '#60a5fa', '#a78bfa', '#fb923c', + '#f472b6', '#38bdf8', '#a3e635', '#fdba74', '#c084fc', '#4ade80', + '#fcd34d', '#818cf8', '#f43f5e', '#14b8a6', '#e879f9', '#86efac', +]; + +export default function SkillPieChart({ avatarId }: { avatarId: number }) { + const [mode, setMode] = useState<1 | 2>(2); + const transI18n = useTranslations("DataAnalysisPage"); + + const skillData = useSkillDamageForAvatar(avatarId); + const typeData = useDamageByTypeForAvatar(avatarId); + + const { labels, damageValues } = mode === 1 ? skillData : typeData; + const total = damageValues.reduce((sum, val) => sum + val, 0); + + const labelsWithPercent = labels.map((label, index) => { + const value = damageValues[index]; + const percent = total ? ((value / total) * 100).toFixed(1) : '0.0'; + return `${transI18n(label.toLowerCase())} (${percent}%)`; + }); + + const data = { + labels: labelsWithPercent, + datasets: [ + { + data: damageValues, + backgroundColor: COLORS.slice(0, labels.length), + borderColor: '#fff', + borderWidth: 1, + }, + ], + }; + + return ( +
    +
    + {[ + { mode: 1, label: `${transI18n("type")} 1`, className: "btn-primary" }, + { mode: 2, label: `${transI18n("type")} 2`, className: "btn-warning" }, + ].map(({ mode: m, label, className }) => ( + + ))} +
    + +
    + ); +} diff --git a/src/components/enemybar/index.tsx b/src/components/enemybar/index.tsx index ea294df..940e98e 100644 --- a/src/components/enemybar/index.tsx +++ b/src/components/enemybar/index.tsx @@ -1,79 +1,79 @@ -"use client" - -import useBattleDataStore from "@/stores/battleDataStore"; -import Image from "next/image"; -import useAvatarDataStore from "@/stores/avatarDataStore"; -import { getNameEnemy } from "@/helper/getNameChar"; - -import useLocaleStore from "@/stores/localeStore"; -import NameAvatar from "../nameAvatar"; - -export default function EnemyBar() { - const { enemyDetail } = useBattleDataStore() - const { mapEnemy } = useAvatarDataStore() - const { locale } = useLocaleStore() - - return ( -
    -
    - {enemyDetail && Object.values(enemyDetail).filter((enemy) => ( - enemy.stats.HP <= enemy.maxHP)).map((enemy, uid) => ( -
    -
    -
    - {mapEnemy?.[enemy.id.toString()]?.icon && ( - {enemy.name} - )} - -
    - -

    Level {enemy.level || 1}

    -
    -
    -
    - -
    -
    -
    HP:
    -
    -
    - {Number(enemy?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    /
    -
    - {Number(enemy?.maxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    -
    - -
    -
    -
    - {Math.round(((enemy.stats?.HP || 0) / (enemy.maxHP || 100)) * 100)}% -
    -
    - -
    -
    - ))} -
    -
    - ) +"use client" + +import useBattleDataStore from "@/stores/battleDataStore"; +import Image from "next/image"; +import useAvatarDataStore from "@/stores/avatarDataStore"; +import { getNameEnemy } from "@/helper/getNameChar"; + +import useLocaleStore from "@/stores/localeStore"; +import NameAvatar from "../nameAvatar"; + +export default function EnemyBar() { + const { enemyDetail } = useBattleDataStore() + const { mapEnemy } = useAvatarDataStore() + const { locale } = useLocaleStore() + + return ( +
    +
    + {enemyDetail && Object.values(enemyDetail).filter((enemy) => ( + enemy.stats.HP <= enemy.maxHP)).map((enemy, uid) => ( +
    +
    +
    + {mapEnemy?.[enemy.id.toString()]?.icon && ( + {enemy.name} + )} + +
    + +

    Level {enemy.level || 1}

    +
    +
    +
    + +
    +
    +
    HP:
    +
    +
    + {Number(enemy?.stats?.HP ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    /
    +
    + {Number(enemy?.maxHP ?? 100).toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    +
    + +
    +
    +
    + {Math.round(((enemy.stats?.HP || 0) / (enemy.maxHP || 100)) * 100)}% +
    +
    + +
    +
    + ))} +
    +
    + ) } \ No newline at end of file diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx index b3372bd..3761b67 100644 --- a/src/components/footer/index.tsx +++ b/src/components/footer/index.tsx @@ -1,11 +1,11 @@ - -export default function Footer() { - return( -
    - - -
    - ) -} + +export default function Footer() { + return( +
    + + +
    + ) +} diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index 5c8cefe..494645a 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -1,499 +1,499 @@ -"use client" -import { exportBattleData, importBattleData } from "@/helper"; -import { useChangeTheme } from "@/hooks/useChangeTheme"; -import { checkConnectTcpApi } from "@/lib/api"; -import { listCurrentLanguage } from "@/lib/constant"; -import { connectSocket, disconnectSocket, getSocket, isSocketConnected } from "@/lib/socket"; -import useBattleDataStore from "@/stores/battleDataStore"; -import useLocaleStore from "@/stores/localeStore"; -import useSocketStore from "@/stores/socketSettingStore"; -import { motion } from "framer-motion"; -import { useTranslations } from "next-intl"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import Image from "next/image"; - -const themes = [ - { label: "Winter" }, - { label: "Night" }, - { label: "Cupcake" }, - { label: "Coffee" }, -]; - -export default function Header() { - const { changeTheme } = useChangeTheme() - const { locale, setLocale } = useLocaleStore() - const { loadBattleDataFromJSON, version } = useBattleDataStore() - const router = useRouter() - const transI18n = useTranslations("DataAnalysisPage") - const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore(); - const [message, setMessage] = useState({ text: '', type: '' }); - const [isModalOpen, setIsModalOpen] = useState(false); - - - useEffect(() => { - - const cookieLocale = document.cookie.split("; ") - .find((row) => row.startsWith("MYNEXTAPP_LOCALE")) - ?.split("=")[1]; - - if (cookieLocale) { - if (!listCurrentLanguage.hasOwnProperty(cookieLocale)) { - setLocale("en") - } else { - setLocale(cookieLocale) - } - - } else { - let browserLocale = navigator.language.slice(0, 2); - - if (!listCurrentLanguage.hasOwnProperty(browserLocale)) { - browserLocale = "en" - } - setLocale(browserLocale); - document.cookie = `MYNEXTAPP_LOCALE=${browserLocale};` - router.refresh() - } - }, [router, setLocale]) - - const changeLocale = (newLocale: string) => { - setLocale(newLocale) - document.cookie = `MYNEXTAPP_LOCALE=${newLocale};` - router.refresh() - } - - useEffect(() => { - const checkStatus = () => { - const connected = isSocketConnected(); - if (connected !== status) { - setStatus(connected); - } - }; - - checkStatus(); - const intervalId = setInterval(checkStatus, 5000); - - return () => clearInterval(intervalId); - }, [status, setStatus]); - - - useEffect(() => { - const socket = getSocket(); - - if (!socket) return; - - return () => { - disconnectSocket() - }; - }, []); - - const handleConnect = () => { - if (!host || !port) { - setMessage({ text: 'IP and Port are required', type: 'error' }); - return; - } - connectSocket(); - - setTimeout(() => { - const connected = isSocketConnected(); - setStatus(connected); - - if (connected) { - setMessage({ text: 'Socket connected successfully!', type: 'success' }); - } else { - setMessage({ text: 'Failed to connect. Please check IP and port.', type: 'error' }); - } - }, 2000); - }; - - const checkConnection = async () => { - setMessage({ text: 'Checking connection...', type: 'info' }); - let isConnect = false - try { - isConnect = await checkConnectTcpApi() - } catch { - - } - - if (!isConnect) { - setMessage({ text: 'Connection bridge to TCP Server failed', type: 'error' }) - } else { - setMessage({ text: 'Connection OK!', type: 'success' }); - } - } - - const handleShow = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - setIsModalOpen(true); - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - setIsModalOpen(false); - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close() - } - }; - - // Handle ESC key to close modal - useEffect(() => { - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape' && isModalOpen) { - handleCloseModal("character_detail_modal"); - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isModalOpen]); - - return ( -
    - - - {/* Desktop navigation */} -
    -
      -
    • - <> - { - const file = e.target.files?.[0]; - if (file) { - importBattleData(file, loadBattleDataFromJSON) - .then(() => console.log('Data loaded')) - .catch(err => alert('Failed to load data: ' + err.message)); - } - }} - /> - - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - {/* Right side items */} -
    -
    -
    -
    - {status ? transI18n("connected") : transI18n("unconnected")} -
    -
    -
    -
    - - {/* Language selector - REFINED */} -
    -
    - - - - - -
    -
    - -
    -
    - - - - - - -
    - -
    -
      - {themes.map((theme) => ( -
    • { - if (changeTheme) changeTheme(theme.label.toLowerCase()); - }} - > - -
    • - ))} -
    -
    -
    - - {/* GitHub Link */} - - - - - -
    - - -
    -
    - handleCloseModal("socket_connection_modal")} - > - ✕ - -
    - -
    -

    - {transI18n("socketConnection").toUpperCase()} -

    -
    - -
    - {/* Select connection type */} -
    - - -
    - - {/* Show host/port if Other */} - {connectionType === "Other" && ( -
    -
    - - setHost(e.target.value)} - /> -
    - -
    - - setPort(parseInt(e.target.value) || 0)} - /> -
    -
    - )} - - {message.text && ( -
    - {message.text} -
    - )} - -
    -
    - {transI18n("status")}: - - {status ? transI18n("connected") : transI18n("unconnected")} - -
    - -
    - - {connectionType !== "Native" && ( - - )} -
    -
    -
    -
    -
    -
    - ) +"use client" +import { exportBattleData, importBattleData } from "@/helper"; +import { useChangeTheme } from "@/hooks/useChangeTheme"; +import { checkConnectTcpApi } from "@/lib/api"; +import { listCurrentLanguage } from "@/lib/constant"; +import { connectSocket, disconnectSocket, getSocket, isSocketConnected } from "@/lib/socket"; +import useBattleDataStore from "@/stores/battleDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import useSocketStore from "@/stores/socketSettingStore"; +import { motion } from "framer-motion"; +import { useTranslations } from "next-intl"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import Image from "next/image"; + +const themes = [ + { label: "Winter" }, + { label: "Night" }, + { label: "Cupcake" }, + { label: "Coffee" }, +]; + +export default function Header() { + const { changeTheme } = useChangeTheme() + const { locale, setLocale } = useLocaleStore() + const { loadBattleDataFromJSON, version } = useBattleDataStore() + const router = useRouter() + const transI18n = useTranslations("DataAnalysisPage") + const { host, port, status, connectionType, setHost, setPort, setStatus, setConnectionType } = useSocketStore(); + const [message, setMessage] = useState({ text: '', type: '' }); + const [isModalOpen, setIsModalOpen] = useState(false); + + + useEffect(() => { + + const cookieLocale = document.cookie.split("; ") + .find((row) => row.startsWith("MYNEXTAPP_LOCALE")) + ?.split("=")[1]; + + if (cookieLocale) { + if (!listCurrentLanguage.hasOwnProperty(cookieLocale)) { + setLocale("en") + } else { + setLocale(cookieLocale) + } + + } else { + let browserLocale = navigator.language.slice(0, 2); + + if (!listCurrentLanguage.hasOwnProperty(browserLocale)) { + browserLocale = "en" + } + setLocale(browserLocale); + document.cookie = `MYNEXTAPP_LOCALE=${browserLocale};` + router.refresh() + } + }, [router, setLocale]) + + const changeLocale = (newLocale: string) => { + setLocale(newLocale) + document.cookie = `MYNEXTAPP_LOCALE=${newLocale};` + router.refresh() + } + + useEffect(() => { + const checkStatus = () => { + const connected = isSocketConnected(); + if (connected !== status) { + setStatus(connected); + } + }; + + checkStatus(); + const intervalId = setInterval(checkStatus, 5000); + + return () => clearInterval(intervalId); + }, [status, setStatus]); + + + useEffect(() => { + const socket = getSocket(); + + if (!socket) return; + + return () => { + disconnectSocket() + }; + }, []); + + const handleConnect = () => { + if (!host || !port) { + setMessage({ text: 'IP and Port are required', type: 'error' }); + return; + } + connectSocket(); + + setTimeout(() => { + const connected = isSocketConnected(); + setStatus(connected); + + if (connected) { + setMessage({ text: 'Socket connected successfully!', type: 'success' }); + } else { + setMessage({ text: 'Failed to connect. Please check IP and port.', type: 'error' }); + } + }, 2000); + }; + + const checkConnection = async () => { + setMessage({ text: 'Checking connection...', type: 'info' }); + let isConnect = false + try { + isConnect = await checkConnectTcpApi() + } catch { + + } + + if (!isConnect) { + setMessage({ text: 'Connection bridge to TCP Server failed', type: 'error' }) + } else { + setMessage({ text: 'Connection OK!', type: 'success' }); + } + } + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + setIsModalOpen(true); + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + setIsModalOpen(false); + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close() + } + }; + + // Handle ESC key to close modal + useEffect(() => { + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isModalOpen) { + handleCloseModal("character_detail_modal"); + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isModalOpen]); + + return ( +
    +
    + {/* Mobile menu dropdown */} +
    +
    + + + +
    +
      +
    • + <> + { + const file = e.target.files?.[0]; + if (file) { + importBattleData(file, loadBattleDataFromJSON) + .then(() => console.log('Data loaded')) + .catch(err => alert('Failed to load data: ' + err.message)); + } + }} + /> + + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + + {/* Logo */} + + +
    + Logo +
    +

    + Firefly Analy + + sis + +

    +

    For Veritas

    +
    +
    +
    + {version && ( +
    +
    +
    +
    + {version} +
    +
    +
    + )} + +
    + + {/* Desktop navigation */} +
    +
      +
    • + <> + { + const file = e.target.files?.[0]; + if (file) { + importBattleData(file, loadBattleDataFromJSON) + .then(() => console.log('Data loaded')) + .catch(err => alert('Failed to load data: ' + err.message)); + } + }} + /> + + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + + + {/* Right side items */} +
    +
    +
    +
    + {status ? transI18n("connected") : transI18n("unconnected")} +
    +
    +
    +
    + + {/* Language selector - REFINED */} +
    +
    + + + + + +
    +
    + +
    +
    + + + + + + +
    + +
    +
      + {themes.map((theme) => ( +
    • { + if (changeTheme) changeTheme(theme.label.toLowerCase()); + }} + > + +
    • + ))} +
    +
    +
    + + {/* GitHub Link */} + + + + + +
    + + +
    +
    + handleCloseModal("socket_connection_modal")} + > + ✕ + +
    + +
    +

    + {transI18n("socketConnection").toUpperCase()} +

    +
    + +
    + {/* Select connection type */} +
    + + +
    + + {/* Show host/port if Other */} + {connectionType === "Other" && ( +
    +
    + + setHost(e.target.value)} + /> +
    + +
    + + setPort(parseInt(e.target.value) || 0)} + /> +
    +
    + )} + + {message.text && ( +
    + {message.text} +
    + )} + +
    +
    + {transI18n("status")}: + + {status ? transI18n("connected") : transI18n("unconnected")} + +
    + +
    + + {connectionType !== "Native" && ( + + )} +
    +
    +
    +
    +
    +
    + ) } \ No newline at end of file diff --git a/src/components/lineupbar/index.tsx b/src/components/lineupbar/index.tsx index 18d6408..3540b09 100644 --- a/src/components/lineupbar/index.tsx +++ b/src/components/lineupbar/index.tsx @@ -1,307 +1,307 @@ -"use client"; -import useAvatarDataStore from "@/stores/avatarDataStore"; -import useBattleDataStore from "@/stores/battleDataStore"; -import CharacterCard from "../card/characterCard"; -import { useTranslations } from "next-intl"; -import { useState, useEffect } from "react"; -import { CharacterBasic } from "@/types"; -import useLocaleStore from "@/stores/localeStore"; -import { getNameChar } from '@/helper/getNameChar'; -import SkillBarChart from "../chart/skillBarChart"; -import SkillPieChart from "../chart/skillPieChart"; -import { motion } from "framer-motion"; -import { DamageLineForOne } from "../chart/damageLineForOne"; -import { DamagePerCycleForOne } from "../chart/damagePerCycleForOne"; -import { useCalcTotalDmgAvatar, useCalcTotalTurnAvatar } from "@/hooks/useCalcAvatarData"; -import Image from "next/image"; -import NameAvatar from "../nameAvatar"; -// import ShowCaseInfo from "../card/showCaseCard"; - -export default function LineupBar() { - const [selectedCharacter, setSelectedCharacter] = useState(undefined); - const [isModalOpen, setIsModalOpen] = useState(false); - - const transI18n = useTranslations("DataAnalysisPage"); - const { lineup, turnHistory, dataAvatar } = useBattleDataStore(); - const { listAvatar, mapAvatar } = useAvatarDataStore(); - const { locale } = useLocaleStore(); - const totalDamage = useCalcTotalDmgAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0); - const totalTurn = useCalcTotalTurnAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0) - - - const lineupAvatars = listAvatar?.filter(item => - lineup?.some(av => av?.avatarId?.toString() === item.id) - ); - - const handleShow = (modalId: string, item: CharacterBasic) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - setSelectedCharacter(item); - setIsModalOpen(true); - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - setIsModalOpen(false); - setSelectedCharacter(undefined); - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close() - } - }; - - // Handle ESC key to close modal - useEffect(() => { - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape' && isModalOpen) { - handleCloseModal("character_detail_modal"); - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isModalOpen]); - - return ( -
    - - {transI18n("lineupInfo")} - - -
    -
    - - - - - -
    - {transI18n("lastTurn")}: -
    - - i?.avatarId)?.avatarId?.toString() || ""])} - /> -
    -
    -
    - - -
    - {lineupAvatars.length === 0 ? ( -
    -

    {transI18n("noCharactersInLineup")}

    -
    - ) : ( -
    -
    - {lineupAvatars?.map((item, index) => { - const lastTurnAvatarId = turnHistory.findLast(i => i?.avatarId)?.avatarId || -1; - const isLastTurn = item.id === lastTurnAvatarId.toString(); - - return ( - handleShow("character_detail_modal", item)} - className={`cursor-pointer shrink-0 justify-items-center ${isLastTurn ? "shadow-[inset_0_0_10px_2px_rgba(59,130,246,0.7),0_0_20px_5px_rgba(59,130,246,0.3)]" : "" - }`} - > - - - ); - })} - -
    -
    - )} - - {/* Character Detail Modal */} - -
    -
    - handleCloseModal("character_detail_modal")} - > - ✕ - -
    - -
    - - -
    - - {selectedCharacter && ( - -
    -

    {transI18n("characterInformation")}

    -
    -
    -
    -

    - {transI18n("id")}: {selectedCharacter.id} -

    -

    - {transI18n("path")}: - {transI18n(selectedCharacter.baseType.toLowerCase())} - {selectedCharacter.baseType && ( - {selectedCharacter.baseType.toLowerCase()} - )} -

    -

    - {transI18n("rarity")}: {selectedCharacter.rank === "CombatPowerAvatarRarityType5" ? "5*" : "4*"} -

    -

    - {transI18n("element")}: - {selectedCharacter.damageType.toLowerCase()} -

    - - {(() => { - const avatar = dataAvatar.find(it => it.avatar_id.toString() === selectedCharacter.id); - if (!avatar) return null; - const relicIds = avatar.relics.map(item => { - const relicIdStr = item.relic_id.toString().slice(1); - const slot = relicIdStr.slice(-1); - return `${item.relic_set_id}_${slot}`; - }); - return ( - <> -

    - {transI18n("level")}: {avatar.level} -

    -

    - {transI18n("eidolons")}: {avatar?.data?.rank} -

    -
    - {transI18n("lightcones")}: - {avatar?.Lightcone?.item_id?.toString() -
    -
    - {transI18n("relics")}: -
    - {relicIds.map(it => ( - {avatar?.Lightcone?.item_id?.toString() - ))} -
    -
    - - ); - })()} - -
    - - -
    - {getNameChar(locale, -
    -
    - {/*
    -

    {transI18n("characterInformation")}

    - -
    */} -
    -

    {transI18n("totalTurn")}: {Number(totalTurn).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

    -
    -
    -

    {transI18n("totalDamage")}: {Number(totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

    -
    - -
    -

    {transI18n("skillDamageBreakdown")}

    - -
    - -
    -
    -

    {transI18n("damageOverTime")}

    - -
    - - -
    - -
    -

    {transI18n("skillUsageDistribution")}

    - -
    - -
    -
    -

    {transI18n("damagePerCycle")}

    -
    - -
    - -
    - )} -
    -
    -
    -
    - ); +"use client"; +import useAvatarDataStore from "@/stores/avatarDataStore"; +import useBattleDataStore from "@/stores/battleDataStore"; +import CharacterCard from "../card/characterCard"; +import { useTranslations } from "next-intl"; +import { useState, useEffect } from "react"; +import { CharacterBasic } from "@/types"; +import useLocaleStore from "@/stores/localeStore"; +import { getNameChar } from '@/helper/getNameChar'; +import SkillBarChart from "../chart/skillBarChart"; +import SkillPieChart from "../chart/skillPieChart"; +import { motion } from "framer-motion"; +import { DamageLineForOne } from "../chart/damageLineForOne"; +import { DamagePerCycleForOne } from "../chart/damagePerCycleForOne"; +import { useCalcTotalDmgAvatar, useCalcTotalTurnAvatar } from "@/hooks/useCalcAvatarData"; +import Image from "next/image"; +import NameAvatar from "../nameAvatar"; +// import ShowCaseInfo from "../card/showCaseCard"; + +export default function LineupBar() { + const [selectedCharacter, setSelectedCharacter] = useState(undefined); + const [isModalOpen, setIsModalOpen] = useState(false); + + const transI18n = useTranslations("DataAnalysisPage"); + const { lineup, turnHistory, dataAvatar } = useBattleDataStore(); + const { listAvatar, mapAvatar } = useAvatarDataStore(); + const { locale } = useLocaleStore(); + const totalDamage = useCalcTotalDmgAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0); + const totalTurn = useCalcTotalTurnAvatar(selectedCharacter ? Number(selectedCharacter.id) : 0) + + + const lineupAvatars = listAvatar?.filter(item => + lineup?.some(av => av?.avatarId?.toString() === item.id) + ); + + const handleShow = (modalId: string, item: CharacterBasic) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + setSelectedCharacter(item); + setIsModalOpen(true); + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + setIsModalOpen(false); + setSelectedCharacter(undefined); + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close() + } + }; + + // Handle ESC key to close modal + useEffect(() => { + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isModalOpen) { + handleCloseModal("character_detail_modal"); + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isModalOpen]); + + return ( +
    + + {transI18n("lineupInfo")} + + +
    +
    + + + + + +
    + {transI18n("lastTurn")}: +
    + + i?.avatarId)?.avatarId?.toString() || ""])} + /> +
    +
    +
    + + +
    + {lineupAvatars.length === 0 ? ( +
    +

    {transI18n("noCharactersInLineup")}

    +
    + ) : ( +
    +
    + {lineupAvatars?.map((item, index) => { + const lastTurnAvatarId = turnHistory.findLast(i => i?.avatarId)?.avatarId || -1; + const isLastTurn = item.id === lastTurnAvatarId.toString(); + + return ( + handleShow("character_detail_modal", item)} + className={`cursor-pointer shrink-0 justify-items-center ${isLastTurn ? "shadow-[inset_0_0_10px_2px_rgba(59,130,246,0.7),0_0_20px_5px_rgba(59,130,246,0.3)]" : "" + }`} + > + + + ); + })} + +
    +
    + )} + + {/* Character Detail Modal */} + +
    +
    + handleCloseModal("character_detail_modal")} + > + ✕ + +
    + +
    + + +
    + + {selectedCharacter && ( + +
    +

    {transI18n("characterInformation")}

    +
    +
    +
    +

    + {transI18n("id")}: {selectedCharacter.id} +

    +

    + {transI18n("path")}: + {transI18n(selectedCharacter.baseType.toLowerCase())} + {selectedCharacter.baseType && ( + {selectedCharacter.baseType.toLowerCase()} + )} +

    +

    + {transI18n("rarity")}: {selectedCharacter.rank === "CombatPowerAvatarRarityType5" ? "5*" : "4*"} +

    +

    + {transI18n("element")}: + {selectedCharacter.damageType.toLowerCase()} +

    + + {(() => { + const avatar = dataAvatar.find(it => it.avatar_id.toString() === selectedCharacter.id); + if (!avatar) return null; + const relicIds = avatar.relics.map(item => { + const relicIdStr = item.relic_id.toString().slice(1); + const slot = relicIdStr.slice(-1); + return `${item.relic_set_id}_${slot}`; + }); + return ( + <> +

    + {transI18n("level")}: {avatar.level} +

    +

    + {transI18n("eidolons")}: {avatar?.data?.rank} +

    +
    + {transI18n("lightcones")}: + {avatar?.Lightcone?.item_id?.toString() +
    +
    + {transI18n("relics")}: +
    + {relicIds.map(it => ( + {avatar?.Lightcone?.item_id?.toString() + ))} +
    +
    + + ); + })()} + +
    + + +
    + {getNameChar(locale, +
    +
    + {/*
    +

    {transI18n("characterInformation")}

    + +
    */} +
    +

    {transI18n("totalTurn")}: {Number(totalTurn).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

    +
    +
    +

    {transI18n("totalDamage")}: {Number(totalDamage).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}

    +
    + +
    +

    {transI18n("skillDamageBreakdown")}

    + +
    + +
    +
    +

    {transI18n("damageOverTime")}

    + +
    + + +
    + +
    +

    {transI18n("skillUsageDistribution")}

    + +
    + +
    +
    +

    {transI18n("damagePerCycle")}

    +
    + +
    + +
    + )} +
    +
    +
    +
    + ); } \ No newline at end of file diff --git a/src/components/nameAvatar/index.tsx b/src/components/nameAvatar/index.tsx index fb238c3..b73a6bc 100644 --- a/src/components/nameAvatar/index.tsx +++ b/src/components/nameAvatar/index.tsx @@ -1,16 +1,16 @@ -import { parseRuby } from "@/helper"; - - - -interface TextProps { - text: string; - locale: string; - className?: string; -} - -export default function NameAvatar({ text, locale, className }: TextProps) { - if (locale === "ja") { - return
    ; - } - return
    {text}
    ; +import { parseRuby } from "@/helper"; + + + +interface TextProps { + text: string; + locale: string; + className?: string; +} + +export default function NameAvatar({ text, locale, className }: TextProps) { + if (locale === "ja") { + return
    ; + } + return
    {text}
    ; } \ No newline at end of file diff --git a/src/components/themeController/clientThemeWrapper.tsx b/src/components/themeController/clientThemeWrapper.tsx index 704e8f5..09f456b 100644 --- a/src/components/themeController/clientThemeWrapper.tsx +++ b/src/components/themeController/clientThemeWrapper.tsx @@ -1,8 +1,8 @@ -"use client"; -import { PropsWithChildren, useContext } from "react"; -import { ThemeContext } from "./themeContext"; - -export function ClientThemeWrapper({ children }: PropsWithChildren) { - const { theme } = useContext(ThemeContext); - return
    {children}
    ; +"use client"; +import { PropsWithChildren, useContext } from "react"; +import { ThemeContext } from "./themeContext"; + +export function ClientThemeWrapper({ children }: PropsWithChildren) { + const { theme } = useContext(ThemeContext); + return
    {children}
    ; } \ No newline at end of file diff --git a/src/components/themeController/index.ts b/src/components/themeController/index.ts index 5600303..fbba780 100644 --- a/src/components/themeController/index.ts +++ b/src/components/themeController/index.ts @@ -1,2 +1,2 @@ -export * from "./clientThemeWrapper" -export * from "./themeContext" +export * from "./clientThemeWrapper" +export * from "./themeContext" diff --git a/src/components/themeController/themeContext.tsx b/src/components/themeController/themeContext.tsx index 714eefb..aaf9e5e 100644 --- a/src/components/themeController/themeContext.tsx +++ b/src/components/themeController/themeContext.tsx @@ -1,27 +1,27 @@ -"use client"; -import useLocaleStore from "@/stores/localeStore"; -import { createContext, PropsWithChildren } from "react"; - -interface ThemeContextType { - theme?: string; - changeTheme?: (nextTheme: string | null) => void; -} -export const ThemeContext = createContext({}); - -export const ThemeProvider = ({ children }: PropsWithChildren) => { - const { theme, setTheme } = useLocaleStore() - - const changeTheme = (nextTheme: string | null) => { - if (nextTheme) { - setTheme(nextTheme); - } else { - setTheme(theme === "winter" ? "night" : "winter"); - } - }; - - return ( - - {children} - - ); +"use client"; +import useLocaleStore from "@/stores/localeStore"; +import { createContext, PropsWithChildren } from "react"; + +interface ThemeContextType { + theme?: string; + changeTheme?: (nextTheme: string | null) => void; +} +export const ThemeContext = createContext({}); + +export const ThemeProvider = ({ children }: PropsWithChildren) => { + const { theme, setTheme } = useLocaleStore() + + const changeTheme = (nextTheme: string | null) => { + if (nextTheme) { + setTheme(nextTheme); + } else { + setTheme(theme === "winter" ? "night" : "winter"); + } + }; + + return ( + + {children} + + ); }; \ No newline at end of file diff --git a/src/global.d.ts b/src/global.d.ts index 444f793..5e279b7 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,4 +1,4 @@ -declare module '*.css' { - const content: { [className: string]: string }; - export default content; -} +declare module '*.css' { + const content: { [className: string]: string }; + export default content; +} diff --git a/src/helper/exportDataBattle.ts b/src/helper/exportDataBattle.ts index ac599fd..7617974 100644 --- a/src/helper/exportDataBattle.ts +++ b/src/helper/exportDataBattle.ts @@ -1,48 +1,48 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { BattleDataStateJson } from "@/types/mics"; - -export const exportBattleData = ( - filename = 'battle_data.json' -) => { - const { lineup, - turnHistory, - skillHistory, - totalAV, - totalDamage, - damagePerAV, - cycleIndex, - waveIndex, - dataAvatar, - maxWave, - maxCycle, - version, - avatarDetail, - enemyDetail - } = useBattleDataStore.getState(); - - const data: BattleDataStateJson = { - lineup, - turnHistory, - skillHistory, - dataAvatar, - totalAV, - totalDamage, - damagePerAV, - cycleIndex, - waveIndex, - maxWave, - maxCycle, - version, - avatarDetail, - enemyDetail - } - - const dataStr = JSON.stringify(data, null, 2); - const blob = new Blob([dataStr], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - a.click(); - URL.revokeObjectURL(url); +import useBattleDataStore from "@/stores/battleDataStore"; +import { BattleDataStateJson } from "@/types/mics"; + +export const exportBattleData = ( + filename = 'battle_data.json' +) => { + const { lineup, + turnHistory, + skillHistory, + totalAV, + totalDamage, + damagePerAV, + cycleIndex, + waveIndex, + dataAvatar, + maxWave, + maxCycle, + version, + avatarDetail, + enemyDetail + } = useBattleDataStore.getState(); + + const data: BattleDataStateJson = { + lineup, + turnHistory, + skillHistory, + dataAvatar, + totalAV, + totalDamage, + damagePerAV, + cycleIndex, + waveIndex, + maxWave, + maxCycle, + version, + avatarDetail, + enemyDetail + } + + const dataStr = JSON.stringify(data, null, 2); + const blob = new Blob([dataStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); }; \ No newline at end of file diff --git a/src/helper/getNameChar.ts b/src/helper/getNameChar.ts index 078f5a3..820f680 100644 --- a/src/helper/getNameChar.ts +++ b/src/helper/getNameChar.ts @@ -1,63 +1,63 @@ -import { listCurrentLanguage } from "@/lib/constant"; -import { CharacterBasic, MonsterBasic } from "@/types"; -import { useTranslations } from "next-intl"; - - -type TFunc = ReturnType - -function cleanText(text: string): string { - if (!text) return "" - return text.replace(/(.*?)<\/unbreak>/g, "$1") -} - -export function getNameChar( - locale: string, - t: TFunc, - data: CharacterBasic | undefined -): string { - if (!data) return ""; - - if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { - return ""; - } - - const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); - - let text = data.lang[langKey] ?? ""; - - if (!text) { - text = data.lang["en"] ?? ""; - } - - if (Number(data.id) > 8000) { - text = `${t("trailblazer")} • ${t(data?.baseType?.toLowerCase() ?? "")}`; - } - - return cleanText(text); -} - -export function getNameEnemy(locale: string, data: MonsterBasic | undefined): string { - if (!data) { - return "" - } - if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { - return "" - } - - const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); - - - let text = data.lang[langKey] ?? ""; - - if (!text) { - text = data.lang["en"] ?? ""; - } - return text -} - -export function parseRuby(text: string): string { - const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs; - return text.replace(rubyRegex, (_match, furigana, kanji) => { - return `${kanji}${furigana}`; - }); +import { listCurrentLanguage } from "@/lib/constant"; +import { CharacterBasic, MonsterBasic } from "@/types"; +import { useTranslations } from "next-intl"; + + +type TFunc = ReturnType + +function cleanText(text: string): string { + if (!text) return "" + return text.replace(/(.*?)<\/unbreak>/g, "$1") +} + +export function getNameChar( + locale: string, + t: TFunc, + data: CharacterBasic | undefined +): string { + if (!data) return ""; + + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { + return ""; + } + + const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); + + let text = data.lang[langKey] ?? ""; + + if (!text) { + text = data.lang["en"] ?? ""; + } + + if (Number(data.id) > 8000) { + text = `${t("trailblazer")} • ${t(data?.baseType?.toLowerCase() ?? "")}`; + } + + return cleanText(text); +} + +export function getNameEnemy(locale: string, data: MonsterBasic | undefined): string { + if (!data) { + return "" + } + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { + return "" + } + + const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); + + + let text = data.lang[langKey] ?? ""; + + if (!text) { + text = data.lang["en"] ?? ""; + } + return text +} + +export function parseRuby(text: string): string { + const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs; + return text.replace(rubyRegex, (_match, furigana, kanji) => { + return `${kanji}${furigana}`; + }); } \ No newline at end of file diff --git a/src/helper/importDataBattle.ts b/src/helper/importDataBattle.ts index ccefa99..9c9da96 100644 --- a/src/helper/importDataBattle.ts +++ b/src/helper/importDataBattle.ts @@ -1,21 +1,21 @@ -import { BattleDataStateJson } from "@/types/mics"; - -export const importBattleData = (file: File, loadBattleDataFromJSON: (data: BattleDataStateJson) => void) => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - try { - const result = reader.result as string; - const parsed = JSON.parse(result) as BattleDataStateJson; - loadBattleDataFromJSON(parsed); - resolve(); - } catch (err) { - reject(err); - } - }; - reader.onerror = () => reject(reader.error); - reader.readAsText(file); - }); -}; - +import { BattleDataStateJson } from "@/types/mics"; + +export const importBattleData = (file: File, loadBattleDataFromJSON: (data: BattleDataStateJson) => void) => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + try { + const result = reader.result as string; + const parsed = JSON.parse(result) as BattleDataStateJson; + loadBattleDataFromJSON(parsed); + resolve(); + } catch (err) { + reject(err); + } + }; + reader.onerror = () => reject(reader.error); + reader.readAsText(file); + }); +}; + \ No newline at end of file diff --git a/src/helper/index.ts b/src/helper/index.ts index f1cf26d..340d120 100644 --- a/src/helper/index.ts +++ b/src/helper/index.ts @@ -1,3 +1,3 @@ -export * from "./getNameChar" -export * from "./exportDataBattle" +export * from "./getNameChar" +export * from "./exportDataBattle" export * from "./importDataBattle" \ No newline at end of file diff --git a/src/hooks/useCalcAvatarData.ts b/src/hooks/useCalcAvatarData.ts index bd7aefc..ae66843 100644 --- a/src/hooks/useCalcAvatarData.ts +++ b/src/hooks/useCalcAvatarData.ts @@ -1,28 +1,28 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { useMemo } from "react"; - -export function useCalcTotalDmgAvatar(avatarId: number) { - const { skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - return skillHistory - .filter(t => t.avatarId === avatarId) - .reduce((sum, turn) => sum + turn.totalDamage, 0); - }, [avatarId, skillHistory]); -} - - -export function useCalcTotalTurnAvatar(avatarId: number) { - const { turnHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const uniqueActionValues = new Set(); - turnHistory.forEach(turn => { - if (turn.avatarId === avatarId) { - uniqueActionValues.add(turn.actionValue); - } - }); - - return uniqueActionValues.size; - }, [avatarId, turnHistory]); +import useBattleDataStore from "@/stores/battleDataStore"; +import { useMemo } from "react"; + +export function useCalcTotalDmgAvatar(avatarId: number) { + const { skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + return skillHistory + .filter(t => t.avatarId === avatarId) + .reduce((sum, turn) => sum + turn.totalDamage, 0); + }, [avatarId, skillHistory]); +} + + +export function useCalcTotalTurnAvatar(avatarId: number) { + const { turnHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const uniqueActionValues = new Set(); + turnHistory.forEach(turn => { + if (turn.avatarId === avatarId) { + uniqueActionValues.add(turn.actionValue); + } + }); + + return uniqueActionValues.size; + }, [avatarId, turnHistory]); } \ No newline at end of file diff --git a/src/hooks/useChangeTheme.ts b/src/hooks/useChangeTheme.ts index 9007ded..ef0e42a 100644 --- a/src/hooks/useChangeTheme.ts +++ b/src/hooks/useChangeTheme.ts @@ -1,4 +1,4 @@ -import { useContext } from "react"; -import { ThemeContext } from "@/components/themeController"; - +import { useContext } from "react"; +import { ThemeContext } from "@/components/themeController"; + export const useChangeTheme = () => useContext(ThemeContext); \ No newline at end of file diff --git a/src/hooks/useDamageLineForOne.ts b/src/hooks/useDamageLineForOne.ts index d34fded..141fb91 100644 --- a/src/hooks/useDamageLineForOne.ts +++ b/src/hooks/useDamageLineForOne.ts @@ -1,31 +1,31 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { useMemo } from "react"; - -export function useDamageLineForOne(avatarId: number, mode: 0 | 1 = 0) { - const { skillHistory, turnHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const map = new Map(); - - for (const skill of skillHistory) { - if (skill.avatarId !== avatarId) continue; - const actionValue = turnHistory[skill.turnBattleId].actionValue - const prev = map.get(actionValue) || 0; - map.set(actionValue, prev + skill.totalDamage); - } - - const points = Array.from(map.entries()) - .map(([x, y]) => ({ x, y })) - .sort((a, b) => a.x - b.x); - - if (mode === 1) { - let cumulative = 0; - return points.map(p => { - cumulative += p.y; - return { x: p.x, y: Number(cumulative.toFixed(2)) }; - }); - } - - return points.map(p => ({ x: p.x, y: Number(p.y.toFixed(2)) })); - }, [avatarId, skillHistory, turnHistory, mode]); -} +import useBattleDataStore from "@/stores/battleDataStore"; +import { useMemo } from "react"; + +export function useDamageLineForOne(avatarId: number, mode: 0 | 1 = 0) { + const { skillHistory, turnHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const map = new Map(); + + for (const skill of skillHistory) { + if (skill.avatarId !== avatarId) continue; + const actionValue = turnHistory[skill.turnBattleId].actionValue + const prev = map.get(actionValue) || 0; + map.set(actionValue, prev + skill.totalDamage); + } + + const points = Array.from(map.entries()) + .map(([x, y]) => ({ x, y })) + .sort((a, b) => a.x - b.x); + + if (mode === 1) { + let cumulative = 0; + return points.map(p => { + cumulative += p.y; + return { x: p.x, y: Number(cumulative.toFixed(2)) }; + }); + } + + return points.map(p => ({ x: p.x, y: Number(p.y.toFixed(2)) })); + }, [avatarId, skillHistory, turnHistory, mode]); +} diff --git a/src/hooks/useDamageLinesForAll.ts b/src/hooks/useDamageLinesForAll.ts index 4431be3..7c4c0da 100644 --- a/src/hooks/useDamageLinesForAll.ts +++ b/src/hooks/useDamageLinesForAll.ts @@ -1,44 +1,44 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { useMemo } from "react"; - -export function useDamageLinesForAll(mode: 1 | 2 = 1) { - const { turnHistory, skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const avatarMap = new Map>(); - - for (const skill of skillHistory) { - if (!avatarMap.has(skill.avatarId)) { - avatarMap.set(skill.avatarId, new Map()); - } - - const charMap = avatarMap.get(skill.avatarId)!; - const actionValue = turnHistory[skill.turnBattleId].actionValue - const prev = charMap.get(actionValue) || 0; - charMap.set(actionValue, prev + skill.totalDamage); - } - - const result: Record = {}; - - for (const [avatarId, damageMap] of avatarMap.entries()) { - const points = Array.from(damageMap.entries()) - .map(([x, y]) => ({ x, y })) - .sort((a, b) => a.x - b.x); - - if (mode === 1) { - let cumulative = 0; - result[avatarId] = points.map(p => { - cumulative += Number(p.y); - return { x: p.x, y: Number(cumulative.toFixed(2)) }; - }); - } else { - result[avatarId] = points.map(p => ({ - x: p.x, - y: Number(p.y.toFixed(2)), - })); - } - } - - return result; - }, [turnHistory, skillHistory, mode]); -} +import useBattleDataStore from "@/stores/battleDataStore"; +import { useMemo } from "react"; + +export function useDamageLinesForAll(mode: 1 | 2 = 1) { + const { turnHistory, skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const avatarMap = new Map>(); + + for (const skill of skillHistory) { + if (!avatarMap.has(skill.avatarId)) { + avatarMap.set(skill.avatarId, new Map()); + } + + const charMap = avatarMap.get(skill.avatarId)!; + const actionValue = turnHistory[skill.turnBattleId].actionValue + const prev = charMap.get(actionValue) || 0; + charMap.set(actionValue, prev + skill.totalDamage); + } + + const result: Record = {}; + + for (const [avatarId, damageMap] of avatarMap.entries()) { + const points = Array.from(damageMap.entries()) + .map(([x, y]) => ({ x, y })) + .sort((a, b) => a.x - b.x); + + if (mode === 1) { + let cumulative = 0; + result[avatarId] = points.map(p => { + cumulative += Number(p.y); + return { x: p.x, y: Number(cumulative.toFixed(2)) }; + }); + } else { + result[avatarId] = points.map(p => ({ + x: p.x, + y: Number(p.y.toFixed(2)), + })); + } + } + + return result; + }, [turnHistory, skillHistory, mode]); +} diff --git a/src/hooks/useDamagePerCycle.ts b/src/hooks/useDamagePerCycle.ts index ae20b2d..f778255 100644 --- a/src/hooks/useDamagePerCycle.ts +++ b/src/hooks/useDamagePerCycle.ts @@ -1,69 +1,69 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { useTranslations } from "next-intl"; -import { useMemo } from "react"; - - -type Mode = 0 | 1 | 2; - -export function useDamagePerCycleForOne(avatarId: number, mode: Mode) { - const { skillHistory, turnHistory } = useBattleDataStore.getState(); - const transI18n = useTranslations("DataAnalysisPage"); - return useMemo(() => { - const damageMap = new Map(); - - skillHistory - .filter(s => s.avatarId === avatarId) - .forEach(s => { - const turn = turnHistory[s.turnBattleId]; - if (!turn) return; - - let key = ''; - if (mode === 0) { - key = `${transI18n('cycle')} ${turn.cycleIndex} - ${transI18n('wave')} ${turn.waveIndex}`; - } else if (mode === 1) { - key = `${transI18n('cycle')} ${turn.cycleIndex}`; - } else if (mode === 2) { - key = `${transI18n('wave')} ${turn.waveIndex}`; - } - - damageMap.set(key, (damageMap.get(key) || 0) + s.totalDamage); - }); - - const result = Array.from(damageMap.entries()) - .map(([x, y]) => ({ x, y })) - .sort((a, b) => a.x.localeCompare(b.x, undefined, { numeric: true })); - - return result; - }, [avatarId, mode, skillHistory, turnHistory, transI18n]); -} - - -export function useDamagePerCycleForAll(mode: Mode) { - const { skillHistory, turnHistory } = useBattleDataStore.getState(); - const transI18n = useTranslations("DataAnalysisPage"); - return useMemo(() => { - const damageMap = new Map(); - - skillHistory.forEach(s => { - const turn = turnHistory[s.turnBattleId]; - if (!turn) return; - - let key = ''; - if (mode === 0) { - key = `${transI18n('cycle')} ${turn.cycleIndex} - ${transI18n('wave')} ${turn.waveIndex}`; - } else if (mode === 1) { - key = `${transI18n('cycle')} ${turn.cycleIndex}`; - } else if (mode === 2) { - key = `${transI18n('wave')} ${turn.waveIndex}`; - } - - damageMap.set(key, (damageMap.get(key) || 0) + s.totalDamage); - }); - - const result = Array.from(damageMap.entries()) - .map(([x, y]) => ({ x, y })) - .sort((a, b) => a.x.localeCompare(b.x, undefined, { numeric: true })); - - return result; - }, [mode, skillHistory, turnHistory, transI18n]); +import useBattleDataStore from "@/stores/battleDataStore"; +import { useTranslations } from "next-intl"; +import { useMemo } from "react"; + + +type Mode = 0 | 1 | 2; + +export function useDamagePerCycleForOne(avatarId: number, mode: Mode) { + const { skillHistory, turnHistory } = useBattleDataStore.getState(); + const transI18n = useTranslations("DataAnalysisPage"); + return useMemo(() => { + const damageMap = new Map(); + + skillHistory + .filter(s => s.avatarId === avatarId) + .forEach(s => { + const turn = turnHistory[s.turnBattleId]; + if (!turn) return; + + let key = ''; + if (mode === 0) { + key = `${transI18n('cycle')} ${turn.cycleIndex} - ${transI18n('wave')} ${turn.waveIndex}`; + } else if (mode === 1) { + key = `${transI18n('cycle')} ${turn.cycleIndex}`; + } else if (mode === 2) { + key = `${transI18n('wave')} ${turn.waveIndex}`; + } + + damageMap.set(key, (damageMap.get(key) || 0) + s.totalDamage); + }); + + const result = Array.from(damageMap.entries()) + .map(([x, y]) => ({ x, y })) + .sort((a, b) => a.x.localeCompare(b.x, undefined, { numeric: true })); + + return result; + }, [avatarId, mode, skillHistory, turnHistory, transI18n]); +} + + +export function useDamagePerCycleForAll(mode: Mode) { + const { skillHistory, turnHistory } = useBattleDataStore.getState(); + const transI18n = useTranslations("DataAnalysisPage"); + return useMemo(() => { + const damageMap = new Map(); + + skillHistory.forEach(s => { + const turn = turnHistory[s.turnBattleId]; + if (!turn) return; + + let key = ''; + if (mode === 0) { + key = `${transI18n('cycle')} ${turn.cycleIndex} - ${transI18n('wave')} ${turn.waveIndex}`; + } else if (mode === 1) { + key = `${transI18n('cycle')} ${turn.cycleIndex}`; + } else if (mode === 2) { + key = `${transI18n('wave')} ${turn.waveIndex}`; + } + + damageMap.set(key, (damageMap.get(key) || 0) + s.totalDamage); + }); + + const result = Array.from(damageMap.entries()) + .map(([x, y]) => ({ x, y })) + .sort((a, b) => a.x.localeCompare(b.x, undefined, { numeric: true })); + + return result; + }, [mode, skillHistory, turnHistory, transI18n]); } \ No newline at end of file diff --git a/src/hooks/useDamagePercentForDamgeType.ts b/src/hooks/useDamagePercentForDamgeType.ts index 74b2451..25489f5 100644 --- a/src/hooks/useDamagePercentForDamgeType.ts +++ b/src/hooks/useDamagePercentForDamgeType.ts @@ -1,26 +1,26 @@ -import { useMemo } from "react"; -import useBattleDataStore from "@/stores/battleDataStore"; -import {attackTypeToString} from "@/types"; - -export function useDamagePercentByType() { - const { skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const dmgByType = new Map(); - - skillHistory.forEach(skill => { - skill.damageDetail.forEach(detail => { - const type = detail.damage_type; - if (!type) return; - dmgByType.set(attackTypeToString(type), (dmgByType.get(attackTypeToString(type)) || 0) + detail.damage); - }); - }); - - const totalDmg = Array.from(dmgByType.values()).reduce((sum, val) => sum + val, 0); - - return Array.from(dmgByType.entries()).map(([type, dmg]) => ({ - type, - percent: totalDmg > 0 ? (dmg / totalDmg) * 100 : 0, - })); - }, [skillHistory]); -} +import { useMemo } from "react"; +import useBattleDataStore from "@/stores/battleDataStore"; +import {attackTypeToString} from "@/types"; + +export function useDamagePercentByType() { + const { skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const dmgByType = new Map(); + + skillHistory.forEach(skill => { + skill.damageDetail.forEach(detail => { + const type = detail.damage_type; + if (!type) return; + dmgByType.set(attackTypeToString(type), (dmgByType.get(attackTypeToString(type)) || 0) + detail.damage); + }); + }); + + const totalDmg = Array.from(dmgByType.values()).reduce((sum, val) => sum + val, 0); + + return Array.from(dmgByType.entries()).map(([type, dmg]) => ({ + type, + percent: totalDmg > 0 ? (dmg / totalDmg) * 100 : 0, + })); + }, [skillHistory]); +} diff --git a/src/hooks/useDamagePercentPerAvatar.ts b/src/hooks/useDamagePercentPerAvatar.ts index 71aee1b..7848adb 100644 --- a/src/hooks/useDamagePercentPerAvatar.ts +++ b/src/hooks/useDamagePercentPerAvatar.ts @@ -1,21 +1,21 @@ -import useBattleDataStore from "@/stores/battleDataStore"; -import { useMemo } from "react"; - -export function useDamagePercentPerAvatar() { - const { skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const dmgByAvatar = new Map(); - - skillHistory.forEach(t => { - dmgByAvatar.set(t.avatarId, (dmgByAvatar.get(t.avatarId) || 0) + t.totalDamage); - }); - - const totalDmg = Array.from(dmgByAvatar.values()).reduce((sum, v) => sum + v, 0); - - return Array.from(dmgByAvatar.entries()).map(([avatarId, dmg]) => ({ - avatarId, - percent: totalDmg > 0 ? (dmg / totalDmg) * 100 : 0, - })); - }, [skillHistory]); -} +import useBattleDataStore from "@/stores/battleDataStore"; +import { useMemo } from "react"; + +export function useDamagePercentPerAvatar() { + const { skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const dmgByAvatar = new Map(); + + skillHistory.forEach(t => { + dmgByAvatar.set(t.avatarId, (dmgByAvatar.get(t.avatarId) || 0) + t.totalDamage); + }); + + const totalDmg = Array.from(dmgByAvatar.values()).reduce((sum, v) => sum + v, 0); + + return Array.from(dmgByAvatar.entries()).map(([avatarId, dmg]) => ({ + avatarId, + percent: totalDmg > 0 ? (dmg / totalDmg) * 100 : 0, + })); + }, [skillHistory]); +} diff --git a/src/hooks/useDamageTypeForAvatar.ts b/src/hooks/useDamageTypeForAvatar.ts index 42c8f0f..cba72d8 100644 --- a/src/hooks/useDamageTypeForAvatar.ts +++ b/src/hooks/useDamageTypeForAvatar.ts @@ -1,26 +1,26 @@ -"use client"; -import { useMemo } from "react"; -import useBattleDataStore from "@/stores/battleDataStore"; -import {attackTypeToString} from "@/types"; - -export function useDamageByTypeForAvatar(avatarId: number) { - const { skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const dmgMap = new Map(); - - for (const skill of skillHistory) { - if (skill.avatarId !== avatarId) continue; - - for (const damage of skill.damageDetail) { - const type = attackTypeToString(damage?.damage_type)?.toLowerCase() || ""; - dmgMap.set(type, (dmgMap.get(type) || 0) + damage.damage); - } - } - - const labels = Array.from(dmgMap.keys()); - const damageValues = Array.from(dmgMap.values()); - - return { labels, damageValues }; - }, [avatarId, skillHistory]); -} +"use client"; +import { useMemo } from "react"; +import useBattleDataStore from "@/stores/battleDataStore"; +import {attackTypeToString} from "@/types"; + +export function useDamageByTypeForAvatar(avatarId: number) { + const { skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const dmgMap = new Map(); + + for (const skill of skillHistory) { + if (skill.avatarId !== avatarId) continue; + + for (const damage of skill.damageDetail) { + const type = attackTypeToString(damage?.damage_type)?.toLowerCase() || ""; + dmgMap.set(type, (dmgMap.get(type) || 0) + damage.damage); + } + } + + const labels = Array.from(dmgMap.keys()); + const damageValues = Array.from(dmgMap.values()); + + return { labels, damageValues }; + }, [avatarId, skillHistory]); +} diff --git a/src/hooks/useSkillDamageForAvatar.ts b/src/hooks/useSkillDamageForAvatar.ts index 0b60df5..434dd35 100644 --- a/src/hooks/useSkillDamageForAvatar.ts +++ b/src/hooks/useSkillDamageForAvatar.ts @@ -1,24 +1,24 @@ -"use client"; -import { useMemo } from "react"; -import useBattleDataStore from "@/stores/battleDataStore"; -import {attackTypeToString} from "@/types"; - -export function useSkillDamageForAvatar(avatarId: number) { - const { skillHistory } = useBattleDataStore.getState(); - - return useMemo(() => { - const dmgMap = new Map(); - - for (const skill of skillHistory) { - if (skill.avatarId !== avatarId) continue; - - const type = attackTypeToString(skill.skillType).toLowerCase(); - dmgMap.set(type, (dmgMap.get(type) || 0) + skill.totalDamage); - } - - const labels = Array.from(dmgMap.keys()); - const damageValues = Array.from(dmgMap.values()); - - return { labels, damageValues }; - }, [avatarId, skillHistory]); -} +"use client"; +import { useMemo } from "react"; +import useBattleDataStore from "@/stores/battleDataStore"; +import {attackTypeToString} from "@/types"; + +export function useSkillDamageForAvatar(avatarId: number) { + const { skillHistory } = useBattleDataStore.getState(); + + return useMemo(() => { + const dmgMap = new Map(); + + for (const skill of skillHistory) { + if (skill.avatarId !== avatarId) continue; + + const type = attackTypeToString(skill.skillType).toLowerCase(); + dmgMap.set(type, (dmgMap.get(type) || 0) + skill.totalDamage); + } + + const labels = Array.from(dmgMap.keys()); + const damageValues = Array.from(dmgMap.values()); + + return { labels, damageValues }; + }, [avatarId, skillHistory]); +} diff --git a/src/lib/api.ts b/src/lib/api.ts index a8a605e..952c501 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,51 +1,51 @@ -import useSocketStore from "@/stores/socketSettingStore"; -import { CharacterBasic, MonsterBasic } from "@/types"; -import axios from 'axios'; - -export async function checkConnectTcpApi(): Promise { - const { host, port, connectionType } = useSocketStore.getState() - let url = `${host}:${port}/check-tcp` - if (connectionType === "PS") { - url = "http://localhost:21000/check-tcp" - } - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (response.ok) { - const data = await response.json(); - return data.status - } - return false -} - -export async function getCharacterListApi(): Promise { - try { - const res = await axios.get("/api/data/avatar_basic"); - return res.data - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - console.log(`Error: ${error.response?.status} - ${error.message}`); - } else { - console.log(`Unexpected error: ${String(error)}`); - } - return []; - } -} - -export async function getEnemyListApi(): Promise { - try { - const res = await axios.get("/api/data/monster_basic"); - return res.data - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - console.log(`Error: ${error.response?.status} - ${error.message}`); - } else { - console.log(`Unexpected error: ${String(error)}`); - } - return []; - } -} +import useSocketStore from "@/stores/socketSettingStore"; +import { CharacterBasic, MonsterBasic } from "@/types"; +import axios from 'axios'; + +export async function checkConnectTcpApi(): Promise { + const { host, port, connectionType } = useSocketStore.getState() + let url = `${host}:${port}/check-tcp` + if (connectionType === "PS") { + url = "http://localhost:21000/check-tcp" + } + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (response.ok) { + const data = await response.json(); + return data.status + } + return false +} + +export async function getCharacterListApi(): Promise { + try { + const res = await axios.get("/api/data/avatar_basic"); + return res.data + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + console.log(`Error: ${error.response?.status} - ${error.message}`); + } else { + console.log(`Unexpected error: ${String(error)}`); + } + return []; + } +} + +export async function getEnemyListApi(): Promise { + try { + const res = await axios.get("/api/data/monster_basic"); + return res.data + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + console.log(`Error: ${error.response?.status} - ${error.message}`); + } else { + console.log(`Unexpected error: ${String(error)}`); + } + return []; + } +} diff --git a/src/lib/cache/cache.ts b/src/lib/cache/cache.ts index de5b5a2..edb14c5 100644 --- a/src/lib/cache/cache.ts +++ b/src/lib/cache/cache.ts @@ -1,38 +1,38 @@ -import { readFileSync, readdirSync } from "fs" -import path from "path" - -type CacheItem = { - buf: Uint8Array - type: "json" | "br" -} - -const cache = new Map() - -const dir = path.join(process.cwd(), "data") - -for (const f of readdirSync(dir)) { - const file = path.join(dir, f) - - if (f.endsWith(".json.br")) { - const name = f.replace(".json.br", "") - const buf = new Uint8Array(readFileSync(file)) - cache.set(name, { - buf, - type: "br" - }) - } - - if (f.endsWith(".json")) { - const name = f.replace(".json", "") - const buf = new Uint8Array(readFileSync(file)) - - cache.set(name, { - buf, - type: "json" - }) - } -} - -export function getDataCache(name: string) { - return cache.get(name) +import { readFileSync, readdirSync } from "fs" +import path from "path" + +type CacheItem = { + buf: Uint8Array + type: "json" | "br" +} + +const cache = new Map() + +const dir = path.join(process.cwd(), "data") + +for (const f of readdirSync(dir)) { + const file = path.join(dir, f) + + if (f.endsWith(".json.br")) { + const name = f.replace(".json.br", "") + const buf = new Uint8Array(readFileSync(file)) + cache.set(name, { + buf, + type: "br" + }) + } + + if (f.endsWith(".json")) { + const name = f.replace(".json", "") + const buf = new Uint8Array(readFileSync(file)) + + cache.set(name, { + buf, + type: "json" + }) + } +} + +export function getDataCache(name: string) { + return cache.get(name) } \ No newline at end of file diff --git a/src/lib/constant.ts b/src/lib/constant.ts index 071fb5d..d51a842 100644 --- a/src/lib/constant.ts +++ b/src/lib/constant.ts @@ -1,7 +1,7 @@ -export const listCurrentLanguage = { - ja: "JP", - ko: "KR", - en: "US", - vi: "VN", - zh: "CN" +export const listCurrentLanguage = { + ja: "JP", + ko: "KR", + en: "US", + vi: "VN", + zh: "CN" }; \ No newline at end of file diff --git a/src/lib/socket.ts b/src/lib/socket.ts index 262a855..ec9f6fe 100644 --- a/src/lib/socket.ts +++ b/src/lib/socket.ts @@ -1,255 +1,255 @@ -import { io, Socket } from "socket.io-client"; -import useSocketStore from "@/stores/socketSettingStore"; -import { toast } from "react-toastify"; -import useBattleDataStore from "@/stores/battleDataStore"; -import type { - AvatarAnalysisJson, - BattleBeginType, - BattleEndType, - DamageType, - EntityDefeatedType, - InitializeEnemyType, - SetBattleLineupType, - StatChangeType, - TurnBeginType, - TurnEndType, - UpdateCycleType, - UpdateTeamFormationType, - UpdateWaveType, - UseSkillType, - VersionType, -} from "@/types"; - -let socket: Socket | null = null; - -const notify = (msg: string, type: "info" | "success" | "error" = "info") => { - if (type === "success") toast.success(msg); - else if (type === "error") toast.error(msg); - else toast.info(msg); -}; - -interface SocketEvents { - Connected: VersionType; - OnBattleBegin: BattleBeginType; - OnSetBattleLineup: SetBattleLineupType; - OnDamage: DamageType; - OnTurnBegin: TurnBeginType; - OnTurnEnd: TurnEndType; - OnEntityDefeated: EntityDefeatedType; - OnUseSkill: UseSkillType; - OnUpdateWave: UpdateWaveType; - OnUpdateCycle: UpdateCycleType; - OnStatChange: StatChangeType; - OnUpdateTeamFormation: UpdateTeamFormationType; - OnInitializeEnemy: InitializeEnemyType; - OnBattleEnd: BattleEndType; - OnCreateBattle: AvatarAnalysisJson[]; - Error: string; -} - -type ListenerMap = { - [K in keyof SocketEvents]: (payload: SocketEvents[K]) => void; -}; - -let listeners: ListenerMap | null = null; - -let workerPool: Worker[] | null = null; -let nextWorker = 0; -let reqId = 1; -const pendingMap = new Map void>(); -let workerBlobUrl: string | null = null; - -const createWorkerPool = (size = Math.max(1, (navigator.hardwareConcurrency || 4) - 1)) => { - if (workerPool) return workerPool; - const code = `self.onmessage = function(e) { try { const { id, raw } = e.data; const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; self.postMessage({ id, parsed }); } catch (err) { self.postMessage({ id, err: err && err.message ? err.message : String(err) }); } }`; - - if (!workerBlobUrl) { - const blob = new Blob([code], { type: 'application/javascript' }); - workerBlobUrl = URL.createObjectURL(blob); - } - - workerPool = Array.from({ length: size }).map(() => new Worker(workerBlobUrl!)); - workerPool.forEach((w) => { - w.onmessage = (ev) => { - const { id, parsed, err } = ev.data as { id: number; parsed: unknown; err?: string }; - const cb = pendingMap.get(id); - if (cb) { - cb({ parsed: parsed ?? null, err }); - pendingMap.delete(id); - } - }; - w.onerror = (ev) => { - console.error('worker error', ev.message); - }; - }); - return workerPool; -}; - -const parseWithWorker = (raw: unknown): Promise<{ parsed: unknown | null; err?: string }> => { - if (!workerPool) createWorkerPool(); - if (!workerPool) return Promise.resolve({ parsed: null, err: 'no worker' }); - const id = reqId++; - return new Promise((resolve) => { - const timeout = setTimeout(() => { - pendingMap.delete(id); - resolve({ parsed: null, err: "worker timeout" }); - }, 3000); - - pendingMap.set(id, (res) => { - clearTimeout(timeout); - resolve(res); - }); - - const w = workerPool![nextWorker]; - nextWorker = (nextWorker + 1) % workerPool!.length; - try { - w.postMessage({ id, raw }); - } catch (err) { - pendingMap.delete(id); - resolve({ parsed: null, err: err && (err as Error).message ? (err as Error).message : String(err) }); - } - }); -}; - -let eventQueue: { name: keyof SocketEvents; data: unknown }[] = []; -let rafScheduled = false; -const MAX_QUEUE = 1000; - -const flushQueue = () => { - const items = eventQueue.splice(0, eventQueue.length); - if (items.length === 0) return; - const l = listeners; - if (!l) return; - for (const it of items) { - try { - l[it.name](it.data as never); - } catch (err) { - console.error('listener handler error', err); - } - } -}; - -const scheduleFlush = () => { - if (rafScheduled) return; - rafScheduled = true; - requestAnimationFrame(() => { - rafScheduled = false; - flushQueue(); - }); -}; - -const safeEnqueue = (name: keyof SocketEvents, data: unknown) => { - if (eventQueue.length > MAX_QUEUE) { - eventQueue.shift(); - } - eventQueue.push({ name, data }); - scheduleFlush(); -}; - -export const connectSocket = async (): Promise => { - const { host, port, connectionType, setStatus } = useSocketStore.getState(); - let url = `${host}:${port}`; - if (connectionType === "Native") url = "http://localhost:1305"; - else if (connectionType === "PS") url = "http://localhost:21000"; - - if (socket) socket.disconnect(); - - socket = io(url, { - reconnectionAttempts: 5, - timeout: 10000, - autoConnect: true, - }); - - socket.on("connect", () => { - setStatus(true); - notify(`Connected: ${socket?.id}`, "success"); - }); - - socket.on("disconnect", () => setStatus(false)); - socket.on("connect_error", () => setStatus(false)); - socket.on("connect_timeout", () => setStatus(false)); - socket.on("reconnect_failed", () => setStatus(false)); - - listeners = { - Connected: (payload) => useBattleDataStore.getState().onConnectedService(payload), - OnBattleBegin: (payload) => { - notify("Battle Started!", "info"); - useBattleDataStore.getState().onBattleBeginService(payload); - }, - OnSetBattleLineup: (payload) => useBattleDataStore.getState().onSetBattleLineupService(payload), - OnDamage: (payload) => useBattleDataStore.getState().onDamageService(payload), - OnTurnBegin: (payload) => useBattleDataStore.getState().onTurnBeginService(payload), - OnTurnEnd: (payload) => useBattleDataStore.getState().onTurnEndService(payload), - OnEntityDefeated: (payload) => useBattleDataStore.getState().onEntityDefeatedService(payload), - OnUseSkill: (payload) => useBattleDataStore.getState().onUseSkillService(payload), - OnUpdateWave: (payload) => useBattleDataStore.getState().onUpdateWaveService(payload), - OnUpdateCycle: (payload) => useBattleDataStore.getState().onUpdateCycleService(payload), - OnStatChange: (payload) => useBattleDataStore.getState().onStatChange(payload), - OnUpdateTeamFormation: (payload) => useBattleDataStore.getState().onUpdateTeamFormation(payload), - OnInitializeEnemy: (payload) => useBattleDataStore.getState().onInitializeEnemyService(payload), - OnBattleEnd: (payload) => useBattleDataStore.getState().onBattleEndService(payload), - OnCreateBattle: (payload) => useBattleDataStore.getState().onCreateBattleService(payload), - Error: (msg) => console.error("Server Error:", msg), - }; - - const register = (eventName: string) => { - socket?.on(eventName, async (raw: unknown) => { - if (!listeners) return; - if (workerPool) { - const res = await parseWithWorker(raw); - if (res.err) { - safeEnqueue("Error", `Worker parse error for ${eventName}: ${res.err}`); - } else if (res.parsed !== null) { - safeEnqueue(eventName as keyof SocketEvents, res.parsed); - } - } else { - try { - const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; - safeEnqueue(eventName as keyof SocketEvents, parsed); - } catch (err) { - safeEnqueue("Error", `Parse error for ${eventName}: ${err && (err as Error).message ? (err as Error).message : String(err)}`); - } - } - }); - }; - - Object.keys(listeners).forEach((k) => register(k)); - - createWorkerPool(); - - return socket; -}; - -export const disconnectSocket = (): void => { - if (!socket) return; - if (listeners) { - Object.keys(listeners).forEach((eventName) => { - socket?.off(eventName); - }); - } - if (workerBlobUrl) { - URL.revokeObjectURL(workerBlobUrl); - workerBlobUrl = null; - } - socket.disconnect(); - useSocketStore.getState().setStatus(false); - listeners = null; - if (workerPool) { - workerPool.forEach((w) => w.terminate()); - workerPool = null; - } - pendingMap.clear(); - eventQueue = []; -}; - -export const isSocketConnected = (): boolean => socket?.connected || false; -export const getSocket = (): Socket | null => socket; - -export const setWorkerPoolSize = (size: number) => { - if (workerPool) { - workerPool.forEach((w) => w.terminate()); - workerPool = null; - } - createWorkerPool(Math.max(1, size)); -}; - +import { io, Socket } from "socket.io-client"; +import useSocketStore from "@/stores/socketSettingStore"; +import { toast } from "react-toastify"; +import useBattleDataStore from "@/stores/battleDataStore"; +import type { + AvatarAnalysisJson, + BattleBeginType, + BattleEndType, + DamageType, + EntityDefeatedType, + InitializeEnemyType, + SetBattleLineupType, + StatChangeType, + TurnBeginType, + TurnEndType, + UpdateCycleType, + UpdateTeamFormationType, + UpdateWaveType, + UseSkillType, + VersionType, +} from "@/types"; + +let socket: Socket | null = null; + +const notify = (msg: string, type: "info" | "success" | "error" = "info") => { + if (type === "success") toast.success(msg); + else if (type === "error") toast.error(msg); + else toast.info(msg); +}; + +interface SocketEvents { + Connected: VersionType; + OnBattleBegin: BattleBeginType; + OnSetBattleLineup: SetBattleLineupType; + OnDamage: DamageType; + OnTurnBegin: TurnBeginType; + OnTurnEnd: TurnEndType; + OnEntityDefeated: EntityDefeatedType; + OnUseSkill: UseSkillType; + OnUpdateWave: UpdateWaveType; + OnUpdateCycle: UpdateCycleType; + OnStatChange: StatChangeType; + OnUpdateTeamFormation: UpdateTeamFormationType; + OnInitializeEnemy: InitializeEnemyType; + OnBattleEnd: BattleEndType; + OnCreateBattle: AvatarAnalysisJson[]; + Error: string; +} + +type ListenerMap = { + [K in keyof SocketEvents]: (payload: SocketEvents[K]) => void; +}; + +let listeners: ListenerMap | null = null; + +let workerPool: Worker[] | null = null; +let nextWorker = 0; +let reqId = 1; +const pendingMap = new Map void>(); +let workerBlobUrl: string | null = null; + +const createWorkerPool = (size = Math.max(1, (navigator.hardwareConcurrency || 4) - 1)) => { + if (workerPool) return workerPool; + const code = `self.onmessage = function(e) { try { const { id, raw } = e.data; const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; self.postMessage({ id, parsed }); } catch (err) { self.postMessage({ id, err: err && err.message ? err.message : String(err) }); } }`; + + if (!workerBlobUrl) { + const blob = new Blob([code], { type: 'application/javascript' }); + workerBlobUrl = URL.createObjectURL(blob); + } + + workerPool = Array.from({ length: size }).map(() => new Worker(workerBlobUrl!)); + workerPool.forEach((w) => { + w.onmessage = (ev) => { + const { id, parsed, err } = ev.data as { id: number; parsed: unknown; err?: string }; + const cb = pendingMap.get(id); + if (cb) { + cb({ parsed: parsed ?? null, err }); + pendingMap.delete(id); + } + }; + w.onerror = (ev) => { + console.error('worker error', ev.message); + }; + }); + return workerPool; +}; + +const parseWithWorker = (raw: unknown): Promise<{ parsed: unknown | null; err?: string }> => { + if (!workerPool) createWorkerPool(); + if (!workerPool) return Promise.resolve({ parsed: null, err: 'no worker' }); + const id = reqId++; + return new Promise((resolve) => { + const timeout = setTimeout(() => { + pendingMap.delete(id); + resolve({ parsed: null, err: "worker timeout" }); + }, 3000); + + pendingMap.set(id, (res) => { + clearTimeout(timeout); + resolve(res); + }); + + const w = workerPool![nextWorker]; + nextWorker = (nextWorker + 1) % workerPool!.length; + try { + w.postMessage({ id, raw }); + } catch (err) { + pendingMap.delete(id); + resolve({ parsed: null, err: err && (err as Error).message ? (err as Error).message : String(err) }); + } + }); +}; + +let eventQueue: { name: keyof SocketEvents; data: unknown }[] = []; +let rafScheduled = false; +const MAX_QUEUE = 1000; + +const flushQueue = () => { + const items = eventQueue.splice(0, eventQueue.length); + if (items.length === 0) return; + const l = listeners; + if (!l) return; + for (const it of items) { + try { + l[it.name](it.data as never); + } catch (err) { + console.error('listener handler error', err); + } + } +}; + +const scheduleFlush = () => { + if (rafScheduled) return; + rafScheduled = true; + requestAnimationFrame(() => { + rafScheduled = false; + flushQueue(); + }); +}; + +const safeEnqueue = (name: keyof SocketEvents, data: unknown) => { + if (eventQueue.length > MAX_QUEUE) { + eventQueue.shift(); + } + eventQueue.push({ name, data }); + scheduleFlush(); +}; + +export const connectSocket = async (): Promise => { + const { host, port, connectionType, setStatus } = useSocketStore.getState(); + let url = `${host}:${port}`; + if (connectionType === "Native") url = "http://localhost:1305"; + else if (connectionType === "PS") url = "http://localhost:21000"; + + if (socket) socket.disconnect(); + + socket = io(url, { + reconnectionAttempts: 5, + timeout: 10000, + autoConnect: true, + }); + + socket.on("connect", () => { + setStatus(true); + notify(`Connected: ${socket?.id}`, "success"); + }); + + socket.on("disconnect", () => setStatus(false)); + socket.on("connect_error", () => setStatus(false)); + socket.on("connect_timeout", () => setStatus(false)); + socket.on("reconnect_failed", () => setStatus(false)); + + listeners = { + Connected: (payload) => useBattleDataStore.getState().onConnectedService(payload), + OnBattleBegin: (payload) => { + notify("Battle Started!", "info"); + useBattleDataStore.getState().onBattleBeginService(payload); + }, + OnSetBattleLineup: (payload) => useBattleDataStore.getState().onSetBattleLineupService(payload), + OnDamage: (payload) => useBattleDataStore.getState().onDamageService(payload), + OnTurnBegin: (payload) => useBattleDataStore.getState().onTurnBeginService(payload), + OnTurnEnd: (payload) => useBattleDataStore.getState().onTurnEndService(payload), + OnEntityDefeated: (payload) => useBattleDataStore.getState().onEntityDefeatedService(payload), + OnUseSkill: (payload) => useBattleDataStore.getState().onUseSkillService(payload), + OnUpdateWave: (payload) => useBattleDataStore.getState().onUpdateWaveService(payload), + OnUpdateCycle: (payload) => useBattleDataStore.getState().onUpdateCycleService(payload), + OnStatChange: (payload) => useBattleDataStore.getState().onStatChange(payload), + OnUpdateTeamFormation: (payload) => useBattleDataStore.getState().onUpdateTeamFormation(payload), + OnInitializeEnemy: (payload) => useBattleDataStore.getState().onInitializeEnemyService(payload), + OnBattleEnd: (payload) => useBattleDataStore.getState().onBattleEndService(payload), + OnCreateBattle: (payload) => useBattleDataStore.getState().onCreateBattleService(payload), + Error: (msg) => console.error("Server Error:", msg), + }; + + const register = (eventName: string) => { + socket?.on(eventName, async (raw: unknown) => { + if (!listeners) return; + if (workerPool) { + const res = await parseWithWorker(raw); + if (res.err) { + safeEnqueue("Error", `Worker parse error for ${eventName}: ${res.err}`); + } else if (res.parsed !== null) { + safeEnqueue(eventName as keyof SocketEvents, res.parsed); + } + } else { + try { + const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; + safeEnqueue(eventName as keyof SocketEvents, parsed); + } catch (err) { + safeEnqueue("Error", `Parse error for ${eventName}: ${err && (err as Error).message ? (err as Error).message : String(err)}`); + } + } + }); + }; + + Object.keys(listeners).forEach((k) => register(k)); + + createWorkerPool(); + + return socket; +}; + +export const disconnectSocket = (): void => { + if (!socket) return; + if (listeners) { + Object.keys(listeners).forEach((eventName) => { + socket?.off(eventName); + }); + } + if (workerBlobUrl) { + URL.revokeObjectURL(workerBlobUrl); + workerBlobUrl = null; + } + socket.disconnect(); + useSocketStore.getState().setStatus(false); + listeners = null; + if (workerPool) { + workerPool.forEach((w) => w.terminate()); + workerPool = null; + } + pendingMap.clear(); + eventQueue = []; +}; + +export const isSocketConnected = (): boolean => socket?.connected || false; +export const getSocket = (): Socket | null => socket; + +export const setWorkerPoolSize = (size: number) => { + if (workerPool) { + workerPool.forEach((w) => w.terminate()); + workerPool = null; + } + createWorkerPool(Math.max(1, size)); +}; + diff --git a/src/stores/avatarDataStore.ts b/src/stores/avatarDataStore.ts index ac453e1..c20e640 100644 --- a/src/stores/avatarDataStore.ts +++ b/src/stores/avatarDataStore.ts @@ -1,30 +1,30 @@ -import { CharacterBasic, MonsterBasic } from '@/types'; -import { create } from 'zustand' - - -interface AvatarDataState { - listAvatar: CharacterBasic[]; - mapAvatar: Record; - listEnemy: MonsterBasic[]; - mapEnemy: Record; - setListAvatar: (list: CharacterBasic[]) => void; - setMapAvatar: (data: Record) => void; - setListEnemy: (list: MonsterBasic[]) => void; - setMapEnemy: (data: Record) => void; - - -} - -const useAvatarDataStore = create((set) => ({ - listAvatar: [], - listEnemy: [], - mapAvatar: {}, - mapEnemy: {}, - setListAvatar: (list: CharacterBasic[]) => set({ listAvatar: list }), - setMapAvatar: (data: Record) => set({ mapAvatar: data }), - setListEnemy: (list: MonsterBasic[]) => set({ listEnemy: list }), - setMapEnemy: (data: Record) => set({ mapEnemy: data }) - -})); - +import { CharacterBasic, MonsterBasic } from '@/types'; +import { create } from 'zustand' + + +interface AvatarDataState { + listAvatar: CharacterBasic[]; + mapAvatar: Record; + listEnemy: MonsterBasic[]; + mapEnemy: Record; + setListAvatar: (list: CharacterBasic[]) => void; + setMapAvatar: (data: Record) => void; + setListEnemy: (list: MonsterBasic[]) => void; + setMapEnemy: (data: Record) => void; + + +} + +const useAvatarDataStore = create((set) => ({ + listAvatar: [], + listEnemy: [], + mapAvatar: {}, + mapEnemy: {}, + setListAvatar: (list: CharacterBasic[]) => set({ listAvatar: list }), + setMapAvatar: (data: Record) => set({ mapAvatar: data }), + setListEnemy: (list: MonsterBasic[]) => set({ listEnemy: list }), + setMapEnemy: (data: Record) => set({ mapEnemy: data }) + +})); + export default useAvatarDataStore; \ No newline at end of file diff --git a/src/stores/battleDataStore.ts b/src/stores/battleDataStore.ts index 709724b..e78df35 100644 --- a/src/stores/battleDataStore.ts +++ b/src/stores/battleDataStore.ts @@ -1,369 +1,369 @@ -import { DamageType, AvatarAnalysisJson, UseSkillType, BattleBeginType, BattleEndType, DamageDetailType, EntityDefeatedType, SetBattleLineupType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType, VersionType, StatChangeType, UpdateTeamFormationType, Team, ParseAttackType } from '@/types'; -import { InitializeEnemyType } from '@/types/enemy'; -import { AvatarBattleInfo, AvatarInfo, BattleDataStateJson, EnemyInfo, SkillBattleInfo, TurnBattleInfo } from '@/types/mics'; -import { create } from 'zustand' - - -interface BattleDataState { - lineup: AvatarBattleInfo[]; - turnHistory: TurnBattleInfo[] - skillHistory: SkillBattleInfo[] - dataAvatar: AvatarAnalysisJson[] - totalAV: number; - totalDamage: number; - damagePerAV: number; - cycleIndex: number; - waveIndex: number; - maxWave: number; - maxCycle: number; - version?: string; - avatarDetail?: Record; - enemyDetail?: Record; - - onConnectedService: (data: VersionType) => void - onBattleBeginService: (data: BattleBeginType) => void; - onSetBattleLineupService: (data: SetBattleLineupType) => void; - onDamageService: (data: DamageType) => void; - onTurnBeginService: (data: TurnBeginType) => void; - onTurnEndService: (data: TurnEndType) => void; - onEntityDefeatedService: (data: EntityDefeatedType) => void; - onUseSkillService: (data: UseSkillType) => void; - onUpdateWaveService: (data: UpdateWaveType) => 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; -} - -const useBattleDataStore = create((set, get) => ({ - lineup: [], - turnHistory: [], - skillHistory: [], - dataAvatar: [], - totalAV: 0, - totalDamage: 0, - damagePerAV: 0, - cycleIndex: 0, - waveIndex: 1, - maxWave: Infinity, - maxCycle: Infinity, - version: undefined, - avatarDetail: undefined, - enemyDetail: undefined, - loadBattleDataFromJSON: (data: BattleDataStateJson) => { - const skillHistory = data.skillHistory.map(it => { - it.damageDetail = it.damageDetail.map(it => { - return { - ...it, - damage_type: ParseAttackType(it.damage_type) - } - }) - return it - }) - set({ - lineup: data.lineup, - turnHistory: data.turnHistory, - skillHistory: skillHistory, - dataAvatar: data.dataAvatar, - totalAV: data.totalAV, - totalDamage: data.totalDamage, - damagePerAV: data.damagePerAV, - cycleIndex: data.cycleIndex, - waveIndex: data.waveIndex, - maxWave: data.maxWave, - maxCycle: data.maxCycle, - version: data.version, - avatarDetail: data.avatarDetail, - enemyDetail: data.enemyDetail - }) - }, - onConnectedService: (data: VersionType) => { - set({ - version: data.version - }) - }, - onCreateBattleService: (data: AvatarAnalysisJson[]) => { - set({ - dataAvatar: data - }) - }, - onBattleBeginService: (data: BattleBeginType) => { - set({ - maxWave: data.max_waves, - maxCycle: data.max_cycles - }) - }, - - onSetBattleLineupService: (data: SetBattleLineupType) => { - const lineups: AvatarBattleInfo[] = [] - for (const avatar of data.avatars) { - lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo) - } - set(() => ({ - lineup: lineups, - turnHistory: [{ - avatarId: -1, - actionValue: 0, - waveIndex: 1, - cycleIndex: 0, - } as TurnBattleInfo], - skillHistory: [], - totalAV: 0, - totalDamage: 0, - damagePerAV: 0, - cycleIndex: 0, - maxWave: Infinity, - maxCycle: Infinity, - waveIndex: 1, - })); - }, - - 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, - overkill_damage: data.overkill_damage, - damage_type: ParseAttackType(data.damage_type ? data.damage_type : data.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) => ({ - totalAV: data.action_value, - damagePerAV: state.totalDamage / (data.action_value === 0 ? 1 : data.action_value), - turnHistory: [...state.turnHistory, { - avatarId: data?.turn_owner?.uid, - actionValue: data.action_value, - waveIndex: state.waveIndex, - cycleIndex: state.cycleIndex - } as TurnBattleInfo] - })) - }, - - onTurnEndService: (data: TurnEndType) => { - set((state) => ({ - totalDamage: state.totalDamage === data.turn_info.total_damage ? data.turn_info.total_damage : state.totalDamage, - currentAV: data.turn_info.action_value, - damagePerAV: (state.totalDamage === data.turn_info.total_damage ? data.turn_info.total_damage : state.totalDamage) - / (data.turn_info.action_value === 0 ? 1 : data.turn_info.action_value) - })); - }, - - onEntityDefeatedService: (data: EntityDefeatedType) => { - let avatarDetail = get().avatarDetail - let enemyDetail = get().enemyDetail - if (!enemyDetail) { - enemyDetail = {} as Record - } - if (!avatarDetail) { - avatarDetail = {} as Record - } - 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 - } - set({ - avatarDetail: avatarDetail, - enemyDetail: enemyDetail - }) - }, - - onUseSkillService: (data: UseSkillType) => { - set((state) => ({ - skillHistory: [...state.skillHistory, { - avatarId: data.avatar.uid, - damageDetail: [], - totalDamage: 0, - skillType: ParseAttackType(data.skill.type), - skillName: data.skill.name, - turnBattleId: state.turnHistory.length - 1 - } 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 - } - if (!avatarDetail) { - avatarDetail = {} as Record - } - - let key: string; - let value: number; - if (data.property) { - key = data.property.type; - value = data.property.value; - } else if (data.stat) { - if ( - data.stat && - typeof data.stat === 'object' && - 'type' in data.stat && - 'value' in data.stat && - typeof data.stat.type === 'string' - ) { - key = data.stat.type; - value = Number(data.stat.value); - } else { - const entries = Object.entries(data.stat); - if (entries.length === 0) return; - [key, value] = entries[0] as [string, number]; - } - } else { - return; - } - - if (key === "CurrentHP") key = "HP"; - - if (data.entity.team === "Player") { - 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: { [key]: value }, - turnBattleId: get().turnHistory.length - 1 - }) - } else { - 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: { [key]: value }, - turnBattleId: get().turnHistory.length - 1 - }) - } - set({ - avatarDetail: avatarDetail, - enemyDetail: enemyDetail - }) - }, - - onUpdateTeamFormation: (data: UpdateTeamFormationType) => { - let avatarDetail = get().avatarDetail - let enemyDetail = get().enemyDetail - if (!enemyDetail) { - enemyDetail = {} as Record - } - - if (!avatarDetail) { - avatarDetail = {} as Record - } - if (data.team === Team.Enemy) { - for (let i = 0; i < data.entities.length; i++) { - const entity = data.entities[i]; - if (entity.team === Team.Enemy && enemyDetail?.[entity.uid]) { - enemyDetail[entity.uid].positionIndex = i - enemyDetail[entity.uid].waveIndex = get().waveIndex - } - } - } - - - set({ - avatarDetail: avatarDetail, - enemyDetail: enemyDetail - }) - }, - - onInitializeEnemyService: (data: InitializeEnemyType) => { - const enemyDetail = get().enemyDetail - if (!enemyDetail) { - return - } - let maxHP = 0; - let level = 0; - if ('properties' in data.enemy.base_stats) { - maxHP = data.enemy.base_stats.properties["MaxHP"] || 0; - level = data.enemy.base_stats.properties["Level"] || 0; - } else { - maxHP = data.enemy.base_stats.hp || data.enemy.base_stats.CurrentHP || 0; - level = data.enemy.base_stats.level; - } - - enemyDetail[data.enemy.uid] = { - id: data.enemy.id, - isDie: false, - killer_uid: -1, - positionIndex: enemyDetail?.[data.enemy.uid]?.positionIndex || 0, - waveIndex: get().waveIndex, - name: data.enemy.name, - maxHP: maxHP, - level: level, - stats: {}, - statsHistory: [] - } - set({ - enemyDetail: enemyDetail - }) - }, - - onBattleEndService: (data: BattleEndType) => { - const lineups: AvatarBattleInfo[] = [] - for (const avatar of data.avatars) { - lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo) - } - set({ - lineup: lineups, - totalDamage: data.total_damage, - totalAV: data.action_value, - damagePerAV: data.total_damage / (data.action_value === 0 ? 1 : data.action_value) - }) - }, -})); - +import { DamageType, AvatarAnalysisJson, UseSkillType, BattleBeginType, BattleEndType, DamageDetailType, EntityDefeatedType, SetBattleLineupType, TurnBeginType, TurnEndType, UpdateCycleType, UpdateWaveType, VersionType, StatChangeType, UpdateTeamFormationType, Team, ParseAttackType } from '@/types'; +import { InitializeEnemyType } from '@/types/enemy'; +import { AvatarBattleInfo, AvatarInfo, BattleDataStateJson, EnemyInfo, SkillBattleInfo, TurnBattleInfo } from '@/types/mics'; +import { create } from 'zustand' + + +interface BattleDataState { + lineup: AvatarBattleInfo[]; + turnHistory: TurnBattleInfo[] + skillHistory: SkillBattleInfo[] + dataAvatar: AvatarAnalysisJson[] + totalAV: number; + totalDamage: number; + damagePerAV: number; + cycleIndex: number; + waveIndex: number; + maxWave: number; + maxCycle: number; + version?: string; + avatarDetail?: Record; + enemyDetail?: Record; + + onConnectedService: (data: VersionType) => void + onBattleBeginService: (data: BattleBeginType) => void; + onSetBattleLineupService: (data: SetBattleLineupType) => void; + onDamageService: (data: DamageType) => void; + onTurnBeginService: (data: TurnBeginType) => void; + onTurnEndService: (data: TurnEndType) => void; + onEntityDefeatedService: (data: EntityDefeatedType) => void; + onUseSkillService: (data: UseSkillType) => void; + onUpdateWaveService: (data: UpdateWaveType) => 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; +} + +const useBattleDataStore = create((set, get) => ({ + lineup: [], + turnHistory: [], + skillHistory: [], + dataAvatar: [], + totalAV: 0, + totalDamage: 0, + damagePerAV: 0, + cycleIndex: 0, + waveIndex: 1, + maxWave: Infinity, + maxCycle: Infinity, + version: undefined, + avatarDetail: undefined, + enemyDetail: undefined, + loadBattleDataFromJSON: (data: BattleDataStateJson) => { + const skillHistory = data.skillHistory.map(it => { + it.damageDetail = it.damageDetail.map(it => { + return { + ...it, + damage_type: ParseAttackType(it.damage_type) + } + }) + return it + }) + set({ + lineup: data.lineup, + turnHistory: data.turnHistory, + skillHistory: skillHistory, + dataAvatar: data.dataAvatar, + totalAV: data.totalAV, + totalDamage: data.totalDamage, + damagePerAV: data.damagePerAV, + cycleIndex: data.cycleIndex, + waveIndex: data.waveIndex, + maxWave: data.maxWave, + maxCycle: data.maxCycle, + version: data.version, + avatarDetail: data.avatarDetail, + enemyDetail: data.enemyDetail + }) + }, + onConnectedService: (data: VersionType) => { + set({ + version: data.version + }) + }, + onCreateBattleService: (data: AvatarAnalysisJson[]) => { + set({ + dataAvatar: data + }) + }, + onBattleBeginService: (data: BattleBeginType) => { + set({ + maxWave: data.max_waves, + maxCycle: data.max_cycles + }) + }, + + onSetBattleLineupService: (data: SetBattleLineupType) => { + const lineups: AvatarBattleInfo[] = [] + for (const avatar of data.avatars) { + lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo) + } + set(() => ({ + lineup: lineups, + turnHistory: [{ + avatarId: -1, + actionValue: 0, + waveIndex: 1, + cycleIndex: 0, + } as TurnBattleInfo], + skillHistory: [], + totalAV: 0, + totalDamage: 0, + damagePerAV: 0, + cycleIndex: 0, + maxWave: Infinity, + maxCycle: Infinity, + waveIndex: 1, + })); + }, + + 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, + overkill_damage: data.overkill_damage, + damage_type: ParseAttackType(data.damage_type ? data.damage_type : data.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) => ({ + totalAV: data.action_value, + damagePerAV: state.totalDamage / (data.action_value === 0 ? 1 : data.action_value), + turnHistory: [...state.turnHistory, { + avatarId: data?.turn_owner?.uid, + actionValue: data.action_value, + waveIndex: state.waveIndex, + cycleIndex: state.cycleIndex + } as TurnBattleInfo] + })) + }, + + onTurnEndService: (data: TurnEndType) => { + set((state) => ({ + totalDamage: state.totalDamage === data.turn_info.total_damage ? data.turn_info.total_damage : state.totalDamage, + currentAV: data.turn_info.action_value, + damagePerAV: (state.totalDamage === data.turn_info.total_damage ? data.turn_info.total_damage : state.totalDamage) + / (data.turn_info.action_value === 0 ? 1 : data.turn_info.action_value) + })); + }, + + onEntityDefeatedService: (data: EntityDefeatedType) => { + let avatarDetail = get().avatarDetail + let enemyDetail = get().enemyDetail + if (!enemyDetail) { + enemyDetail = {} as Record + } + if (!avatarDetail) { + avatarDetail = {} as Record + } + 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 + } + set({ + avatarDetail: avatarDetail, + enemyDetail: enemyDetail + }) + }, + + onUseSkillService: (data: UseSkillType) => { + set((state) => ({ + skillHistory: [...state.skillHistory, { + avatarId: data.avatar.uid, + damageDetail: [], + totalDamage: 0, + skillType: ParseAttackType(data.skill.type), + skillName: data.skill.name, + turnBattleId: state.turnHistory.length - 1 + } 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 + } + if (!avatarDetail) { + avatarDetail = {} as Record + } + + let key: string; + let value: number; + if (data.property) { + key = data.property.type; + value = data.property.value; + } else if (data.stat) { + if ( + data.stat && + typeof data.stat === 'object' && + 'type' in data.stat && + 'value' in data.stat && + typeof data.stat.type === 'string' + ) { + key = data.stat.type; + value = Number(data.stat.value); + } else { + const entries = Object.entries(data.stat); + if (entries.length === 0) return; + [key, value] = entries[0] as [string, number]; + } + } else { + return; + } + + if (key === "CurrentHP") key = "HP"; + + if (data.entity.team === "Player") { + 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: { [key]: value }, + turnBattleId: get().turnHistory.length - 1 + }) + } else { + 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: { [key]: value }, + turnBattleId: get().turnHistory.length - 1 + }) + } + set({ + avatarDetail: avatarDetail, + enemyDetail: enemyDetail + }) + }, + + onUpdateTeamFormation: (data: UpdateTeamFormationType) => { + let avatarDetail = get().avatarDetail + let enemyDetail = get().enemyDetail + if (!enemyDetail) { + enemyDetail = {} as Record + } + + if (!avatarDetail) { + avatarDetail = {} as Record + } + if (data.team === Team.Enemy) { + for (let i = 0; i < data.entities.length; i++) { + const entity = data.entities[i]; + if (entity.team === Team.Enemy && enemyDetail?.[entity.uid]) { + enemyDetail[entity.uid].positionIndex = i + enemyDetail[entity.uid].waveIndex = get().waveIndex + } + } + } + + + set({ + avatarDetail: avatarDetail, + enemyDetail: enemyDetail + }) + }, + + onInitializeEnemyService: (data: InitializeEnemyType) => { + const enemyDetail = get().enemyDetail + if (!enemyDetail) { + return + } + let maxHP = 0; + let level = 0; + if ('properties' in data.enemy.base_stats) { + maxHP = data.enemy.base_stats.properties["MaxHP"] || 0; + level = data.enemy.base_stats.properties["Level"] || 0; + } else { + maxHP = data.enemy.base_stats.hp || data.enemy.base_stats.CurrentHP || 0; + level = data.enemy.base_stats.level; + } + + enemyDetail[data.enemy.uid] = { + id: data.enemy.id, + isDie: false, + killer_uid: -1, + positionIndex: enemyDetail?.[data.enemy.uid]?.positionIndex || 0, + waveIndex: get().waveIndex, + name: data.enemy.name, + maxHP: maxHP, + level: level, + stats: {}, + statsHistory: [] + } + set({ + enemyDetail: enemyDetail + }) + }, + + onBattleEndService: (data: BattleEndType) => { + const lineups: AvatarBattleInfo[] = [] + for (const avatar of data.avatars) { + lineups.push({ avatarId: avatar.id, isDie: false } as AvatarBattleInfo) + } + set({ + lineup: lineups, + totalDamage: data.total_damage, + totalAV: data.action_value, + damagePerAV: data.total_damage / (data.action_value === 0 ? 1 : data.action_value) + }) + }, +})); + export default useBattleDataStore; \ No newline at end of file diff --git a/src/stores/localeStore.ts b/src/stores/localeStore.ts index 75183a7..fc7557b 100644 --- a/src/stores/localeStore.ts +++ b/src/stores/localeStore.ts @@ -1,25 +1,25 @@ -import { create } from 'zustand' -import { createJSONStorage, persist } from 'zustand/middleware'; - - -interface LocaleState { - locale: string; - theme: string - setTheme: (newTheme: string) => void; - setLocale: (newLocale: string) => void; -} - -const useLocaleStore = create()(persist( - (set) => ({ - locale: "en", - theme: "night", - setTheme: (newTheme: string) => set({ theme: newTheme }), - setLocale: (newLocale: string) => set({ locale: newLocale }), - }), - { - name: 'locale-storage', - storage: createJSONStorage(() => localStorage), - } -)); - +import { create } from 'zustand' +import { createJSONStorage, persist } from 'zustand/middleware'; + + +interface LocaleState { + locale: string; + theme: string + setTheme: (newTheme: string) => void; + setLocale: (newLocale: string) => void; +} + +const useLocaleStore = create()(persist( + (set) => ({ + locale: "en", + theme: "night", + setTheme: (newTheme: string) => set({ theme: newTheme }), + setLocale: (newLocale: string) => set({ locale: newLocale }), + }), + { + name: 'locale-storage', + storage: createJSONStorage(() => localStorage), + } +)); + export default useLocaleStore; \ No newline at end of file diff --git a/src/stores/socketSettingStore.ts b/src/stores/socketSettingStore.ts index 209ed4d..f442af6 100644 --- a/src/stores/socketSettingStore.ts +++ b/src/stores/socketSettingStore.ts @@ -1,33 +1,33 @@ -import { create } from 'zustand'; -import { persist } from 'zustand/middleware'; - -interface SocketState { - host: string; - port: number; - status: boolean; - connectionType: string; - setHost: (host: string) => void; - setConnectionType: (host: string) => void; - setPort: (port: number) => void; - setStatus: (status: boolean) => void; -} - -const useSocketStore = create()( - persist( - (set) => ({ - host: "http://localhost", - port: 3443, - status: false, - connectionType: "Native", - setHost: (host: string) => set({ host }), - setConnectionType: (connectionType: string) => set({ connectionType }), - setPort: (port: number) => set({ port }), - setStatus: (status: boolean) => set({ status }), - }), - { - name: 'socket-storage', - } - ) -); - -export default useSocketStore; +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; + +interface SocketState { + host: string; + port: number; + status: boolean; + connectionType: string; + setHost: (host: string) => void; + setConnectionType: (host: string) => void; + setPort: (port: number) => void; + setStatus: (status: boolean) => void; +} + +const useSocketStore = create()( + persist( + (set) => ({ + host: "http://localhost", + port: 3443, + status: false, + connectionType: "Native", + setHost: (host: string) => set({ host }), + setConnectionType: (connectionType: string) => set({ connectionType }), + setPort: (port: number) => set({ port }), + setStatus: (status: boolean) => set({ status }), + }), + { + name: 'socket-storage', + } + ) +); + +export default useSocketStore; diff --git a/src/types/attack.ts b/src/types/attack.ts index a94343b..1415347 100644 --- a/src/types/attack.ts +++ b/src/types/attack.ts @@ -1,101 +1,101 @@ -import { EntityType } from "./entity"; - -export interface DamageType { - attacker: EntityType; - damage: number; - overkill_damage?: number; - damage_type?: AttackType | string; - type?: AttackType | string; -} - -export interface DamageDetailType { - damage: number; - overkill_damage?: number; - damage_type?: AttackType; -} - - -export enum AttackType { - Unknown = 0, - Normal = 1, - BPSkill = 2, - Ultra = 3, - QTE = 4, - DOT = 5, - Pursued = 6, - Maze = 7, - MazeNormal = 8, - Insert = 9, - ElementDamage = 10, - Level = 11, - Servant = 12, - TrueDamage = 13, - ElationDamage = 14 -} - - -const attackTypeMap: Record = { - Talent: AttackType.Unknown, - Basic: AttackType.Normal, - Skill: AttackType.BPSkill, - Ultimate: AttackType.Ultra, - QTE: AttackType.QTE, - DOT: AttackType.DOT, - DoT: AttackType.DOT, - Pursued: AttackType.Pursued, - Additional: AttackType.Pursued, - Technique: AttackType.Maze, - MazeNormal: AttackType.MazeNormal, - "Follow-up": AttackType.Insert, - "Follow-Up": AttackType.Insert, - "Elemental Damage": AttackType.ElementDamage, - Break: AttackType.ElementDamage, - Level: AttackType.Level, - Servant: AttackType.Servant, - "True Damage": AttackType.TrueDamage, - True: AttackType.TrueDamage, - "Elation Damage": AttackType.ElationDamage, - Elation: AttackType.ElationDamage, -}; - -export function ParseAttackType(type: AttackType | string | undefined): AttackType { - if (type === undefined || type === null) { - return AttackType.Unknown; - } - - if (typeof type === "number") { - return type in AttackType ? type : AttackType.Unknown; - } - - const num = Number(type); - if (!isNaN(num)) { - return num in AttackType ? num as AttackType : AttackType.Unknown; - } - - return attackTypeMap[type] ?? AttackType.Unknown; -} - -export function attackTypeToString(type: AttackType | undefined): string { - if (type === undefined) { - return "" - } - - switch (type) { - case AttackType.Unknown: return "Talent"; - case AttackType.Normal: return "Basic"; - case AttackType.BPSkill: return "Skill"; - case AttackType.Ultra: return "Ultimate"; - case AttackType.QTE: return "QTE"; - case AttackType.DOT: return "DOT"; - case AttackType.Pursued: return "Pursued"; - case AttackType.Maze: return "Technique"; - case AttackType.MazeNormal: return "MazeNormal"; - case AttackType.Insert: return "Follow-up"; - case AttackType.ElementDamage: return "Elemental Damage"; - case AttackType.Level: return "Level"; - case AttackType.Servant: return "Servant"; - case AttackType.TrueDamage: return "True Damage"; - case AttackType.ElationDamage: return "Elation Damage"; - default: return "Unknown"; - } -} +import { EntityType } from "./entity"; + +export interface DamageType { + attacker: EntityType; + damage: number; + overkill_damage?: number; + damage_type?: AttackType | string; + type?: AttackType | string; +} + +export interface DamageDetailType { + damage: number; + overkill_damage?: number; + damage_type?: AttackType; +} + + +export enum AttackType { + Unknown = 0, + Normal = 1, + BPSkill = 2, + Ultra = 3, + QTE = 4, + DOT = 5, + Pursued = 6, + Maze = 7, + MazeNormal = 8, + Insert = 9, + ElementDamage = 10, + Level = 11, + Servant = 12, + TrueDamage = 13, + ElationDamage = 14 +} + + +const attackTypeMap: Record = { + Talent: AttackType.Unknown, + Basic: AttackType.Normal, + Skill: AttackType.BPSkill, + Ultimate: AttackType.Ultra, + QTE: AttackType.QTE, + DOT: AttackType.DOT, + DoT: AttackType.DOT, + Pursued: AttackType.Pursued, + Additional: AttackType.Pursued, + Technique: AttackType.Maze, + MazeNormal: AttackType.MazeNormal, + "Follow-up": AttackType.Insert, + "Follow-Up": AttackType.Insert, + "Elemental Damage": AttackType.ElementDamage, + Break: AttackType.ElementDamage, + Level: AttackType.Level, + Servant: AttackType.Servant, + "True Damage": AttackType.TrueDamage, + True: AttackType.TrueDamage, + "Elation Damage": AttackType.ElationDamage, + Elation: AttackType.ElationDamage, +}; + +export function ParseAttackType(type: AttackType | string | undefined): AttackType { + if (type === undefined || type === null) { + return AttackType.Unknown; + } + + if (typeof type === "number") { + return type in AttackType ? type : AttackType.Unknown; + } + + const num = Number(type); + if (!isNaN(num)) { + return num in AttackType ? num as AttackType : AttackType.Unknown; + } + + return attackTypeMap[type] ?? AttackType.Unknown; +} + +export function attackTypeToString(type: AttackType | undefined): string { + if (type === undefined) { + return "" + } + + switch (type) { + case AttackType.Unknown: return "Talent"; + case AttackType.Normal: return "Basic"; + case AttackType.BPSkill: return "Skill"; + case AttackType.Ultra: return "Ultimate"; + case AttackType.QTE: return "QTE"; + case AttackType.DOT: return "DOT"; + case AttackType.Pursued: return "Pursued"; + case AttackType.Maze: return "Technique"; + case AttackType.MazeNormal: return "MazeNormal"; + case AttackType.Insert: return "Follow-up"; + case AttackType.ElementDamage: return "Elemental Damage"; + case AttackType.Level: return "Level"; + case AttackType.Servant: return "Servant"; + case AttackType.TrueDamage: return "True Damage"; + case AttackType.ElationDamage: return "Elation Damage"; + default: return "Unknown"; + } +} diff --git a/src/types/avatar.ts b/src/types/avatar.ts index 32503a0..c8ebe74 100644 --- a/src/types/avatar.ts +++ b/src/types/avatar.ts @@ -1,8 +1,8 @@ -export interface CharacterBasic { - id: string; - icon: string; - rank: string; - baseType: string; - damageType: string; - lang: Record; -} +export interface CharacterBasic { + id: string; + icon: string; + rank: string; + baseType: string; + damageType: string; + lang: Record; +} diff --git a/src/types/battle.ts b/src/types/battle.ts index 79b7bdc..84a415e 100644 --- a/src/types/battle.ts +++ b/src/types/battle.ts @@ -1,18 +1,18 @@ -import { AvatarType } from "./lineup"; -import { TurnInfoType } from "./turn"; - -export interface BattleEndType { - avatars: AvatarType[]; - turn_history: TurnInfoType[]; - av_history: TurnInfoType[]; - turn_count: number; - total_damage: number; - action_value: number; - stage_id: number; -} - -export interface BattleBeginType { - max_waves: number - max_cycles: number - stage_id: number +import { AvatarType } from "./lineup"; +import { TurnInfoType } from "./turn"; + +export interface BattleEndType { + avatars: AvatarType[]; + turn_history: TurnInfoType[]; + av_history: TurnInfoType[]; + turn_count: number; + total_damage: number; + action_value: number; + stage_id: number; +} + +export interface BattleBeginType { + max_waves: number + max_cycles: number + stage_id: number } \ No newline at end of file diff --git a/src/types/enemy.ts b/src/types/enemy.ts index 292a8ad..ca470ea 100644 --- a/src/types/enemy.ts +++ b/src/types/enemy.ts @@ -1,26 +1,26 @@ -import { StatsType } from "./stat"; - -export interface BattleStatsType { - properties: Record; -} - -export interface EnemyType { - id: number; - uid: number; - name: string; - base_stats: BattleStatsType | StatsType; -} - -export interface InitializeEnemyType { - enemy: EnemyType -} - -export interface MonsterBasic { - id: string; - rank: string; - icon: string; - image: string; - weak: string[]; - desc: Record; - lang: Record; -} +import { StatsType } from "./stat"; + +export interface BattleStatsType { + properties: Record; +} + +export interface EnemyType { + id: number; + uid: number; + name: string; + base_stats: BattleStatsType | StatsType; +} + +export interface InitializeEnemyType { + enemy: EnemyType +} + +export interface MonsterBasic { + id: string; + rank: string; + icon: string; + image: string; + weak: string[]; + desc: Record; + lang: Record; +} diff --git a/src/types/entity.ts b/src/types/entity.ts index 09dd1d1..b2c780b 100644 --- a/src/types/entity.ts +++ b/src/types/entity.ts @@ -1,11 +1,11 @@ -import { Team } from "./team"; - -export interface EntityType { - uid: number; - team: Team; -} - -export interface EntityDefeatedType { - killer: EntityType, - entity_defeated: EntityType +import { Team } from "./team"; + +export interface EntityType { + uid: number; + team: Team; +} + +export interface EntityDefeatedType { + killer: EntityType, + entity_defeated: EntityType } \ No newline at end of file diff --git a/src/types/error.ts b/src/types/error.ts index ac8fd6f..2591b0b 100644 --- a/src/types/error.ts +++ b/src/types/error.ts @@ -1,3 +1,3 @@ -export interface ErrorType { - msg: string +export interface ErrorType { + msg: string } \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 9602ad6..8325890 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,13 +1,13 @@ -export * from "./attack" -export * from "./avatar" -export * from "./battle" -export * from "./lineup" -export * from "./skill" -export * from "./turn" -export * from "./waveAndCycle" -export * from "./srtools" -export * from "./version" -export * from "./entity" -export * from "./stat" -export * from "./enemy" -export * from "./team" +export * from "./attack" +export * from "./avatar" +export * from "./battle" +export * from "./lineup" +export * from "./skill" +export * from "./turn" +export * from "./waveAndCycle" +export * from "./srtools" +export * from "./version" +export * from "./entity" +export * from "./stat" +export * from "./enemy" +export * from "./team" diff --git a/src/types/lineup.ts b/src/types/lineup.ts index b35a078..2d31f98 100644 --- a/src/types/lineup.ts +++ b/src/types/lineup.ts @@ -1,17 +1,17 @@ -import { EntityType } from "./entity"; -import { Team } from "./team"; - -export interface AvatarType{ - id: number; - name: string; -} - -export interface SetBattleLineupType { - avatars: AvatarType[]; -} - - -export interface UpdateTeamFormationType { - entities: EntityType[], - team: Team +import { EntityType } from "./entity"; +import { Team } from "./team"; + +export interface AvatarType{ + id: number; + name: string; +} + +export interface SetBattleLineupType { + avatars: AvatarType[]; +} + + +export interface UpdateTeamFormationType { + entities: EntityType[], + team: Team } \ No newline at end of file diff --git a/src/types/mics.ts b/src/types/mics.ts index 5529134..9a2ac99 100644 --- a/src/types/mics.ts +++ b/src/types/mics.ts @@ -1,68 +1,68 @@ -import {AttackType, DamageDetailType} from "./attack"; -import { AvatarAnalysisJson } from "./srtools"; -import { StatType } from "./stat"; - - -export interface AvatarBattleInfo { - avatarId: number; - isDie?: boolean; -} - -export interface SkillBattleInfo { - avatarId: number; - damageDetail: DamageDetailType[]; - totalDamage: number; - skillType: AttackType; - skillName: string; - turnBattleId: number; -} - -export interface TurnBattleInfo { - avatarId: number; - actionValue: number; - waveIndex: number; - cycleIndex: number; -} - -export interface BattleDataStateJson { - lineup: AvatarBattleInfo[]; - turnHistory: TurnBattleInfo[] - skillHistory: SkillBattleInfo[] - dataAvatar: AvatarAnalysisJson[] - totalAV: number; - totalDamage: number; - damagePerAV: number; - maxWave: number; - cycleIndex: number, - waveIndex: number, - maxCycle: number, - version?: string, - avatarDetail?: Record; - enemyDetail?: Record; -} - -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; - statsHistory: StatsHistoryType[]; -} -export interface AvatarInfo { - id: number; - isDie: boolean; - killer_uid: number; - stats: Record; - statsHistory: StatsHistoryType[]; -} - +import {AttackType, DamageDetailType} from "./attack"; +import { AvatarAnalysisJson } from "./srtools"; +import { StatType } from "./stat"; + + +export interface AvatarBattleInfo { + avatarId: number; + isDie?: boolean; +} + +export interface SkillBattleInfo { + avatarId: number; + damageDetail: DamageDetailType[]; + totalDamage: number; + skillType: AttackType; + skillName: string; + turnBattleId: number; +} + +export interface TurnBattleInfo { + avatarId: number; + actionValue: number; + waveIndex: number; + cycleIndex: number; +} + +export interface BattleDataStateJson { + lineup: AvatarBattleInfo[]; + turnHistory: TurnBattleInfo[] + skillHistory: SkillBattleInfo[] + dataAvatar: AvatarAnalysisJson[] + totalAV: number; + totalDamage: number; + damagePerAV: number; + maxWave: number; + cycleIndex: number, + waveIndex: number, + maxCycle: number, + version?: string, + avatarDetail?: Record; + enemyDetail?: Record; +} + +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; + statsHistory: StatsHistoryType[]; +} +export interface AvatarInfo { + id: number; + isDie: boolean; + killer_uid: number; + stats: Record; + statsHistory: StatsHistoryType[]; +} + diff --git a/src/types/skill.ts b/src/types/skill.ts index a579fc7..12d02c9 100644 --- a/src/types/skill.ts +++ b/src/types/skill.ts @@ -1,14 +1,14 @@ - -import { AttackType } from "@/types/attack"; -import { EntityType } from "./entity"; - -export interface SkillInfo { - name: string; - type: AttackType | string; - skill_config_id: number; -} - -export interface UseSkillType { - avatar: EntityType; - skill: SkillInfo; + +import { AttackType } from "@/types/attack"; +import { EntityType } from "./entity"; + +export interface SkillInfo { + name: string; + type: AttackType | string; + skill_config_id: number; +} + +export interface UseSkillType { + avatar: EntityType; + skill: SkillInfo; } \ No newline at end of file diff --git a/src/types/srtools.ts b/src/types/srtools.ts index 0fce82e..b18dc44 100644 --- a/src/types/srtools.ts +++ b/src/types/srtools.ts @@ -1,42 +1,42 @@ -export interface AvatarAnalysisJson { - owner_uid: number; - avatar_id: number; - data: AvatarData | null; - level: number; - promotion: number; - techniques: number[]; - relics: RelicJson[]; - Lightcone: LightconeJson | null; - sp_value: number; - sp_max: number; -} - -export interface AvatarData { - rank: number; - skills: Record; -} - -export interface SubAffix { - sub_affix_id: number; - count: number; - step: number; -} - -export interface RelicJson { - level: number; - relic_id: number; - relic_set_id: number; - main_affix_id: number; - sub_affixes: SubAffix[]; - internal_uid: number; - equip_avatar: number; -} - -export interface LightconeJson { - level: number; - item_id: number; - equip_avatar: number; - rank: number; - promotion: number; - internal_uid: number; -} +export interface AvatarAnalysisJson { + owner_uid: number; + avatar_id: number; + data: AvatarData | null; + level: number; + promotion: number; + techniques: number[]; + relics: RelicJson[]; + Lightcone: LightconeJson | null; + sp_value: number; + sp_max: number; +} + +export interface AvatarData { + rank: number; + skills: Record; +} + +export interface SubAffix { + sub_affix_id: number; + count: number; + step: number; +} + +export interface RelicJson { + level: number; + relic_id: number; + relic_set_id: number; + main_affix_id: number; + sub_affixes: SubAffix[]; + internal_uid: number; + equip_avatar: number; +} + +export interface LightconeJson { + level: number; + item_id: number; + equip_avatar: number; + rank: number; + promotion: number; + internal_uid: number; +} diff --git a/src/types/team.ts b/src/types/team.ts index 16adc38..8b8932f 100644 --- a/src/types/team.ts +++ b/src/types/team.ts @@ -1,4 +1,4 @@ -export enum Team { - Player = "Player", - Enemy = "Enemy" +export enum Team { + Player = "Player", + Enemy = "Enemy" } \ No newline at end of file diff --git a/src/types/turn.ts b/src/types/turn.ts index a69badd..4aa2f54 100644 --- a/src/types/turn.ts +++ b/src/types/turn.ts @@ -1,18 +1,18 @@ -import { EntityType } from "./entity"; - -export interface TurnInfoType { - avatars_turn_damage: number[]; - total_damage: number; - action_value: number, - cycle: number, - wave: number, -} - -export interface TurnBeginType { - action_value: number; - turn_owner?: EntityType | null -} - -export interface TurnEndType { - turn_info: TurnInfoType +import { EntityType } from "./entity"; + +export interface TurnInfoType { + avatars_turn_damage: number[]; + total_damage: number; + action_value: number, + cycle: number, + wave: number, +} + +export interface TurnBeginType { + action_value: number; + turn_owner?: EntityType | null +} + +export interface TurnEndType { + turn_info: TurnInfoType } \ No newline at end of file diff --git a/src/types/version.ts b/src/types/version.ts index dd25046..18f9def 100644 --- a/src/types/version.ts +++ b/src/types/version.ts @@ -1,3 +1,3 @@ -export interface VersionType { - version: string +export interface VersionType { + version: string } \ No newline at end of file diff --git a/src/types/waveAndCycle.ts b/src/types/waveAndCycle.ts index b0f5c3d..6c7d92a 100644 --- a/src/types/waveAndCycle.ts +++ b/src/types/waveAndCycle.ts @@ -1,7 +1,7 @@ -export interface UpdateWaveType { - wave: number -} - -export interface UpdateCycleType { - cycle: number +export interface UpdateWaveType { + wave: number +} + +export interface UpdateCycleType { + cycle: number } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 2169551..a86c232 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,4 +38,4 @@ "exclude": [ "node_modules" ] -} +}