Refactor TypeScript interfaces and types for consistency and readability
Gitea Auto Deploy / Deploy-Container (push) Successful in 49s
Gitea Auto Deploy / Deploy-Container (push) Successful in 49s
- Reformatted multiple TypeScript interfaces across various files to ensure consistent spacing and indentation. - Updated `changelog.ts`, `enka.ts`, `extraData.ts`, `gloval.d.ts`, `index.ts`, `lightconeDetail.ts`, `metaData.ts`, `mics.ts`, `modelConfig.ts`, `monsterDetail.ts`, `peakDetail.ts`, `pfDetail.ts`, `psConnect.ts`, `relicDetail.ts`, `showcase.ts`, `srtools.ts`, and `zod/index.ts` for improved code clarity. - Ensured all interfaces are properly formatted with consistent line breaks and spacing. - Made minor adjustments to the `tsconfig.json` file for formatting consistency.
This commit is contained in:
+14
-14
@@ -1,15 +1,15 @@
|
|||||||
name: Gitea Auto Deploy
|
name: Gitea Auto Deploy
|
||||||
run-name: ${{ gitea.actor }} pushed code 🚀
|
run-name: ${{ gitea.actor }} pushed code 🚀
|
||||||
|
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Deploy-Container:
|
Deploy-Container:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Deploy to Container
|
- name: Deploy to Container
|
||||||
run: |
|
run: |
|
||||||
docker compose up -d --build --remove-orphans
|
docker compose up -d --build --remove-orphans
|
||||||
+55
-55
@@ -1,55 +1,55 @@
|
|||||||
FROM oven/bun:canary-alpine AS base
|
FROM oven/bun:canary-alpine AS base
|
||||||
|
|
||||||
FROM base AS deps
|
FROM base AS deps
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json bun.lock ./
|
COPY package.json bun.lock ./
|
||||||
RUN bun install --frozen-lockfile
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
# Rebuild the source code only when needed
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Next.js collects completely anonymous telemetry data about general usage.
|
# Next.js collects completely anonymous telemetry data about general usage.
|
||||||
# Learn more here: https://nextjs.org/telemetry
|
# Learn more here: https://nextjs.org/telemetry
|
||||||
# Disable telemetry during the build
|
# Disable telemetry during the build
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
# Production image, copy all the files and run next
|
# Production image, copy all the files and run next
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
# Disable telemetry
|
# Disable telemetry
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
RUN adduser --system --uid 1001 nextjs
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
COPY --from=builder /app/messages ./messages
|
COPY --from=builder /app/messages ./messages
|
||||||
|
|
||||||
# Set the correct permission for prerender cache
|
# Set the correct permission for prerender cache
|
||||||
RUN mkdir .next
|
RUN mkdir .next
|
||||||
RUN chown nextjs:bun .next
|
RUN chown nextjs:bun .next
|
||||||
|
|
||||||
# Automatically leverage output traces to reduce image size
|
# Automatically leverage output traces to reduce image size
|
||||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
# 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/standalone ./
|
||||||
COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
ENV PORT=3000
|
ENV PORT=3000
|
||||||
|
|
||||||
# Set hostname to localhost
|
# Set hostname to localhost
|
||||||
ENV HOSTNAME=0.0.0.0
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
CMD ["bun", "server.js"]
|
CMD ["bun", "server.js"]
|
||||||
|
|||||||
@@ -1,62 +1,62 @@
|
|||||||
# Firefly SrTools
|
# Firefly SrTools
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
**Firefly SrTools** is a modern, high-performance web toolkit designed to streamline workflows and provide essential services. This project is built upon the powerful synergy of the **Next.js** framework and the ultra-fast **Bun.js** runtime, ensuring lightning-fast development and deployment.
|
**Firefly SrTools** is a modern, high-performance web toolkit designed to streamline workflows and provide essential services. This project is built upon the powerful synergy of the **Next.js** framework and the ultra-fast **Bun.js** runtime, ensuring lightning-fast development and deployment.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Live Sites
|
## Live Sites
|
||||||
|
|
||||||
The application is deployed and available at the following URLs:
|
The application is deployed and available at the following URLs:
|
||||||
|
|
||||||
| Role | URL |
|
| Role | URL |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| **Main Site** | [srtools.punklorde.org](https://srtools.punklorde.org) |
|
| **Main Site** | [srtools.punklorde.org](https://srtools.punklorde.org) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
* **Optimized Performance:** Leveraging Bun for dependency management, scripting, and development server speed.
|
* **Optimized Performance:** Leveraging Bun for dependency management, scripting, and development server speed.
|
||||||
* **Next.js App Router:** Modern routing and data fetching using the latest Next.js architecture.
|
* **Next.js App Router:** Modern routing and data fetching using the latest Next.js architecture.
|
||||||
* **Server-Side Rendering (SSR):** Enhanced SEO and faster initial load times for improved user experience.
|
* **Server-Side Rendering (SSR):** Enhanced SEO and faster initial load times for improved user experience.
|
||||||
* **Robust Type Safety:** Built with TypeScript for reliable and maintainable code.
|
* **Robust Type Safety:** Built with TypeScript for reliable and maintainable code.
|
||||||
* **Modular Design:** Clean separation of concerns with reusable components and utilities.
|
* **Modular Design:** Clean separation of concerns with reusable components and utilities.
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
* **Frontend Framework:** [Next.js 15](https://nextjs.org/) (React)
|
* **Frontend Framework:** [Next.js 15](https://nextjs.org/) (React)
|
||||||
* **Runtime & Package Manager:** [Bun](https://bun.sh/)
|
* **Runtime & Package Manager:** [Bun](https://bun.sh/)
|
||||||
* **Language:** TypeScript
|
* **Language:** TypeScript
|
||||||
* **Styling:** (Tailwind CSS, DaisyUI)
|
* **Styling:** (Tailwind CSS, DaisyUI)
|
||||||
* **State Management:** (Zustand)
|
* **State Management:** (Zustand)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Ensure you have **Bun** installed on your system.
|
Ensure you have **Bun** installed on your system.
|
||||||
|
|
||||||
Install Bun (macOS, WSL, Linux):
|
Install Bun (macOS, WSL, Linux):
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL [https://bun.sh/install](https://bun.sh/install) | bash
|
curl -fsSL [https://bun.sh/install](https://bun.sh/install) | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
bun run build
|
bun run build
|
||||||
bun run start
|
bun run start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+10
-2
@@ -1,10 +1,18 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"version": "4.2.5",
|
"version": "4.4.0",
|
||||||
|
"date": "15/07/2026",
|
||||||
|
"type": "update",
|
||||||
|
"items": [
|
||||||
|
"New data for 4.4.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "4.3.0",
|
||||||
"date": "01/05/2026",
|
"date": "01/05/2026",
|
||||||
"type": "update",
|
"type": "update",
|
||||||
"items": [
|
"items": [
|
||||||
"New data for 4.2.5"
|
"New data for 4.3.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+15
-15
@@ -1,15 +1,15 @@
|
|||||||
services:
|
services:
|
||||||
srtools-live:
|
srtools-live:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: srtools-live
|
container_name: srtools-live
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "3009:3000"
|
- "3009:3000"
|
||||||
networks:
|
networks:
|
||||||
- srtools-live-network
|
- srtools-live-network
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
srtools-live-network:
|
srtools-live-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Firefly-Tools von Firefly Shelter"
|
"description": "Firefly-Tools von Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Fähigkeitstyp",
|
"skillType": "Fähigkeitstyp",
|
||||||
"skillName": "Fähigkeitsname",
|
"skillName": "Fähigkeitsname",
|
||||||
"character": "Charakter",
|
"character": "Charakter",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "Pfad",
|
"path": "Pfad",
|
||||||
"rarity": "Seltenheit",
|
"rarity": "Seltenheit",
|
||||||
"element": "Element",
|
"element": "Element",
|
||||||
"technique": "Technik",
|
"technique": "Technik",
|
||||||
"talent": "Talent",
|
"talent": "Talent",
|
||||||
"basic": "Standardangriff",
|
"basic": "Standardangriff",
|
||||||
"skill": "Fähigkeit",
|
"skill": "Fähigkeit",
|
||||||
"ultimate": "Ultimate",
|
"ultimate": "Ultimate",
|
||||||
"servant": "Diener",
|
"servant": "Diener",
|
||||||
"damage": "Schaden",
|
"damage": "Schaden",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"warrior": "Die Zerstörung",
|
"warrior": "Die Zerstörung",
|
||||||
"knight": "Die Bewahrung",
|
"knight": "Die Bewahrung",
|
||||||
"mage": "Die Gelehrsamkeit",
|
"mage": "Die Gelehrsamkeit",
|
||||||
"priest": "Der Überfluss",
|
"priest": "Der Überfluss",
|
||||||
"rogue": "Die Jagd",
|
"rogue": "Die Jagd",
|
||||||
"shaman": "Die Harmonie",
|
"shaman": "Die Harmonie",
|
||||||
"warlock": "Die Nichtigkeit",
|
"warlock": "Die Nichtigkeit",
|
||||||
"memory": "Die Erinnerung",
|
"memory": "Die Erinnerung",
|
||||||
"elation": "Die Freude",
|
"elation": "Die Freude",
|
||||||
"fire": "Feuer",
|
"fire": "Feuer",
|
||||||
"ice": "Eis",
|
"ice": "Eis",
|
||||||
"imaginary": "Imaginär",
|
"imaginary": "Imaginär",
|
||||||
"physical": "Physisch",
|
"physical": "Physisch",
|
||||||
"quantum": "Quanten",
|
"quantum": "Quanten",
|
||||||
"thunder": "Blitz",
|
"thunder": "Blitz",
|
||||||
"wind": "Wind",
|
"wind": "Wind",
|
||||||
"hp": "LP",
|
"hp": "LP",
|
||||||
"atk": "ANG",
|
"atk": "ANG",
|
||||||
"speed": "GES",
|
"speed": "GES",
|
||||||
"critRate": "Krit. Rate",
|
"critRate": "Krit. Rate",
|
||||||
"critDmg": "Krit. SCH",
|
"critDmg": "Krit. SCH",
|
||||||
"breakEffect": "Brucheffekt",
|
"breakEffect": "Brucheffekt",
|
||||||
"effectRes": "Effekt-WDS",
|
"effectRes": "Effekt-WDS",
|
||||||
"energyRegenerationRate": "Energie-Regenerationsrate",
|
"energyRegenerationRate": "Energie-Regenerationsrate",
|
||||||
"effectHitRate": "Effekttrefferquote",
|
"effectHitRate": "Effekttrefferquote",
|
||||||
"outgoingHealingBoost": "Ausgehender Heilungsbonus",
|
"outgoingHealingBoost": "Ausgehender Heilungsbonus",
|
||||||
"fireDmgBoost": "Feuer-SCH-Bonus",
|
"fireDmgBoost": "Feuer-SCH-Bonus",
|
||||||
"iceDmgBoost": "Eis-SCH-Bonus",
|
"iceDmgBoost": "Eis-SCH-Bonus",
|
||||||
"imaginaryDmgBoost": "Imaginär-SCH-Bonus",
|
"imaginaryDmgBoost": "Imaginär-SCH-Bonus",
|
||||||
"physicalDmgBoost": "Physisch-SCH-Bonus",
|
"physicalDmgBoost": "Physisch-SCH-Bonus",
|
||||||
"quantumDmgBoost": "Quanten-SCH-Bonus",
|
"quantumDmgBoost": "Quanten-SCH-Bonus",
|
||||||
"thunderDmgBoost": "Blitz-SCH-Bonus",
|
"thunderDmgBoost": "Blitz-SCH-Bonus",
|
||||||
"windDmgBoost": "Wind-SCH-Bonus",
|
"windDmgBoost": "Wind-SCH-Bonus",
|
||||||
"pursued": "Zusätzlicher Schaden",
|
"pursued": "Zusätzlicher Schaden",
|
||||||
"true damage": "Wahrer Schaden",
|
"true damage": "Wahrer Schaden",
|
||||||
"elationdamage": "Freude-Schaden",
|
"elationdamage": "Freude-Schaden",
|
||||||
"follow-up": "Folgeangriff-Schaden",
|
"follow-up": "Folgeangriff-Schaden",
|
||||||
"elemental damage": "Bruch- und Superbruch-Schaden",
|
"elemental damage": "Bruch- und Superbruch-Schaden",
|
||||||
"dot": "Schaden über Zeit",
|
"dot": "Schaden über Zeit",
|
||||||
"qte": "QTE-Fähigkeit",
|
"qte": "QTE-Fähigkeit",
|
||||||
"level": "Stufe",
|
"level": "Stufe",
|
||||||
"relics": "Relikte",
|
"relics": "Relikte",
|
||||||
"eidolons": "Eidolons",
|
"eidolons": "Eidolons",
|
||||||
"lightcones": "Lichtkegel",
|
"lightcones": "Lichtkegel",
|
||||||
"loadData": "Daten laden",
|
"loadData": "Daten laden",
|
||||||
"exportData": "Daten exportieren",
|
"exportData": "Daten exportieren",
|
||||||
"connectSetting": "Verbindungseinstellungen",
|
"connectSetting": "Verbindungseinstellungen",
|
||||||
"connected": "Verbunden",
|
"connected": "Verbunden",
|
||||||
"unconnected": "Nicht verbunden",
|
"unconnected": "Nicht verbunden",
|
||||||
"psConnection": "PS-Verbindung",
|
"psConnection": "PS-Verbindung",
|
||||||
"connectionType": "Verbindungstyp",
|
"connectionType": "Verbindungstyp",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"connectPs": "Mit PS verbinden",
|
"connectPs": "Mit PS verbinden",
|
||||||
"disconnect": "Trennen",
|
"disconnect": "Trennen",
|
||||||
"other": "Andere",
|
"other": "Andere",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Datenbank",
|
"database": "Datenbank",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Monstereinstellungen",
|
"monsterSetting": "Monstereinstellungen",
|
||||||
"serverUrl": "Server-URL",
|
"serverUrl": "Server-URL",
|
||||||
"privateType": "Privater Typ",
|
"privateType": "Privater Typ",
|
||||||
"local": "Lokal",
|
"local": "Lokal",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"placeholderServerUrl": "Server-URL eingeben",
|
"placeholderServerUrl": "Server-URL eingeben",
|
||||||
"placeholderUsername": "Benutzernamen eingeben",
|
"placeholderUsername": "Benutzernamen eingeben",
|
||||||
"placeholderPassword": "Passwort eingeben",
|
"placeholderPassword": "Passwort eingeben",
|
||||||
"connectedSuccess": "Erfolgreich mit PS verbunden",
|
"connectedSuccess": "Erfolgreich mit PS verbunden",
|
||||||
"connectedFailed": "Verbindung zu PS fehlgeschlagen",
|
"connectedFailed": "Verbindung zu PS fehlgeschlagen",
|
||||||
"syncSuccess": "Daten erfolgreich mit PS synchronisiert",
|
"syncSuccess": "Daten erfolgreich mit PS synchronisiert",
|
||||||
"syncFailed": "Fehler beim Synchronisieren",
|
"syncFailed": "Fehler beim Synchronisieren",
|
||||||
"sync": "Synchronisieren",
|
"sync": "Synchronisieren",
|
||||||
"importSetting": "Import-Einstellungen",
|
"importSetting": "Import-Einstellungen",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"copyProfiles": "Profile kopieren",
|
"copyProfiles": "Profile kopieren",
|
||||||
"addNewProfile": "Neues Profil hinzufügen",
|
"addNewProfile": "Neues Profil hinzufügen",
|
||||||
"createNewProfile": "Neues Profil erstellen",
|
"createNewProfile": "Neues Profil erstellen",
|
||||||
"editProfile": "Profil bearbeiten",
|
"editProfile": "Profil bearbeiten",
|
||||||
"placeholderProfileName": "Profilnamen eingeben",
|
"placeholderProfileName": "Profilnamen eingeben",
|
||||||
"profileName": "Profilname",
|
"profileName": "Profilname",
|
||||||
"create": "Erstellen",
|
"create": "Erstellen",
|
||||||
"update": "Aktualisieren",
|
"update": "Aktualisieren",
|
||||||
"characterInformation": "Charakterinformationen",
|
"characterInformation": "Charakterinformationen",
|
||||||
"skills": "Fähigkeiten",
|
"skills": "Fähigkeiten",
|
||||||
"showcaseCard": "Showcase-Karte",
|
"showcaseCard": "Showcase-Karte",
|
||||||
"comingSoon": "Demnächst",
|
"comingSoon": "Demnächst",
|
||||||
"characterName": "Charaktername",
|
"characterName": "Charaktername",
|
||||||
"placeholderCharacter": "Charakternamen eingeben",
|
"placeholderCharacter": "Charakternamen eingeben",
|
||||||
"characterSettings": "Charaktereinstellungen",
|
"characterSettings": "Charaktereinstellungen",
|
||||||
"levelConfiguration": "Stufenkonfiguration",
|
"levelConfiguration": "Stufenkonfiguration",
|
||||||
"characterLevel": "Charakterstufe",
|
"characterLevel": "Charakterstufe",
|
||||||
"max": "MAX",
|
"max": "MAX",
|
||||||
"ultimateEnergy": "Ultimate-Energie",
|
"ultimateEnergy": "Ultimate-Energie",
|
||||||
"currentEnergy": "Aktuelle Energie",
|
"currentEnergy": "Aktuelle Energie",
|
||||||
"setTo50": "Auf 50% setzen",
|
"setTo50": "Auf 50% setzen",
|
||||||
"battleConfiguration": "Kampfkonfiguration",
|
"battleConfiguration": "Kampfkonfiguration",
|
||||||
"useTechnique": "Technik verwenden",
|
"useTechnique": "Technik verwenden",
|
||||||
"techniqueNote": "Technikeffekte vor dem Kampf aktivieren",
|
"techniqueNote": "Technikeffekte vor dem Kampf aktivieren",
|
||||||
"enhancement": "Verbesserung",
|
"enhancement": "Verbesserung",
|
||||||
"enhancementLevel": "Verbesserungsstufe",
|
"enhancementLevel": "Verbesserungsstufe",
|
||||||
"origin": "Ursprung",
|
"origin": "Ursprung",
|
||||||
"enhancedNote": "Höhere Verbesserungen schalten Fähigkeiten frei",
|
"enhancedNote": "Höhere Verbesserungen schalten Fähigkeiten frei",
|
||||||
"lightconeEquipment": "Lichtkegel-Ausrüstung",
|
"lightconeEquipment": "Lichtkegel-Ausrüstung",
|
||||||
"lightconeSettings": "Lichtkegel-Einstellungen",
|
"lightconeSettings": "Lichtkegel-Einstellungen",
|
||||||
"placeholderLevel": "Stufe eingeben",
|
"placeholderLevel": "Stufe eingeben",
|
||||||
"superimpositionRank": "Überlagerungsrang",
|
"superimpositionRank": "Überlagerungsrang",
|
||||||
"ranksNote": "Höhere Ränge bieten stärkere Effekte",
|
"ranksNote": "Höhere Ränge bieten stärkere Effekte",
|
||||||
"changeLightcone": "Lichtkegel wechseln",
|
"changeLightcone": "Lichtkegel wechseln",
|
||||||
"removeLightcone": "Lichtkegel entfernen",
|
"removeLightcone": "Lichtkegel entfernen",
|
||||||
"equipLightcone": "Lichtkegel ausrüsten",
|
"equipLightcone": "Lichtkegel ausrüsten",
|
||||||
"noLightconeEquipped": "Kein Lichtkegel ausgerüstet",
|
"noLightconeEquipped": "Kein Lichtkegel ausgerüstet",
|
||||||
"equipLightconeNote": "Rüste einen Lichtkegel aus, um deinen Charakter zu stärken",
|
"equipLightconeNote": "Rüste einen Lichtkegel aus, um deinen Charakter zu stärken",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"selectedCharacters": "Ausgewählte Charaktere",
|
"selectedCharacters": "Ausgewählte Charaktere",
|
||||||
"selectedProfiles": "Ausgewählte Profile",
|
"selectedProfiles": "Ausgewählte Profile",
|
||||||
"clearAll": "Alles löschen",
|
"clearAll": "Alles löschen",
|
||||||
"selectAll": "Alles auswählen",
|
"selectAll": "Alles auswählen",
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"copied": "Kopiert",
|
"copied": "Kopiert",
|
||||||
"noAvatarSelected": "Kein Charakter ausgewählt",
|
"noAvatarSelected": "Kein Charakter ausgewählt",
|
||||||
"noAvatarToCopySelected": "Kein Charakter zum Kopieren ausgewählt",
|
"noAvatarToCopySelected": "Kein Charakter zum Kopieren ausgewählt",
|
||||||
"pleaseSelectAtLeastOneProfile": "Bitte wähle mindestens ein Profil aus",
|
"pleaseSelectAtLeastOneProfile": "Bitte wähle mindestens ein Profil aus",
|
||||||
"pleaseEnterUid": "Bitte UID eingeben",
|
"pleaseEnterUid": "Bitte UID eingeben",
|
||||||
"failedToFetchEnkaData": "Fehler beim Abrufen der Enka-Daten",
|
"failedToFetchEnkaData": "Fehler beim Abrufen der Enka-Daten",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Bitte wähle mindestens einen Charakter aus",
|
"pleaseSelectAtLeastOneCharacter": "Bitte wähle mindestens einen Charakter aus",
|
||||||
"noDataToImport": "Keine Daten zum Importieren",
|
"noDataToImport": "Keine Daten zum Importieren",
|
||||||
"pleaseSelectAFile": "Bitte wähle eine Datei",
|
"pleaseSelectAFile": "Bitte wähle eine Datei",
|
||||||
"fileMustBeAValidJsonFile": "Die Datei muss eine gültige JSON-Datei sein",
|
"fileMustBeAValidJsonFile": "Die Datei muss eine gültige JSON-Datei sein",
|
||||||
"importEnkaDataSuccess": "Enka-Daten erfolgreich importiert",
|
"importEnkaDataSuccess": "Enka-Daten erfolgreich importiert",
|
||||||
"importFreeSRDataSuccess": "FreeSR-Daten erfolgreich importiert",
|
"importFreeSRDataSuccess": "FreeSR-Daten erfolgreich importiert",
|
||||||
"importDatabaseSuccess": "Datenbank erfolgreich importiert",
|
"importDatabaseSuccess": "Datenbank erfolgreich importiert",
|
||||||
"getData": "Daten abrufen",
|
"getData": "Daten abrufen",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
"freeSRImport": "FreeSR-Import",
|
"freeSRImport": "FreeSR-Import",
|
||||||
"onlySupportFreeSRJsonFile": "Unterstützt nur FreeSR-JSON-Dateien",
|
"onlySupportFreeSRJsonFile": "Unterstützt nur FreeSR-JSON-Dateien",
|
||||||
"pickAFile": "Datei auswählen",
|
"pickAFile": "Datei auswählen",
|
||||||
"lightConeSetting": "Lichtkegel-Einstellung",
|
"lightConeSetting": "Lichtkegel-Einstellung",
|
||||||
"relicMaker": "Relikt-Ersteller",
|
"relicMaker": "Relikt-Ersteller",
|
||||||
"pleaseSelectAllOptions": "Bitte wähle alle Optionen",
|
"pleaseSelectAllOptions": "Bitte wähle alle Optionen",
|
||||||
"relicSavedSuccessfully": "Relikt erfolgreich gespeichert",
|
"relicSavedSuccessfully": "Relikt erfolgreich gespeichert",
|
||||||
"mainSettings": "Haupteinstellungen",
|
"mainSettings": "Haupteinstellungen",
|
||||||
"mainStat": "Hauptwert",
|
"mainStat": "Hauptwert",
|
||||||
"set": "Set",
|
"set": "Set",
|
||||||
"pleaseSelectASet": "Bitte wähle ein Set",
|
"pleaseSelectASet": "Bitte wähle ein Set",
|
||||||
"effectBonus": "Effektbonus",
|
"effectBonus": "Effektbonus",
|
||||||
"totalRoll": "Gesamte Aufwertungen",
|
"totalRoll": "Gesamte Aufwertungen",
|
||||||
"randomizeStats": "Werte zufällig",
|
"randomizeStats": "Werte zufällig",
|
||||||
"randomizeRolls": "Aufwertungen zufällig",
|
"randomizeRolls": "Aufwertungen zufällig",
|
||||||
"selectASubStat": "Wähle einen Nebenwert",
|
"selectASubStat": "Wähle einen Nebenwert",
|
||||||
"selectASet": "Wähle ein Set",
|
"selectASet": "Wähle ein Set",
|
||||||
"selectAMainStat": "Wähle einen Hauptwert",
|
"selectAMainStat": "Wähle einen Hauptwert",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"roll": "Aufwertung",
|
"roll": "Aufwertung",
|
||||||
"step": "Schritt",
|
"step": "Schritt",
|
||||||
"memoryOfChaos": "Vergessene Halle",
|
"memoryOfChaos": "Vergessene Halle",
|
||||||
"pureFiction": "Reine Fiktion",
|
"pureFiction": "Reine Fiktion",
|
||||||
"apocalypticShadow": "Apokalyptischer Schatten",
|
"apocalypticShadow": "Apokalyptischer Schatten",
|
||||||
"customEnemy": "Benutzerdefinierter Feind",
|
"customEnemy": "Benutzerdefinierter Feind",
|
||||||
"simulatedUniverse": "Universum-Simulation",
|
"simulatedUniverse": "Universum-Simulation",
|
||||||
"floor": "Ebene",
|
"floor": "Ebene",
|
||||||
"side": "Seite",
|
"side": "Seite",
|
||||||
"wave": "Welle",
|
"wave": "Welle",
|
||||||
"stage": "Phase",
|
"stage": "Phase",
|
||||||
"useCycleCount": "Zyklen-Zählung verwenden?",
|
"useCycleCount": "Zyklen-Zählung verwenden?",
|
||||||
"useTurbulenceBuff": "Turbulenz-Buff verwenden?",
|
"useTurbulenceBuff": "Turbulenz-Buff verwenden?",
|
||||||
"firstHalfEnemies": "Gegner erste Hälfte",
|
"firstHalfEnemies": "Gegner erste Hälfte",
|
||||||
"secondHalfEnemies": "Gegner zweite Hälfte",
|
"secondHalfEnemies": "Gegner zweite Hälfte",
|
||||||
"firstNodeEnemies": "Gegner Knoten 1",
|
"firstNodeEnemies": "Gegner Knoten 1",
|
||||||
"secondNodeEnemies": "Gegner Knoten 2",
|
"secondNodeEnemies": "Gegner Knoten 2",
|
||||||
"thirdNodeEnemies": "Gegner Knoten 3",
|
"thirdNodeEnemies": "Gegner Knoten 3",
|
||||||
"firstNode": "Knoten 1",
|
"firstNode": "Knoten 1",
|
||||||
"secondNode": "Knoten 2",
|
"secondNode": "Knoten 2",
|
||||||
"thirdNode": "Knoten 3",
|
"thirdNode": "Knoten 3",
|
||||||
"listEnemies": "Gegnerliste",
|
"listEnemies": "Gegnerliste",
|
||||||
"turbulenceBuff": "Turbulenz-Buff",
|
"turbulenceBuff": "Turbulenz-Buff",
|
||||||
"noEventSelected": "Kein Ereignis ausgewählt",
|
"noEventSelected": "Kein Ereignis ausgewählt",
|
||||||
"noTurbulenceBuff": "Kein Turbulenz-Buff",
|
"noTurbulenceBuff": "Kein Turbulenz-Buff",
|
||||||
"upper": "Oben",
|
"upper": "Oben",
|
||||||
"lower": "Unten",
|
"lower": "Unten",
|
||||||
"upperToLower": "Oben -> Unten",
|
"upperToLower": "Oben -> Unten",
|
||||||
"lowerToUpper": "Unten -> Oben",
|
"lowerToUpper": "Unten -> Oben",
|
||||||
"selectMOCEvent": "MOC-Ereignis wählen",
|
"selectMOCEvent": "MOC-Ereignis wählen",
|
||||||
"selectPFEvent": "PF-Ereignis wählen",
|
"selectPFEvent": "PF-Ereignis wählen",
|
||||||
"selectASEvent": "AS-Ereignis wählen",
|
"selectASEvent": "AS-Ereignis wählen",
|
||||||
"selectCEEvent": "CE-Ereignis wählen",
|
"selectCEEvent": "CE-Ereignis wählen",
|
||||||
"selectEvent": "Ereignis wählen",
|
"selectEvent": "Ereignis wählen",
|
||||||
"selectFloor": "Ebene wählen",
|
"selectFloor": "Ebene wählen",
|
||||||
"selectSide": "Seite wählen",
|
"selectSide": "Seite wählen",
|
||||||
"selectBuff": "Buff wählen",
|
"selectBuff": "Buff wählen",
|
||||||
"selectStage": "Phase wählen",
|
"selectStage": "Phase wählen",
|
||||||
"previous": "Zurück",
|
"previous": "Zurück",
|
||||||
"next": "Weiter",
|
"next": "Weiter",
|
||||||
"noMonstersFound": "Keine Monster gefunden",
|
"noMonstersFound": "Keine Monster gefunden",
|
||||||
"addNewWave": "Neue Welle hinzufügen",
|
"addNewWave": "Neue Welle hinzufügen",
|
||||||
"searchStage": "Phase suchen...",
|
"searchStage": "Phase suchen...",
|
||||||
"noStageFound": "Keine Phase gefunden",
|
"noStageFound": "Keine Phase gefunden",
|
||||||
"searchMonster": "Monster suchen...",
|
"searchMonster": "Monster suchen...",
|
||||||
"changeRelic": "Relikt wechseln",
|
"changeRelic": "Relikt wechseln",
|
||||||
"deleteRelic": "Relikt löschen",
|
"deleteRelic": "Relikt löschen",
|
||||||
"deleteRelicConfirm": "Möchtest du das Relikt auf diesem Platz wirklich löschen?",
|
"deleteRelicConfirm": "Möchtest du das Relikt auf diesem Platz wirklich löschen?",
|
||||||
"setEffects": "Effekte einstellen",
|
"setEffects": "Effekte einstellen",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"normal": "Standardangriff",
|
"normal": "Standardangriff",
|
||||||
"bpskill": "Fähigkeit",
|
"bpskill": "Fähigkeit",
|
||||||
"maze": "Technik",
|
"maze": "Technik",
|
||||||
"ultra": "Ultimate",
|
"ultra": "Ultimate",
|
||||||
"servantskill": "Memosprite-Fähigkeit",
|
"servantskill": "Memosprite-Fähigkeit",
|
||||||
"severaltalent": "Memosprite-Talent",
|
"severaltalent": "Memosprite-Talent",
|
||||||
"singleattack": "Einzelangriff",
|
"singleattack": "Einzelangriff",
|
||||||
"enhance": "Verbessern",
|
"enhance": "Verbessern",
|
||||||
"summon": "Beschwören",
|
"summon": "Beschwören",
|
||||||
"mazeattack": "Technik-Angriff",
|
"mazeattack": "Technik-Angriff",
|
||||||
"blast": "Explosion",
|
"blast": "Explosion",
|
||||||
"restore": "Wiederherstellen",
|
"restore": "Wiederherstellen",
|
||||||
"support": "Unterstützung",
|
"support": "Unterstützung",
|
||||||
"aoeattack": "Flächenangriff",
|
"aoeattack": "Flächenangriff",
|
||||||
"impair": "Beeinträchtigen",
|
"impair": "Beeinträchtigen",
|
||||||
"bounce": "Abprallen",
|
"bounce": "Abprallen",
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"defence": "Verteidigung",
|
"defence": "Verteidigung",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"maxAll": "Alles maximieren",
|
"maxAll": "Alles maximieren",
|
||||||
"maxAllSuccess": "Fähigkeiten erfolgreich maximiert.",
|
"maxAllSuccess": "Fähigkeiten erfolgreich maximiert.",
|
||||||
"maxAllFailed": "Fehler beim Maximieren.",
|
"maxAllFailed": "Fehler beim Maximieren.",
|
||||||
"noRelicEquipped": "Kein Relikt ausgerüstet",
|
"noRelicEquipped": "Kein Relikt ausgerüstet",
|
||||||
"anomalyArbitration": "Anomalie-Schiedsgericht",
|
"anomalyArbitration": "Anomalie-Schiedsgericht",
|
||||||
"normalMode": "Normaler Modus",
|
"normalMode": "Normaler Modus",
|
||||||
"hardMode": "Schwerer Modus",
|
"hardMode": "Schwerer Modus",
|
||||||
"selectPEAKEvent": "PEAK-Ereignis wählen",
|
"selectPEAKEvent": "PEAK-Ereignis wählen",
|
||||||
"mode": "Modus",
|
"mode": "Modus",
|
||||||
"selectMode": "Modus wählen",
|
"selectMode": "Modus wählen",
|
||||||
"rollBack": "Rückgängig",
|
"rollBack": "Rückgängig",
|
||||||
"upRoll": "Aufwertung hoch",
|
"upRoll": "Aufwertung hoch",
|
||||||
"downRoll": "Aufwertung runter",
|
"downRoll": "Aufwertung runter",
|
||||||
"actions": "Aktionen",
|
"actions": "Aktionen",
|
||||||
"avatars": "Avatare",
|
"avatars": "Avatare",
|
||||||
"quickView": "Schnellansicht",
|
"quickView": "Schnellansicht",
|
||||||
"extraSetting": "Extra-Einstellungen",
|
"extraSetting": "Extra-Einstellungen",
|
||||||
"disableCensorship": "Zensur deaktivieren",
|
"disableCensorship": "Zensur deaktivieren",
|
||||||
"hideUI": "UI verstecken",
|
"hideUI": "UI verstecken",
|
||||||
"theoryCraftMode": "Theorycraft-Modus",
|
"theoryCraftMode": "Theorycraft-Modus",
|
||||||
"cycleCount": "Zyklen-Anzahl",
|
"cycleCount": "Zyklen-Anzahl",
|
||||||
"pleaseSelectAllSubStats": "Bitte alle Nebenwerte auswählen",
|
"pleaseSelectAllSubStats": "Bitte alle Nebenwerte auswählen",
|
||||||
"subStatRollCountCannotBeZero": "Nebenwert-Aufwertungen dürfen nicht null sein",
|
"subStatRollCountCannotBeZero": "Nebenwert-Aufwertungen dürfen nicht null sein",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Mehrpfad-Charakter",
|
"multipathCharacter": "Mehrpfad-Charakter",
|
||||||
"mainPath": "Hauptpfad",
|
"mainPath": "Hauptpfad",
|
||||||
"march7Path": "Pfad 7. März",
|
"march7Path": "Pfad 7. März",
|
||||||
"challenge": "Herausforderung",
|
"challenge": "Herausforderung",
|
||||||
"skipNode": "Knoten überspringen",
|
"skipNode": "Knoten überspringen",
|
||||||
"disableSkip": "Überspringen deaktivieren",
|
"disableSkip": "Überspringen deaktivieren",
|
||||||
"skipNode1": "Knoten 1 überspringen",
|
"skipNode1": "Knoten 1 überspringen",
|
||||||
"skipNode2": "Knoten 2 überspringen",
|
"skipNode2": "Knoten 2 überspringen",
|
||||||
"extraFeatures": "Zusatzfunktionen",
|
"extraFeatures": "Zusatzfunktionen",
|
||||||
"detailTheoryCraft": "Ermöglicht die Anpassung der Zykluszahl und der Gegner-LP.",
|
"detailTheoryCraft": "Ermöglicht die Anpassung der Zykluszahl und der Gegner-LP.",
|
||||||
"detailSkipNode": "Ermöglicht das Überspringen (Knoten 1/2) im Memory of Chaos oder Pure Fiction.",
|
"detailSkipNode": "Ermöglicht das Überspringen (Knoten 1/2) im Memory of Chaos oder Pure Fiction.",
|
||||||
"detailChallengePeak": "Ändert die Peak-Saison in der aktuellen Anomalie.",
|
"detailChallengePeak": "Ändert die Peak-Saison in der aktuellen Anomalie.",
|
||||||
"detailHiddenUi": "Versteckt die Spiel-Benutzeroberfläche.",
|
"detailHiddenUi": "Versteckt die Spiel-Benutzeroberfläche.",
|
||||||
"detailDisableCensorship": "Deaktiviert die Zensur im Spiel.",
|
"detailDisableCensorship": "Deaktiviert die Zensur im Spiel.",
|
||||||
"detailMultipathCharacter": "Ermöglicht das Ändern des Pfades bestimmter Charaktere.",
|
"detailMultipathCharacter": "Ermöglicht das Ändern des Pfades bestimmter Charaktere.",
|
||||||
"trailblazer": "Trailblazer",
|
"trailblazer": "Trailblazer",
|
||||||
"listExtraEffect": "Liste Zusatzeffekte",
|
"listExtraEffect": "Liste Zusatzeffekte",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"customLineup": "Benutzerdefinierte Aufstellung"
|
"customLineup": "Benutzerdefinierte Aufstellung"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Firefly tools by Firefly Shelter"
|
"description": "Firefly tools by Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Skill Type",
|
"skillType": "Skill Type",
|
||||||
"skillName": "Skill Name",
|
"skillName": "Skill Name",
|
||||||
"character": "Character",
|
"character": "Character",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
"rarity": "Rarity",
|
"rarity": "Rarity",
|
||||||
"element": "Element",
|
"element": "Element",
|
||||||
"technique": "Technique",
|
"technique": "Technique",
|
||||||
"talent": "Talent",
|
"talent": "Talent",
|
||||||
"basic": "Basic Attack",
|
"basic": "Basic Attack",
|
||||||
"skill": "Skill",
|
"skill": "Skill",
|
||||||
"ultimate": "Ultimate",
|
"ultimate": "Ultimate",
|
||||||
"servant": "Servant",
|
"servant": "Servant",
|
||||||
"damage": "Damage",
|
"damage": "Damage",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"warrior": "The Destruction",
|
"warrior": "The Destruction",
|
||||||
"knight": "The Preservation",
|
"knight": "The Preservation",
|
||||||
"mage": "The Erudition",
|
"mage": "The Erudition",
|
||||||
"priest": "The Abundance",
|
"priest": "The Abundance",
|
||||||
"rogue": "The Hunt",
|
"rogue": "The Hunt",
|
||||||
"shaman": "The Harmony",
|
"shaman": "The Harmony",
|
||||||
"warlock": "The Nihility",
|
"warlock": "The Nihility",
|
||||||
"memory": "The Remembrance",
|
"memory": "The Remembrance",
|
||||||
"elation": "The Elation",
|
"elation": "The Elation",
|
||||||
"fire": "Fire",
|
"fire": "Fire",
|
||||||
"ice": "Ice",
|
"ice": "Ice",
|
||||||
"imaginary": "Imaginary",
|
"imaginary": "Imaginary",
|
||||||
"physical": "Physical",
|
"physical": "Physical",
|
||||||
"quantum": "Quantum",
|
"quantum": "Quantum",
|
||||||
"thunder": "Thunder",
|
"thunder": "Thunder",
|
||||||
"wind": "Wind",
|
"wind": "Wind",
|
||||||
"hp": "Hp",
|
"hp": "Hp",
|
||||||
"atk": "Atk",
|
"atk": "Atk",
|
||||||
"speed": "Speed",
|
"speed": "Speed",
|
||||||
"critRate": "Crit Rate",
|
"critRate": "Crit Rate",
|
||||||
"critDmg": "Crit Dmg",
|
"critDmg": "Crit Dmg",
|
||||||
"breakEffect": "Break Effect",
|
"breakEffect": "Break Effect",
|
||||||
"effectRes": "Effect Res",
|
"effectRes": "Effect Res",
|
||||||
"energyRegenerationRate": "Energy Regeneration Rate",
|
"energyRegenerationRate": "Energy Regeneration Rate",
|
||||||
"effectHitRate": "Effect Hit Rate",
|
"effectHitRate": "Effect Hit Rate",
|
||||||
"outgoingHealingBoost": "Outgoing Healing Boost",
|
"outgoingHealingBoost": "Outgoing Healing Boost",
|
||||||
"fireDmgBoost": "Fire damage boost",
|
"fireDmgBoost": "Fire damage boost",
|
||||||
"iceDmgBoost": "Ice damage Boost",
|
"iceDmgBoost": "Ice damage Boost",
|
||||||
"imaginaryDmgBoost": "Imaginary damage boost",
|
"imaginaryDmgBoost": "Imaginary damage boost",
|
||||||
"physicalDmgBoost": "Physical damage boost",
|
"physicalDmgBoost": "Physical damage boost",
|
||||||
"quantumDmgBoost": "Quantum damage boost",
|
"quantumDmgBoost": "Quantum damage boost",
|
||||||
"thunderDmgBoost": "Thunder damage boost",
|
"thunderDmgBoost": "Thunder damage boost",
|
||||||
"windDmgBoost": "Wind damage boost",
|
"windDmgBoost": "Wind damage boost",
|
||||||
"pursued": "Additional damage",
|
"pursued": "Additional damage",
|
||||||
"true damage": "True damage",
|
"true damage": "True damage",
|
||||||
"elationdamage": "Elation damage",
|
"elationdamage": "Elation damage",
|
||||||
"follow-up": "Follow-up Damage",
|
"follow-up": "Follow-up Damage",
|
||||||
"elemental damage": "Break and Super break damage",
|
"elemental damage": "Break and Super break damage",
|
||||||
"dot": "Damage over time ",
|
"dot": "Damage over time ",
|
||||||
"qte": "QTE Skill",
|
"qte": "QTE Skill",
|
||||||
"level": "Level",
|
"level": "Level",
|
||||||
"relics": "Relics",
|
"relics": "Relics",
|
||||||
"eidolons": "Eidolons",
|
"eidolons": "Eidolons",
|
||||||
"lightcones": "Lightcones",
|
"lightcones": "Lightcones",
|
||||||
"loadData": "Load data",
|
"loadData": "Load data",
|
||||||
"exportData": "Export data",
|
"exportData": "Export data",
|
||||||
"connectSetting": "Connection Setting",
|
"connectSetting": "Connection Setting",
|
||||||
"connected": "Connected",
|
"connected": "Connected",
|
||||||
"unconnected": "Unconnected",
|
"unconnected": "Unconnected",
|
||||||
"psConnection": "PS Connection",
|
"psConnection": "PS Connection",
|
||||||
"connectionType": "Connection Type",
|
"connectionType": "Connection Type",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"connectPs": "Connect PS",
|
"connectPs": "Connect PS",
|
||||||
"disconnect": "Disconnect",
|
"disconnect": "Disconnect",
|
||||||
"other": "Other",
|
"other": "Other",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Database",
|
"database": "Database",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Monster Setting",
|
"monsterSetting": "Monster Setting",
|
||||||
"serverUrl": "Server URL",
|
"serverUrl": "Server URL",
|
||||||
"privateType": "Private Type",
|
"privateType": "Private Type",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"placeholderServerUrl": "Enter server URL",
|
"placeholderServerUrl": "Enter server URL",
|
||||||
"placeholderUsername": "Enter username",
|
"placeholderUsername": "Enter username",
|
||||||
"placeholderPassword": "Enter password",
|
"placeholderPassword": "Enter password",
|
||||||
"connectedSuccess": "Connected to PS successfully",
|
"connectedSuccess": "Connected to PS successfully",
|
||||||
"connectedFailed": "Failed to connect to PS",
|
"connectedFailed": "Failed to connect to PS",
|
||||||
"syncSuccess": "Synced data to PS successfully",
|
"syncSuccess": "Synced data to PS successfully",
|
||||||
"syncFailed": "Failed to sync data to PS",
|
"syncFailed": "Failed to sync data to PS",
|
||||||
"sync": "Sync",
|
"sync": "Sync",
|
||||||
"importSetting": "Import Setting",
|
"importSetting": "Import Setting",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"copyProfiles": "Copy profiles",
|
"copyProfiles": "Copy profiles",
|
||||||
"addNewProfile": "Add new profile",
|
"addNewProfile": "Add new profile",
|
||||||
"createNewProfile": "Create new profile",
|
"createNewProfile": "Create new profile",
|
||||||
"editProfile": "Edit profile",
|
"editProfile": "Edit profile",
|
||||||
"placeholderProfileName": "Enter profile name",
|
"placeholderProfileName": "Enter profile name",
|
||||||
"profileName": "Profile name",
|
"profileName": "Profile name",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"characterInformation": "Character Information",
|
"characterInformation": "Character Information",
|
||||||
"skills": "Skills",
|
"skills": "Skills",
|
||||||
"showcaseCard": "Showcase Card",
|
"showcaseCard": "Showcase Card",
|
||||||
"comingSoon": "Coming soon",
|
"comingSoon": "Coming soon",
|
||||||
"characterName": "Character name",
|
"characterName": "Character name",
|
||||||
"placeholderCharacter": "Enter character name",
|
"placeholderCharacter": "Enter character name",
|
||||||
"characterSettings": "Character Settings",
|
"characterSettings": "Character Settings",
|
||||||
"levelConfiguration": "Level Configuration",
|
"levelConfiguration": "Level Configuration",
|
||||||
"characterLevel": "Character Level",
|
"characterLevel": "Character Level",
|
||||||
"max": "MAX",
|
"max": "MAX",
|
||||||
"ultimateEnergy": "Ultimate Energy",
|
"ultimateEnergy": "Ultimate Energy",
|
||||||
"currentEnergy": "Current Energy",
|
"currentEnergy": "Current Energy",
|
||||||
"setTo50": "Set to 50%",
|
"setTo50": "Set to 50%",
|
||||||
"battleConfiguration": "Battle Configuration",
|
"battleConfiguration": "Battle Configuration",
|
||||||
"useTechnique": "Use Technique",
|
"useTechnique": "Use Technique",
|
||||||
"techniqueNote": "Enable pre-battle technique effects",
|
"techniqueNote": "Enable pre-battle technique effects",
|
||||||
"enhancement": "Enhancement",
|
"enhancement": "Enhancement",
|
||||||
"enhancementLevel": "Enhancement Level",
|
"enhancementLevel": "Enhancement Level",
|
||||||
"origin": "Origin",
|
"origin": "Origin",
|
||||||
"enhancedNote": "Higher enhanced unlock additional abilities",
|
"enhancedNote": "Higher enhanced unlock additional abilities",
|
||||||
"lightconeEquipment": "Lightcone Equipment",
|
"lightconeEquipment": "Lightcone Equipment",
|
||||||
"lightconeSettings": "Lightcone Settings",
|
"lightconeSettings": "Lightcone Settings",
|
||||||
"placeholderLevel": "Enter level",
|
"placeholderLevel": "Enter level",
|
||||||
"superimpositionRank": "Superimposition Rank",
|
"superimpositionRank": "Superimposition Rank",
|
||||||
"ranksNote": "Higher ranks provide stronger effects",
|
"ranksNote": "Higher ranks provide stronger effects",
|
||||||
"changeLightcone": "Change Lightcone",
|
"changeLightcone": "Change Lightcone",
|
||||||
"removeLightcone": "Remove Lightcone",
|
"removeLightcone": "Remove Lightcone",
|
||||||
"equipLightcone": "Equip Lightcone",
|
"equipLightcone": "Equip Lightcone",
|
||||||
"noLightconeEquipped": "No Lightcone Equipped",
|
"noLightconeEquipped": "No Lightcone Equipped",
|
||||||
"equipLightconeNote": "Equip a lightcone to enhance your character's abilities",
|
"equipLightconeNote": "Equip a lightcone to enhance your character's abilities",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"selectedCharacters": "Selected Characters",
|
"selectedCharacters": "Selected Characters",
|
||||||
"selectedProfiles": "Selected Profiles",
|
"selectedProfiles": "Selected Profiles",
|
||||||
"clearAll": "Clear All",
|
"clearAll": "Clear All",
|
||||||
"selectAll": "Select All",
|
"selectAll": "Select All",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"copied": "Copied",
|
"copied": "Copied",
|
||||||
"noAvatarSelected": "No avatar selected",
|
"noAvatarSelected": "No avatar selected",
|
||||||
"noAvatarToCopySelected": "No avatar to copy selected",
|
"noAvatarToCopySelected": "No avatar to copy selected",
|
||||||
"pleaseSelectAtLeastOneProfile": "Please select at least one profile",
|
"pleaseSelectAtLeastOneProfile": "Please select at least one profile",
|
||||||
"pleaseEnterUid": "Please enter UID",
|
"pleaseEnterUid": "Please enter UID",
|
||||||
"failedToFetchEnkaData": "Failed to fetch enka data",
|
"failedToFetchEnkaData": "Failed to fetch enka data",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Please select at least one character",
|
"pleaseSelectAtLeastOneCharacter": "Please select at least one character",
|
||||||
"noDataToImport": "No data to import",
|
"noDataToImport": "No data to import",
|
||||||
"pleaseSelectAFile": "Please select a file",
|
"pleaseSelectAFile": "Please select a file",
|
||||||
"fileMustBeAValidJsonFile": "File must be a valid json file",
|
"fileMustBeAValidJsonFile": "File must be a valid json file",
|
||||||
"importEnkaDataSuccess": "Import Enka data success",
|
"importEnkaDataSuccess": "Import Enka data success",
|
||||||
"importFreeSRDataSuccess": "Import FreeSR data success",
|
"importFreeSRDataSuccess": "Import FreeSR data success",
|
||||||
"importDatabaseSuccess": "Import database success",
|
"importDatabaseSuccess": "Import database success",
|
||||||
"getData": "Get Data",
|
"getData": "Get Data",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"freeSRImport": "FreeSR Import",
|
"freeSRImport": "FreeSR Import",
|
||||||
"onlySupportFreeSRJsonFile": "Only support FreeSR json file",
|
"onlySupportFreeSRJsonFile": "Only support FreeSR json file",
|
||||||
"pickAFile": "Pick a file",
|
"pickAFile": "Pick a file",
|
||||||
"lightConeSetting": "LightCone Setting",
|
"lightConeSetting": "LightCone Setting",
|
||||||
"relicMaker": "Relic Maker",
|
"relicMaker": "Relic Maker",
|
||||||
"pleaseSelectAllOptions": "Please select all options",
|
"pleaseSelectAllOptions": "Please select all options",
|
||||||
"relicSavedSuccessfully": "Relic saved successfully",
|
"relicSavedSuccessfully": "Relic saved successfully",
|
||||||
"mainSettings": "Main Settings",
|
"mainSettings": "Main Settings",
|
||||||
"mainStat": "Main Stat",
|
"mainStat": "Main Stat",
|
||||||
"set": "Set",
|
"set": "Set",
|
||||||
"pleaseSelectASet": "Please select a set",
|
"pleaseSelectASet": "Please select a set",
|
||||||
"effectBonus": "Effect Bonus",
|
"effectBonus": "Effect Bonus",
|
||||||
"totalRoll": "Total Roll",
|
"totalRoll": "Total Roll",
|
||||||
"randomizeStats": "Randomize Stats",
|
"randomizeStats": "Randomize Stats",
|
||||||
"randomizeRolls": "Randomize Rolls",
|
"randomizeRolls": "Randomize Rolls",
|
||||||
"selectASubStat": "Select a sub stat",
|
"selectASubStat": "Select a sub stat",
|
||||||
"selectASet": "Select a set",
|
"selectASet": "Select a set",
|
||||||
"selectAMainStat": "Select a main stat",
|
"selectAMainStat": "Select a main stat",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"step": "Step",
|
"step": "Step",
|
||||||
"memoryOfChaos": "Memory of Chaos",
|
"memoryOfChaos": "Memory of Chaos",
|
||||||
"pureFiction": "Pure Fiction",
|
"pureFiction": "Pure Fiction",
|
||||||
"apocalypticShadow": "Apocalyptic Shadow",
|
"apocalypticShadow": "Apocalyptic Shadow",
|
||||||
"customEnemy": "Custom Enemy",
|
"customEnemy": "Custom Enemy",
|
||||||
"simulatedUniverse": "Simulated Universe",
|
"simulatedUniverse": "Simulated Universe",
|
||||||
"floor": "Floor",
|
"floor": "Floor",
|
||||||
"side": "Side",
|
"side": "Side",
|
||||||
"wave": "Wave",
|
"wave": "Wave",
|
||||||
"stage": "Stage",
|
"stage": "Stage",
|
||||||
"useCycleCount": "Use cycle count?",
|
"useCycleCount": "Use cycle count?",
|
||||||
"useTurbulenceBuff": "Use turbulence buff?",
|
"useTurbulenceBuff": "Use turbulence buff?",
|
||||||
"firstHalfEnemies": "First half enemies",
|
"firstHalfEnemies": "First half enemies",
|
||||||
"secondHalfEnemies": "Second half enemies",
|
"secondHalfEnemies": "Second half enemies",
|
||||||
"firstNodeEnemies": "First node enemies",
|
"firstNodeEnemies": "First node enemies",
|
||||||
"secondNodeEnemies": "Second node enemies",
|
"secondNodeEnemies": "Second node enemies",
|
||||||
"thirdNodeEnemies": "Third node enemies",
|
"thirdNodeEnemies": "Third node enemies",
|
||||||
"firstNode": "First Node",
|
"firstNode": "First Node",
|
||||||
"secondNode": "Second Node",
|
"secondNode": "Second Node",
|
||||||
"thirdNode": "Third Node",
|
"thirdNode": "Third Node",
|
||||||
"listEnemies": "List enemies",
|
"listEnemies": "List enemies",
|
||||||
"turbulenceBuff": "Turbulence Buff",
|
"turbulenceBuff": "Turbulence Buff",
|
||||||
"noEventSelected": "No event selected",
|
"noEventSelected": "No event selected",
|
||||||
"noTurbulenceBuff": "No Turbulence Buff",
|
"noTurbulenceBuff": "No Turbulence Buff",
|
||||||
"upper": "Upper",
|
"upper": "Upper",
|
||||||
"lower": "Lower",
|
"lower": "Lower",
|
||||||
"upperToLower": "Upper -> Lower",
|
"upperToLower": "Upper -> Lower",
|
||||||
"lowerToUpper": "Lower -> Upper",
|
"lowerToUpper": "Lower -> Upper",
|
||||||
"selectMOCEvent": "Select MOC Event",
|
"selectMOCEvent": "Select MOC Event",
|
||||||
"selectPFEvent": "Select PF Event",
|
"selectPFEvent": "Select PF Event",
|
||||||
"selectASEvent": "Select AS Event",
|
"selectASEvent": "Select AS Event",
|
||||||
"selectCEEvent": "Select CE Event",
|
"selectCEEvent": "Select CE Event",
|
||||||
"selectEvent": "Select Event",
|
"selectEvent": "Select Event",
|
||||||
"selectFloor": "Select a Floor",
|
"selectFloor": "Select a Floor",
|
||||||
"selectSide": "Select a Side",
|
"selectSide": "Select a Side",
|
||||||
"selectBuff": "Select a Buff",
|
"selectBuff": "Select a Buff",
|
||||||
"selectStage": "Select a Stage",
|
"selectStage": "Select a Stage",
|
||||||
"previous": "Previous",
|
"previous": "Previous",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"noMonstersFound": "No monsters found",
|
"noMonstersFound": "No monsters found",
|
||||||
"addNewWave": "Add New Wave",
|
"addNewWave": "Add New Wave",
|
||||||
"searchStage": "Search stage...",
|
"searchStage": "Search stage...",
|
||||||
"noStageFound": "No stage found",
|
"noStageFound": "No stage found",
|
||||||
"searchMonster": "Search monster...",
|
"searchMonster": "Search monster...",
|
||||||
"changeRelic": "Change relic",
|
"changeRelic": "Change relic",
|
||||||
"deleteRelic": "Delete relic",
|
"deleteRelic": "Delete relic",
|
||||||
"deleteRelicConfirm": "Are you sure you want to delete relic in slot",
|
"deleteRelicConfirm": "Are you sure you want to delete relic in slot",
|
||||||
"setEffects": "Set Effects",
|
"setEffects": "Set Effects",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"normal": "Basic ATK",
|
"normal": "Basic ATK",
|
||||||
"bpskill": "Skill",
|
"bpskill": "Skill",
|
||||||
"maze": "Technique",
|
"maze": "Technique",
|
||||||
"ultra": "Ultimate",
|
"ultra": "Ultimate",
|
||||||
"servantskill": "Memosprite Skill",
|
"servantskill": "Memosprite Skill",
|
||||||
"severaltalent": "Memosprite Talent",
|
"severaltalent": "Memosprite Talent",
|
||||||
"singleattack": "Single Attack",
|
"singleattack": "Single Attack",
|
||||||
"enhance": "Enhance",
|
"enhance": "Enhance",
|
||||||
"summon": "Summon",
|
"summon": "Summon",
|
||||||
"mazeattack": "Technique Attack",
|
"mazeattack": "Technique Attack",
|
||||||
"blast": "Blast",
|
"blast": "Blast",
|
||||||
"restore": "Restore",
|
"restore": "Restore",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
"aoeattack": "AoE Attack",
|
"aoeattack": "AoE Attack",
|
||||||
"impair": "Impair",
|
"impair": "Impair",
|
||||||
"bounce": "Bounce",
|
"bounce": "Bounce",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
"defence": "Defence",
|
"defence": "Defence",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"maxAll": "Max All",
|
"maxAll": "Max All",
|
||||||
"maxAllSuccess": "Successfully set skill level to max.",
|
"maxAllSuccess": "Successfully set skill level to max.",
|
||||||
"maxAllFailed": "Failed to set skill level to max.",
|
"maxAllFailed": "Failed to set skill level to max.",
|
||||||
"noRelicEquipped": "No relic equipped",
|
"noRelicEquipped": "No relic equipped",
|
||||||
"anomalyArbitration": "Anomaly Arbitration",
|
"anomalyArbitration": "Anomaly Arbitration",
|
||||||
"normalMode": "Normal Mode",
|
"normalMode": "Normal Mode",
|
||||||
"hardMode": "Hard Mode",
|
"hardMode": "Hard Mode",
|
||||||
"selectPEAKEvent": "Select PEAK Event",
|
"selectPEAKEvent": "Select PEAK Event",
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"selectMode": "Select a mode",
|
"selectMode": "Select a mode",
|
||||||
"rollBack": "Roll Back",
|
"rollBack": "Roll Back",
|
||||||
"upRoll": "Up Roll",
|
"upRoll": "Up Roll",
|
||||||
"downRoll": "Down Roll",
|
"downRoll": "Down Roll",
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"avatars": "Avatars",
|
"avatars": "Avatars",
|
||||||
"quickView": "Quick View",
|
"quickView": "Quick View",
|
||||||
"extraSetting": "Extra Settings",
|
"extraSetting": "Extra Settings",
|
||||||
"disableCensorship": "Disable Censorship",
|
"disableCensorship": "Disable Censorship",
|
||||||
"hideUI": "Hide UI",
|
"hideUI": "Hide UI",
|
||||||
"theoryCraftMode": "Theorycraft Mode",
|
"theoryCraftMode": "Theorycraft Mode",
|
||||||
"cycleCount": "Cycle Count",
|
"cycleCount": "Cycle Count",
|
||||||
"pleaseSelectAllSubStats": "Please select all sub stats",
|
"pleaseSelectAllSubStats": "Please select all sub stats",
|
||||||
"subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero",
|
"subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Multipath Character",
|
"multipathCharacter": "Multipath Character",
|
||||||
"mainPath": "Main Path",
|
"mainPath": "Main Path",
|
||||||
"march7Path": "March 7 Path",
|
"march7Path": "March 7 Path",
|
||||||
"challenge": "Challenge",
|
"challenge": "Challenge",
|
||||||
"skipNode": "Skip Node",
|
"skipNode": "Skip Node",
|
||||||
"disableSkip": "Disable skip",
|
"disableSkip": "Disable skip",
|
||||||
"skipNode1": "Skip node 1",
|
"skipNode1": "Skip node 1",
|
||||||
"skipNode2": "Skip node 2",
|
"skipNode2": "Skip node 2",
|
||||||
"extraFeatures": "Extra Features",
|
"extraFeatures": "Extra Features",
|
||||||
"detailTheoryCraft": "Enabling this feature allows you to customize the cycle count and adjust enemy HP in the enemy settings.",
|
"detailTheoryCraft": "Enabling this feature allows you to customize the cycle count and adjust enemy HP in the enemy settings.",
|
||||||
"detailSkipNode": "Enabling this feature allows you to skip (Node 1/Node 2) in Memory of Chaos or Pure Fiction.",
|
"detailSkipNode": "Enabling this feature allows you to skip (Node 1/Node 2) in Memory of Chaos or Pure Fiction.",
|
||||||
"detailChallengePeak": "Allows changing the Peak season in the current Anomaly.",
|
"detailChallengePeak": "Allows changing the Peak season in the current Anomaly.",
|
||||||
"detailHiddenUi": "Enabling this feature will hide the game UI.",
|
"detailHiddenUi": "Enabling this feature will hide the game UI.",
|
||||||
"detailDisableCensorship": "Enabling this feature will disable in-game censorship.",
|
"detailDisableCensorship": "Enabling this feature will disable in-game censorship.",
|
||||||
"detailMultipathCharacter": "Allows changing the Path of certain characters.",
|
"detailMultipathCharacter": "Allows changing the Path of certain characters.",
|
||||||
"trailblazer": "Trailblazer",
|
"trailblazer": "Trailblazer",
|
||||||
"listExtraEffect": "List Extra Effect",
|
"listExtraEffect": "List Extra Effect",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"customLineup": "Custom Lineup"
|
"customLineup": "Custom Lineup"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Herramientas Firefly por Firefly Shelter"
|
"description": "Herramientas Firefly por Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Tipo de habilidad",
|
"skillType": "Tipo de habilidad",
|
||||||
"skillName": "Nombre de la habilidad",
|
"skillName": "Nombre de la habilidad",
|
||||||
"character": "Personaje",
|
"character": "Personaje",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"path": "Vía",
|
"path": "Vía",
|
||||||
"rarity": "Rareza",
|
"rarity": "Rareza",
|
||||||
"element": "Elemento",
|
"element": "Elemento",
|
||||||
"technique": "Técnica",
|
"technique": "Técnica",
|
||||||
"talent": "Talento",
|
"talent": "Talento",
|
||||||
"basic": "Ataque Básico",
|
"basic": "Ataque Básico",
|
||||||
"skill": "Habilidad",
|
"skill": "Habilidad",
|
||||||
"ultimate": "Habilidad Definitiva",
|
"ultimate": "Habilidad Definitiva",
|
||||||
"servant": "Mnemoduende",
|
"servant": "Mnemoduende",
|
||||||
"damage": "Daño",
|
"damage": "Daño",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"warrior": "La Destrucción",
|
"warrior": "La Destrucción",
|
||||||
"knight": "La Conservación",
|
"knight": "La Conservación",
|
||||||
"mage": "La Erudición",
|
"mage": "La Erudición",
|
||||||
"priest": "La Abundancia",
|
"priest": "La Abundancia",
|
||||||
"rogue": "La Cacería",
|
"rogue": "La Cacería",
|
||||||
"shaman": "La Armonía",
|
"shaman": "La Armonía",
|
||||||
"warlock": "La Nihilidad",
|
"warlock": "La Nihilidad",
|
||||||
"memory": "La Reminiscencia",
|
"memory": "La Reminiscencia",
|
||||||
"elation": "La Exultación",
|
"elation": "La Exultación",
|
||||||
"fire": "Fuego",
|
"fire": "Fuego",
|
||||||
"ice": "Hielo",
|
"ice": "Hielo",
|
||||||
"imaginary": "Imaginario",
|
"imaginary": "Imaginario",
|
||||||
"physical": "Físico",
|
"physical": "Físico",
|
||||||
"quantum": "Cuántico",
|
"quantum": "Cuántico",
|
||||||
"thunder": "Rayo",
|
"thunder": "Rayo",
|
||||||
"wind": "Viento",
|
"wind": "Viento",
|
||||||
"hp": "PV",
|
"hp": "PV",
|
||||||
"atk": "ATQ",
|
"atk": "ATQ",
|
||||||
"speed": "VEL",
|
"speed": "VEL",
|
||||||
"critRate": "Prob. CRIT",
|
"critRate": "Prob. CRIT",
|
||||||
"critDmg": "Daño CRIT",
|
"critDmg": "Daño CRIT",
|
||||||
"breakEffect": "Efecto de Ruptura",
|
"breakEffect": "Efecto de Ruptura",
|
||||||
"effectRes": "RES a Efecto",
|
"effectRes": "RES a Efecto",
|
||||||
"energyRegenerationRate": "Recuperación de Energía",
|
"energyRegenerationRate": "Recuperación de Energía",
|
||||||
"effectHitRate": "Acierto de Efecto",
|
"effectHitRate": "Acierto de Efecto",
|
||||||
"outgoingHealingBoost": "Bono de Curación",
|
"outgoingHealingBoost": "Bono de Curación",
|
||||||
"fireDmgBoost": "Aumento de Daño de Fuego",
|
"fireDmgBoost": "Aumento de Daño de Fuego",
|
||||||
"iceDmgBoost": "Aumento de Daño de Hielo",
|
"iceDmgBoost": "Aumento de Daño de Hielo",
|
||||||
"imaginaryDmgBoost": "Aumento de Daño Imaginario",
|
"imaginaryDmgBoost": "Aumento de Daño Imaginario",
|
||||||
"physicalDmgBoost": "Aumento de Daño Físico",
|
"physicalDmgBoost": "Aumento de Daño Físico",
|
||||||
"quantumDmgBoost": "Aumento de Daño Cuántico",
|
"quantumDmgBoost": "Aumento de Daño Cuántico",
|
||||||
"thunderDmgBoost": "Aumento de Daño de Rayo",
|
"thunderDmgBoost": "Aumento de Daño de Rayo",
|
||||||
"windDmgBoost": "Aumento de Daño de Viento",
|
"windDmgBoost": "Aumento de Daño de Viento",
|
||||||
"pursued": "Daño adicional",
|
"pursued": "Daño adicional",
|
||||||
"true damage": "Daño verdadero",
|
"true damage": "Daño verdadero",
|
||||||
"elationdamage": "Daño de Exultación",
|
"elationdamage": "Daño de Exultación",
|
||||||
"follow-up": "Daño de Ataque Adicional",
|
"follow-up": "Daño de Ataque Adicional",
|
||||||
"elemental damage": "Daño de Ruptura y Superruptura",
|
"elemental damage": "Daño de Ruptura y Superruptura",
|
||||||
"dot": "Daño con el tiempo",
|
"dot": "Daño con el tiempo",
|
||||||
"qte": "Habilidad QTE",
|
"qte": "Habilidad QTE",
|
||||||
"level": "Nivel",
|
"level": "Nivel",
|
||||||
"relics": "Artefactos",
|
"relics": "Artefactos",
|
||||||
"eidolons": "Eidolones",
|
"eidolons": "Eidolones",
|
||||||
"lightcones": "Conos de Luz",
|
"lightcones": "Conos de Luz",
|
||||||
"loadData": "Cargar datos",
|
"loadData": "Cargar datos",
|
||||||
"exportData": "Exportar datos",
|
"exportData": "Exportar datos",
|
||||||
"connectSetting": "Ajustes de conexión",
|
"connectSetting": "Ajustes de conexión",
|
||||||
"connected": "Conectado",
|
"connected": "Conectado",
|
||||||
"unconnected": "Desconectado",
|
"unconnected": "Desconectado",
|
||||||
"psConnection": "Conexión PS",
|
"psConnection": "Conexión PS",
|
||||||
"connectionType": "Tipo de conexión",
|
"connectionType": "Tipo de conexión",
|
||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"connectPs": "Conectar PS",
|
"connectPs": "Conectar PS",
|
||||||
"disconnect": "Desconectar",
|
"disconnect": "Desconectar",
|
||||||
"other": "Otro",
|
"other": "Otro",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Base de datos",
|
"database": "Base de datos",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Ajuste de Monstruos",
|
"monsterSetting": "Ajuste de Monstruos",
|
||||||
"serverUrl": "URL del Servidor",
|
"serverUrl": "URL del Servidor",
|
||||||
"privateType": "Tipo privado",
|
"privateType": "Tipo privado",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
"server": "Servidor",
|
"server": "Servidor",
|
||||||
"username": "Usuario",
|
"username": "Usuario",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"placeholderServerUrl": "Introducir URL del servidor",
|
"placeholderServerUrl": "Introducir URL del servidor",
|
||||||
"placeholderUsername": "Introducir usuario",
|
"placeholderUsername": "Introducir usuario",
|
||||||
"placeholderPassword": "Introducir contraseña",
|
"placeholderPassword": "Introducir contraseña",
|
||||||
"connectedSuccess": "Conectado a PS exitosamente",
|
"connectedSuccess": "Conectado a PS exitosamente",
|
||||||
"connectedFailed": "Fallo al conectar a PS",
|
"connectedFailed": "Fallo al conectar a PS",
|
||||||
"syncSuccess": "Datos sincronizados a PS exitosamente",
|
"syncSuccess": "Datos sincronizados a PS exitosamente",
|
||||||
"syncFailed": "Fallo al sincronizar datos a PS",
|
"syncFailed": "Fallo al sincronizar datos a PS",
|
||||||
"sync": "Sincronizar",
|
"sync": "Sincronizar",
|
||||||
"importSetting": "Ajustes de Importación",
|
"importSetting": "Ajustes de Importación",
|
||||||
"profile": "Perfil",
|
"profile": "Perfil",
|
||||||
"default": "Por defecto",
|
"default": "Por defecto",
|
||||||
"copyProfiles": "Copiar perfiles",
|
"copyProfiles": "Copiar perfiles",
|
||||||
"addNewProfile": "Añadir nuevo perfil",
|
"addNewProfile": "Añadir nuevo perfil",
|
||||||
"createNewProfile": "Crear nuevo perfil",
|
"createNewProfile": "Crear nuevo perfil",
|
||||||
"editProfile": "Editar perfil",
|
"editProfile": "Editar perfil",
|
||||||
"placeholderProfileName": "Introducir nombre de perfil",
|
"placeholderProfileName": "Introducir nombre de perfil",
|
||||||
"profileName": "Nombre del perfil",
|
"profileName": "Nombre del perfil",
|
||||||
"create": "Crear",
|
"create": "Crear",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"characterInformation": "Información del personaje",
|
"characterInformation": "Información del personaje",
|
||||||
"skills": "Habilidades",
|
"skills": "Habilidades",
|
||||||
"showcaseCard": "Tarjeta de presentación del personaje",
|
"showcaseCard": "Tarjeta de presentación del personaje",
|
||||||
"comingSoon": "Próximamente",
|
"comingSoon": "Próximamente",
|
||||||
"characterName": "Nombre del personaje",
|
"characterName": "Nombre del personaje",
|
||||||
"placeholderCharacter": "Introducir nombre del personaje",
|
"placeholderCharacter": "Introducir nombre del personaje",
|
||||||
"characterSettings": "Ajustes del Personaje",
|
"characterSettings": "Ajustes del Personaje",
|
||||||
"levelConfiguration": "Configuración de Nivel",
|
"levelConfiguration": "Configuración de Nivel",
|
||||||
"characterLevel": "Nivel de Personaje",
|
"characterLevel": "Nivel de Personaje",
|
||||||
"max": "MÁX",
|
"max": "MÁX",
|
||||||
"ultimateEnergy": "Energía de Habilidad Definitiva",
|
"ultimateEnergy": "Energía de Habilidad Definitiva",
|
||||||
"currentEnergy": "Energía al inicio del combate",
|
"currentEnergy": "Energía al inicio del combate",
|
||||||
"setTo50": "Ajustar al 50%",
|
"setTo50": "Ajustar al 50%",
|
||||||
"battleConfiguration": "Configuración de Batalla",
|
"battleConfiguration": "Configuración de Batalla",
|
||||||
"useTechnique": "Usar Técnica",
|
"useTechnique": "Usar Técnica",
|
||||||
"techniqueNote": "Activar efectos de técnica pre-batalla",
|
"techniqueNote": "Activar efectos de técnica pre-batalla",
|
||||||
"enhancement": "Mejora",
|
"enhancement": "Mejora",
|
||||||
"enhancementLevel": "Nivel de Mejora",
|
"enhancementLevel": "Nivel de Mejora",
|
||||||
"origin": "Original",
|
"origin": "Original",
|
||||||
"enhancedNote": "Niveles superiores desbloquean más habilidades",
|
"enhancedNote": "Niveles superiores desbloquean más habilidades",
|
||||||
"lightconeEquipment": "Equipamiento de Cono de Luz",
|
"lightconeEquipment": "Equipamiento de Cono de Luz",
|
||||||
"lightconeSettings": "Ajustes de Cono de Luz",
|
"lightconeSettings": "Ajustes de Cono de Luz",
|
||||||
"placeholderLevel": "Introducir nivel",
|
"placeholderLevel": "Introducir nivel",
|
||||||
"superimpositionRank": "Rango de Superposición",
|
"superimpositionRank": "Rango de Superposición",
|
||||||
"ranksNote": "Rangos mayores ofrecen efectos más fuertes",
|
"ranksNote": "Rangos mayores ofrecen efectos más fuertes",
|
||||||
"changeLightcone": "Cambiar Cono de Luz",
|
"changeLightcone": "Cambiar Cono de Luz",
|
||||||
"removeLightcone": "Quitar Cono de Luz",
|
"removeLightcone": "Quitar Cono de Luz",
|
||||||
"equipLightcone": "Equipar Cono de Luz",
|
"equipLightcone": "Equipar Cono de Luz",
|
||||||
"noLightconeEquipped": "Sin Cono de Luz",
|
"noLightconeEquipped": "Sin Cono de Luz",
|
||||||
"equipLightconeNote": "Equipa un cono de luz para mejorar tu personaje",
|
"equipLightconeNote": "Equipa un cono de luz para mejorar tu personaje",
|
||||||
"filter": "Filtro",
|
"filter": "Filtro",
|
||||||
"selectedCharacters": "Personajes Seleccionados",
|
"selectedCharacters": "Personajes Seleccionados",
|
||||||
"selectedProfiles": "Perfiles Seleccionados",
|
"selectedProfiles": "Perfiles Seleccionados",
|
||||||
"clearAll": "Limpiar Todo",
|
"clearAll": "Limpiar Todo",
|
||||||
"selectAll": "Seleccionar Todo",
|
"selectAll": "Seleccionar Todo",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copied": "Copiado",
|
"copied": "Copiado",
|
||||||
"noAvatarSelected": "Ningún personaje seleccionado",
|
"noAvatarSelected": "Ningún personaje seleccionado",
|
||||||
"noAvatarToCopySelected": "Ningún personaje seleccionado para copiar",
|
"noAvatarToCopySelected": "Ningún personaje seleccionado para copiar",
|
||||||
"pleaseSelectAtLeastOneProfile": "Por favor, selecciona al menos un perfil",
|
"pleaseSelectAtLeastOneProfile": "Por favor, selecciona al menos un perfil",
|
||||||
"pleaseEnterUid": "Por favor, introduce UID",
|
"pleaseEnterUid": "Por favor, introduce UID",
|
||||||
"failedToFetchEnkaData": "Fallo al obtener datos de Enka",
|
"failedToFetchEnkaData": "Fallo al obtener datos de Enka",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Por favor, selecciona al menos un personaje",
|
"pleaseSelectAtLeastOneCharacter": "Por favor, selecciona al menos un personaje",
|
||||||
"noDataToImport": "Sin datos para importar",
|
"noDataToImport": "Sin datos para importar",
|
||||||
"pleaseSelectAFile": "Por favor, selecciona un archivo",
|
"pleaseSelectAFile": "Por favor, selecciona un archivo",
|
||||||
"fileMustBeAValidJsonFile": "El archivo debe ser un JSON válido",
|
"fileMustBeAValidJsonFile": "El archivo debe ser un JSON válido",
|
||||||
"importEnkaDataSuccess": "Datos de Enka importados con éxito",
|
"importEnkaDataSuccess": "Datos de Enka importados con éxito",
|
||||||
"importFreeSRDataSuccess": "Datos de FreeSR importados con éxito",
|
"importFreeSRDataSuccess": "Datos de FreeSR importados con éxito",
|
||||||
"importDatabaseSuccess": "Base de datos importada con éxito",
|
"importDatabaseSuccess": "Base de datos importada con éxito",
|
||||||
"getData": "Obtener Datos",
|
"getData": "Obtener Datos",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
"freeSRImport": "Importar FreeSR",
|
"freeSRImport": "Importar FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Solo soporta archivos JSON de FreeSR",
|
"onlySupportFreeSRJsonFile": "Solo soporta archivos JSON de FreeSR",
|
||||||
"pickAFile": "Elegir un archivo",
|
"pickAFile": "Elegir un archivo",
|
||||||
"lightConeSetting": "Ajuste de Cono de Luz",
|
"lightConeSetting": "Ajuste de Cono de Luz",
|
||||||
"relicMaker": "Creador de Artefactos",
|
"relicMaker": "Creador de Artefactos",
|
||||||
"pleaseSelectAllOptions": "Por favor selecciona todas las opciones",
|
"pleaseSelectAllOptions": "Por favor selecciona todas las opciones",
|
||||||
"relicSavedSuccessfully": "Artefacto guardado exitosamente",
|
"relicSavedSuccessfully": "Artefacto guardado exitosamente",
|
||||||
"mainSettings": "Ajustes Principales",
|
"mainSettings": "Ajustes Principales",
|
||||||
"mainStat": "Estadística Principal",
|
"mainStat": "Estadística Principal",
|
||||||
"set": "Conjunto",
|
"set": "Conjunto",
|
||||||
"pleaseSelectASet": "Por favor, selecciona un conjunto",
|
"pleaseSelectASet": "Por favor, selecciona un conjunto",
|
||||||
"effectBonus": "Bono de Efecto",
|
"effectBonus": "Bono de Efecto",
|
||||||
"totalRoll": "Total de Mejoras",
|
"totalRoll": "Total de Mejoras",
|
||||||
"randomizeStats": "Estadísticas Aleatorias",
|
"randomizeStats": "Estadísticas Aleatorias",
|
||||||
"randomizeRolls": "Mejoras Aleatorias",
|
"randomizeRolls": "Mejoras Aleatorias",
|
||||||
"selectASubStat": "Seleccionar estadística secundaria",
|
"selectASubStat": "Seleccionar estadística secundaria",
|
||||||
"selectASet": "Seleccionar conjunto",
|
"selectASet": "Seleccionar conjunto",
|
||||||
"selectAMainStat": "Seleccionar estadística principal",
|
"selectAMainStat": "Seleccionar estadística principal",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"reset": "Reiniciar",
|
"reset": "Reiniciar",
|
||||||
"roll": "Mejora",
|
"roll": "Mejora",
|
||||||
"step": "Paso",
|
"step": "Paso",
|
||||||
"memoryOfChaos": "Memoria del Caos",
|
"memoryOfChaos": "Memoria del Caos",
|
||||||
"pureFiction": "Pura Ficción",
|
"pureFiction": "Pura Ficción",
|
||||||
"apocalypticShadow": "Sombra Apocalíptica",
|
"apocalypticShadow": "Sombra Apocalíptica",
|
||||||
"customEnemy": "Enemigo Personalizado",
|
"customEnemy": "Enemigo Personalizado",
|
||||||
"simulatedUniverse": "Universo Simulado",
|
"simulatedUniverse": "Universo Simulado",
|
||||||
"floor": "Piso",
|
"floor": "Piso",
|
||||||
"side": "Lado",
|
"side": "Lado",
|
||||||
"wave": "Oleada",
|
"wave": "Oleada",
|
||||||
"stage": "Etapa",
|
"stage": "Etapa",
|
||||||
"useCycleCount": "¿Usar conteo de ciclos?",
|
"useCycleCount": "¿Usar conteo de ciclos?",
|
||||||
"useTurbulenceBuff": "¿Usar buff de turbulencia?",
|
"useTurbulenceBuff": "¿Usar buff de turbulencia?",
|
||||||
"firstHalfEnemies": "Enemigos primera mitad",
|
"firstHalfEnemies": "Enemigos primera mitad",
|
||||||
"secondHalfEnemies": "Enemigos segunda mitad",
|
"secondHalfEnemies": "Enemigos segunda mitad",
|
||||||
"firstNodeEnemies": "Enemigos del nodo 1",
|
"firstNodeEnemies": "Enemigos del nodo 1",
|
||||||
"secondNodeEnemies": "Enemigos del nodo 2",
|
"secondNodeEnemies": "Enemigos del nodo 2",
|
||||||
"thirdNodeEnemies": "Enemigos del nodo 3",
|
"thirdNodeEnemies": "Enemigos del nodo 3",
|
||||||
"firstNode": "Nodo 1",
|
"firstNode": "Nodo 1",
|
||||||
"secondNode": "Nodo 2",
|
"secondNode": "Nodo 2",
|
||||||
"thirdNode": "Nodo 3",
|
"thirdNode": "Nodo 3",
|
||||||
"listEnemies": "Lista de enemigos",
|
"listEnemies": "Lista de enemigos",
|
||||||
"turbulenceBuff": "Buff de Turbulencia",
|
"turbulenceBuff": "Buff de Turbulencia",
|
||||||
"noEventSelected": "Ningún evento seleccionado",
|
"noEventSelected": "Ningún evento seleccionado",
|
||||||
"noTurbulenceBuff": "Sin Buff de Turbulencia",
|
"noTurbulenceBuff": "Sin Buff de Turbulencia",
|
||||||
"upper": "Superior",
|
"upper": "Superior",
|
||||||
"lower": "Inferior",
|
"lower": "Inferior",
|
||||||
"upperToLower": "Superior -> Inferior",
|
"upperToLower": "Superior -> Inferior",
|
||||||
"lowerToUpper": "Inferior -> Superior",
|
"lowerToUpper": "Inferior -> Superior",
|
||||||
"selectMOCEvent": "Seleccionar evento de MOC",
|
"selectMOCEvent": "Seleccionar evento de MOC",
|
||||||
"selectPFEvent": "Seleccionar evento de PF",
|
"selectPFEvent": "Seleccionar evento de PF",
|
||||||
"selectASEvent": "Seleccionar evento de AS",
|
"selectASEvent": "Seleccionar evento de AS",
|
||||||
"selectCEEvent": "Seleccionar evento de CE",
|
"selectCEEvent": "Seleccionar evento de CE",
|
||||||
"selectEvent": "Seleccionar Evento",
|
"selectEvent": "Seleccionar Evento",
|
||||||
"selectFloor": "Seleccionar Piso",
|
"selectFloor": "Seleccionar Piso",
|
||||||
"selectSide": "Seleccionar Lado",
|
"selectSide": "Seleccionar Lado",
|
||||||
"selectBuff": "Seleccionar Buff",
|
"selectBuff": "Seleccionar Buff",
|
||||||
"selectStage": "Seleccionar Etapa",
|
"selectStage": "Seleccionar Etapa",
|
||||||
"previous": "Anterior",
|
"previous": "Anterior",
|
||||||
"next": "Siguiente",
|
"next": "Siguiente",
|
||||||
"noMonstersFound": "No se encontraron monstruos",
|
"noMonstersFound": "No se encontraron monstruos",
|
||||||
"addNewWave": "Añadir Nueva Oleada",
|
"addNewWave": "Añadir Nueva Oleada",
|
||||||
"searchStage": "Buscar etapa...",
|
"searchStage": "Buscar etapa...",
|
||||||
"noStageFound": "No se encontró etapa",
|
"noStageFound": "No se encontró etapa",
|
||||||
"searchMonster": "Buscar monstruo...",
|
"searchMonster": "Buscar monstruo...",
|
||||||
"changeRelic": "Cambiar artefacto",
|
"changeRelic": "Cambiar artefacto",
|
||||||
"deleteRelic": "Borrar artefacto",
|
"deleteRelic": "Borrar artefacto",
|
||||||
"deleteRelicConfirm": "¿Seguro que quieres borrar el artefacto en este espacio?",
|
"deleteRelicConfirm": "¿Seguro que quieres borrar el artefacto en este espacio?",
|
||||||
"setEffects": "Configurar Efectos",
|
"setEffects": "Configurar Efectos",
|
||||||
"details": "Detalles",
|
"details": "Detalles",
|
||||||
"normal": "Ataque Básico",
|
"normal": "Ataque Básico",
|
||||||
"bpskill": "Habilidad",
|
"bpskill": "Habilidad",
|
||||||
"maze": "Técnica",
|
"maze": "Técnica",
|
||||||
"ultra": "Habilidad Definitiva",
|
"ultra": "Habilidad Definitiva",
|
||||||
"servantskill": "Habilidad de Mnemoduende",
|
"servantskill": "Habilidad de Mnemoduende",
|
||||||
"severaltalent": "Talento de Mnemoduende",
|
"severaltalent": "Talento de Mnemoduende",
|
||||||
"singleattack": "Ataque Individual",
|
"singleattack": "Ataque Individual",
|
||||||
"enhance": "Mejorar",
|
"enhance": "Mejorar",
|
||||||
"summon": "Invocar",
|
"summon": "Invocar",
|
||||||
"mazeattack": "Ataque de Técnica",
|
"mazeattack": "Ataque de Técnica",
|
||||||
"blast": "Ráfaga",
|
"blast": "Ráfaga",
|
||||||
"restore": "Restaurar",
|
"restore": "Restaurar",
|
||||||
"support": "Soporte",
|
"support": "Soporte",
|
||||||
"aoeattack": "Ataque en Área",
|
"aoeattack": "Ataque en Área",
|
||||||
"impair": "Debilitación",
|
"impair": "Debilitación",
|
||||||
"bounce": "Rebote",
|
"bounce": "Rebote",
|
||||||
"active": "Activo",
|
"active": "Activo",
|
||||||
"defence": "Defensa",
|
"defence": "Defensa",
|
||||||
"inactive": "Inactivo",
|
"inactive": "Inactivo",
|
||||||
"maxAll": "Maximizar Todo",
|
"maxAll": "Maximizar Todo",
|
||||||
"maxAllSuccess": "Habilidades maximizadas con éxito.",
|
"maxAllSuccess": "Habilidades maximizadas con éxito.",
|
||||||
"maxAllFailed": "Fallo al maximizar habilidades.",
|
"maxAllFailed": "Fallo al maximizar habilidades.",
|
||||||
"noRelicEquipped": "Sin artefacto equipado",
|
"noRelicEquipped": "Sin artefacto equipado",
|
||||||
"anomalyArbitration": "Arbitraje Atípico",
|
"anomalyArbitration": "Arbitraje Atípico",
|
||||||
"normalMode": "Modo Normal",
|
"normalMode": "Modo Normal",
|
||||||
"hardMode": "Modo Difícil",
|
"hardMode": "Modo Difícil",
|
||||||
"selectPEAKEvent": "Seleccionar evento PEAK",
|
"selectPEAKEvent": "Seleccionar evento PEAK",
|
||||||
"mode": "Modo",
|
"mode": "Modo",
|
||||||
"selectMode": "Seleccionar un modo",
|
"selectMode": "Seleccionar un modo",
|
||||||
"rollBack": "Retroceder",
|
"rollBack": "Retroceder",
|
||||||
"upRoll": "Subir Mejora",
|
"upRoll": "Subir Mejora",
|
||||||
"downRoll": "Bajar Mejora",
|
"downRoll": "Bajar Mejora",
|
||||||
"actions": "Acciones",
|
"actions": "Acciones",
|
||||||
"avatars": "Avatares",
|
"avatars": "Avatares",
|
||||||
"quickView": "Vista Rápida",
|
"quickView": "Vista Rápida",
|
||||||
"extraSetting": "Ajustes Extra",
|
"extraSetting": "Ajustes Extra",
|
||||||
"disableCensorship": "Desactivar Censura",
|
"disableCensorship": "Desactivar Censura",
|
||||||
"hideUI": "Ocultar UI",
|
"hideUI": "Ocultar UI",
|
||||||
"theoryCraftMode": "Modo Theorycraft",
|
"theoryCraftMode": "Modo Theorycraft",
|
||||||
"cycleCount": "Conteo de Ciclos",
|
"cycleCount": "Conteo de Ciclos",
|
||||||
"pleaseSelectAllSubStats": "Por favor selecciona todas las sub-estadísticas",
|
"pleaseSelectAllSubStats": "Por favor selecciona todas las sub-estadísticas",
|
||||||
"subStatRollCountCannotBeZero": "El conteo de mejoras no puede ser cero",
|
"subStatRollCountCannotBeZero": "El conteo de mejoras no puede ser cero",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Personaje Multivía",
|
"multipathCharacter": "Personaje Multivía",
|
||||||
"mainPath": "Vía Principal",
|
"mainPath": "Vía Principal",
|
||||||
"march7Path": "Vía de Siete de Marzo",
|
"march7Path": "Vía de Siete de Marzo",
|
||||||
"challenge": "Desafío",
|
"challenge": "Desafío",
|
||||||
"skipNode": "Saltar Nodo",
|
"skipNode": "Saltar Nodo",
|
||||||
"disableSkip": "Desactivar salto",
|
"disableSkip": "Desactivar salto",
|
||||||
"skipNode1": "Saltar nodo 1",
|
"skipNode1": "Saltar nodo 1",
|
||||||
"skipNode2": "Saltar nodo 2",
|
"skipNode2": "Saltar nodo 2",
|
||||||
"extraFeatures": "Características Extra",
|
"extraFeatures": "Características Extra",
|
||||||
"detailTheoryCraft": "Permite personalizar los ciclos y la vida del enemigo en los ajustes.",
|
"detailTheoryCraft": "Permite personalizar los ciclos y la vida del enemigo en los ajustes.",
|
||||||
"detailSkipNode": "Permite saltar el Nodo 1 o 2 en Memoria del Caos o Pura Ficción.",
|
"detailSkipNode": "Permite saltar el Nodo 1 o 2 en Memoria del Caos o Pura Ficción.",
|
||||||
"detailChallengePeak": "Permite cambiar la temporada actual de Arbitraje Atípico.",
|
"detailChallengePeak": "Permite cambiar la temporada actual de Arbitraje Atípico.",
|
||||||
"detailHiddenUi": "Oculta la interfaz del juego.",
|
"detailHiddenUi": "Oculta la interfaz del juego.",
|
||||||
"detailDisableCensorship": "Desactiva la censura dentro del juego.",
|
"detailDisableCensorship": "Desactiva la censura dentro del juego.",
|
||||||
"detailMultipathCharacter": "Permite cambiar la Vía de algunos personajes.",
|
"detailMultipathCharacter": "Permite cambiar la Vía de algunos personajes.",
|
||||||
"trailblazer": "Trazacaminos",
|
"trailblazer": "Trazacaminos",
|
||||||
"listExtraEffect": "Lista de Efectos Extra",
|
"listExtraEffect": "Lista de Efectos Extra",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"customLineup": "Alineación de equipo personalizada"
|
"customLineup": "Alineación de equipo personalizada"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Outils Firefly par Firefly Shelter"
|
"description": "Outils Firefly par Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Type de Compétence",
|
"skillType": "Type de Compétence",
|
||||||
"skillName": "Nom de Compétence",
|
"skillName": "Nom de Compétence",
|
||||||
"character": "Personnage",
|
"character": "Personnage",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"path": "Voie",
|
"path": "Voie",
|
||||||
"rarity": "Rareté",
|
"rarity": "Rareté",
|
||||||
"element": "Élément",
|
"element": "Élément",
|
||||||
"technique": "Technique",
|
"technique": "Technique",
|
||||||
"talent": "Talent",
|
"talent": "Talent",
|
||||||
"basic": "Attaque Normale",
|
"basic": "Attaque Normale",
|
||||||
"skill": "Compétence",
|
"skill": "Compétence",
|
||||||
"ultimate": "Ultime",
|
"ultimate": "Ultime",
|
||||||
"servant": "Serviteur",
|
"servant": "Serviteur",
|
||||||
"damage": "Dégâts",
|
"damage": "Dégâts",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"warrior": "La Destruction",
|
"warrior": "La Destruction",
|
||||||
"knight": "La Préservation",
|
"knight": "La Préservation",
|
||||||
"mage": "L'Érudition",
|
"mage": "L'Érudition",
|
||||||
"priest": "L'Abondance",
|
"priest": "L'Abondance",
|
||||||
"rogue": "La Chasse",
|
"rogue": "La Chasse",
|
||||||
"shaman": "L'Harmonie",
|
"shaman": "L'Harmonie",
|
||||||
"warlock": "La Nihilité",
|
"warlock": "La Nihilité",
|
||||||
"memory": "Le Souvenir",
|
"memory": "Le Souvenir",
|
||||||
"elation": "L'Allégresse",
|
"elation": "L'Allégresse",
|
||||||
"fire": "Feu",
|
"fire": "Feu",
|
||||||
"ice": "Glace",
|
"ice": "Glace",
|
||||||
"imaginary": "Imaginaire",
|
"imaginary": "Imaginaire",
|
||||||
"physical": "Physique",
|
"physical": "Physique",
|
||||||
"quantum": "Quantique",
|
"quantum": "Quantique",
|
||||||
"thunder": "Foudre",
|
"thunder": "Foudre",
|
||||||
"wind": "Vent",
|
"wind": "Vent",
|
||||||
"hp": "PV",
|
"hp": "PV",
|
||||||
"atk": "ATQ",
|
"atk": "ATQ",
|
||||||
"speed": "VIT",
|
"speed": "VIT",
|
||||||
"critRate": "Taux CRIT",
|
"critRate": "Taux CRIT",
|
||||||
"critDmg": "DGT CRIT",
|
"critDmg": "DGT CRIT",
|
||||||
"breakEffect": "Effet de Rupture",
|
"breakEffect": "Effet de Rupture",
|
||||||
"effectRes": "RÉS aux effets",
|
"effectRes": "RÉS aux effets",
|
||||||
"energyRegenerationRate": "Taux de régénération d'énergie",
|
"energyRegenerationRate": "Taux de régénération d'énergie",
|
||||||
"effectHitRate": "Chances d'application d'effets",
|
"effectHitRate": "Chances d'application d'effets",
|
||||||
"outgoingHealingBoost": "Augmentation des soins",
|
"outgoingHealingBoost": "Augmentation des soins",
|
||||||
"fireDmgBoost": "Bonus de dégâts de Feu",
|
"fireDmgBoost": "Bonus de dégâts de Feu",
|
||||||
"iceDmgBoost": "Bonus de dégâts de Glace",
|
"iceDmgBoost": "Bonus de dégâts de Glace",
|
||||||
"imaginaryDmgBoost": "Bonus de dégâts Imaginaire",
|
"imaginaryDmgBoost": "Bonus de dégâts Imaginaire",
|
||||||
"physicalDmgBoost": "Bonus de dégâts Physique",
|
"physicalDmgBoost": "Bonus de dégâts Physique",
|
||||||
"quantumDmgBoost": "Bonus de dégâts Quantique",
|
"quantumDmgBoost": "Bonus de dégâts Quantique",
|
||||||
"thunderDmgBoost": "Bonus de dégâts de Foudre",
|
"thunderDmgBoost": "Bonus de dégâts de Foudre",
|
||||||
"windDmgBoost": "Bonus de dégâts de Vent",
|
"windDmgBoost": "Bonus de dégâts de Vent",
|
||||||
"pursued": "Dégâts additionnels",
|
"pursued": "Dégâts additionnels",
|
||||||
"true damage": "Dégâts bruts",
|
"true damage": "Dégâts bruts",
|
||||||
"elationdamage": "Dégâts d'Allégresse",
|
"elationdamage": "Dégâts d'Allégresse",
|
||||||
"follow-up": "Dégâts d'attaque de suivi",
|
"follow-up": "Dégâts d'attaque de suivi",
|
||||||
"elemental damage": "Dégâts de Rupture et Super Rupture",
|
"elemental damage": "Dégâts de Rupture et Super Rupture",
|
||||||
"dot": "Dégâts sur la durée",
|
"dot": "Dégâts sur la durée",
|
||||||
"qte": "Compétence QTE",
|
"qte": "Compétence QTE",
|
||||||
"level": "Niveau",
|
"level": "Niveau",
|
||||||
"relics": "Reliques",
|
"relics": "Reliques",
|
||||||
"eidolons": "Eidolons",
|
"eidolons": "Eidolons",
|
||||||
"lightcones": "Cônes de Lumière",
|
"lightcones": "Cônes de Lumière",
|
||||||
"loadData": "Charger les données",
|
"loadData": "Charger les données",
|
||||||
"exportData": "Exporter les données",
|
"exportData": "Exporter les données",
|
||||||
"connectSetting": "Paramètres de connexion",
|
"connectSetting": "Paramètres de connexion",
|
||||||
"connected": "Connecté",
|
"connected": "Connecté",
|
||||||
"unconnected": "Déconnecté",
|
"unconnected": "Déconnecté",
|
||||||
"psConnection": "Connexion PS",
|
"psConnection": "Connexion PS",
|
||||||
"connectionType": "Type de connexion",
|
"connectionType": "Type de connexion",
|
||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
"connectPs": "Connecter PS",
|
"connectPs": "Connecter PS",
|
||||||
"disconnect": "Déconnecter",
|
"disconnect": "Déconnecter",
|
||||||
"other": "Autre",
|
"other": "Autre",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Base de données",
|
"database": "Base de données",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Paramètre de monstres",
|
"monsterSetting": "Paramètre de monstres",
|
||||||
"serverUrl": "URL du Serveur",
|
"serverUrl": "URL du Serveur",
|
||||||
"privateType": "Type privé",
|
"privateType": "Type privé",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
"server": "Serveur",
|
"server": "Serveur",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"placeholderServerUrl": "Entrer l'URL du serveur",
|
"placeholderServerUrl": "Entrer l'URL du serveur",
|
||||||
"placeholderUsername": "Entrer le nom d'utilisateur",
|
"placeholderUsername": "Entrer le nom d'utilisateur",
|
||||||
"placeholderPassword": "Entrer le mot de passe",
|
"placeholderPassword": "Entrer le mot de passe",
|
||||||
"connectedSuccess": "Connecté au PS avec succès",
|
"connectedSuccess": "Connecté au PS avec succès",
|
||||||
"connectedFailed": "Échec de connexion au PS",
|
"connectedFailed": "Échec de connexion au PS",
|
||||||
"syncSuccess": "Données synchronisées au PS avec succès",
|
"syncSuccess": "Données synchronisées au PS avec succès",
|
||||||
"syncFailed": "Échec de synchronisation au PS",
|
"syncFailed": "Échec de synchronisation au PS",
|
||||||
"sync": "Synchroniser",
|
"sync": "Synchroniser",
|
||||||
"importSetting": "Paramètres d'importation",
|
"importSetting": "Paramètres d'importation",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"default": "Défaut",
|
"default": "Défaut",
|
||||||
"copyProfiles": "Copier les profils",
|
"copyProfiles": "Copier les profils",
|
||||||
"addNewProfile": "Ajouter un nouveau profil",
|
"addNewProfile": "Ajouter un nouveau profil",
|
||||||
"createNewProfile": "Créer un nouveau profil",
|
"createNewProfile": "Créer un nouveau profil",
|
||||||
"editProfile": "Éditer le profil",
|
"editProfile": "Éditer le profil",
|
||||||
"placeholderProfileName": "Entrer le nom du profil",
|
"placeholderProfileName": "Entrer le nom du profil",
|
||||||
"profileName": "Nom du profil",
|
"profileName": "Nom du profil",
|
||||||
"create": "Créer",
|
"create": "Créer",
|
||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
"characterInformation": "Informations du personnage",
|
"characterInformation": "Informations du personnage",
|
||||||
"skills": "Compétences",
|
"skills": "Compétences",
|
||||||
"showcaseCard": "Carte de présentation",
|
"showcaseCard": "Carte de présentation",
|
||||||
"comingSoon": "Bientôt disponible",
|
"comingSoon": "Bientôt disponible",
|
||||||
"characterName": "Nom du personnage",
|
"characterName": "Nom du personnage",
|
||||||
"placeholderCharacter": "Entrer le nom du personnage",
|
"placeholderCharacter": "Entrer le nom du personnage",
|
||||||
"characterSettings": "Paramètres du personnage",
|
"characterSettings": "Paramètres du personnage",
|
||||||
"levelConfiguration": "Configuration de niveau",
|
"levelConfiguration": "Configuration de niveau",
|
||||||
"characterLevel": "Niveau du personnage",
|
"characterLevel": "Niveau du personnage",
|
||||||
"max": "MAX",
|
"max": "MAX",
|
||||||
"ultimateEnergy": "Énergie de l'Ultime",
|
"ultimateEnergy": "Énergie de l'Ultime",
|
||||||
"currentEnergy": "Énergie actuelle",
|
"currentEnergy": "Énergie actuelle",
|
||||||
"setTo50": "Régler sur 50%",
|
"setTo50": "Régler sur 50%",
|
||||||
"battleConfiguration": "Configuration de combat",
|
"battleConfiguration": "Configuration de combat",
|
||||||
"useTechnique": "Utiliser Technique",
|
"useTechnique": "Utiliser Technique",
|
||||||
"techniqueNote": "Activer les effets de technique avant le combat",
|
"techniqueNote": "Activer les effets de technique avant le combat",
|
||||||
"enhancement": "Amélioration",
|
"enhancement": "Amélioration",
|
||||||
"enhancementLevel": "Niveau d'amélioration",
|
"enhancementLevel": "Niveau d'amélioration",
|
||||||
"origin": "Origine",
|
"origin": "Origine",
|
||||||
"enhancedNote": "Les améliorations élevées débloquent des compétences",
|
"enhancedNote": "Les améliorations élevées débloquent des compétences",
|
||||||
"lightconeEquipment": "Équipement de Cône de Lumière",
|
"lightconeEquipment": "Équipement de Cône de Lumière",
|
||||||
"lightconeSettings": "Paramètres de Cône de Lumière",
|
"lightconeSettings": "Paramètres de Cône de Lumière",
|
||||||
"placeholderLevel": "Entrer le niveau",
|
"placeholderLevel": "Entrer le niveau",
|
||||||
"superimpositionRank": "Rang de Superposition",
|
"superimpositionRank": "Rang de Superposition",
|
||||||
"ranksNote": "Les rangs élevés offrent des effets plus forts",
|
"ranksNote": "Les rangs élevés offrent des effets plus forts",
|
||||||
"changeLightcone": "Changer de Cône de Lumière",
|
"changeLightcone": "Changer de Cône de Lumière",
|
||||||
"removeLightcone": "Retirer le Cône de Lumière",
|
"removeLightcone": "Retirer le Cône de Lumière",
|
||||||
"equipLightcone": "Équiper un Cône de Lumière",
|
"equipLightcone": "Équiper un Cône de Lumière",
|
||||||
"noLightconeEquipped": "Aucun Cône de Lumière",
|
"noLightconeEquipped": "Aucun Cône de Lumière",
|
||||||
"equipLightconeNote": "Équipez un cône pour renforcer votre personnage",
|
"equipLightconeNote": "Équipez un cône pour renforcer votre personnage",
|
||||||
"filter": "Filtre",
|
"filter": "Filtre",
|
||||||
"selectedCharacters": "Personnages sélectionnés",
|
"selectedCharacters": "Personnages sélectionnés",
|
||||||
"selectedProfiles": "Profils sélectionnés",
|
"selectedProfiles": "Profils sélectionnés",
|
||||||
"clearAll": "Tout effacer",
|
"clearAll": "Tout effacer",
|
||||||
"selectAll": "Tout sélectionner",
|
"selectAll": "Tout sélectionner",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"copied": "Copié",
|
"copied": "Copié",
|
||||||
"noAvatarSelected": "Aucun personnage sélectionné",
|
"noAvatarSelected": "Aucun personnage sélectionné",
|
||||||
"noAvatarToCopySelected": "Aucun personnage à copier sélectionné",
|
"noAvatarToCopySelected": "Aucun personnage à copier sélectionné",
|
||||||
"pleaseSelectAtLeastOneProfile": "Veuillez sélectionner au moins un profil",
|
"pleaseSelectAtLeastOneProfile": "Veuillez sélectionner au moins un profil",
|
||||||
"pleaseEnterUid": "Veuillez entrer un UID",
|
"pleaseEnterUid": "Veuillez entrer un UID",
|
||||||
"failedToFetchEnkaData": "Échec de récupération des données Enka",
|
"failedToFetchEnkaData": "Échec de récupération des données Enka",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Veuillez sélectionner au moins un personnage",
|
"pleaseSelectAtLeastOneCharacter": "Veuillez sélectionner au moins un personnage",
|
||||||
"noDataToImport": "Aucune donnée à importer",
|
"noDataToImport": "Aucune donnée à importer",
|
||||||
"pleaseSelectAFile": "Veuillez sélectionner un fichier",
|
"pleaseSelectAFile": "Veuillez sélectionner un fichier",
|
||||||
"fileMustBeAValidJsonFile": "Le fichier doit être un JSON valide",
|
"fileMustBeAValidJsonFile": "Le fichier doit être un JSON valide",
|
||||||
"importEnkaDataSuccess": "Importation Enka réussie",
|
"importEnkaDataSuccess": "Importation Enka réussie",
|
||||||
"importFreeSRDataSuccess": "Importation FreeSR réussie",
|
"importFreeSRDataSuccess": "Importation FreeSR réussie",
|
||||||
"importDatabaseSuccess": "Importation de base de données réussie",
|
"importDatabaseSuccess": "Importation de base de données réussie",
|
||||||
"getData": "Obtenir des données",
|
"getData": "Obtenir des données",
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
"freeSRImport": "Importation FreeSR",
|
"freeSRImport": "Importation FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Supporte uniquement les fichiers JSON FreeSR",
|
"onlySupportFreeSRJsonFile": "Supporte uniquement les fichiers JSON FreeSR",
|
||||||
"pickAFile": "Choisir un fichier",
|
"pickAFile": "Choisir un fichier",
|
||||||
"lightConeSetting": "Paramètre de Cône de Lumière",
|
"lightConeSetting": "Paramètre de Cône de Lumière",
|
||||||
"relicMaker": "Créateur de Reliques",
|
"relicMaker": "Créateur de Reliques",
|
||||||
"pleaseSelectAllOptions": "Veuillez sélectionner toutes les options",
|
"pleaseSelectAllOptions": "Veuillez sélectionner toutes les options",
|
||||||
"relicSavedSuccessfully": "Relique sauvegardée avec succès",
|
"relicSavedSuccessfully": "Relique sauvegardée avec succès",
|
||||||
"mainSettings": "Paramètres principaux",
|
"mainSettings": "Paramètres principaux",
|
||||||
"mainStat": "Statistique principale",
|
"mainStat": "Statistique principale",
|
||||||
"set": "Set",
|
"set": "Set",
|
||||||
"pleaseSelectASet": "Veuillez sélectionner un set",
|
"pleaseSelectASet": "Veuillez sélectionner un set",
|
||||||
"effectBonus": "Bonus d'effet",
|
"effectBonus": "Bonus d'effet",
|
||||||
"totalRoll": "Total d'améliorations",
|
"totalRoll": "Total d'améliorations",
|
||||||
"randomizeStats": "Statistiques aléatoires",
|
"randomizeStats": "Statistiques aléatoires",
|
||||||
"randomizeRolls": "Améliorations aléatoires",
|
"randomizeRolls": "Améliorations aléatoires",
|
||||||
"selectASubStat": "Sélectionner une stat secondaire",
|
"selectASubStat": "Sélectionner une stat secondaire",
|
||||||
"selectASet": "Sélectionner un set",
|
"selectASet": "Sélectionner un set",
|
||||||
"selectAMainStat": "Sélectionner une stat principale",
|
"selectAMainStat": "Sélectionner une stat principale",
|
||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"reset": "Réinitialiser",
|
"reset": "Réinitialiser",
|
||||||
"roll": "Amélioration",
|
"roll": "Amélioration",
|
||||||
"step": "Étape",
|
"step": "Étape",
|
||||||
"memoryOfChaos": "Mémoire du Chaos",
|
"memoryOfChaos": "Mémoire du Chaos",
|
||||||
"pureFiction": "Pure Fiction",
|
"pureFiction": "Pure Fiction",
|
||||||
"apocalypticShadow": "Ombre Apocalyptique",
|
"apocalypticShadow": "Ombre Apocalyptique",
|
||||||
"customEnemy": "Ennemi personnalisé",
|
"customEnemy": "Ennemi personnalisé",
|
||||||
"simulatedUniverse": "Univers Simulé",
|
"simulatedUniverse": "Univers Simulé",
|
||||||
"floor": "Étage",
|
"floor": "Étage",
|
||||||
"side": "Côté",
|
"side": "Côté",
|
||||||
"wave": "Vague",
|
"wave": "Vague",
|
||||||
"stage": "Niveau",
|
"stage": "Niveau",
|
||||||
"useCycleCount": "Utiliser le compteur de cycles ?",
|
"useCycleCount": "Utiliser le compteur de cycles ?",
|
||||||
"useTurbulenceBuff": "Utiliser le buff de turbulence ?",
|
"useTurbulenceBuff": "Utiliser le buff de turbulence ?",
|
||||||
"firstHalfEnemies": "Ennemis première moitié",
|
"firstHalfEnemies": "Ennemis première moitié",
|
||||||
"secondHalfEnemies": "Ennemis deuxième moitié",
|
"secondHalfEnemies": "Ennemis deuxième moitié",
|
||||||
"firstNodeEnemies": "Ennemis du nœud 1",
|
"firstNodeEnemies": "Ennemis du nœud 1",
|
||||||
"secondNodeEnemies": "Ennemis du nœud 2",
|
"secondNodeEnemies": "Ennemis du nœud 2",
|
||||||
"thirdNodeEnemies": "Ennemis du nœud 3",
|
"thirdNodeEnemies": "Ennemis du nœud 3",
|
||||||
"firstNode": "Nœud 1",
|
"firstNode": "Nœud 1",
|
||||||
"secondNode": "Nœud 2",
|
"secondNode": "Nœud 2",
|
||||||
"thirdNode": "Nœud 3",
|
"thirdNode": "Nœud 3",
|
||||||
"listEnemies": "Liste des ennemis",
|
"listEnemies": "Liste des ennemis",
|
||||||
"turbulenceBuff": "Buff de Turbulence",
|
"turbulenceBuff": "Buff de Turbulence",
|
||||||
"noEventSelected": "Aucun événement sélectionné",
|
"noEventSelected": "Aucun événement sélectionné",
|
||||||
"noTurbulenceBuff": "Aucun Buff de Turbulence",
|
"noTurbulenceBuff": "Aucun Buff de Turbulence",
|
||||||
"upper": "Supérieur",
|
"upper": "Supérieur",
|
||||||
"lower": "Inférieur",
|
"lower": "Inférieur",
|
||||||
"upperToLower": "Supérieur -> Inférieur",
|
"upperToLower": "Supérieur -> Inférieur",
|
||||||
"lowerToUpper": "Inférieur -> Supérieur",
|
"lowerToUpper": "Inférieur -> Supérieur",
|
||||||
"selectMOCEvent": "Sélectionner un événement MOC",
|
"selectMOCEvent": "Sélectionner un événement MOC",
|
||||||
"selectPFEvent": "Sélectionner un événement PF",
|
"selectPFEvent": "Sélectionner un événement PF",
|
||||||
"selectASEvent": "Sélectionner un événement AS",
|
"selectASEvent": "Sélectionner un événement AS",
|
||||||
"selectCEEvent": "Sélectionner un événement CE",
|
"selectCEEvent": "Sélectionner un événement CE",
|
||||||
"selectEvent": "Sélectionner un événement",
|
"selectEvent": "Sélectionner un événement",
|
||||||
"selectFloor": "Sélectionner un étage",
|
"selectFloor": "Sélectionner un étage",
|
||||||
"selectSide": "Sélectionner un côté",
|
"selectSide": "Sélectionner un côté",
|
||||||
"selectBuff": "Sélectionner un buff",
|
"selectBuff": "Sélectionner un buff",
|
||||||
"selectStage": "Sélectionner un niveau",
|
"selectStage": "Sélectionner un niveau",
|
||||||
"previous": "Précédent",
|
"previous": "Précédent",
|
||||||
"next": "Suivant",
|
"next": "Suivant",
|
||||||
"noMonstersFound": "Aucun monstre trouvé",
|
"noMonstersFound": "Aucun monstre trouvé",
|
||||||
"addNewWave": "Ajouter une nouvelle vague",
|
"addNewWave": "Ajouter une nouvelle vague",
|
||||||
"searchStage": "Chercher un niveau...",
|
"searchStage": "Chercher un niveau...",
|
||||||
"noStageFound": "Aucun niveau trouvé",
|
"noStageFound": "Aucun niveau trouvé",
|
||||||
"searchMonster": "Chercher un monstre...",
|
"searchMonster": "Chercher un monstre...",
|
||||||
"changeRelic": "Changer de relique",
|
"changeRelic": "Changer de relique",
|
||||||
"deleteRelic": "Supprimer la relique",
|
"deleteRelic": "Supprimer la relique",
|
||||||
"deleteRelicConfirm": "Êtes-vous sûr de vouloir supprimer la relique de cet emplacement ?",
|
"deleteRelicConfirm": "Êtes-vous sûr de vouloir supprimer la relique de cet emplacement ?",
|
||||||
"setEffects": "Définir les effets",
|
"setEffects": "Définir les effets",
|
||||||
"details": "Détails",
|
"details": "Détails",
|
||||||
"normal": "Attaque Normale",
|
"normal": "Attaque Normale",
|
||||||
"bpskill": "Compétence",
|
"bpskill": "Compétence",
|
||||||
"maze": "Technique",
|
"maze": "Technique",
|
||||||
"ultra": "Ultime",
|
"ultra": "Ultime",
|
||||||
"servantskill": "Compétence de Mémolutin",
|
"servantskill": "Compétence de Mémolutin",
|
||||||
"severaltalent": "Talent de Mémolutin",
|
"severaltalent": "Talent de Mémolutin",
|
||||||
"singleattack": "Attaque à cible unique",
|
"singleattack": "Attaque à cible unique",
|
||||||
"enhance": "Renforcement",
|
"enhance": "Renforcement",
|
||||||
"summon": "Invocation",
|
"summon": "Invocation",
|
||||||
"mazeattack": "Attaque de Technique",
|
"mazeattack": "Attaque de Technique",
|
||||||
"blast": "Diffusion",
|
"blast": "Diffusion",
|
||||||
"restore": "Restauration",
|
"restore": "Restauration",
|
||||||
"support": "Soutien",
|
"support": "Soutien",
|
||||||
"aoeattack": "Attaque de zone",
|
"aoeattack": "Attaque de zone",
|
||||||
"impair": "Malus",
|
"impair": "Malus",
|
||||||
"bounce": "Rebond",
|
"bounce": "Rebond",
|
||||||
"active": "Actif",
|
"active": "Actif",
|
||||||
"defence": "Défense",
|
"defence": "Défense",
|
||||||
"inactive": "Inactif",
|
"inactive": "Inactif",
|
||||||
"maxAll": "Tout maximiser",
|
"maxAll": "Tout maximiser",
|
||||||
"maxAllSuccess": "Compétences maximisées avec succès.",
|
"maxAllSuccess": "Compétences maximisées avec succès.",
|
||||||
"maxAllFailed": "Échec de maximisation.",
|
"maxAllFailed": "Échec de maximisation.",
|
||||||
"noRelicEquipped": "Aucune relique équipée",
|
"noRelicEquipped": "Aucune relique équipée",
|
||||||
"anomalyArbitration": "Arbitrage d'Anomalie",
|
"anomalyArbitration": "Arbitrage d'Anomalie",
|
||||||
"normalMode": "Mode Normal",
|
"normalMode": "Mode Normal",
|
||||||
"hardMode": "Mode Difficile",
|
"hardMode": "Mode Difficile",
|
||||||
"selectPEAKEvent": "Sélectionner un événement PEAK",
|
"selectPEAKEvent": "Sélectionner un événement PEAK",
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"selectMode": "Sélectionner un mode",
|
"selectMode": "Sélectionner un mode",
|
||||||
"rollBack": "Annuler l'étape",
|
"rollBack": "Annuler l'étape",
|
||||||
"upRoll": "Augmenter l'amélioration",
|
"upRoll": "Augmenter l'amélioration",
|
||||||
"downRoll": "Diminuer l'amélioration",
|
"downRoll": "Diminuer l'amélioration",
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"avatars": "Avatars",
|
"avatars": "Avatars",
|
||||||
"quickView": "Aperçu rapide",
|
"quickView": "Aperçu rapide",
|
||||||
"extraSetting": "Paramètres supplémentaires",
|
"extraSetting": "Paramètres supplémentaires",
|
||||||
"disableCensorship": "Désactiver la censure",
|
"disableCensorship": "Désactiver la censure",
|
||||||
"hideUI": "Cacher l'UI",
|
"hideUI": "Cacher l'UI",
|
||||||
"theoryCraftMode": "Mode Theorycraft",
|
"theoryCraftMode": "Mode Theorycraft",
|
||||||
"cycleCount": "Nombre de cycles",
|
"cycleCount": "Nombre de cycles",
|
||||||
"pleaseSelectAllSubStats": "Veuillez sélectionner toutes les stats secondaires",
|
"pleaseSelectAllSubStats": "Veuillez sélectionner toutes les stats secondaires",
|
||||||
"subStatRollCountCannotBeZero": "Les améliorations de stat secondaire ne peuvent être à zéro",
|
"subStatRollCountCannotBeZero": "Les améliorations de stat secondaire ne peuvent être à zéro",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Personnage Multi-Voies",
|
"multipathCharacter": "Personnage Multi-Voies",
|
||||||
"mainPath": "Voie Principale",
|
"mainPath": "Voie Principale",
|
||||||
"march7Path": "Voie de March 7th",
|
"march7Path": "Voie de March 7th",
|
||||||
"challenge": "Défi",
|
"challenge": "Défi",
|
||||||
"skipNode": "Passer le nœud",
|
"skipNode": "Passer le nœud",
|
||||||
"disableSkip": "Désactiver le passage",
|
"disableSkip": "Désactiver le passage",
|
||||||
"skipNode1": "Passer le nœud 1",
|
"skipNode1": "Passer le nœud 1",
|
||||||
"skipNode2": "Passer le nœud 2",
|
"skipNode2": "Passer le nœud 2",
|
||||||
"extraFeatures": "Fonctionnalités supplémentaires",
|
"extraFeatures": "Fonctionnalités supplémentaires",
|
||||||
"detailTheoryCraft": "Permet de personnaliser le nombre de cycles et d'ajuster les PV ennemis.",
|
"detailTheoryCraft": "Permet de personnaliser le nombre de cycles et d'ajuster les PV ennemis.",
|
||||||
"detailSkipNode": "Permet de passer le nœud (1 ou 2) dans Mémoire du Chaos ou Pure Fiction.",
|
"detailSkipNode": "Permet de passer le nœud (1 ou 2) dans Mémoire du Chaos ou Pure Fiction.",
|
||||||
"detailChallengePeak": "Permet de changer la saison de Peak dans l'anomalie actuelle.",
|
"detailChallengePeak": "Permet de changer la saison de Peak dans l'anomalie actuelle.",
|
||||||
"detailHiddenUi": "Cachera l'interface du jeu.",
|
"detailHiddenUi": "Cachera l'interface du jeu.",
|
||||||
"detailDisableCensorship": "Désactivera la censure du jeu.",
|
"detailDisableCensorship": "Désactivera la censure du jeu.",
|
||||||
"detailMultipathCharacter": "Permet de changer la Voie de certains personnages.",
|
"detailMultipathCharacter": "Permet de changer la Voie de certains personnages.",
|
||||||
"trailblazer": "Pionnier",
|
"trailblazer": "Pionnier",
|
||||||
"listExtraEffect": "Liste des effets supplémentaires",
|
"listExtraEffect": "Liste des effets supplémentaires",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"customLineup": "Composition personnalisée"
|
"customLineup": "Composition personnalisée"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Alat Firefly oleh Firefly Shelter"
|
"description": "Alat Firefly oleh Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Tipe Skill",
|
"skillType": "Tipe Skill",
|
||||||
"skillName": "Nama Skill",
|
"skillName": "Nama Skill",
|
||||||
"character": "Karakter",
|
"character": "Karakter",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
"rarity": "Rarity",
|
"rarity": "Rarity",
|
||||||
"element": "Elemen",
|
"element": "Elemen",
|
||||||
"technique": "Technique",
|
"technique": "Technique",
|
||||||
"talent": "Talent",
|
"talent": "Talent",
|
||||||
"basic": "Basic Attack",
|
"basic": "Basic Attack",
|
||||||
"skill": "Skill",
|
"skill": "Skill",
|
||||||
"ultimate": "Ultimate",
|
"ultimate": "Ultimate",
|
||||||
"servant": "Servant",
|
"servant": "Servant",
|
||||||
"damage": "Damage",
|
"damage": "Damage",
|
||||||
"type": "Tipe",
|
"type": "Tipe",
|
||||||
"warrior": "The Destruction",
|
"warrior": "The Destruction",
|
||||||
"knight": "The Preservation",
|
"knight": "The Preservation",
|
||||||
"mage": "The Erudition",
|
"mage": "The Erudition",
|
||||||
"priest": "The Abundance",
|
"priest": "The Abundance",
|
||||||
"rogue": "The Hunt",
|
"rogue": "The Hunt",
|
||||||
"shaman": "The Harmony",
|
"shaman": "The Harmony",
|
||||||
"warlock": "The Nihility",
|
"warlock": "The Nihility",
|
||||||
"memory": "The Remembrance",
|
"memory": "The Remembrance",
|
||||||
"elation": "The Elation",
|
"elation": "The Elation",
|
||||||
"fire": "Fire",
|
"fire": "Fire",
|
||||||
"ice": "Ice",
|
"ice": "Ice",
|
||||||
"imaginary": "Imaginary",
|
"imaginary": "Imaginary",
|
||||||
"physical": "Physical",
|
"physical": "Physical",
|
||||||
"quantum": "Quantum",
|
"quantum": "Quantum",
|
||||||
"thunder": "Lightning",
|
"thunder": "Lightning",
|
||||||
"wind": "Wind",
|
"wind": "Wind",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "ATK",
|
"atk": "ATK",
|
||||||
"speed": "SPD",
|
"speed": "SPD",
|
||||||
"critRate": "CRIT Rate",
|
"critRate": "CRIT Rate",
|
||||||
"critDmg": "CRIT DMG",
|
"critDmg": "CRIT DMG",
|
||||||
"breakEffect": "Break Effect",
|
"breakEffect": "Break Effect",
|
||||||
"effectRes": "Effect RES",
|
"effectRes": "Effect RES",
|
||||||
"energyRegenerationRate": "Energy Regeneration Rate",
|
"energyRegenerationRate": "Energy Regeneration Rate",
|
||||||
"effectHitRate": "Effect Hit Rate",
|
"effectHitRate": "Effect Hit Rate",
|
||||||
"outgoingHealingBoost": "Outgoing Healing Boost",
|
"outgoingHealingBoost": "Outgoing Healing Boost",
|
||||||
"fireDmgBoost": "Fire DMG Boost",
|
"fireDmgBoost": "Fire DMG Boost",
|
||||||
"iceDmgBoost": "Ice DMG Boost",
|
"iceDmgBoost": "Ice DMG Boost",
|
||||||
"imaginaryDmgBoost": "Imaginary DMG Boost",
|
"imaginaryDmgBoost": "Imaginary DMG Boost",
|
||||||
"physicalDmgBoost": "Physical DMG Boost",
|
"physicalDmgBoost": "Physical DMG Boost",
|
||||||
"quantumDmgBoost": "Quantum DMG Boost",
|
"quantumDmgBoost": "Quantum DMG Boost",
|
||||||
"thunderDmgBoost": "Lightning DMG Boost",
|
"thunderDmgBoost": "Lightning DMG Boost",
|
||||||
"windDmgBoost": "Wind DMG Boost",
|
"windDmgBoost": "Wind DMG Boost",
|
||||||
"pursued": "Additional Damage",
|
"pursued": "Additional Damage",
|
||||||
"true damage": "True Damage",
|
"true damage": "True Damage",
|
||||||
"elationdamage": "Elation Damage",
|
"elationdamage": "Elation Damage",
|
||||||
"follow-up": "Follow-up Attack Damage",
|
"follow-up": "Follow-up Attack Damage",
|
||||||
"elemental damage": "Break dan Super Break Damage",
|
"elemental damage": "Break dan Super Break Damage",
|
||||||
"dot": "Damage Over Time",
|
"dot": "Damage Over Time",
|
||||||
"qte": "QTE Skill",
|
"qte": "QTE Skill",
|
||||||
"level": "Level",
|
"level": "Level",
|
||||||
"relics": "Relic",
|
"relics": "Relic",
|
||||||
"eidolons": "Eidolon",
|
"eidolons": "Eidolon",
|
||||||
"lightcones": "Light Cone",
|
"lightcones": "Light Cone",
|
||||||
"loadData": "Muat data",
|
"loadData": "Muat data",
|
||||||
"exportData": "Ekspor data",
|
"exportData": "Ekspor data",
|
||||||
"connectSetting": "Pengaturan Koneksi",
|
"connectSetting": "Pengaturan Koneksi",
|
||||||
"connected": "Terhubung",
|
"connected": "Terhubung",
|
||||||
"unconnected": "Tidak terhubung",
|
"unconnected": "Tidak terhubung",
|
||||||
"psConnection": "Koneksi PS",
|
"psConnection": "Koneksi PS",
|
||||||
"connectionType": "Tipe Koneksi",
|
"connectionType": "Tipe Koneksi",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"connectPs": "Hubungkan PS",
|
"connectPs": "Hubungkan PS",
|
||||||
"disconnect": "Putuskan sambungan",
|
"disconnect": "Putuskan sambungan",
|
||||||
"other": "Lainnya",
|
"other": "Lainnya",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Database",
|
"database": "Database",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Pengaturan Monster",
|
"monsterSetting": "Pengaturan Monster",
|
||||||
"serverUrl": "URL Server",
|
"serverUrl": "URL Server",
|
||||||
"privateType": "Tipe Private",
|
"privateType": "Tipe Private",
|
||||||
"local": "Lokal",
|
"local": "Lokal",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"placeholderServerUrl": "Masukkan URL server",
|
"placeholderServerUrl": "Masukkan URL server",
|
||||||
"placeholderUsername": "Masukkan username",
|
"placeholderUsername": "Masukkan username",
|
||||||
"placeholderPassword": "Masukkan password",
|
"placeholderPassword": "Masukkan password",
|
||||||
"connectedSuccess": "Berhasil terhubung ke PS",
|
"connectedSuccess": "Berhasil terhubung ke PS",
|
||||||
"connectedFailed": "Gagal terhubung ke PS",
|
"connectedFailed": "Gagal terhubung ke PS",
|
||||||
"syncSuccess": "Berhasil sinkronisasi data ke PS",
|
"syncSuccess": "Berhasil sinkronisasi data ke PS",
|
||||||
"syncFailed": "Gagal sinkronisasi data ke PS",
|
"syncFailed": "Gagal sinkronisasi data ke PS",
|
||||||
"sync": "Sinkronisasi",
|
"sync": "Sinkronisasi",
|
||||||
"importSetting": "Pengaturan Impor",
|
"importSetting": "Pengaturan Impor",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"copyProfiles": "Salin profil",
|
"copyProfiles": "Salin profil",
|
||||||
"addNewProfile": "Tambah profil baru",
|
"addNewProfile": "Tambah profil baru",
|
||||||
"createNewProfile": "Buat profil baru",
|
"createNewProfile": "Buat profil baru",
|
||||||
"editProfile": "Edit profil",
|
"editProfile": "Edit profil",
|
||||||
"placeholderProfileName": "Masukkan nama profil",
|
"placeholderProfileName": "Masukkan nama profil",
|
||||||
"profileName": "Nama profil",
|
"profileName": "Nama profil",
|
||||||
"create": "Buat",
|
"create": "Buat",
|
||||||
"update": "Perbarui",
|
"update": "Perbarui",
|
||||||
"characterInformation": "Informasi Karakter",
|
"characterInformation": "Informasi Karakter",
|
||||||
"skills": "Skill",
|
"skills": "Skill",
|
||||||
"showcaseCard": "Showcase Card",
|
"showcaseCard": "Showcase Card",
|
||||||
"comingSoon": "Segera hadir",
|
"comingSoon": "Segera hadir",
|
||||||
"characterName": "Nama Karakter",
|
"characterName": "Nama Karakter",
|
||||||
"placeholderCharacter": "Masukkan nama karakter",
|
"placeholderCharacter": "Masukkan nama karakter",
|
||||||
"characterSettings": "Pengaturan Karakter",
|
"characterSettings": "Pengaturan Karakter",
|
||||||
"levelConfiguration": "Konfigurasi Level",
|
"levelConfiguration": "Konfigurasi Level",
|
||||||
"characterLevel": "Level Karakter",
|
"characterLevel": "Level Karakter",
|
||||||
"max": "MAX",
|
"max": "MAX",
|
||||||
"ultimateEnergy": "Energy Ultimate",
|
"ultimateEnergy": "Energy Ultimate",
|
||||||
"currentEnergy": "Energy Saat Ini",
|
"currentEnergy": "Energy Saat Ini",
|
||||||
"setTo50": "Atur ke 50%",
|
"setTo50": "Atur ke 50%",
|
||||||
"battleConfiguration": "Konfigurasi Pertempuran",
|
"battleConfiguration": "Konfigurasi Pertempuran",
|
||||||
"useTechnique": "Gunakan Technique",
|
"useTechnique": "Gunakan Technique",
|
||||||
"techniqueNote": "Aktifkan efek technique sebelum pertempuran",
|
"techniqueNote": "Aktifkan efek technique sebelum pertempuran",
|
||||||
"enhancement": "Enhancement",
|
"enhancement": "Enhancement",
|
||||||
"enhancementLevel": "Level Enhancement",
|
"enhancementLevel": "Level Enhancement",
|
||||||
"origin": "Asli",
|
"origin": "Asli",
|
||||||
"enhancedNote": "Enhancement lebih tinggi membuka kemampuan tambahan",
|
"enhancedNote": "Enhancement lebih tinggi membuka kemampuan tambahan",
|
||||||
"lightconeEquipment": "Peralatan Light Cone",
|
"lightconeEquipment": "Peralatan Light Cone",
|
||||||
"lightconeSettings": "Pengaturan Light Cone",
|
"lightconeSettings": "Pengaturan Light Cone",
|
||||||
"placeholderLevel": "Masukkan level",
|
"placeholderLevel": "Masukkan level",
|
||||||
"superimpositionRank": "Superimposition Rank",
|
"superimpositionRank": "Superimposition Rank",
|
||||||
"ranksNote": "Rank lebih tinggi memberikan efek lebih kuat",
|
"ranksNote": "Rank lebih tinggi memberikan efek lebih kuat",
|
||||||
"changeLightcone": "Ganti Light Cone",
|
"changeLightcone": "Ganti Light Cone",
|
||||||
"removeLightcone": "Lepas Light Cone",
|
"removeLightcone": "Lepas Light Cone",
|
||||||
"equipLightcone": "Pakai Light Cone",
|
"equipLightcone": "Pakai Light Cone",
|
||||||
"noLightconeEquipped": "Tidak Ada Light Cone",
|
"noLightconeEquipped": "Tidak Ada Light Cone",
|
||||||
"equipLightconeNote": "Pakai light cone untuk memperkuat karaktermu",
|
"equipLightconeNote": "Pakai light cone untuk memperkuat karaktermu",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"selectedCharacters": "Karakter Dipilih",
|
"selectedCharacters": "Karakter Dipilih",
|
||||||
"selectedProfiles": "Profil Dipilih",
|
"selectedProfiles": "Profil Dipilih",
|
||||||
"clearAll": "Bersihkan Semua",
|
"clearAll": "Bersihkan Semua",
|
||||||
"selectAll": "Pilih Semua",
|
"selectAll": "Pilih Semua",
|
||||||
"copy": "Salin",
|
"copy": "Salin",
|
||||||
"copied": "Tersalin",
|
"copied": "Tersalin",
|
||||||
"noAvatarSelected": "Tidak ada karakter dipilih",
|
"noAvatarSelected": "Tidak ada karakter dipilih",
|
||||||
"noAvatarToCopySelected": "Tidak ada karakter untuk disalin",
|
"noAvatarToCopySelected": "Tidak ada karakter untuk disalin",
|
||||||
"pleaseSelectAtLeastOneProfile": "Pilih setidaknya satu profil",
|
"pleaseSelectAtLeastOneProfile": "Pilih setidaknya satu profil",
|
||||||
"pleaseEnterUid": "Silakan masukkan UID",
|
"pleaseEnterUid": "Silakan masukkan UID",
|
||||||
"failedToFetchEnkaData": "Gagal mengambil data Enka",
|
"failedToFetchEnkaData": "Gagal mengambil data Enka",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Pilih setidaknya satu karakter",
|
"pleaseSelectAtLeastOneCharacter": "Pilih setidaknya satu karakter",
|
||||||
"noDataToImport": "Tidak ada data untuk diimpor",
|
"noDataToImport": "Tidak ada data untuk diimpor",
|
||||||
"pleaseSelectAFile": "Silakan pilih file",
|
"pleaseSelectAFile": "Silakan pilih file",
|
||||||
"fileMustBeAValidJsonFile": "File harus berupa file JSON yang valid",
|
"fileMustBeAValidJsonFile": "File harus berupa file JSON yang valid",
|
||||||
"importEnkaDataSuccess": "Impor data Enka berhasil",
|
"importEnkaDataSuccess": "Impor data Enka berhasil",
|
||||||
"importFreeSRDataSuccess": "Impor data FreeSR berhasil",
|
"importFreeSRDataSuccess": "Impor data FreeSR berhasil",
|
||||||
"importDatabaseSuccess": "Impor database berhasil",
|
"importDatabaseSuccess": "Impor database berhasil",
|
||||||
"getData": "Dapatkan Data",
|
"getData": "Dapatkan Data",
|
||||||
"import": "Impor",
|
"import": "Impor",
|
||||||
"freeSRImport": "Impor FreeSR",
|
"freeSRImport": "Impor FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Hanya mendukung file JSON FreeSR",
|
"onlySupportFreeSRJsonFile": "Hanya mendukung file JSON FreeSR",
|
||||||
"pickAFile": "Pilih sebuah file",
|
"pickAFile": "Pilih sebuah file",
|
||||||
"lightConeSetting": "Pengaturan Light Cone",
|
"lightConeSetting": "Pengaturan Light Cone",
|
||||||
"relicMaker": "Pembuat Relic",
|
"relicMaker": "Pembuat Relic",
|
||||||
"pleaseSelectAllOptions": "Silakan pilih semua opsi",
|
"pleaseSelectAllOptions": "Silakan pilih semua opsi",
|
||||||
"relicSavedSuccessfully": "Relic berhasil disimpan",
|
"relicSavedSuccessfully": "Relic berhasil disimpan",
|
||||||
"mainSettings": "Pengaturan Utama",
|
"mainSettings": "Pengaturan Utama",
|
||||||
"mainStat": "Statistik Utama",
|
"mainStat": "Statistik Utama",
|
||||||
"set": "Set",
|
"set": "Set",
|
||||||
"pleaseSelectASet": "Pilih sebuah set",
|
"pleaseSelectASet": "Pilih sebuah set",
|
||||||
"effectBonus": "Bonus Efek",
|
"effectBonus": "Bonus Efek",
|
||||||
"totalRoll": "Total Roll",
|
"totalRoll": "Total Roll",
|
||||||
"randomizeStats": "Acak Statistik",
|
"randomizeStats": "Acak Statistik",
|
||||||
"randomizeRolls": "Acak Roll",
|
"randomizeRolls": "Acak Roll",
|
||||||
"selectASubStat": "Pilih sub stat",
|
"selectASubStat": "Pilih sub stat",
|
||||||
"selectASet": "Pilih sebuah set",
|
"selectASet": "Pilih sebuah set",
|
||||||
"selectAMainStat": "Pilih stat utama",
|
"selectAMainStat": "Pilih stat utama",
|
||||||
"save": "Simpan",
|
"save": "Simpan",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"step": "Langkah",
|
"step": "Langkah",
|
||||||
"memoryOfChaos": "Memory of Chaos",
|
"memoryOfChaos": "Memory of Chaos",
|
||||||
"pureFiction": "Pure Fiction",
|
"pureFiction": "Pure Fiction",
|
||||||
"apocalypticShadow": "Apocalyptic Shadow",
|
"apocalypticShadow": "Apocalyptic Shadow",
|
||||||
"customEnemy": "Musuh Kustom",
|
"customEnemy": "Musuh Kustom",
|
||||||
"simulatedUniverse": "Simulated Universe",
|
"simulatedUniverse": "Simulated Universe",
|
||||||
"floor": "Lantai",
|
"floor": "Lantai",
|
||||||
"side": "Sisi",
|
"side": "Sisi",
|
||||||
"wave": "Gelombang",
|
"wave": "Gelombang",
|
||||||
"stage": "Tahap",
|
"stage": "Tahap",
|
||||||
"useCycleCount": "Gunakan hitungan siklus?",
|
"useCycleCount": "Gunakan hitungan siklus?",
|
||||||
"useTurbulenceBuff": "Gunakan buff turbulence?",
|
"useTurbulenceBuff": "Gunakan buff turbulence?",
|
||||||
"firstHalfEnemies": "Musuh paruh pertama",
|
"firstHalfEnemies": "Musuh paruh pertama",
|
||||||
"secondHalfEnemies": "Musuh paruh kedua",
|
"secondHalfEnemies": "Musuh paruh kedua",
|
||||||
"firstNodeEnemies": "Musuh Node 1",
|
"firstNodeEnemies": "Musuh Node 1",
|
||||||
"secondNodeEnemies": "Musuh Node 2",
|
"secondNodeEnemies": "Musuh Node 2",
|
||||||
"thirdNodeEnemies": "Musuh Node 3",
|
"thirdNodeEnemies": "Musuh Node 3",
|
||||||
"firstNode": "Node 1",
|
"firstNode": "Node 1",
|
||||||
"secondNode": "Node 2",
|
"secondNode": "Node 2",
|
||||||
"thirdNode": "Node 3",
|
"thirdNode": "Node 3",
|
||||||
"listEnemies": "Daftar musuh",
|
"listEnemies": "Daftar musuh",
|
||||||
"turbulenceBuff": "Turbulence Buff",
|
"turbulenceBuff": "Turbulence Buff",
|
||||||
"noEventSelected": "Tidak ada event dipilih",
|
"noEventSelected": "Tidak ada event dipilih",
|
||||||
"noTurbulenceBuff": "Tanpa Turbulence Buff",
|
"noTurbulenceBuff": "Tanpa Turbulence Buff",
|
||||||
"upper": "Atas",
|
"upper": "Atas",
|
||||||
"lower": "Bawah",
|
"lower": "Bawah",
|
||||||
"upperToLower": "Atas -> Bawah",
|
"upperToLower": "Atas -> Bawah",
|
||||||
"lowerToUpper": "Bawah -> Atas",
|
"lowerToUpper": "Bawah -> Atas",
|
||||||
"selectMOCEvent": "Pilih Event MOC",
|
"selectMOCEvent": "Pilih Event MOC",
|
||||||
"selectPFEvent": "Pilih Event PF",
|
"selectPFEvent": "Pilih Event PF",
|
||||||
"selectASEvent": "Pilih Event AS",
|
"selectASEvent": "Pilih Event AS",
|
||||||
"selectCEEvent": "Pilih Event CE",
|
"selectCEEvent": "Pilih Event CE",
|
||||||
"selectEvent": "Pilih Event",
|
"selectEvent": "Pilih Event",
|
||||||
"selectFloor": "Pilih Lantai",
|
"selectFloor": "Pilih Lantai",
|
||||||
"selectSide": "Pilih Sisi",
|
"selectSide": "Pilih Sisi",
|
||||||
"selectBuff": "Pilih Buff",
|
"selectBuff": "Pilih Buff",
|
||||||
"selectStage": "Pilih Tahap",
|
"selectStage": "Pilih Tahap",
|
||||||
"previous": "Sebelumnya",
|
"previous": "Sebelumnya",
|
||||||
"next": "Selanjutnya",
|
"next": "Selanjutnya",
|
||||||
"noMonstersFound": "Tidak ada monster ditemukan",
|
"noMonstersFound": "Tidak ada monster ditemukan",
|
||||||
"addNewWave": "Tambah Gelombang Baru",
|
"addNewWave": "Tambah Gelombang Baru",
|
||||||
"searchStage": "Cari tahap...",
|
"searchStage": "Cari tahap...",
|
||||||
"noStageFound": "Tahap tidak ditemukan",
|
"noStageFound": "Tahap tidak ditemukan",
|
||||||
"searchMonster": "Cari monster...",
|
"searchMonster": "Cari monster...",
|
||||||
"changeRelic": "Ganti relic",
|
"changeRelic": "Ganti relic",
|
||||||
"deleteRelic": "Hapus relic",
|
"deleteRelic": "Hapus relic",
|
||||||
"deleteRelicConfirm": "Yakin ingin menghapus relic di slot ini?",
|
"deleteRelicConfirm": "Yakin ingin menghapus relic di slot ini?",
|
||||||
"setEffects": "Atur Efek",
|
"setEffects": "Atur Efek",
|
||||||
"details": "Detail",
|
"details": "Detail",
|
||||||
"normal": "Basic ATK",
|
"normal": "Basic ATK",
|
||||||
"bpskill": "Skill",
|
"bpskill": "Skill",
|
||||||
"maze": "Technique",
|
"maze": "Technique",
|
||||||
"ultra": "Ultimate",
|
"ultra": "Ultimate",
|
||||||
"servantskill": "Skill Memosprite",
|
"servantskill": "Skill Memosprite",
|
||||||
"severaltalent": "Talent Memosprite",
|
"severaltalent": "Talent Memosprite",
|
||||||
"singleattack": "Single Attack",
|
"singleattack": "Single Attack",
|
||||||
"enhance": "Enhance",
|
"enhance": "Enhance",
|
||||||
"summon": "Summon",
|
"summon": "Summon",
|
||||||
"mazeattack": "Technique Attack",
|
"mazeattack": "Technique Attack",
|
||||||
"blast": "Blast",
|
"blast": "Blast",
|
||||||
"restore": "Restore",
|
"restore": "Restore",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
"aoeattack": "AoE Attack",
|
"aoeattack": "AoE Attack",
|
||||||
"impair": "Impair",
|
"impair": "Impair",
|
||||||
"bounce": "Bounce",
|
"bounce": "Bounce",
|
||||||
"active": "Aktif",
|
"active": "Aktif",
|
||||||
"defence": "Defence",
|
"defence": "Defence",
|
||||||
"inactive": "Nonaktif",
|
"inactive": "Nonaktif",
|
||||||
"maxAll": "Maksimalkan Semua",
|
"maxAll": "Maksimalkan Semua",
|
||||||
"maxAllSuccess": "Berhasil memaksimalkan level skill.",
|
"maxAllSuccess": "Berhasil memaksimalkan level skill.",
|
||||||
"maxAllFailed": "Gagal memaksimalkan level skill.",
|
"maxAllFailed": "Gagal memaksimalkan level skill.",
|
||||||
"noRelicEquipped": "Tidak ada relic dipakai",
|
"noRelicEquipped": "Tidak ada relic dipakai",
|
||||||
"anomalyArbitration": "Anomaly Arbitration",
|
"anomalyArbitration": "Anomaly Arbitration",
|
||||||
"normalMode": "Mode Normal",
|
"normalMode": "Mode Normal",
|
||||||
"hardMode": "Mode Sulit",
|
"hardMode": "Mode Sulit",
|
||||||
"selectPEAKEvent": "Pilih Event PEAK",
|
"selectPEAKEvent": "Pilih Event PEAK",
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"selectMode": "Pilih Mode",
|
"selectMode": "Pilih Mode",
|
||||||
"rollBack": "Roll Back",
|
"rollBack": "Roll Back",
|
||||||
"upRoll": "Up Roll",
|
"upRoll": "Up Roll",
|
||||||
"downRoll": "Down Roll",
|
"downRoll": "Down Roll",
|
||||||
"actions": "Aksi",
|
"actions": "Aksi",
|
||||||
"avatars": "Avatar",
|
"avatars": "Avatar",
|
||||||
"quickView": "Tampilan Cepat",
|
"quickView": "Tampilan Cepat",
|
||||||
"extraSetting": "Pengaturan Ekstra",
|
"extraSetting": "Pengaturan Ekstra",
|
||||||
"disableCensorship": "Nonaktifkan Sensor",
|
"disableCensorship": "Nonaktifkan Sensor",
|
||||||
"hideUI": "Sembunyikan UI",
|
"hideUI": "Sembunyikan UI",
|
||||||
"theoryCraftMode": "Mode Theorycraft",
|
"theoryCraftMode": "Mode Theorycraft",
|
||||||
"cycleCount": "Hitungan Siklus",
|
"cycleCount": "Hitungan Siklus",
|
||||||
"pleaseSelectAllSubStats": "Pilih semua sub statistik",
|
"pleaseSelectAllSubStats": "Pilih semua sub statistik",
|
||||||
"subStatRollCountCannotBeZero": "Hitungan roll sub stat tidak boleh nol",
|
"subStatRollCountCannotBeZero": "Hitungan roll sub stat tidak boleh nol",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Karakter Multipath",
|
"multipathCharacter": "Karakter Multipath",
|
||||||
"mainPath": "Path Utama",
|
"mainPath": "Path Utama",
|
||||||
"march7Path": "Path March 7th",
|
"march7Path": "Path March 7th",
|
||||||
"challenge": "Tantangan",
|
"challenge": "Tantangan",
|
||||||
"skipNode": "Lewati Node",
|
"skipNode": "Lewati Node",
|
||||||
"disableSkip": "Nonaktifkan lewati",
|
"disableSkip": "Nonaktifkan lewati",
|
||||||
"skipNode1": "Lewati node 1",
|
"skipNode1": "Lewati node 1",
|
||||||
"skipNode2": "Lewati node 2",
|
"skipNode2": "Lewati node 2",
|
||||||
"extraFeatures": "Fitur Ekstra",
|
"extraFeatures": "Fitur Ekstra",
|
||||||
"detailTheoryCraft": "Mengaktifkan fitur ini memungkinkan Anda menyesuaikan jumlah siklus dan menyesuaikan HP musuh.",
|
"detailTheoryCraft": "Mengaktifkan fitur ini memungkinkan Anda menyesuaikan jumlah siklus dan menyesuaikan HP musuh.",
|
||||||
"detailSkipNode": "Memungkinkan Anda melewati (Node 1/Node 2) di Memory of Chaos atau Pure Fiction.",
|
"detailSkipNode": "Memungkinkan Anda melewati (Node 1/Node 2) di Memory of Chaos atau Pure Fiction.",
|
||||||
"detailChallengePeak": "Memungkinkan perubahan musim Peak dalam Anomaly saat ini.",
|
"detailChallengePeak": "Memungkinkan perubahan musim Peak dalam Anomaly saat ini.",
|
||||||
"detailHiddenUi": "Mengaktifkan fitur ini akan menyembunyikan UI game.",
|
"detailHiddenUi": "Mengaktifkan fitur ini akan menyembunyikan UI game.",
|
||||||
"detailDisableCensorship": "Mengaktifkan fitur ini akan mematikan sensor dalam game.",
|
"detailDisableCensorship": "Mengaktifkan fitur ini akan mematikan sensor dalam game.",
|
||||||
"detailMultipathCharacter": "Memungkinkan perubahan Path untuk karakter tertentu.",
|
"detailMultipathCharacter": "Memungkinkan perubahan Path untuk karakter tertentu.",
|
||||||
"trailblazer": "Trailblazer",
|
"trailblazer": "Trailblazer",
|
||||||
"listExtraEffect": "Daftar Efek Ekstra",
|
"listExtraEffect": "Daftar Efek Ekstra",
|
||||||
"extra": "Ekstra",
|
"extra": "Ekstra",
|
||||||
"customLineup": "Lineup Kustom"
|
"customLineup": "Lineup Kustom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+288
-288
@@ -1,289 +1,289 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Firefly tools by Firefly Shelter"
|
"description": "Firefly tools by Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "スキルタイプ",
|
"skillType": "スキルタイプ",
|
||||||
"skillName": "スキル名",
|
"skillName": "スキル名",
|
||||||
"character": "キャラクター",
|
"character": "キャラクター",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "運命",
|
"path": "運命",
|
||||||
"rarity": "レアリティ",
|
"rarity": "レアリティ",
|
||||||
"element": "属性",
|
"element": "属性",
|
||||||
"technique": "秘技",
|
"technique": "秘技",
|
||||||
"talent": "天賦",
|
"talent": "天賦",
|
||||||
"basic": "通常攻撃",
|
"basic": "通常攻撃",
|
||||||
"skill": "スキル",
|
"skill": "スキル",
|
||||||
"ultimate": "アルティメット",
|
"ultimate": "アルティメット",
|
||||||
"servant": "サーバント",
|
"servant": "サーバント",
|
||||||
"damage": "ダメージ",
|
"damage": "ダメージ",
|
||||||
"type": "タイプ",
|
"type": "タイプ",
|
||||||
"warrior": "壊滅",
|
"warrior": "壊滅",
|
||||||
"knight": "存護",
|
"knight": "存護",
|
||||||
"mage": "知恵",
|
"mage": "知恵",
|
||||||
"priest": "豊穣",
|
"priest": "豊穣",
|
||||||
"rogue": "巡狩",
|
"rogue": "巡狩",
|
||||||
"shaman": "調和",
|
"shaman": "調和",
|
||||||
"warlock": "虚無",
|
"warlock": "虚無",
|
||||||
"memory": "記憶",
|
"memory": "記憶",
|
||||||
"elation": "愉悦",
|
"elation": "愉悦",
|
||||||
"fire": "火",
|
"fire": "火",
|
||||||
"ice": "氷",
|
"ice": "氷",
|
||||||
"imaginary": "虚数",
|
"imaginary": "虚数",
|
||||||
"physical": "物理",
|
"physical": "物理",
|
||||||
"quantum": "量子",
|
"quantum": "量子",
|
||||||
"thunder": "雷",
|
"thunder": "雷",
|
||||||
"wind": "風",
|
"wind": "風",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "攻撃",
|
"atk": "攻撃",
|
||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"critRate": "会心率",
|
"critRate": "会心率",
|
||||||
"critDmg": "会心ダメージ",
|
"critDmg": "会心ダメージ",
|
||||||
"breakEffect": "撃破特効",
|
"breakEffect": "撃破特効",
|
||||||
"effectRes": "効果抵抗",
|
"effectRes": "効果抵抗",
|
||||||
"energyRegenerationRate": "EP回復効率",
|
"energyRegenerationRate": "EP回復効率",
|
||||||
"effectHitRate": "効果命中率",
|
"effectHitRate": "効果命中率",
|
||||||
"outgoingHealingBoost": "回復増加",
|
"outgoingHealingBoost": "回復増加",
|
||||||
"fireDmgBoost": "火属性ダメージ強化",
|
"fireDmgBoost": "火属性ダメージ強化",
|
||||||
"iceDmgBoost": "氷属性ダメージ強化",
|
"iceDmgBoost": "氷属性ダメージ強化",
|
||||||
"imaginaryDmgBoost": "幻影ダメージ強化",
|
"imaginaryDmgBoost": "幻影ダメージ強化",
|
||||||
"physicalDmgBoost": "物理ダメージ強化",
|
"physicalDmgBoost": "物理ダメージ強化",
|
||||||
"quantumDmgBoost": "量子ダメージ強化",
|
"quantumDmgBoost": "量子ダメージ強化",
|
||||||
"thunderDmgBoost": "雷属性ダメージ強化",
|
"thunderDmgBoost": "雷属性ダメージ強化",
|
||||||
"windDmgBoost": "風属性ダメージ強化",
|
"windDmgBoost": "風属性ダメージ強化",
|
||||||
"pursued": "追加ダメージ",
|
"pursued": "追加ダメージ",
|
||||||
"true damage": "確定ダメージ",
|
"true damage": "確定ダメージ",
|
||||||
"elationdamage": "愉悦ダメージ",
|
"elationdamage": "愉悦ダメージ",
|
||||||
"follow-up": "追撃ダメージ",
|
"follow-up": "追撃ダメージ",
|
||||||
"elemental damage": "属性ダメージ",
|
"elemental damage": "属性ダメージ",
|
||||||
"dot": "継続ダメージ",
|
"dot": "継続ダメージ",
|
||||||
"qte": "QTEスキル",
|
"qte": "QTEスキル",
|
||||||
"level": "レベル",
|
"level": "レベル",
|
||||||
"relics": "遺物",
|
"relics": "遺物",
|
||||||
"eidolons": "エイドロン",
|
"eidolons": "エイドロン",
|
||||||
"lightcones": "光円錐",
|
"lightcones": "光円錐",
|
||||||
"loadData": "データ読み込み",
|
"loadData": "データ読み込み",
|
||||||
"exportData": "データ出力",
|
"exportData": "データ出力",
|
||||||
"connectSetting": "接続設定",
|
"connectSetting": "接続設定",
|
||||||
"connected": "接続済み",
|
"connected": "接続済み",
|
||||||
"unconnected": "未接続",
|
"unconnected": "未接続",
|
||||||
"psConnection": "PS接続",
|
"psConnection": "PS接続",
|
||||||
"connectionType": "接続タイプ",
|
"connectionType": "接続タイプ",
|
||||||
"status": "ステータス",
|
"status": "ステータス",
|
||||||
"connectPs": "PS接続",
|
"connectPs": "PS接続",
|
||||||
"disconnect": "切断",
|
"disconnect": "切断",
|
||||||
"other": "その他",
|
"other": "その他",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "データベース",
|
"database": "データベース",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "モンスター設定",
|
"monsterSetting": "モンスター設定",
|
||||||
"serverUrl": "サーバー URL",
|
"serverUrl": "サーバー URL",
|
||||||
"privateType": "プライベートタイプ",
|
"privateType": "プライベートタイプ",
|
||||||
"local": "ローカル",
|
"local": "ローカル",
|
||||||
"server": "サーバー",
|
"server": "サーバー",
|
||||||
"username": "ユーザー名",
|
"username": "ユーザー名",
|
||||||
"password": "パスワード",
|
"password": "パスワード",
|
||||||
"placeholderServerUrl": "サーバー URL を入力",
|
"placeholderServerUrl": "サーバー URL を入力",
|
||||||
"placeholderUsername": "ユーザー名を入力",
|
"placeholderUsername": "ユーザー名を入力",
|
||||||
"placeholderPassword": "パスワードを入力",
|
"placeholderPassword": "パスワードを入力",
|
||||||
"connectedSuccess": "PS接続成功",
|
"connectedSuccess": "PS接続成功",
|
||||||
"connectedFailed": "PS接続失敗",
|
"connectedFailed": "PS接続失敗",
|
||||||
"syncSuccess": "PSへのデータ同期に成功",
|
"syncSuccess": "PSへのデータ同期に成功",
|
||||||
"syncFailed": "PSへのデータ同期に失敗",
|
"syncFailed": "PSへのデータ同期に失敗",
|
||||||
"sync": "同期",
|
"sync": "同期",
|
||||||
"importSetting": "インポート設定",
|
"importSetting": "インポート設定",
|
||||||
"profile": "プロファイル",
|
"profile": "プロファイル",
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
"copyProfiles": "プロファイルをコピー",
|
"copyProfiles": "プロファイルをコピー",
|
||||||
"addNewProfile": "プロファイルを追加",
|
"addNewProfile": "プロファイルを追加",
|
||||||
"createNewProfile": "新しいプロファイルを作成",
|
"createNewProfile": "新しいプロファイルを作成",
|
||||||
"editProfile": "プロファイル編集",
|
"editProfile": "プロファイル編集",
|
||||||
"placeholderProfileName": "プロファイル名を入力",
|
"placeholderProfileName": "プロファイル名を入力",
|
||||||
"profileName": "プロファイル名",
|
"profileName": "プロファイル名",
|
||||||
"create": "作成",
|
"create": "作成",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
"characterInformation": "キャラクター情報",
|
"characterInformation": "キャラクター情報",
|
||||||
"skills": "スキル",
|
"skills": "スキル",
|
||||||
"showcaseCard": "ショーケースカード",
|
"showcaseCard": "ショーケースカード",
|
||||||
"comingSoon": "近日公開",
|
"comingSoon": "近日公開",
|
||||||
"characterName": "キャラクター名",
|
"characterName": "キャラクター名",
|
||||||
"placeholderCharacter": "キャラクター名を入力",
|
"placeholderCharacter": "キャラクター名を入力",
|
||||||
"characterSettings": "キャラクター設定",
|
"characterSettings": "キャラクター設定",
|
||||||
"levelConfiguration": "レベル構成",
|
"levelConfiguration": "レベル構成",
|
||||||
"characterLevel": "キャラクターレベル",
|
"characterLevel": "キャラクターレベル",
|
||||||
"max": "最大",
|
"max": "最大",
|
||||||
"ultimateEnergy": "アルティメットエネルギー",
|
"ultimateEnergy": "アルティメットエネルギー",
|
||||||
"currentEnergy": "現在のエネルギー",
|
"currentEnergy": "現在のエネルギー",
|
||||||
"setTo50": "50%に設定",
|
"setTo50": "50%に設定",
|
||||||
"battleConfiguration": "バトル構成",
|
"battleConfiguration": "バトル構成",
|
||||||
"useTechnique": "秘技を使用",
|
"useTechnique": "秘技を使用",
|
||||||
"techniqueNote": "バトル前に秘技効果を有効化",
|
"techniqueNote": "バトル前に秘技効果を有効化",
|
||||||
"enhancement": "強化",
|
"enhancement": "強化",
|
||||||
"enhancementLevel": "強化レベル",
|
"enhancementLevel": "強化レベル",
|
||||||
"origin": "由来",
|
"origin": "由来",
|
||||||
"enhancedNote": "強化レベルに応じて追加スキル解放",
|
"enhancedNote": "強化レベルに応じて追加スキル解放",
|
||||||
"lightconeEquipment": "光円錐装備",
|
"lightconeEquipment": "光円錐装備",
|
||||||
"lightconeSettings": "光円錐の設定",
|
"lightconeSettings": "光円錐の設定",
|
||||||
"placeholderLevel": "レベルを入力",
|
"placeholderLevel": "レベルを入力",
|
||||||
"superimpositionRank": "重ねランク",
|
"superimpositionRank": "重ねランク",
|
||||||
"ranksNote": "ランクが高いほど効果強化",
|
"ranksNote": "ランクが高いほど効果強化",
|
||||||
"changeLightcone": "光円錐を変更",
|
"changeLightcone": "光円錐を変更",
|
||||||
"removeLightcone": "光円錐を外す",
|
"removeLightcone": "光円錐を外す",
|
||||||
"equipLightcone": "光円錐を装備",
|
"equipLightcone": "光円錐を装備",
|
||||||
"noLightconeEquipped": "光円錐が装備されていません",
|
"noLightconeEquipped": "光円錐が装備されていません",
|
||||||
"equipLightconeNote": "強化のため光円錐を装備してください",
|
"equipLightconeNote": "強化のため光円錐を装備してください",
|
||||||
"filter": "フィルター",
|
"filter": "フィルター",
|
||||||
"selectedCharacters": "選択中のキャラクター",
|
"selectedCharacters": "選択中のキャラクター",
|
||||||
"selectedProfiles": "選択中のプロファイル",
|
"selectedProfiles": "選択中のプロファイル",
|
||||||
"clearAll": "全てクリア",
|
"clearAll": "全てクリア",
|
||||||
"selectAll": "全て選択",
|
"selectAll": "全て選択",
|
||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
"copied": "コピー済み",
|
"copied": "コピー済み",
|
||||||
"noAvatarSelected": "キャラクターが選択されていません",
|
"noAvatarSelected": "キャラクターが選択されていません",
|
||||||
"noAvatarToCopySelected": "コピーするキャラクター選択されていません",
|
"noAvatarToCopySelected": "コピーするキャラクター選択されていません",
|
||||||
"pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください",
|
"pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください",
|
||||||
"pleaseEnterUid": "UIDを入力してください",
|
"pleaseEnterUid": "UIDを入力してください",
|
||||||
"failedToFetchEnkaData": "Enka のデータ取得に失敗",
|
"failedToFetchEnkaData": "Enka のデータ取得に失敗",
|
||||||
"pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください",
|
"pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください",
|
||||||
"noDataToImport": "インポートするデータがありません",
|
"noDataToImport": "インポートするデータがありません",
|
||||||
"pleaseSelectAFile": "ファイルを選択してください",
|
"pleaseSelectAFile": "ファイルを選択してください",
|
||||||
"fileMustBeAValidJsonFile": "有効な JSON ファイルである必要があります",
|
"fileMustBeAValidJsonFile": "有効な JSON ファイルである必要があります",
|
||||||
"importEnkaDataSuccess": "Enka データのインポート成功",
|
"importEnkaDataSuccess": "Enka データのインポート成功",
|
||||||
"importFreeSRDataSuccess": "FreeSR データのインポート成功",
|
"importFreeSRDataSuccess": "FreeSR データのインポート成功",
|
||||||
"importDatabaseSuccess": "データベースのインポート成功",
|
"importDatabaseSuccess": "データベースのインポート成功",
|
||||||
"getData": "データ取得",
|
"getData": "データ取得",
|
||||||
"import": "インポート",
|
"import": "インポート",
|
||||||
"freeSRImport": "FreeSR インポート",
|
"freeSRImport": "FreeSR インポート",
|
||||||
"onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応",
|
"onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応",
|
||||||
"pickAFile": "ファイル選択",
|
"pickAFile": "ファイル選択",
|
||||||
"lightConeSetting": "ライトコーン設定",
|
"lightConeSetting": "ライトコーン設定",
|
||||||
"relicMaker": "遺物製作",
|
"relicMaker": "遺物製作",
|
||||||
"pleaseSelectAllOptions": "すべてのオプションを選択してください",
|
"pleaseSelectAllOptions": "すべてのオプションを選択してください",
|
||||||
"relicSavedSuccessfully": "遺物の保存に成功しました",
|
"relicSavedSuccessfully": "遺物の保存に成功しました",
|
||||||
"mainSettings": "メイン設定",
|
"mainSettings": "メイン設定",
|
||||||
"mainStat": "メインステータス",
|
"mainStat": "メインステータス",
|
||||||
"set": "セット",
|
"set": "セット",
|
||||||
"pleaseSelectASet": "セットを選択してください",
|
"pleaseSelectASet": "セットを選択してください",
|
||||||
"effectBonus": "効果ボーナス",
|
"effectBonus": "効果ボーナス",
|
||||||
"totalRoll": "総ロール数",
|
"totalRoll": "総ロール数",
|
||||||
"randomizeStats": "ステータスランダム化",
|
"randomizeStats": "ステータスランダム化",
|
||||||
"randomizeRolls": "ロール回数ランダム化",
|
"randomizeRolls": "ロール回数ランダム化",
|
||||||
"selectASubStat": "サブステータスを選択",
|
"selectASubStat": "サブステータスを選択",
|
||||||
"selectASet": "セットを選択",
|
"selectASet": "セットを選択",
|
||||||
"selectAMainStat": "メインステータスを選んでください",
|
"selectAMainStat": "メインステータスを選んでください",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"reset": "リセット",
|
"reset": "リセット",
|
||||||
"roll": "ロール",
|
"roll": "ロール",
|
||||||
"step": "ステップ",
|
"step": "ステップ",
|
||||||
"memoryOfChaos": "混沌の記憶",
|
"memoryOfChaos": "混沌の記憶",
|
||||||
"pureFiction": "虚構叙事",
|
"pureFiction": "虚構叙事",
|
||||||
"apocalypticShadow": "末日の幻影",
|
"apocalypticShadow": "末日の幻影",
|
||||||
"customEnemy": "カスタム敵",
|
"customEnemy": "カスタム敵",
|
||||||
"simulatedUniverse": "模擬宇宙",
|
"simulatedUniverse": "模擬宇宙",
|
||||||
"floor": "階層",
|
"floor": "階層",
|
||||||
"side": "前半/後半",
|
"side": "前半/後半",
|
||||||
"wave": "ウェーブ",
|
"wave": "ウェーブ",
|
||||||
"stage": "ステージ",
|
"stage": "ステージ",
|
||||||
"useCycleCount": "サイクル数を使用しますか?",
|
"useCycleCount": "サイクル数を使用しますか?",
|
||||||
"useTurbulenceBuff": "乱気流バフを使用しますか?",
|
"useTurbulenceBuff": "乱気流バフを使用しますか?",
|
||||||
"firstHalfEnemies": "前半の敵",
|
"firstHalfEnemies": "前半の敵",
|
||||||
"secondHalfEnemies": "後半の敵",
|
"secondHalfEnemies": "後半の敵",
|
||||||
"firstNodeEnemies": "ノード 1 の敵",
|
"firstNodeEnemies": "ノード 1 の敵",
|
||||||
"secondNodeEnemies": "ノード 2 の敵",
|
"secondNodeEnemies": "ノード 2 の敵",
|
||||||
"thirdNodeEnemies": "ノード 3 の敵",
|
"thirdNodeEnemies": "ノード 3 の敵",
|
||||||
"firstNode": "ノード 1",
|
"firstNode": "ノード 1",
|
||||||
"secondNode": "ノード 2",
|
"secondNode": "ノード 2",
|
||||||
"thirdNode": "ノード 3",
|
"thirdNode": "ノード 3",
|
||||||
"turbulenceBuff": "乱気流バフ",
|
"turbulenceBuff": "乱気流バフ",
|
||||||
"noEventSelected": "イベントが選択されていません",
|
"noEventSelected": "イベントが選択されていません",
|
||||||
"noTurbulenceBuff": "乱気流バフがありません",
|
"noTurbulenceBuff": "乱気流バフがありません",
|
||||||
"upper": "上半",
|
"upper": "上半",
|
||||||
"lower": "下半",
|
"lower": "下半",
|
||||||
"upperToLower": "上半 -> 下半",
|
"upperToLower": "上半 -> 下半",
|
||||||
"lowerToUpper": "下半 -> 上半",
|
"lowerToUpper": "下半 -> 上半",
|
||||||
"selectMOCEvent": "MOC イベントを選択",
|
"selectMOCEvent": "MOC イベントを選択",
|
||||||
"selectPFEvent": "PF イベントを選択",
|
"selectPFEvent": "PF イベントを選択",
|
||||||
"selectASEvent": "AS イベントを選択",
|
"selectASEvent": "AS イベントを選択",
|
||||||
"selectCEEvent": "CE イベントを選択",
|
"selectCEEvent": "CE イベントを選択",
|
||||||
"selectEvent": "イベントを選択",
|
"selectEvent": "イベントを選択",
|
||||||
"selectFloor": "階層を選択",
|
"selectFloor": "階層を選択",
|
||||||
"selectSide": "前半/後半を選択",
|
"selectSide": "前半/後半を選択",
|
||||||
"selectBuff": "バフを選択",
|
"selectBuff": "バフを選択",
|
||||||
"selectStage": "ステージを選択",
|
"selectStage": "ステージを選択",
|
||||||
"previous": "前へ",
|
"previous": "前へ",
|
||||||
"next": "次へ",
|
"next": "次へ",
|
||||||
"noMonstersFound": "敵が見つかりません",
|
"noMonstersFound": "敵が見つかりません",
|
||||||
"addNewWave": "新しいウェーブを追加",
|
"addNewWave": "新しいウェーブを追加",
|
||||||
"searchStage": "ステージを検索...",
|
"searchStage": "ステージを検索...",
|
||||||
"noStageFound": "ステージが見つかりません",
|
"noStageFound": "ステージが見つかりません",
|
||||||
"searchMonster": "敵を検索...",
|
"searchMonster": "敵を検索...",
|
||||||
"changeRelic": "遺物を変更",
|
"changeRelic": "遺物を変更",
|
||||||
"deleteRelic": "遺物を削除",
|
"deleteRelic": "遺物を削除",
|
||||||
"deleteRelicConfirm": "この遺物を削除してもよろしいですか?",
|
"deleteRelicConfirm": "この遺物を削除してもよろしいですか?",
|
||||||
"setEffects": "効果を設定",
|
"setEffects": "効果を設定",
|
||||||
"details": "詳細",
|
"details": "詳細",
|
||||||
"normal": "通常攻撃",
|
"normal": "通常攻撃",
|
||||||
"bpskill": "戦闘スキル",
|
"bpskill": "戦闘スキル",
|
||||||
"maze": "秘技",
|
"maze": "秘技",
|
||||||
"ultra": "必殺技",
|
"ultra": "必殺技",
|
||||||
"servantskill": "メモスプライトスキル",
|
"servantskill": "メモスプライトスキル",
|
||||||
"severaltalent": "メモスプライトの才能",
|
"severaltalent": "メモスプライトの才能",
|
||||||
"singleattack": "単体攻撃",
|
"singleattack": "単体攻撃",
|
||||||
"enhance": "強化",
|
"enhance": "強化",
|
||||||
"summon": "召喚",
|
"summon": "召喚",
|
||||||
"blast": "爆発",
|
"blast": "爆発",
|
||||||
"restore": "回復",
|
"restore": "回復",
|
||||||
"support": "支援",
|
"support": "支援",
|
||||||
"aoeattack": "範囲攻撃",
|
"aoeattack": "範囲攻撃",
|
||||||
"mazeattack": "迷宮秘技",
|
"mazeattack": "迷宮秘技",
|
||||||
"impair": "弱体化",
|
"impair": "弱体化",
|
||||||
"bounce": "弾跳",
|
"bounce": "弾跳",
|
||||||
"active": "アクティブ",
|
"active": "アクティブ",
|
||||||
"inactive": "非アクティブ",
|
"inactive": "非アクティブ",
|
||||||
"defence": "防御",
|
"defence": "防御",
|
||||||
"maxAll": "すべて最大化",
|
"maxAll": "すべて最大化",
|
||||||
"maxAllSuccess": "スキルレベルを最大に設定しました。",
|
"maxAllSuccess": "スキルレベルを最大に設定しました。",
|
||||||
"maxAllFailed": "スキルレベルの最大設定に失敗しました。",
|
"maxAllFailed": "スキルレベルの最大設定に失敗しました。",
|
||||||
"noRelicEquipped": "遺物が装備されていません",
|
"noRelicEquipped": "遺物が装備されていません",
|
||||||
"anomalyArbitration": "異相の仲裁",
|
"anomalyArbitration": "異相の仲裁",
|
||||||
"normalMode": "通常モード",
|
"normalMode": "通常モード",
|
||||||
"hardMode": "困難モード",
|
"hardMode": "困難モード",
|
||||||
"selectPEAKEvent": "PEAK イベントを選択",
|
"selectPEAKEvent": "PEAK イベントを選択",
|
||||||
"mode": "モード",
|
"mode": "モード",
|
||||||
"selectMode": "モードを選択",
|
"selectMode": "モードを選択",
|
||||||
"rollBack": "前の状態に戻る",
|
"rollBack": "前の状態に戻る",
|
||||||
"upRoll": "サブステータスを増やす",
|
"upRoll": "サブステータスを増やす",
|
||||||
"downRoll": "サブステータスを減らす",
|
"downRoll": "サブステータスを減らす",
|
||||||
"actions": "アクション",
|
"actions": "アクション",
|
||||||
"avatars": "アバター",
|
"avatars": "アバター",
|
||||||
"quickView": "クイックビュー",
|
"quickView": "クイックビュー",
|
||||||
"extraSetting": "追加設定",
|
"extraSetting": "追加設定",
|
||||||
"disableCensorship": "検閲を無効化",
|
"disableCensorship": "検閲を無効化",
|
||||||
"hideUI": "UIを非表示",
|
"hideUI": "UIを非表示",
|
||||||
"theoryCraftMode": "シアリークラフトモード",
|
"theoryCraftMode": "シアリークラフトモード",
|
||||||
"cycleCount": "サイクル数",
|
"cycleCount": "サイクル数",
|
||||||
"pleaseSelectAllSubStats": "すべてのサブステータスを選択してください",
|
"pleaseSelectAllSubStats": "すべてのサブステータスを選択してください",
|
||||||
"subStatRollCountCannotBeZero": "サブステータスの行数は0にできません",
|
"subStatRollCountCannotBeZero": "サブステータスの行数は0にできません",
|
||||||
"theoryCraft": "シアリークラフト",
|
"theoryCraft": "シアリークラフト",
|
||||||
"multipathCharacter": "複数運命キャラ",
|
"multipathCharacter": "複数運命キャラ",
|
||||||
"mainPath": "主人公の運命",
|
"mainPath": "主人公の運命",
|
||||||
"march7Path": "三月なのかの運命",
|
"march7Path": "三月なのかの運命",
|
||||||
"challenge": "挑戦",
|
"challenge": "挑戦",
|
||||||
"skipNode": "ノードをスキップ",
|
"skipNode": "ノードをスキップ",
|
||||||
"disableSkip": "スキップ無効",
|
"disableSkip": "スキップ無効",
|
||||||
"skipNode1": "ノード1をスキップ",
|
"skipNode1": "ノード1をスキップ",
|
||||||
"skipNode2": "ノード2をスキップ",
|
"skipNode2": "ノード2をスキップ",
|
||||||
"extraFeatures": "追加機能",
|
"extraFeatures": "追加機能",
|
||||||
"detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。",
|
"detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。",
|
||||||
"detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事の(ノード1/ノード2)をスキップできます。",
|
"detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事の(ノード1/ノード2)をスキップできます。",
|
||||||
"detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。",
|
"detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。",
|
||||||
"detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。",
|
"detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。",
|
||||||
"detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。",
|
"detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。",
|
||||||
"detailMultipathCharacter": "一部キャラクターの運命を変更できます。",
|
"detailMultipathCharacter": "一部キャラクターの運命を変更できます。",
|
||||||
"trailblazer": "開拓者",
|
"trailblazer": "開拓者",
|
||||||
"listExtraEffect": "追加効果一覧",
|
"listExtraEffect": "追加効果一覧",
|
||||||
"extra": "追加",
|
"extra": "追加",
|
||||||
"customLineup": "カスタム編成"
|
"customLineup": "カスタム編成"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+288
-288
@@ -1,289 +1,289 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Firefly tools by Firefly Shelter"
|
"description": "Firefly tools by Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "스킬 유형",
|
"skillType": "스킬 유형",
|
||||||
"skillName": "스킬 이름",
|
"skillName": "스킬 이름",
|
||||||
"character": "캐릭터",
|
"character": "캐릭터",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "운명",
|
"path": "운명",
|
||||||
"rarity": "희귀도",
|
"rarity": "희귀도",
|
||||||
"element": "속성",
|
"element": "속성",
|
||||||
"technique": "비기",
|
"technique": "비기",
|
||||||
"talent": "탈렌트",
|
"talent": "탈렌트",
|
||||||
"basic": "기본 공격",
|
"basic": "기본 공격",
|
||||||
"skill": "스킬",
|
"skill": "스킬",
|
||||||
"ultimate": "궁극기",
|
"ultimate": "궁극기",
|
||||||
"servant": "서번트",
|
"servant": "서번트",
|
||||||
"damage": "데미지",
|
"damage": "데미지",
|
||||||
"type": "종류",
|
"type": "종류",
|
||||||
"warrior": "파괴",
|
"warrior": "파괴",
|
||||||
"knight": "보호",
|
"knight": "보호",
|
||||||
"mage": "박학",
|
"mage": "박학",
|
||||||
"priest": "풍요",
|
"priest": "풍요",
|
||||||
"rogue": "사냥",
|
"rogue": "사냥",
|
||||||
"shaman": "조화",
|
"shaman": "조화",
|
||||||
"warlock": "허무",
|
"warlock": "허무",
|
||||||
"memory": "기억",
|
"memory": "기억",
|
||||||
"elation": "환락",
|
"elation": "환락",
|
||||||
"fire": "화염",
|
"fire": "화염",
|
||||||
"ice": "얼음",
|
"ice": "얼음",
|
||||||
"imaginary": "환상",
|
"imaginary": "환상",
|
||||||
"physical": "물리",
|
"physical": "물리",
|
||||||
"quantum": "양자",
|
"quantum": "양자",
|
||||||
"thunder": "번개",
|
"thunder": "번개",
|
||||||
"wind": "바람",
|
"wind": "바람",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "공격력",
|
"atk": "공격력",
|
||||||
"speed": "속도",
|
"speed": "속도",
|
||||||
"critRate": "치명타 확률",
|
"critRate": "치명타 확률",
|
||||||
"critDmg": "치명타 피해",
|
"critDmg": "치명타 피해",
|
||||||
"breakEffect": "파괴 피해",
|
"breakEffect": "파괴 피해",
|
||||||
"effectRes": "효과 저항",
|
"effectRes": "효과 저항",
|
||||||
"energyRegenerationRate": "에너지 회복속도",
|
"energyRegenerationRate": "에너지 회복속도",
|
||||||
"effectHitRate": "효과 적중률",
|
"effectHitRate": "효과 적중률",
|
||||||
"outgoingHealingBoost": "치유 강화",
|
"outgoingHealingBoost": "치유 강화",
|
||||||
"fireDmgBoost": "화염 피해 증가",
|
"fireDmgBoost": "화염 피해 증가",
|
||||||
"iceDmgBoost": "얼음 피해 증가",
|
"iceDmgBoost": "얼음 피해 증가",
|
||||||
"imaginaryDmgBoost": "환상 피해 증가",
|
"imaginaryDmgBoost": "환상 피해 증가",
|
||||||
"physicalDmgBoost": "물리 피해 증가",
|
"physicalDmgBoost": "물리 피해 증가",
|
||||||
"quantumDmgBoost": "양자 피해 증가",
|
"quantumDmgBoost": "양자 피해 증가",
|
||||||
"thunderDmgBoost": "번개 피해 증가",
|
"thunderDmgBoost": "번개 피해 증가",
|
||||||
"windDmgBoost": "바람 피해 증가",
|
"windDmgBoost": "바람 피해 증가",
|
||||||
"pursued": "추가 피해",
|
"pursued": "추가 피해",
|
||||||
"true damage": "진실한 피해",
|
"true damage": "진실한 피해",
|
||||||
"elationdamage": "환락 피해",
|
"elationdamage": "환락 피해",
|
||||||
"follow-up": "후속 피해",
|
"follow-up": "후속 피해",
|
||||||
"elemental damage": "파괴 및 초파괴 피해",
|
"elemental damage": "파괴 및 초파괴 피해",
|
||||||
"dot": "시간 경과 피해",
|
"dot": "시간 경과 피해",
|
||||||
"qte": "QTE 스킬",
|
"qte": "QTE 스킬",
|
||||||
"level": "레벨",
|
"level": "레벨",
|
||||||
"relics": "유물",
|
"relics": "유물",
|
||||||
"eidolons": "에이돌론",
|
"eidolons": "에이돌론",
|
||||||
"lightcones": "라이트콘",
|
"lightcones": "라이트콘",
|
||||||
"loadData": "데이터 불러오기",
|
"loadData": "데이터 불러오기",
|
||||||
"exportData": "데이터 내보내기",
|
"exportData": "데이터 내보내기",
|
||||||
"connectSetting": "연결 설정",
|
"connectSetting": "연결 설정",
|
||||||
"connected": "연결됨",
|
"connected": "연결됨",
|
||||||
"unconnected": "연결 안됨",
|
"unconnected": "연결 안됨",
|
||||||
"psConnection": "PS 연결",
|
"psConnection": "PS 연결",
|
||||||
"connectionType": "연결 타입",
|
"connectionType": "연결 타입",
|
||||||
"status": "상태",
|
"status": "상태",
|
||||||
"connectPs": "PS 연결",
|
"connectPs": "PS 연결",
|
||||||
"disconnect": "연결 끊기",
|
"disconnect": "연결 끊기",
|
||||||
"other": "기타",
|
"other": "기타",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "데이터베이스",
|
"database": "데이터베이스",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "몬스터 설정",
|
"monsterSetting": "몬스터 설정",
|
||||||
"serverUrl": "서버 URL",
|
"serverUrl": "서버 URL",
|
||||||
"privateType": "프라이빗 타입",
|
"privateType": "프라이빗 타입",
|
||||||
"local": "로컬",
|
"local": "로컬",
|
||||||
"server": "서버",
|
"server": "서버",
|
||||||
"username": "사용자명",
|
"username": "사용자명",
|
||||||
"password": "비밀번호",
|
"password": "비밀번호",
|
||||||
"placeholderServerUrl": "서버 URL 입력",
|
"placeholderServerUrl": "서버 URL 입력",
|
||||||
"placeholderUsername": "사용자명 입력",
|
"placeholderUsername": "사용자명 입력",
|
||||||
"placeholderPassword": "비밀번호 입력",
|
"placeholderPassword": "비밀번호 입력",
|
||||||
"connectedSuccess": "PS에 성공적으로 연결됨",
|
"connectedSuccess": "PS에 성공적으로 연결됨",
|
||||||
"connectedFailed": "PS 연결 실패",
|
"connectedFailed": "PS 연결 실패",
|
||||||
"syncSuccess": "PS에 데이터 동기화 성공",
|
"syncSuccess": "PS에 데이터 동기화 성공",
|
||||||
"syncFailed": "PS에 데이터 동기화 실패",
|
"syncFailed": "PS에 데이터 동기화 실패",
|
||||||
"sync": "동기화",
|
"sync": "동기화",
|
||||||
"importSetting": "설정 가져오기",
|
"importSetting": "설정 가져오기",
|
||||||
"profile": "프로필",
|
"profile": "프로필",
|
||||||
"default": "기본",
|
"default": "기본",
|
||||||
"copyProfiles": "프로필 복사",
|
"copyProfiles": "프로필 복사",
|
||||||
"addNewProfile": "새 프로필 추가",
|
"addNewProfile": "새 프로필 추가",
|
||||||
"createNewProfile": "새 프로필 생성",
|
"createNewProfile": "새 프로필 생성",
|
||||||
"editProfile": "프로필 편집",
|
"editProfile": "프로필 편집",
|
||||||
"placeholderProfileName": "프로필 이름 입력",
|
"placeholderProfileName": "프로필 이름 입력",
|
||||||
"profileName": "프로필 이름",
|
"profileName": "프로필 이름",
|
||||||
"create": "생성",
|
"create": "생성",
|
||||||
"update": "업데이트",
|
"update": "업데이트",
|
||||||
"characterInformation": "캐릭터 정보",
|
"characterInformation": "캐릭터 정보",
|
||||||
"skills": "스킬",
|
"skills": "스킬",
|
||||||
"showcaseCard": "쇼케이스 카드",
|
"showcaseCard": "쇼케이스 카드",
|
||||||
"comingSoon": "곧 출시",
|
"comingSoon": "곧 출시",
|
||||||
"characterName": "캐릭터 이름",
|
"characterName": "캐릭터 이름",
|
||||||
"placeholderCharacter": "캐릭터 이름 입력",
|
"placeholderCharacter": "캐릭터 이름 입력",
|
||||||
"characterSettings": "캐릭터 설정",
|
"characterSettings": "캐릭터 설정",
|
||||||
"levelConfiguration": "레벨 구성",
|
"levelConfiguration": "레벨 구성",
|
||||||
"characterLevel": "캐릭터 레벨",
|
"characterLevel": "캐릭터 레벨",
|
||||||
"max": "최대",
|
"max": "최대",
|
||||||
"ultimateEnergy": "궁극 에너지",
|
"ultimateEnergy": "궁극 에너지",
|
||||||
"currentEnergy": "현재 에너지",
|
"currentEnergy": "현재 에너지",
|
||||||
"setTo50": "50%로 설정",
|
"setTo50": "50%로 설정",
|
||||||
"battleConfiguration": "전투 구성",
|
"battleConfiguration": "전투 구성",
|
||||||
"useTechnique": "비기 사용",
|
"useTechnique": "비기 사용",
|
||||||
"techniqueNote": "전투 전 비기 효과 활성화",
|
"techniqueNote": "전투 전 비기 효과 활성화",
|
||||||
"enhancement": "강화",
|
"enhancement": "강화",
|
||||||
"enhancementLevel": "강화 레벨",
|
"enhancementLevel": "강화 레벨",
|
||||||
"origin": "출처",
|
"origin": "출처",
|
||||||
"enhancedNote": "강화 레벨이 높을수록 추가 효과 해제",
|
"enhancedNote": "강화 레벨이 높을수록 추가 효과 해제",
|
||||||
"lightconeEquipment": "라이트콘 장비",
|
"lightconeEquipment": "라이트콘 장비",
|
||||||
"lightconeSettings": "라이트콘 설정",
|
"lightconeSettings": "라이트콘 설정",
|
||||||
"placeholderLevel": "레벨 입력",
|
"placeholderLevel": "레벨 입력",
|
||||||
"superimpositionRank": "중첩 등급",
|
"superimpositionRank": "중첩 등급",
|
||||||
"ranksNote": "등급이 높을수록 효과가 강함",
|
"ranksNote": "등급이 높을수록 효과가 강함",
|
||||||
"changeLightcone": "라이트콘 변경",
|
"changeLightcone": "라이트콘 변경",
|
||||||
"removeLightcone": "라이트콘 제거",
|
"removeLightcone": "라이트콘 제거",
|
||||||
"equipLightcone": "라이트콘 장착",
|
"equipLightcone": "라이트콘 장착",
|
||||||
"noLightconeEquipped": "라이트콘 미장착",
|
"noLightconeEquipped": "라이트콘 미장착",
|
||||||
"equipLightconeNote": "캐릭터 능력 향상을 위해 장착하세요",
|
"equipLightconeNote": "캐릭터 능력 향상을 위해 장착하세요",
|
||||||
"filter": "필터",
|
"filter": "필터",
|
||||||
"selectedCharacters": "선택된 캐릭터",
|
"selectedCharacters": "선택된 캐릭터",
|
||||||
"selectedProfiles": "선택된 프로필",
|
"selectedProfiles": "선택된 프로필",
|
||||||
"clearAll": "전체 지우기",
|
"clearAll": "전체 지우기",
|
||||||
"selectAll": "전체 선택",
|
"selectAll": "전체 선택",
|
||||||
"copy": "복사",
|
"copy": "복사",
|
||||||
"copied": "복사됨",
|
"copied": "복사됨",
|
||||||
"noAvatarSelected": "캐릭터가 선택되지 않음",
|
"noAvatarSelected": "캐릭터가 선택되지 않음",
|
||||||
"noAvatarToCopySelected": "복사할 캐릭터가 선택되지 않음",
|
"noAvatarToCopySelected": "복사할 캐릭터가 선택되지 않음",
|
||||||
"pleaseSelectAtLeastOneProfile": "프로필을 최소 하나 선택하세요",
|
"pleaseSelectAtLeastOneProfile": "프로필을 최소 하나 선택하세요",
|
||||||
"pleaseEnterUid": "UID를 입력해주세요",
|
"pleaseEnterUid": "UID를 입력해주세요",
|
||||||
"failedToFetchEnkaData": "Enka 데이터 가져오기 실패",
|
"failedToFetchEnkaData": "Enka 데이터 가져오기 실패",
|
||||||
"pleaseSelectAtLeastOneCharacter": "캐릭터를 최소 하나 선택하세요",
|
"pleaseSelectAtLeastOneCharacter": "캐릭터를 최소 하나 선택하세요",
|
||||||
"noDataToImport": "가져올 데이터가 없습니다",
|
"noDataToImport": "가져올 데이터가 없습니다",
|
||||||
"pleaseSelectAFile": "파일을 선택하세요",
|
"pleaseSelectAFile": "파일을 선택하세요",
|
||||||
"fileMustBeAValidJsonFile": "유효한 JSON 파일이어야 합니다",
|
"fileMustBeAValidJsonFile": "유효한 JSON 파일이어야 합니다",
|
||||||
"importEnkaDataSuccess": "Enka 데이터 가져오기 성공",
|
"importEnkaDataSuccess": "Enka 데이터 가져오기 성공",
|
||||||
"importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공",
|
"importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공",
|
||||||
"importDatabaseSuccess": "데이터베이스 가져오기 성공",
|
"importDatabaseSuccess": "데이터베이스 가져오기 성공",
|
||||||
"getData": "데이터 가져오기",
|
"getData": "데이터 가져오기",
|
||||||
"import": "가져오기",
|
"import": "가져오기",
|
||||||
"freeSRImport": "FreeSR 가져오기",
|
"freeSRImport": "FreeSR 가져오기",
|
||||||
"onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원",
|
"onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원",
|
||||||
"pickAFile": "파일 선택",
|
"pickAFile": "파일 선택",
|
||||||
"lightConeSetting": "라이트콘 설정",
|
"lightConeSetting": "라이트콘 설정",
|
||||||
"relicMaker": "유물 제작",
|
"relicMaker": "유물 제작",
|
||||||
"pleaseSelectAllOptions": "모든 옵션을 선택해주세요",
|
"pleaseSelectAllOptions": "모든 옵션을 선택해주세요",
|
||||||
"relicSavedSuccessfully": "유물 저장 성공",
|
"relicSavedSuccessfully": "유물 저장 성공",
|
||||||
"mainSettings": "메인 설정",
|
"mainSettings": "메인 설정",
|
||||||
"mainStat": "주 속성",
|
"mainStat": "주 속성",
|
||||||
"set": "세트",
|
"set": "세트",
|
||||||
"pleaseSelectASet": "세트 하나를 선택해주세요",
|
"pleaseSelectASet": "세트 하나를 선택해주세요",
|
||||||
"effectBonus": "효과 보너스",
|
"effectBonus": "효과 보너스",
|
||||||
"totalRoll": "총 횟수",
|
"totalRoll": "총 횟수",
|
||||||
"randomizeStats": "속성 랜덤화",
|
"randomizeStats": "속성 랜덤화",
|
||||||
"randomizeRolls": "횟수 랜덤화",
|
"randomizeRolls": "횟수 랜덤화",
|
||||||
"selectASubStat": "부 속성 선택",
|
"selectASubStat": "부 속성 선택",
|
||||||
"selectASet": "세트 선택",
|
"selectASet": "세트 선택",
|
||||||
"selectAMainStat": "주 속성 선택",
|
"selectAMainStat": "주 속성 선택",
|
||||||
"save": "저장",
|
"save": "저장",
|
||||||
"reset": "초기화",
|
"reset": "초기화",
|
||||||
"roll": "굴리기",
|
"roll": "굴리기",
|
||||||
"step": "단계",
|
"step": "단계",
|
||||||
"memoryOfChaos": "망각의 정원",
|
"memoryOfChaos": "망각의 정원",
|
||||||
"pureFiction": "허구 이야기",
|
"pureFiction": "허구 이야기",
|
||||||
"apocalypticShadow": "종말의 환영",
|
"apocalypticShadow": "종말의 환영",
|
||||||
"customEnemy": "커스텀 적",
|
"customEnemy": "커스텀 적",
|
||||||
"simulatedUniverse": "시뮬레이티드 유니버스",
|
"simulatedUniverse": "시뮬레이티드 유니버스",
|
||||||
"floor": "층수",
|
"floor": "층수",
|
||||||
"side": "전반/후반",
|
"side": "전반/후반",
|
||||||
"wave": "웨이브",
|
"wave": "웨이브",
|
||||||
"stage": "스테이지",
|
"stage": "스테이지",
|
||||||
"useCycleCount": "사이클 수 사용?",
|
"useCycleCount": "사이클 수 사용?",
|
||||||
"useTurbulenceBuff": "난류 버프 사용?",
|
"useTurbulenceBuff": "난류 버프 사용?",
|
||||||
"firstHalfEnemies": "전반 적",
|
"firstHalfEnemies": "전반 적",
|
||||||
"secondHalfEnemies": "후반 적",
|
"secondHalfEnemies": "후반 적",
|
||||||
"firstNodeEnemies": "노드 1 적",
|
"firstNodeEnemies": "노드 1 적",
|
||||||
"secondNodeEnemies": "노드 2 적",
|
"secondNodeEnemies": "노드 2 적",
|
||||||
"thirdNodeEnemies": "노드 3 적",
|
"thirdNodeEnemies": "노드 3 적",
|
||||||
"firstNode": "노드 1",
|
"firstNode": "노드 1",
|
||||||
"secondNode": "노드 2",
|
"secondNode": "노드 2",
|
||||||
"thirdNode": "노드 3",
|
"thirdNode": "노드 3",
|
||||||
"turbulenceBuff": "난류 버프",
|
"turbulenceBuff": "난류 버프",
|
||||||
"noEventSelected": "이벤트가 선택되지 않음",
|
"noEventSelected": "이벤트가 선택되지 않음",
|
||||||
"noTurbulenceBuff": "난류 버프가 없음",
|
"noTurbulenceBuff": "난류 버프가 없음",
|
||||||
"upper": "상반",
|
"upper": "상반",
|
||||||
"lower": "하반",
|
"lower": "하반",
|
||||||
"upperToLower": "상반 -> 하반",
|
"upperToLower": "상반 -> 하반",
|
||||||
"lowerToUpper": "하반 -> 상반",
|
"lowerToUpper": "하반 -> 상반",
|
||||||
"selectMOCEvent": "MOC 이벤트 선택",
|
"selectMOCEvent": "MOC 이벤트 선택",
|
||||||
"selectPFEvent": "PF 이벤트 선택",
|
"selectPFEvent": "PF 이벤트 선택",
|
||||||
"selectASEvent": "AS 이벤트 선택",
|
"selectASEvent": "AS 이벤트 선택",
|
||||||
"selectCEEvent": "CE 이벤트 선택",
|
"selectCEEvent": "CE 이벤트 선택",
|
||||||
"selectEvent": "이벤트 선택",
|
"selectEvent": "이벤트 선택",
|
||||||
"selectFloor": "층 선택",
|
"selectFloor": "층 선택",
|
||||||
"selectSide": "전반/후반 선택",
|
"selectSide": "전반/후반 선택",
|
||||||
"selectBuff": "버프 선택",
|
"selectBuff": "버프 선택",
|
||||||
"selectStage": "스테이지 선택",
|
"selectStage": "스테이지 선택",
|
||||||
"previous": "이전",
|
"previous": "이전",
|
||||||
"next": "다음",
|
"next": "다음",
|
||||||
"noMonstersFound": "적을 찾을 수 없음",
|
"noMonstersFound": "적을 찾을 수 없음",
|
||||||
"addNewWave": "새 웨이브 추가",
|
"addNewWave": "새 웨이브 추가",
|
||||||
"searchStage": "스테이지 검색...",
|
"searchStage": "스테이지 검색...",
|
||||||
"noStageFound": "스테이지를 찾을 수 없음",
|
"noStageFound": "스테이지를 찾을 수 없음",
|
||||||
"searchMonster": "적 검색...",
|
"searchMonster": "적 검색...",
|
||||||
"changeRelic": "유물 변경",
|
"changeRelic": "유물 변경",
|
||||||
"deleteRelic": "유물 삭제",
|
"deleteRelic": "유물 삭제",
|
||||||
"deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?",
|
"deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?",
|
||||||
"setEffects": "효과 설정",
|
"setEffects": "효과 설정",
|
||||||
"details": "상세",
|
"details": "상세",
|
||||||
"normal": "기본 공격",
|
"normal": "기본 공격",
|
||||||
"bpskill": "스킬",
|
"bpskill": "스킬",
|
||||||
"maze": "전술",
|
"maze": "전술",
|
||||||
"ultra": "필살기",
|
"ultra": "필살기",
|
||||||
"servantskill": "메모스프라이트 스킬",
|
"servantskill": "메모스프라이트 스킬",
|
||||||
"severaltalent": "메모스프라이트 재능",
|
"severaltalent": "메모스프라이트 재능",
|
||||||
"singleattack": "단일 공격",
|
"singleattack": "단일 공격",
|
||||||
"enhance": "강화",
|
"enhance": "강화",
|
||||||
"summon": "소환",
|
"summon": "소환",
|
||||||
"blast": "광역 공격",
|
"blast": "광역 공격",
|
||||||
"restore": "회복",
|
"restore": "회복",
|
||||||
"support": "지원",
|
"support": "지원",
|
||||||
"aoeattack": "광역 공격",
|
"aoeattack": "광역 공격",
|
||||||
"mazeattack": "미궁 비기",
|
"mazeattack": "미궁 비기",
|
||||||
"impair": "약화",
|
"impair": "약화",
|
||||||
"bounce": "튕김",
|
"bounce": "튕김",
|
||||||
"active": "활성",
|
"active": "활성",
|
||||||
"inactive": "비활성",
|
"inactive": "비활성",
|
||||||
"defence": "방어",
|
"defence": "방어",
|
||||||
"maxAll": "모두 최대치",
|
"maxAll": "모두 최대치",
|
||||||
"maxAllSuccess": "스킬 레벨이 최대치로 설정되었습니다.",
|
"maxAllSuccess": "스킬 레벨이 최대치로 설정되었습니다.",
|
||||||
"maxAllFailed": "스킬 레벨을 최대치로 설정하지 못했습니다.",
|
"maxAllFailed": "스킬 레벨을 최대치로 설정하지 못했습니다.",
|
||||||
"noRelicEquipped": "성유물이 장착되지 않음",
|
"noRelicEquipped": "성유물이 장착되지 않음",
|
||||||
"anomalyArbitration": "이상 중재",
|
"anomalyArbitration": "이상 중재",
|
||||||
"normalMode": "일반 모드",
|
"normalMode": "일반 모드",
|
||||||
"hardMode": "어려움 모드",
|
"hardMode": "어려움 모드",
|
||||||
"selectPEAKEvent": "PEAK 이벤트 선택",
|
"selectPEAKEvent": "PEAK 이벤트 선택",
|
||||||
"mode": "모드",
|
"mode": "모드",
|
||||||
"selectMode": "모드를 선택",
|
"selectMode": "모드를 선택",
|
||||||
"rollBack": "이전 상태로 되돌리기",
|
"rollBack": "이전 상태로 되돌리기",
|
||||||
"upRoll": "부옵션 추가",
|
"upRoll": "부옵션 추가",
|
||||||
"downRoll": "부옵션 감소",
|
"downRoll": "부옵션 감소",
|
||||||
"actions": "동작",
|
"actions": "동작",
|
||||||
"avatars": "아바타",
|
"avatars": "아바타",
|
||||||
"quickView": "빠른 조회",
|
"quickView": "빠른 조회",
|
||||||
"extraSetting": "추가 설정",
|
"extraSetting": "추가 설정",
|
||||||
"disableCensorship": "검열 비활성화",
|
"disableCensorship": "검열 비활성화",
|
||||||
"hideUI": "UI 숨기기",
|
"hideUI": "UI 숨기기",
|
||||||
"theoryCraftMode": "Theory Craft 모드",
|
"theoryCraftMode": "Theory Craft 모드",
|
||||||
"cycleCount": "사이클 수",
|
"cycleCount": "사이클 수",
|
||||||
"pleaseSelectAllSubStats": "모든 부옵션을 선택하세요",
|
"pleaseSelectAllSubStats": "모든 부옵션을 선택하세요",
|
||||||
"subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다",
|
"subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다",
|
||||||
"theoryCraft": "Theory Craft",
|
"theoryCraft": "Theory Craft",
|
||||||
"multipathCharacter": "다중 운명 캐릭터",
|
"multipathCharacter": "다중 운명 캐릭터",
|
||||||
"mainPath": "개척자 운명",
|
"mainPath": "개척자 운명",
|
||||||
"march7Path": "삼월칠일 운명",
|
"march7Path": "삼월칠일 운명",
|
||||||
"challenge": "도전",
|
"challenge": "도전",
|
||||||
"skipNode": "노드 건너뛰기",
|
"skipNode": "노드 건너뛰기",
|
||||||
"disableSkip": "건너뛰기 비활성화",
|
"disableSkip": "건너뛰기 비활성화",
|
||||||
"skipNode1": "노드 1 건너뛰기",
|
"skipNode1": "노드 1 건너뛰기",
|
||||||
"skipNode2": "노드 2 건너뛰기",
|
"skipNode2": "노드 2 건너뛰기",
|
||||||
"extraFeatures": "추가 기능",
|
"extraFeatures": "추가 기능",
|
||||||
"detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.",
|
"detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.",
|
||||||
"detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.",
|
"detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.",
|
||||||
"detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.",
|
"detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.",
|
||||||
"detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.",
|
"detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.",
|
||||||
"detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.",
|
"detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.",
|
||||||
"detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.",
|
"detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.",
|
||||||
"trailblazer": "개척자",
|
"trailblazer": "개척자",
|
||||||
"listExtraEffect": "추가 효과 목록",
|
"listExtraEffect": "추가 효과 목록",
|
||||||
"extra": "추가",
|
"extra": "추가",
|
||||||
"customLineup": "커스텀 편성"
|
"customLineup": "커스텀 편성"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Ferramentas Firefly por Firefly Shelter"
|
"description": "Ferramentas Firefly por Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Tipo de Habilidade",
|
"skillType": "Tipo de Habilidade",
|
||||||
"skillName": "Nome da Habilidade",
|
"skillName": "Nome da Habilidade",
|
||||||
"character": "Personagem",
|
"character": "Personagem",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"path": "Caminho",
|
"path": "Caminho",
|
||||||
"rarity": "Raridade",
|
"rarity": "Raridade",
|
||||||
"element": "Elemento",
|
"element": "Elemento",
|
||||||
"technique": "Técnica",
|
"technique": "Técnica",
|
||||||
"talent": "Talento",
|
"talent": "Talento",
|
||||||
"basic": "Ataque Básico",
|
"basic": "Ataque Básico",
|
||||||
"skill": "Perícia",
|
"skill": "Perícia",
|
||||||
"ultimate": "Perícia Suprema",
|
"ultimate": "Perícia Suprema",
|
||||||
"servant": "Servo",
|
"servant": "Servo",
|
||||||
"damage": "Dano",
|
"damage": "Dano",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"warrior": "A Destruição",
|
"warrior": "A Destruição",
|
||||||
"knight": "A Preservação",
|
"knight": "A Preservação",
|
||||||
"mage": "A Erudição",
|
"mage": "A Erudição",
|
||||||
"priest": "A Abundância",
|
"priest": "A Abundância",
|
||||||
"rogue": "A Caça",
|
"rogue": "A Caça",
|
||||||
"shaman": "A Harmonia",
|
"shaman": "A Harmonia",
|
||||||
"warlock": "A Inexistência",
|
"warlock": "A Inexistência",
|
||||||
"memory": "A Lembrança",
|
"memory": "A Lembrança",
|
||||||
"elation": "A Alegria",
|
"elation": "A Alegria",
|
||||||
"fire": "Fogo",
|
"fire": "Fogo",
|
||||||
"ice": "Gelo",
|
"ice": "Gelo",
|
||||||
"imaginary": "Imaginário",
|
"imaginary": "Imaginário",
|
||||||
"physical": "Físico",
|
"physical": "Físico",
|
||||||
"quantum": "Quântico",
|
"quantum": "Quântico",
|
||||||
"thunder": "Raio",
|
"thunder": "Raio",
|
||||||
"wind": "Vento",
|
"wind": "Vento",
|
||||||
"hp": "PV",
|
"hp": "PV",
|
||||||
"atk": "ATQ",
|
"atk": "ATQ",
|
||||||
"speed": "VEL",
|
"speed": "VEL",
|
||||||
"critRate": "Taxa CRIT",
|
"critRate": "Taxa CRIT",
|
||||||
"critDmg": "Dano CRIT",
|
"critDmg": "Dano CRIT",
|
||||||
"breakEffect": "Efeito de Quebra",
|
"breakEffect": "Efeito de Quebra",
|
||||||
"effectRes": "RES a Efeito",
|
"effectRes": "RES a Efeito",
|
||||||
"energyRegenerationRate": "Taxa de Regeneração de Energia",
|
"energyRegenerationRate": "Taxa de Regeneração de Energia",
|
||||||
"effectHitRate": "Taxa de Acerto de Efeito",
|
"effectHitRate": "Taxa de Acerto de Efeito",
|
||||||
"outgoingHealingBoost": "Bônus de Cura",
|
"outgoingHealingBoost": "Bônus de Cura",
|
||||||
"fireDmgBoost": "Bônus de Dano de Fogo",
|
"fireDmgBoost": "Bônus de Dano de Fogo",
|
||||||
"iceDmgBoost": "Bônus de Dano de Gelo",
|
"iceDmgBoost": "Bônus de Dano de Gelo",
|
||||||
"imaginaryDmgBoost": "Bônus de Dano Imaginário",
|
"imaginaryDmgBoost": "Bônus de Dano Imaginário",
|
||||||
"physicalDmgBoost": "Bônus de Dano Físico",
|
"physicalDmgBoost": "Bônus de Dano Físico",
|
||||||
"quantumDmgBoost": "Bônus de Dano Quântico",
|
"quantumDmgBoost": "Bônus de Dano Quântico",
|
||||||
"thunderDmgBoost": "Bônus de Dano de Raio",
|
"thunderDmgBoost": "Bônus de Dano de Raio",
|
||||||
"windDmgBoost": "Bônus de Dano de Vento",
|
"windDmgBoost": "Bônus de Dano de Vento",
|
||||||
"pursued": "Dano Adicional",
|
"pursued": "Dano Adicional",
|
||||||
"true damage": "Dano Verdadeiro",
|
"true damage": "Dano Verdadeiro",
|
||||||
"elationdamage": "Dano de Alegria",
|
"elationdamage": "Dano de Alegria",
|
||||||
"follow-up": "Dano de Ataque Extra",
|
"follow-up": "Dano de Ataque Extra",
|
||||||
"elemental damage": "Dano de Quebra e Superquebra",
|
"elemental damage": "Dano de Quebra e Superquebra",
|
||||||
"dot": "Dano Contínuo",
|
"dot": "Dano Contínuo",
|
||||||
"qte": "Habilidade QTE",
|
"qte": "Habilidade QTE",
|
||||||
"level": "Nível",
|
"level": "Nível",
|
||||||
"relics": "Relíquias",
|
"relics": "Relíquias",
|
||||||
"eidolons": "Eidolons",
|
"eidolons": "Eidolons",
|
||||||
"lightcones": "Cones de Luz",
|
"lightcones": "Cones de Luz",
|
||||||
"loadData": "Carregar dados",
|
"loadData": "Carregar dados",
|
||||||
"exportData": "Exportar dados",
|
"exportData": "Exportar dados",
|
||||||
"connectSetting": "Configuração de Conexão",
|
"connectSetting": "Configuração de Conexão",
|
||||||
"connected": "Conectado",
|
"connected": "Conectado",
|
||||||
"unconnected": "Desconectado",
|
"unconnected": "Desconectado",
|
||||||
"psConnection": "Conexão PS",
|
"psConnection": "Conexão PS",
|
||||||
"connectionType": "Tipo de Conexão",
|
"connectionType": "Tipo de Conexão",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"connectPs": "Conectar PS",
|
"connectPs": "Conectar PS",
|
||||||
"disconnect": "Desconectar",
|
"disconnect": "Desconectar",
|
||||||
"other": "Outro",
|
"other": "Outro",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Banco de dados",
|
"database": "Banco de dados",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Configuração de Monstros",
|
"monsterSetting": "Configuração de Monstros",
|
||||||
"serverUrl": "URL do Servidor",
|
"serverUrl": "URL do Servidor",
|
||||||
"privateType": "Tipo Privado",
|
"privateType": "Tipo Privado",
|
||||||
"local": "Local",
|
"local": "Local",
|
||||||
"server": "Servidor",
|
"server": "Servidor",
|
||||||
"username": "Usuário",
|
"username": "Usuário",
|
||||||
"password": "Senha",
|
"password": "Senha",
|
||||||
"placeholderServerUrl": "Digite o URL do servidor",
|
"placeholderServerUrl": "Digite o URL do servidor",
|
||||||
"placeholderUsername": "Digite o usuário",
|
"placeholderUsername": "Digite o usuário",
|
||||||
"placeholderPassword": "Digite a senha",
|
"placeholderPassword": "Digite a senha",
|
||||||
"connectedSuccess": "Conectado ao PS com sucesso",
|
"connectedSuccess": "Conectado ao PS com sucesso",
|
||||||
"connectedFailed": "Falha ao conectar ao PS",
|
"connectedFailed": "Falha ao conectar ao PS",
|
||||||
"syncSuccess": "Dados sincronizados com o PS com sucesso",
|
"syncSuccess": "Dados sincronizados com o PS com sucesso",
|
||||||
"syncFailed": "Falha ao sincronizar dados com o PS",
|
"syncFailed": "Falha ao sincronizar dados com o PS",
|
||||||
"sync": "Sincronizar",
|
"sync": "Sincronizar",
|
||||||
"importSetting": "Configuração de Importação",
|
"importSetting": "Configuração de Importação",
|
||||||
"profile": "Perfil",
|
"profile": "Perfil",
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"copyProfiles": "Copiar perfis",
|
"copyProfiles": "Copiar perfis",
|
||||||
"addNewProfile": "Adicionar novo perfil",
|
"addNewProfile": "Adicionar novo perfil",
|
||||||
"createNewProfile": "Criar novo perfil",
|
"createNewProfile": "Criar novo perfil",
|
||||||
"editProfile": "Editar perfil",
|
"editProfile": "Editar perfil",
|
||||||
"placeholderProfileName": "Digite o nome do perfil",
|
"placeholderProfileName": "Digite o nome do perfil",
|
||||||
"profileName": "Nome do perfil",
|
"profileName": "Nome do perfil",
|
||||||
"create": "Criar",
|
"create": "Criar",
|
||||||
"update": "Atualizar",
|
"update": "Atualizar",
|
||||||
"characterInformation": "Informação do Personagem",
|
"characterInformation": "Informação do Personagem",
|
||||||
"skills": "Habilidades",
|
"skills": "Habilidades",
|
||||||
"showcaseCard": "Cartão de Exibição",
|
"showcaseCard": "Cartão de Exibição",
|
||||||
"comingSoon": "Em breve",
|
"comingSoon": "Em breve",
|
||||||
"characterName": "Nome do personagem",
|
"characterName": "Nome do personagem",
|
||||||
"placeholderCharacter": "Digite o nome do personagem",
|
"placeholderCharacter": "Digite o nome do personagem",
|
||||||
"characterSettings": "Configurações de Personagem",
|
"characterSettings": "Configurações de Personagem",
|
||||||
"levelConfiguration": "Configuração de Nível",
|
"levelConfiguration": "Configuração de Nível",
|
||||||
"characterLevel": "Nível do Personagem",
|
"characterLevel": "Nível do Personagem",
|
||||||
"max": "MÁX",
|
"max": "MÁX",
|
||||||
"ultimateEnergy": "Energia da Perícia Suprema",
|
"ultimateEnergy": "Energia da Perícia Suprema",
|
||||||
"currentEnergy": "Energia Atual",
|
"currentEnergy": "Energia Atual",
|
||||||
"setTo50": "Definir para 50%",
|
"setTo50": "Definir para 50%",
|
||||||
"battleConfiguration": "Configuração de Batalha",
|
"battleConfiguration": "Configuração de Batalha",
|
||||||
"useTechnique": "Usar Técnica",
|
"useTechnique": "Usar Técnica",
|
||||||
"techniqueNote": "Ativar efeitos de técnica pré-batalha",
|
"techniqueNote": "Ativar efeitos de técnica pré-batalha",
|
||||||
"enhancement": "Aprimoramento",
|
"enhancement": "Aprimoramento",
|
||||||
"enhancementLevel": "Nível de Aprimoramento",
|
"enhancementLevel": "Nível de Aprimoramento",
|
||||||
"origin": "Origem",
|
"origin": "Origem",
|
||||||
"enhancedNote": "Níveis altos desbloqueiam habilidades",
|
"enhancedNote": "Níveis altos desbloqueiam habilidades",
|
||||||
"lightconeEquipment": "Equipamento de Cone de Luz",
|
"lightconeEquipment": "Equipamento de Cone de Luz",
|
||||||
"lightconeSettings": "Configurações de Cone de Luz",
|
"lightconeSettings": "Configurações de Cone de Luz",
|
||||||
"placeholderLevel": "Digite o nível",
|
"placeholderLevel": "Digite o nível",
|
||||||
"superimpositionRank": "Rank de Sobreposição",
|
"superimpositionRank": "Rank de Sobreposição",
|
||||||
"ranksNote": "Ranks maiores dão efeitos mais fortes",
|
"ranksNote": "Ranks maiores dão efeitos mais fortes",
|
||||||
"changeLightcone": "Mudar Cone de Luz",
|
"changeLightcone": "Mudar Cone de Luz",
|
||||||
"removeLightcone": "Remover Cone de Luz",
|
"removeLightcone": "Remover Cone de Luz",
|
||||||
"equipLightcone": "Equipar Cone de Luz",
|
"equipLightcone": "Equipar Cone de Luz",
|
||||||
"noLightconeEquipped": "Sem Cone de Luz",
|
"noLightconeEquipped": "Sem Cone de Luz",
|
||||||
"equipLightconeNote": "Equipe um cone de luz para melhorar o personagem",
|
"equipLightconeNote": "Equipe um cone de luz para melhorar o personagem",
|
||||||
"filter": "Filtro",
|
"filter": "Filtro",
|
||||||
"selectedCharacters": "Personagens Selecionados",
|
"selectedCharacters": "Personagens Selecionados",
|
||||||
"selectedProfiles": "Perfis Selecionados",
|
"selectedProfiles": "Perfis Selecionados",
|
||||||
"clearAll": "Limpar Tudo",
|
"clearAll": "Limpar Tudo",
|
||||||
"selectAll": "Selecionar Tudo",
|
"selectAll": "Selecionar Tudo",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copied": "Copiado",
|
"copied": "Copiado",
|
||||||
"noAvatarSelected": "Nenhum personagem selecionado",
|
"noAvatarSelected": "Nenhum personagem selecionado",
|
||||||
"noAvatarToCopySelected": "Nenhum personagem para copiar",
|
"noAvatarToCopySelected": "Nenhum personagem para copiar",
|
||||||
"pleaseSelectAtLeastOneProfile": "Por favor, selecione ao menos um perfil",
|
"pleaseSelectAtLeastOneProfile": "Por favor, selecione ao menos um perfil",
|
||||||
"pleaseEnterUid": "Por favor, insira o UID",
|
"pleaseEnterUid": "Por favor, insira o UID",
|
||||||
"failedToFetchEnkaData": "Falha ao obter dados Enka",
|
"failedToFetchEnkaData": "Falha ao obter dados Enka",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Selecione ao menos um personagem",
|
"pleaseSelectAtLeastOneCharacter": "Selecione ao menos um personagem",
|
||||||
"noDataToImport": "Sem dados para importar",
|
"noDataToImport": "Sem dados para importar",
|
||||||
"pleaseSelectAFile": "Por favor, selecione um arquivo",
|
"pleaseSelectAFile": "Por favor, selecione um arquivo",
|
||||||
"fileMustBeAValidJsonFile": "O arquivo deve ser um JSON válido",
|
"fileMustBeAValidJsonFile": "O arquivo deve ser um JSON válido",
|
||||||
"importEnkaDataSuccess": "Dados Enka importados com sucesso",
|
"importEnkaDataSuccess": "Dados Enka importados com sucesso",
|
||||||
"importFreeSRDataSuccess": "Dados FreeSR importados com sucesso",
|
"importFreeSRDataSuccess": "Dados FreeSR importados com sucesso",
|
||||||
"importDatabaseSuccess": "Banco de dados importado com sucesso",
|
"importDatabaseSuccess": "Banco de dados importado com sucesso",
|
||||||
"getData": "Obter Dados",
|
"getData": "Obter Dados",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
"freeSRImport": "Importação FreeSR",
|
"freeSRImport": "Importação FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Suporta apenas arquivos JSON FreeSR",
|
"onlySupportFreeSRJsonFile": "Suporta apenas arquivos JSON FreeSR",
|
||||||
"pickAFile": "Escolher um arquivo",
|
"pickAFile": "Escolher um arquivo",
|
||||||
"lightConeSetting": "Configuração de Cone de Luz",
|
"lightConeSetting": "Configuração de Cone de Luz",
|
||||||
"relicMaker": "Criador de Relíquias",
|
"relicMaker": "Criador de Relíquias",
|
||||||
"pleaseSelectAllOptions": "Por favor selecione todas as opções",
|
"pleaseSelectAllOptions": "Por favor selecione todas as opções",
|
||||||
"relicSavedSuccessfully": "Relíquia salva com sucesso",
|
"relicSavedSuccessfully": "Relíquia salva com sucesso",
|
||||||
"mainSettings": "Configurações Principais",
|
"mainSettings": "Configurações Principais",
|
||||||
"mainStat": "Atributo Principal",
|
"mainStat": "Atributo Principal",
|
||||||
"set": "Conjunto",
|
"set": "Conjunto",
|
||||||
"pleaseSelectASet": "Selecione um conjunto",
|
"pleaseSelectASet": "Selecione um conjunto",
|
||||||
"effectBonus": "Bônus de Efeito",
|
"effectBonus": "Bônus de Efeito",
|
||||||
"totalRoll": "Total de Melhorias",
|
"totalRoll": "Total de Melhorias",
|
||||||
"randomizeStats": "Atributos Aleatórios",
|
"randomizeStats": "Atributos Aleatórios",
|
||||||
"randomizeRolls": "Melhorias Aleatórias",
|
"randomizeRolls": "Melhorias Aleatórias",
|
||||||
"selectASubStat": "Selecionar sub-atributo",
|
"selectASubStat": "Selecionar sub-atributo",
|
||||||
"selectASet": "Selecionar conjunto",
|
"selectASet": "Selecionar conjunto",
|
||||||
"selectAMainStat": "Selecionar atributo principal",
|
"selectAMainStat": "Selecionar atributo principal",
|
||||||
"save": "Salvar",
|
"save": "Salvar",
|
||||||
"reset": "Redefinir",
|
"reset": "Redefinir",
|
||||||
"roll": "Melhoria",
|
"roll": "Melhoria",
|
||||||
"step": "Passo",
|
"step": "Passo",
|
||||||
"memoryOfChaos": "Memória do Caos",
|
"memoryOfChaos": "Memória do Caos",
|
||||||
"pureFiction": "Pura Ficção",
|
"pureFiction": "Pura Ficção",
|
||||||
"apocalypticShadow": "Sombra Apocalíptica",
|
"apocalypticShadow": "Sombra Apocalíptica",
|
||||||
"customEnemy": "Inimigo Personalizado",
|
"customEnemy": "Inimigo Personalizado",
|
||||||
"simulatedUniverse": "Universo Simulado",
|
"simulatedUniverse": "Universo Simulado",
|
||||||
"floor": "Andar",
|
"floor": "Andar",
|
||||||
"side": "Lado",
|
"side": "Lado",
|
||||||
"wave": "Onda",
|
"wave": "Onda",
|
||||||
"stage": "Fase",
|
"stage": "Fase",
|
||||||
"useCycleCount": "Usar contagem de ciclos?",
|
"useCycleCount": "Usar contagem de ciclos?",
|
||||||
"useTurbulenceBuff": "Usar buff de turbulência?",
|
"useTurbulenceBuff": "Usar buff de turbulência?",
|
||||||
"firstHalfEnemies": "Inimigos da primeira metade",
|
"firstHalfEnemies": "Inimigos da primeira metade",
|
||||||
"secondHalfEnemies": "Inimigos da segunda metade",
|
"secondHalfEnemies": "Inimigos da segunda metade",
|
||||||
"firstNodeEnemies": "Inimigos do nodo 1",
|
"firstNodeEnemies": "Inimigos do nodo 1",
|
||||||
"secondNodeEnemies": "Inimigos do nodo 2",
|
"secondNodeEnemies": "Inimigos do nodo 2",
|
||||||
"thirdNodeEnemies": "Inimigos do nodo 3",
|
"thirdNodeEnemies": "Inimigos do nodo 3",
|
||||||
"firstNode": "Nodo 1",
|
"firstNode": "Nodo 1",
|
||||||
"secondNode": "Nodo 2",
|
"secondNode": "Nodo 2",
|
||||||
"thirdNode": "Nodo 3",
|
"thirdNode": "Nodo 3",
|
||||||
"listEnemies": "Lista de inimigos",
|
"listEnemies": "Lista de inimigos",
|
||||||
"turbulenceBuff": "Buff de Turbulência",
|
"turbulenceBuff": "Buff de Turbulência",
|
||||||
"noEventSelected": "Nenhum evento selecionado",
|
"noEventSelected": "Nenhum evento selecionado",
|
||||||
"noTurbulenceBuff": "Sem Buff de Turbulência",
|
"noTurbulenceBuff": "Sem Buff de Turbulência",
|
||||||
"upper": "Superior",
|
"upper": "Superior",
|
||||||
"lower": "Inferior",
|
"lower": "Inferior",
|
||||||
"upperToLower": "Superior -> Inferior",
|
"upperToLower": "Superior -> Inferior",
|
||||||
"lowerToUpper": "Inferior -> Superior",
|
"lowerToUpper": "Inferior -> Superior",
|
||||||
"selectMOCEvent": "Selecionar Evento MOC",
|
"selectMOCEvent": "Selecionar Evento MOC",
|
||||||
"selectPFEvent": "Selecionar Evento PF",
|
"selectPFEvent": "Selecionar Evento PF",
|
||||||
"selectASEvent": "Selecionar Evento AS",
|
"selectASEvent": "Selecionar Evento AS",
|
||||||
"selectCEEvent": "Selecionar Evento CE",
|
"selectCEEvent": "Selecionar Evento CE",
|
||||||
"selectEvent": "Selecionar Evento",
|
"selectEvent": "Selecionar Evento",
|
||||||
"selectFloor": "Selecionar Andar",
|
"selectFloor": "Selecionar Andar",
|
||||||
"selectSide": "Selecionar Lado",
|
"selectSide": "Selecionar Lado",
|
||||||
"selectBuff": "Selecionar Buff",
|
"selectBuff": "Selecionar Buff",
|
||||||
"selectStage": "Selecionar Fase",
|
"selectStage": "Selecionar Fase",
|
||||||
"previous": "Anterior",
|
"previous": "Anterior",
|
||||||
"next": "Próximo",
|
"next": "Próximo",
|
||||||
"noMonstersFound": "Nenhum monstro encontrado",
|
"noMonstersFound": "Nenhum monstro encontrado",
|
||||||
"addNewWave": "Adicionar Nova Onda",
|
"addNewWave": "Adicionar Nova Onda",
|
||||||
"searchStage": "Buscar fase...",
|
"searchStage": "Buscar fase...",
|
||||||
"noStageFound": "Fase não encontrada",
|
"noStageFound": "Fase não encontrada",
|
||||||
"searchMonster": "Buscar monstro...",
|
"searchMonster": "Buscar monstro...",
|
||||||
"changeRelic": "Mudar relíquia",
|
"changeRelic": "Mudar relíquia",
|
||||||
"deleteRelic": "Deletar relíquia",
|
"deleteRelic": "Deletar relíquia",
|
||||||
"deleteRelicConfirm": "Tem certeza que deseja deletar a relíquia neste slot?",
|
"deleteRelicConfirm": "Tem certeza que deseja deletar a relíquia neste slot?",
|
||||||
"setEffects": "Definir Efeitos",
|
"setEffects": "Definir Efeitos",
|
||||||
"details": "Detalhes",
|
"details": "Detalhes",
|
||||||
"normal": "Ataque Básico",
|
"normal": "Ataque Básico",
|
||||||
"bpskill": "Perícia",
|
"bpskill": "Perícia",
|
||||||
"maze": "Técnica",
|
"maze": "Técnica",
|
||||||
"ultra": "Perícia Suprema",
|
"ultra": "Perícia Suprema",
|
||||||
"servantskill": "Habilidade do Memoespirito",
|
"servantskill": "Habilidade do Memoespirito",
|
||||||
"severaltalent": "Talento do Memoespirito",
|
"severaltalent": "Talento do Memoespirito",
|
||||||
"singleattack": "Ataque Único",
|
"singleattack": "Ataque Único",
|
||||||
"enhance": "Aprimorar",
|
"enhance": "Aprimorar",
|
||||||
"summon": "Invocar",
|
"summon": "Invocar",
|
||||||
"mazeattack": "Ataque de Técnica",
|
"mazeattack": "Ataque de Técnica",
|
||||||
"blast": "Explosão",
|
"blast": "Explosão",
|
||||||
"restore": "Restaurar",
|
"restore": "Restaurar",
|
||||||
"support": "Suporte",
|
"support": "Suporte",
|
||||||
"aoeattack": "Ataque em Área",
|
"aoeattack": "Ataque em Área",
|
||||||
"impair": "Enfraquecer",
|
"impair": "Enfraquecer",
|
||||||
"bounce": "Rebote",
|
"bounce": "Rebote",
|
||||||
"active": "Ativo",
|
"active": "Ativo",
|
||||||
"defence": "Defesa",
|
"defence": "Defesa",
|
||||||
"inactive": "Inativo",
|
"inactive": "Inativo",
|
||||||
"maxAll": "Maximizar Tudo",
|
"maxAll": "Maximizar Tudo",
|
||||||
"maxAllSuccess": "Habilidades maximizadas com sucesso.",
|
"maxAllSuccess": "Habilidades maximizadas com sucesso.",
|
||||||
"maxAllFailed": "Falha ao maximizar habilidades.",
|
"maxAllFailed": "Falha ao maximizar habilidades.",
|
||||||
"noRelicEquipped": "Sem relíquia equipada",
|
"noRelicEquipped": "Sem relíquia equipada",
|
||||||
"anomalyArbitration": "Arbitragem de Anomalia",
|
"anomalyArbitration": "Arbitragem de Anomalia",
|
||||||
"normalMode": "Modo Normal",
|
"normalMode": "Modo Normal",
|
||||||
"hardMode": "Modo Difícil",
|
"hardMode": "Modo Difícil",
|
||||||
"selectPEAKEvent": "Selecionar Evento PEAK",
|
"selectPEAKEvent": "Selecionar Evento PEAK",
|
||||||
"mode": "Modo",
|
"mode": "Modo",
|
||||||
"selectMode": "Selecionar um modo",
|
"selectMode": "Selecionar um modo",
|
||||||
"rollBack": "Reverter",
|
"rollBack": "Reverter",
|
||||||
"upRoll": "Aumentar Melhoria",
|
"upRoll": "Aumentar Melhoria",
|
||||||
"downRoll": "Diminuir Melhoria",
|
"downRoll": "Diminuir Melhoria",
|
||||||
"actions": "Ações",
|
"actions": "Ações",
|
||||||
"avatars": "Avatares",
|
"avatars": "Avatares",
|
||||||
"quickView": "Visualização Rápida",
|
"quickView": "Visualização Rápida",
|
||||||
"extraSetting": "Configurações Extras",
|
"extraSetting": "Configurações Extras",
|
||||||
"disableCensorship": "Desativar Censura",
|
"disableCensorship": "Desativar Censura",
|
||||||
"hideUI": "Ocultar UI",
|
"hideUI": "Ocultar UI",
|
||||||
"theoryCraftMode": "Modo Theorycraft",
|
"theoryCraftMode": "Modo Theorycraft",
|
||||||
"cycleCount": "Contagem de Ciclos",
|
"cycleCount": "Contagem de Ciclos",
|
||||||
"pleaseSelectAllSubStats": "Selecione todos os sub-atributos",
|
"pleaseSelectAllSubStats": "Selecione todos os sub-atributos",
|
||||||
"subStatRollCountCannotBeZero": "As melhorias do sub-atributo não podem ser zero",
|
"subStatRollCountCannotBeZero": "As melhorias do sub-atributo não podem ser zero",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Personagem Multicaminho",
|
"multipathCharacter": "Personagem Multicaminho",
|
||||||
"mainPath": "Caminho Principal",
|
"mainPath": "Caminho Principal",
|
||||||
"march7Path": "Caminho de 7 de Março",
|
"march7Path": "Caminho de 7 de Março",
|
||||||
"challenge": "Desafio",
|
"challenge": "Desafio",
|
||||||
"skipNode": "Pular Nó",
|
"skipNode": "Pular Nó",
|
||||||
"disableSkip": "Desativar pulo",
|
"disableSkip": "Desativar pulo",
|
||||||
"skipNode1": "Pular nó 1",
|
"skipNode1": "Pular nó 1",
|
||||||
"skipNode2": "Pular nó 2",
|
"skipNode2": "Pular nó 2",
|
||||||
"extraFeatures": "Recursos Extras",
|
"extraFeatures": "Recursos Extras",
|
||||||
"detailTheoryCraft": "Ativar isso permite personalizar os ciclos e ajustar o HP dos inimigos.",
|
"detailTheoryCraft": "Ativar isso permite personalizar os ciclos e ajustar o HP dos inimigos.",
|
||||||
"detailSkipNode": "Permite pular (Nó 1/Nó 2) em Memória do Caos ou Pura Ficção.",
|
"detailSkipNode": "Permite pular (Nó 1/Nó 2) em Memória do Caos ou Pura Ficção.",
|
||||||
"detailChallengePeak": "Permite mudar a temporada de Peak na Anomalia atual.",
|
"detailChallengePeak": "Permite mudar a temporada de Peak na Anomalia atual.",
|
||||||
"detailHiddenUi": "Irá esconder a interface do jogo.",
|
"detailHiddenUi": "Irá esconder a interface do jogo.",
|
||||||
"detailDisableCensorship": "Desativa a censura no jogo.",
|
"detailDisableCensorship": "Desativa a censura no jogo.",
|
||||||
"detailMultipathCharacter": "Permite mudar o Caminho de certos personagens.",
|
"detailMultipathCharacter": "Permite mudar o Caminho de certos personagens.",
|
||||||
"trailblazer": "Desbravador",
|
"trailblazer": "Desbravador",
|
||||||
"listExtraEffect": "Lista de Efeitos Extras",
|
"listExtraEffect": "Lista de Efeitos Extras",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"customLineup": "Formação Personalizada"
|
"customLineup": "Formação Personalizada"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Инструменты Firefly от Firefly Shelter"
|
"description": "Инструменты Firefly от Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Тип навыка",
|
"skillType": "Тип навыка",
|
||||||
"skillName": "Название навыка",
|
"skillName": "Название навыка",
|
||||||
"character": "Персонаж",
|
"character": "Персонаж",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "Путь",
|
"path": "Путь",
|
||||||
"rarity": "Редкость",
|
"rarity": "Редкость",
|
||||||
"element": "Элемент",
|
"element": "Элемент",
|
||||||
"technique": "Техника",
|
"technique": "Техника",
|
||||||
"talent": "Талант",
|
"talent": "Талант",
|
||||||
"basic": "Базовая атака",
|
"basic": "Базовая атака",
|
||||||
"skill": "Навык",
|
"skill": "Навык",
|
||||||
"ultimate": "Сверхспособность",
|
"ultimate": "Сверхспособность",
|
||||||
"servant": "Слуга",
|
"servant": "Слуга",
|
||||||
"damage": "Урон",
|
"damage": "Урон",
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
"warrior": "Разрушение",
|
"warrior": "Разрушение",
|
||||||
"knight": "Сохранение",
|
"knight": "Сохранение",
|
||||||
"mage": "Эрудиция",
|
"mage": "Эрудиция",
|
||||||
"priest": "Изобилие",
|
"priest": "Изобилие",
|
||||||
"rogue": "Охота",
|
"rogue": "Охота",
|
||||||
"shaman": "Гармония",
|
"shaman": "Гармония",
|
||||||
"warlock": "Небытие",
|
"warlock": "Небытие",
|
||||||
"memory": "Память",
|
"memory": "Память",
|
||||||
"elation": "Радость",
|
"elation": "Радость",
|
||||||
"fire": "Огонь",
|
"fire": "Огонь",
|
||||||
"ice": "Лед",
|
"ice": "Лед",
|
||||||
"imaginary": "Мнимый",
|
"imaginary": "Мнимый",
|
||||||
"physical": "Физический",
|
"physical": "Физический",
|
||||||
"quantum": "Квантовый",
|
"quantum": "Квантовый",
|
||||||
"thunder": "Электрический",
|
"thunder": "Электрический",
|
||||||
"wind": "Ветряной",
|
"wind": "Ветряной",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "Сила атаки",
|
"atk": "Сила атаки",
|
||||||
"speed": "Скорость",
|
"speed": "Скорость",
|
||||||
"critRate": "Крит. шанс",
|
"critRate": "Крит. шанс",
|
||||||
"critDmg": "Крит. урон",
|
"critDmg": "Крит. урон",
|
||||||
"breakEffect": "Эффект пробития",
|
"breakEffect": "Эффект пробития",
|
||||||
"effectRes": "Сопротивление эффектам",
|
"effectRes": "Сопротивление эффектам",
|
||||||
"energyRegenerationRate": "Скорость восст. энергии",
|
"energyRegenerationRate": "Скорость восст. энергии",
|
||||||
"effectHitRate": "Шанс попадания эффектов",
|
"effectHitRate": "Шанс попадания эффектов",
|
||||||
"outgoingHealingBoost": "Бонус исходящего исцеления",
|
"outgoingHealingBoost": "Бонус исходящего исцеления",
|
||||||
"fireDmgBoost": "Бонус огненного урона",
|
"fireDmgBoost": "Бонус огненного урона",
|
||||||
"iceDmgBoost": "Бонус ледяного урона",
|
"iceDmgBoost": "Бонус ледяного урона",
|
||||||
"imaginaryDmgBoost": "Бонус мнимого урона",
|
"imaginaryDmgBoost": "Бонус мнимого урона",
|
||||||
"physicalDmgBoost": "Бонус физ. урона",
|
"physicalDmgBoost": "Бонус физ. урона",
|
||||||
"quantumDmgBoost": "Бонус квантового урона",
|
"quantumDmgBoost": "Бонус квантового урона",
|
||||||
"thunderDmgBoost": "Бонус электр. урона",
|
"thunderDmgBoost": "Бонус электр. урона",
|
||||||
"windDmgBoost": "Бонус ветряного урона",
|
"windDmgBoost": "Бонус ветряного урона",
|
||||||
"pursued": "Дополнительный урон",
|
"pursued": "Дополнительный урон",
|
||||||
"true damage": "Истинный урон",
|
"true damage": "Истинный урон",
|
||||||
"elationdamage": "Урон радости",
|
"elationdamage": "Урон радости",
|
||||||
"follow-up": "Урон бонус-атаки",
|
"follow-up": "Урон бонус-атаки",
|
||||||
"elemental damage": "Урон пробития и суперпробития",
|
"elemental damage": "Урон пробития и суперпробития",
|
||||||
"dot": "Периодический урон",
|
"dot": "Периодический урон",
|
||||||
"qte": "QTE Навык",
|
"qte": "QTE Навык",
|
||||||
"level": "Уровень",
|
"level": "Уровень",
|
||||||
"relics": "Реликвии",
|
"relics": "Реликвии",
|
||||||
"eidolons": "Эйдолоны",
|
"eidolons": "Эйдолоны",
|
||||||
"lightcones": "Световые конусы",
|
"lightcones": "Световые конусы",
|
||||||
"loadData": "Загрузить данные",
|
"loadData": "Загрузить данные",
|
||||||
"exportData": "Экспортировать данные",
|
"exportData": "Экспортировать данные",
|
||||||
"connectSetting": "Настройки подключения",
|
"connectSetting": "Настройки подключения",
|
||||||
"connected": "Подключено",
|
"connected": "Подключено",
|
||||||
"unconnected": "Не подключено",
|
"unconnected": "Не подключено",
|
||||||
"psConnection": "Подключение к PS",
|
"psConnection": "Подключение к PS",
|
||||||
"connectionType": "Тип подключения",
|
"connectionType": "Тип подключения",
|
||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
"connectPs": "Подключить PS",
|
"connectPs": "Подключить PS",
|
||||||
"disconnect": "Отключить",
|
"disconnect": "Отключить",
|
||||||
"other": "Другое",
|
"other": "Другое",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "База данных",
|
"database": "База данных",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Настройки монстров",
|
"monsterSetting": "Настройки монстров",
|
||||||
"serverUrl": "URL сервера",
|
"serverUrl": "URL сервера",
|
||||||
"privateType": "Приватный тип",
|
"privateType": "Приватный тип",
|
||||||
"local": "Локальный",
|
"local": "Локальный",
|
||||||
"server": "Сервер",
|
"server": "Сервер",
|
||||||
"username": "Имя пользователя",
|
"username": "Имя пользователя",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"placeholderServerUrl": "Введите URL сервера",
|
"placeholderServerUrl": "Введите URL сервера",
|
||||||
"placeholderUsername": "Введите имя пользователя",
|
"placeholderUsername": "Введите имя пользователя",
|
||||||
"placeholderPassword": "Введите пароль",
|
"placeholderPassword": "Введите пароль",
|
||||||
"connectedSuccess": "Успешное подключение к PS",
|
"connectedSuccess": "Успешное подключение к PS",
|
||||||
"connectedFailed": "Ошибка подключения к PS",
|
"connectedFailed": "Ошибка подключения к PS",
|
||||||
"syncSuccess": "Данные успешно синхронизированы с PS",
|
"syncSuccess": "Данные успешно синхронизированы с PS",
|
||||||
"syncFailed": "Ошибка синхронизации данных",
|
"syncFailed": "Ошибка синхронизации данных",
|
||||||
"sync": "Синхронизация",
|
"sync": "Синхронизация",
|
||||||
"importSetting": "Настройки импорта",
|
"importSetting": "Настройки импорта",
|
||||||
"profile": "Профиль",
|
"profile": "Профиль",
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"copyProfiles": "Копировать профили",
|
"copyProfiles": "Копировать профили",
|
||||||
"addNewProfile": "Добавить новый профиль",
|
"addNewProfile": "Добавить новый профиль",
|
||||||
"createNewProfile": "Создать новый профиль",
|
"createNewProfile": "Создать новый профиль",
|
||||||
"editProfile": "Редактировать профиль",
|
"editProfile": "Редактировать профиль",
|
||||||
"placeholderProfileName": "Введите имя профиля",
|
"placeholderProfileName": "Введите имя профиля",
|
||||||
"profileName": "Имя профиля",
|
"profileName": "Имя профиля",
|
||||||
"create": "Создать",
|
"create": "Создать",
|
||||||
"update": "Обновить",
|
"update": "Обновить",
|
||||||
"characterInformation": "Информация о персонаже",
|
"characterInformation": "Информация о персонаже",
|
||||||
"skills": "Навыки",
|
"skills": "Навыки",
|
||||||
"showcaseCard": "Карточка персонажа",
|
"showcaseCard": "Карточка персонажа",
|
||||||
"comingSoon": "Скоро",
|
"comingSoon": "Скоро",
|
||||||
"characterName": "Имя персонажа",
|
"characterName": "Имя персонажа",
|
||||||
"placeholderCharacter": "Введите имя персонажа",
|
"placeholderCharacter": "Введите имя персонажа",
|
||||||
"characterSettings": "Настройки персонажа",
|
"characterSettings": "Настройки персонажа",
|
||||||
"levelConfiguration": "Конфигурация уровня",
|
"levelConfiguration": "Конфигурация уровня",
|
||||||
"characterLevel": "Уровень персонажа",
|
"characterLevel": "Уровень персонажа",
|
||||||
"max": "МАКС",
|
"max": "МАКС",
|
||||||
"ultimateEnergy": "Энергия сверхспособности",
|
"ultimateEnergy": "Энергия сверхспособности",
|
||||||
"currentEnergy": "Текущая энергия",
|
"currentEnergy": "Текущая энергия",
|
||||||
"setTo50": "Установить на 50%",
|
"setTo50": "Установить на 50%",
|
||||||
"battleConfiguration": "Конфигурация боя",
|
"battleConfiguration": "Конфигурация боя",
|
||||||
"useTechnique": "Использовать технику",
|
"useTechnique": "Использовать технику",
|
||||||
"techniqueNote": "Включить эффекты техники до боя",
|
"techniqueNote": "Включить эффекты техники до боя",
|
||||||
"enhancement": "Усиление",
|
"enhancement": "Усиление",
|
||||||
"enhancementLevel": "Уровень усиления",
|
"enhancementLevel": "Уровень усиления",
|
||||||
"origin": "Оригинал",
|
"origin": "Оригинал",
|
||||||
"enhancedNote": "Высокие уровни открывают новые способности",
|
"enhancedNote": "Высокие уровни открывают новые способности",
|
||||||
"lightconeEquipment": "Снаряжение конуса",
|
"lightconeEquipment": "Снаряжение конуса",
|
||||||
"lightconeSettings": "Настройки светового конуса",
|
"lightconeSettings": "Настройки светового конуса",
|
||||||
"placeholderLevel": "Введите уровень",
|
"placeholderLevel": "Введите уровень",
|
||||||
"superimpositionRank": "Уровень наложения",
|
"superimpositionRank": "Уровень наложения",
|
||||||
"ranksNote": "Высокие ранги дают более сильные эффекты",
|
"ranksNote": "Высокие ранги дают более сильные эффекты",
|
||||||
"changeLightcone": "Изменить световой конус",
|
"changeLightcone": "Изменить световой конус",
|
||||||
"removeLightcone": "Снять световой конус",
|
"removeLightcone": "Снять световой конус",
|
||||||
"equipLightcone": "Экипировать конус",
|
"equipLightcone": "Экипировать конус",
|
||||||
"noLightconeEquipped": "Световой конус не экипирован",
|
"noLightconeEquipped": "Световой конус не экипирован",
|
||||||
"equipLightconeNote": "Наденьте конус для усиления персонажа",
|
"equipLightconeNote": "Наденьте конус для усиления персонажа",
|
||||||
"filter": "Фильтр",
|
"filter": "Фильтр",
|
||||||
"selectedCharacters": "Выбранные персонажи",
|
"selectedCharacters": "Выбранные персонажи",
|
||||||
"selectedProfiles": "Выбранные профили",
|
"selectedProfiles": "Выбранные профили",
|
||||||
"clearAll": "Очистить всё",
|
"clearAll": "Очистить всё",
|
||||||
"selectAll": "Выбрать всё",
|
"selectAll": "Выбрать всё",
|
||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
"copied": "Скопировано",
|
"copied": "Скопировано",
|
||||||
"noAvatarSelected": "Персонаж не выбран",
|
"noAvatarSelected": "Персонаж не выбран",
|
||||||
"noAvatarToCopySelected": "Нет персонажа для копирования",
|
"noAvatarToCopySelected": "Нет персонажа для копирования",
|
||||||
"pleaseSelectAtLeastOneProfile": "Пожалуйста, выберите хотя бы один профиль",
|
"pleaseSelectAtLeastOneProfile": "Пожалуйста, выберите хотя бы один профиль",
|
||||||
"pleaseEnterUid": "Пожалуйста, введите UID",
|
"pleaseEnterUid": "Пожалуйста, введите UID",
|
||||||
"failedToFetchEnkaData": "Не удалось получить данные Enka",
|
"failedToFetchEnkaData": "Не удалось получить данные Enka",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Пожалуйста, выберите хотя бы одного персонажа",
|
"pleaseSelectAtLeastOneCharacter": "Пожалуйста, выберите хотя бы одного персонажа",
|
||||||
"noDataToImport": "Нет данных для импорта",
|
"noDataToImport": "Нет данных для импорта",
|
||||||
"pleaseSelectAFile": "Пожалуйста, выберите файл",
|
"pleaseSelectAFile": "Пожалуйста, выберите файл",
|
||||||
"fileMustBeAValidJsonFile": "Файл должен быть в формате JSON",
|
"fileMustBeAValidJsonFile": "Файл должен быть в формате JSON",
|
||||||
"importEnkaDataSuccess": "Успешный импорт из Enka",
|
"importEnkaDataSuccess": "Успешный импорт из Enka",
|
||||||
"importFreeSRDataSuccess": "Успешный импорт из FreeSR",
|
"importFreeSRDataSuccess": "Успешный импорт из FreeSR",
|
||||||
"importDatabaseSuccess": "Успешный импорт базы данных",
|
"importDatabaseSuccess": "Успешный импорт базы данных",
|
||||||
"getData": "Получить данные",
|
"getData": "Получить данные",
|
||||||
"import": "Импорт",
|
"import": "Импорт",
|
||||||
"freeSRImport": "Импорт FreeSR",
|
"freeSRImport": "Импорт FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Поддерживаются только JSON от FreeSR",
|
"onlySupportFreeSRJsonFile": "Поддерживаются только JSON от FreeSR",
|
||||||
"pickAFile": "Выберите файл",
|
"pickAFile": "Выберите файл",
|
||||||
"lightConeSetting": "Настройки конуса",
|
"lightConeSetting": "Настройки конуса",
|
||||||
"relicMaker": "Создатель реликвий",
|
"relicMaker": "Создатель реликвий",
|
||||||
"pleaseSelectAllOptions": "Пожалуйста, выберите все параметры",
|
"pleaseSelectAllOptions": "Пожалуйста, выберите все параметры",
|
||||||
"relicSavedSuccessfully": "Реликвия успешно сохранена",
|
"relicSavedSuccessfully": "Реликвия успешно сохранена",
|
||||||
"mainSettings": "Основные настройки",
|
"mainSettings": "Основные настройки",
|
||||||
"mainStat": "Основная характеристика",
|
"mainStat": "Основная характеристика",
|
||||||
"set": "Набор",
|
"set": "Набор",
|
||||||
"pleaseSelectASet": "Выберите набор",
|
"pleaseSelectASet": "Выберите набор",
|
||||||
"effectBonus": "Бонус эффекта",
|
"effectBonus": "Бонус эффекта",
|
||||||
"totalRoll": "Всего улучшений",
|
"totalRoll": "Всего улучшений",
|
||||||
"randomizeStats": "Случайные статы",
|
"randomizeStats": "Случайные статы",
|
||||||
"randomizeRolls": "Случайные улучшения",
|
"randomizeRolls": "Случайные улучшения",
|
||||||
"selectASubStat": "Выберите доп. стат",
|
"selectASubStat": "Выберите доп. стат",
|
||||||
"selectASet": "Выберите набор",
|
"selectASet": "Выберите набор",
|
||||||
"selectAMainStat": "Выберите основной стат",
|
"selectAMainStat": "Выберите основной стат",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"reset": "Сброс",
|
"reset": "Сброс",
|
||||||
"roll": "Улучшение",
|
"roll": "Улучшение",
|
||||||
"step": "Шаг",
|
"step": "Шаг",
|
||||||
"memoryOfChaos": "Зал забвения",
|
"memoryOfChaos": "Зал забвения",
|
||||||
"pureFiction": "Чистый вымысел",
|
"pureFiction": "Чистый вымысел",
|
||||||
"apocalypticShadow": "Апокалиптическая тень",
|
"apocalypticShadow": "Апокалиптическая тень",
|
||||||
"customEnemy": "Свой противник",
|
"customEnemy": "Свой противник",
|
||||||
"simulatedUniverse": "Виртуальная вселенная",
|
"simulatedUniverse": "Виртуальная вселенная",
|
||||||
"floor": "Этаж",
|
"floor": "Этаж",
|
||||||
"side": "Половина",
|
"side": "Половина",
|
||||||
"wave": "Волна",
|
"wave": "Волна",
|
||||||
"stage": "Стадия",
|
"stage": "Стадия",
|
||||||
"useCycleCount": "Использовать счетчик циклов?",
|
"useCycleCount": "Использовать счетчик циклов?",
|
||||||
"useTurbulenceBuff": "Использовать бафф турбулентности?",
|
"useTurbulenceBuff": "Использовать бафф турбулентности?",
|
||||||
"firstHalfEnemies": "Враги первой половины",
|
"firstHalfEnemies": "Враги первой половины",
|
||||||
"secondHalfEnemies": "Враги второй половины",
|
"secondHalfEnemies": "Враги второй половины",
|
||||||
"firstNodeEnemies": "Враги узла 1",
|
"firstNodeEnemies": "Враги узла 1",
|
||||||
"secondNodeEnemies": "Враги узла 2",
|
"secondNodeEnemies": "Враги узла 2",
|
||||||
"thirdNodeEnemies": "Враги узла 3",
|
"thirdNodeEnemies": "Враги узла 3",
|
||||||
"firstNode": "Узел 1",
|
"firstNode": "Узел 1",
|
||||||
"secondNode": "Узел 2",
|
"secondNode": "Узел 2",
|
||||||
"thirdNode": "Узел 3",
|
"thirdNode": "Узел 3",
|
||||||
"listEnemies": "Список врагов",
|
"listEnemies": "Список врагов",
|
||||||
"turbulenceBuff": "Бафф турбулентности",
|
"turbulenceBuff": "Бафф турбулентности",
|
||||||
"noEventSelected": "Событие не выбрано",
|
"noEventSelected": "Событие не выбрано",
|
||||||
"noTurbulenceBuff": "Нет баффа турбулентности",
|
"noTurbulenceBuff": "Нет баффа турбулентности",
|
||||||
"upper": "Верх",
|
"upper": "Верх",
|
||||||
"lower": "Низ",
|
"lower": "Низ",
|
||||||
"upperToLower": "Верх -> Низ",
|
"upperToLower": "Верх -> Низ",
|
||||||
"lowerToUpper": "Низ -> Верх",
|
"lowerToUpper": "Низ -> Верх",
|
||||||
"selectMOCEvent": "Выбрать событие MOC",
|
"selectMOCEvent": "Выбрать событие MOC",
|
||||||
"selectPFEvent": "Выбрать событие PF",
|
"selectPFEvent": "Выбрать событие PF",
|
||||||
"selectASEvent": "Выбрать событие AS",
|
"selectASEvent": "Выбрать событие AS",
|
||||||
"selectCEEvent": "Выбрать событие CE",
|
"selectCEEvent": "Выбрать событие CE",
|
||||||
"selectEvent": "Выбрать событие",
|
"selectEvent": "Выбрать событие",
|
||||||
"selectFloor": "Выбрать этаж",
|
"selectFloor": "Выбрать этаж",
|
||||||
"selectSide": "Выбрать половину",
|
"selectSide": "Выбрать половину",
|
||||||
"selectBuff": "Выбрать бафф",
|
"selectBuff": "Выбрать бафф",
|
||||||
"selectStage": "Выбрать стадию",
|
"selectStage": "Выбрать стадию",
|
||||||
"previous": "Назад",
|
"previous": "Назад",
|
||||||
"next": "Вперед",
|
"next": "Вперед",
|
||||||
"noMonstersFound": "Монстры не найдены",
|
"noMonstersFound": "Монстры не найдены",
|
||||||
"addNewWave": "Добавить новую волну",
|
"addNewWave": "Добавить новую волну",
|
||||||
"searchStage": "Поиск стадии...",
|
"searchStage": "Поиск стадии...",
|
||||||
"noStageFound": "Стадия не найдена",
|
"noStageFound": "Стадия не найдена",
|
||||||
"searchMonster": "Поиск монстра...",
|
"searchMonster": "Поиск монстра...",
|
||||||
"changeRelic": "Изменить реликвию",
|
"changeRelic": "Изменить реликвию",
|
||||||
"deleteRelic": "Удалить реликвию",
|
"deleteRelic": "Удалить реликвию",
|
||||||
"deleteRelicConfirm": "Удалить реликвию в этом слоте?",
|
"deleteRelicConfirm": "Удалить реликвию в этом слоте?",
|
||||||
"setEffects": "Настроить эффекты",
|
"setEffects": "Настроить эффекты",
|
||||||
"details": "Детали",
|
"details": "Детали",
|
||||||
"normal": "Базовая атака",
|
"normal": "Базовая атака",
|
||||||
"bpskill": "Навык",
|
"bpskill": "Навык",
|
||||||
"maze": "Техника",
|
"maze": "Техника",
|
||||||
"ultra": "Сверхспособность",
|
"ultra": "Сверхспособность",
|
||||||
"servantskill": "Навык мемоспрайта",
|
"servantskill": "Навык мемоспрайта",
|
||||||
"severaltalent": "Талант мемоспрайта",
|
"severaltalent": "Талант мемоспрайта",
|
||||||
"singleattack": "Одиночная атака",
|
"singleattack": "Одиночная атака",
|
||||||
"enhance": "Усиление",
|
"enhance": "Усиление",
|
||||||
"summon": "Призыв",
|
"summon": "Призыв",
|
||||||
"mazeattack": "Атака в технике",
|
"mazeattack": "Атака в технике",
|
||||||
"blast": "Взрыв",
|
"blast": "Взрыв",
|
||||||
"restore": "Восстановление",
|
"restore": "Восстановление",
|
||||||
"support": "Поддержка",
|
"support": "Поддержка",
|
||||||
"aoeattack": "АоЕ атака",
|
"aoeattack": "АоЕ атака",
|
||||||
"impair": "Ослабление",
|
"impair": "Ослабление",
|
||||||
"bounce": "Отскок",
|
"bounce": "Отскок",
|
||||||
"active": "Активен",
|
"active": "Активен",
|
||||||
"defence": "Защита",
|
"defence": "Защита",
|
||||||
"inactive": "Неактивен",
|
"inactive": "Неактивен",
|
||||||
"maxAll": "Улучшить всё",
|
"maxAll": "Улучшить всё",
|
||||||
"maxAllSuccess": "Уровни навыков улучшены до максимума.",
|
"maxAllSuccess": "Уровни навыков улучшены до максимума.",
|
||||||
"maxAllFailed": "Не удалось улучшить навыки.",
|
"maxAllFailed": "Не удалось улучшить навыки.",
|
||||||
"noRelicEquipped": "Нет экипированных реликвий",
|
"noRelicEquipped": "Нет экипированных реликвий",
|
||||||
"anomalyArbitration": "Аномальный арбитраж",
|
"anomalyArbitration": "Аномальный арбитраж",
|
||||||
"normalMode": "Обычный режим",
|
"normalMode": "Обычный режим",
|
||||||
"hardMode": "Сложный режим",
|
"hardMode": "Сложный режим",
|
||||||
"selectPEAKEvent": "Выбрать событие PEAK",
|
"selectPEAKEvent": "Выбрать событие PEAK",
|
||||||
"mode": "Режим",
|
"mode": "Режим",
|
||||||
"selectMode": "Выбрать режим",
|
"selectMode": "Выбрать режим",
|
||||||
"rollBack": "Отменить шаг",
|
"rollBack": "Отменить шаг",
|
||||||
"upRoll": "Повысить улучшение",
|
"upRoll": "Повысить улучшение",
|
||||||
"downRoll": "Понизить улучшение",
|
"downRoll": "Понизить улучшение",
|
||||||
"actions": "Действия",
|
"actions": "Действия",
|
||||||
"avatars": "Персонажи",
|
"avatars": "Персонажи",
|
||||||
"quickView": "Быстрый просмотр",
|
"quickView": "Быстрый просмотр",
|
||||||
"extraSetting": "Дополнительные настройки",
|
"extraSetting": "Дополнительные настройки",
|
||||||
"disableCensorship": "Отключить цензуру",
|
"disableCensorship": "Отключить цензуру",
|
||||||
"hideUI": "Скрыть интерфейс",
|
"hideUI": "Скрыть интерфейс",
|
||||||
"theoryCraftMode": "Режим Theorycraft",
|
"theoryCraftMode": "Режим Theorycraft",
|
||||||
"cycleCount": "Счетчик циклов",
|
"cycleCount": "Счетчик циклов",
|
||||||
"pleaseSelectAllSubStats": "Пожалуйста, выберите все саб-статы",
|
"pleaseSelectAllSubStats": "Пожалуйста, выберите все саб-статы",
|
||||||
"subStatRollCountCannotBeZero": "Количество улучшений саб-стата не может быть нулем",
|
"subStatRollCountCannotBeZero": "Количество улучшений саб-стата не может быть нулем",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "Многопутевой персонаж",
|
"multipathCharacter": "Многопутевой персонаж",
|
||||||
"mainPath": "Основной Путь",
|
"mainPath": "Основной Путь",
|
||||||
"march7Path": "Путь Март 7",
|
"march7Path": "Путь Март 7",
|
||||||
"challenge": "Испытание",
|
"challenge": "Испытание",
|
||||||
"skipNode": "Пропустить узел",
|
"skipNode": "Пропустить узел",
|
||||||
"disableSkip": "Отключить пропуск",
|
"disableSkip": "Отключить пропуск",
|
||||||
"skipNode1": "Пропустить узел 1",
|
"skipNode1": "Пропустить узел 1",
|
||||||
"skipNode2": "Пропустить узел 2",
|
"skipNode2": "Пропустить узел 2",
|
||||||
"extraFeatures": "Дополнительные функции",
|
"extraFeatures": "Дополнительные функции",
|
||||||
"detailTheoryCraft": "Включение этой функции позволяет настроить количество циклов и здоровье противников.",
|
"detailTheoryCraft": "Включение этой функции позволяет настроить количество циклов и здоровье противников.",
|
||||||
"detailSkipNode": "Позволяет пропустить (Узел 1/Узел 2) в Зале забвения или Чистом вымысле.",
|
"detailSkipNode": "Позволяет пропустить (Узел 1/Узел 2) в Зале забвения или Чистом вымысле.",
|
||||||
"detailChallengePeak": "Позволяет изменить сезон Peak в текущей Аномалии.",
|
"detailChallengePeak": "Позволяет изменить сезон Peak в текущей Аномалии.",
|
||||||
"detailHiddenUi": "Скрывает игровой интерфейс.",
|
"detailHiddenUi": "Скрывает игровой интерфейс.",
|
||||||
"detailDisableCensorship": "Отключает внутриигровую цензуру.",
|
"detailDisableCensorship": "Отключает внутриигровую цензуру.",
|
||||||
"detailMultipathCharacter": "Позволяет изменить Путь некоторых персонажей.",
|
"detailMultipathCharacter": "Позволяет изменить Путь некоторых персонажей.",
|
||||||
"trailblazer": "Первопроходец",
|
"trailblazer": "Первопроходец",
|
||||||
"listExtraEffect": "Список доп. эффектов",
|
"listExtraEffect": "Список доп. эффектов",
|
||||||
"extra": "Экстра",
|
"extra": "Экстра",
|
||||||
"customLineup": "Пользовательский отряд"
|
"customLineup": "Пользовательский отряд"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+289
-289
@@ -1,290 +1,290 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "เครื่องมือ Firefly โดย Firefly Shelter"
|
"description": "เครื่องมือ Firefly โดย Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "ประเภทสกิล",
|
"skillType": "ประเภทสกิล",
|
||||||
"skillName": "ชื่อสกิล",
|
"skillName": "ชื่อสกิล",
|
||||||
"character": "ตัวละคร",
|
"character": "ตัวละคร",
|
||||||
"id": "ไอดี",
|
"id": "ไอดี",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
"rarity": "ระดับความหายาก",
|
"rarity": "ระดับความหายาก",
|
||||||
"element": "ธาตุ",
|
"element": "ธาตุ",
|
||||||
"technique": "เทคนิค",
|
"technique": "เทคนิค",
|
||||||
"talent": "พรสวรรค์",
|
"talent": "พรสวรรค์",
|
||||||
"basic": "โจมตีปกติ",
|
"basic": "โจมตีปกติ",
|
||||||
"skill": "สกิลต่อสู้",
|
"skill": "สกิลต่อสู้",
|
||||||
"ultimate": "ท่าไม้ตาย",
|
"ultimate": "ท่าไม้ตาย",
|
||||||
"servant": "ผู้ติดตาม",
|
"servant": "ผู้ติดตาม",
|
||||||
"damage": "ความเสียหาย",
|
"damage": "ความเสียหาย",
|
||||||
"type": "ประเภท",
|
"type": "ประเภท",
|
||||||
"warrior": "ทำลายล้าง",
|
"warrior": "ทำลายล้าง",
|
||||||
"knight": "อนุรักษ์",
|
"knight": "อนุรักษ์",
|
||||||
"mage": "ปัญญา",
|
"mage": "ปัญญา",
|
||||||
"priest": "เฟื่องฟู",
|
"priest": "เฟื่องฟู",
|
||||||
"rogue": "ล่าสังหาร",
|
"rogue": "ล่าสังหาร",
|
||||||
"shaman": "ประสาน",
|
"shaman": "ประสาน",
|
||||||
"warlock": "ลบล้าง",
|
"warlock": "ลบล้าง",
|
||||||
"memory": "ลบล้าง",
|
"memory": "ลบล้าง",
|
||||||
"elation": "ปิติสุข",
|
"elation": "ปิติสุข",
|
||||||
"fire": "ไฟ",
|
"fire": "ไฟ",
|
||||||
"ice": "น้ำแข็ง",
|
"ice": "น้ำแข็ง",
|
||||||
"imaginary": "จินตภาพ",
|
"imaginary": "จินตภาพ",
|
||||||
"physical": "กายภาพ",
|
"physical": "กายภาพ",
|
||||||
"quantum": "ควอนตัม",
|
"quantum": "ควอนตัม",
|
||||||
"thunder": "สายฟ้า",
|
"thunder": "สายฟ้า",
|
||||||
"wind": "ลม",
|
"wind": "ลม",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "ATK",
|
"atk": "ATK",
|
||||||
"speed": "ความเร็ว",
|
"speed": "ความเร็ว",
|
||||||
"critRate": "อัตราคริติคอล",
|
"critRate": "อัตราคริติคอล",
|
||||||
"critDmg": "ความเสียหายคริติคอล",
|
"critDmg": "ความเสียหายคริติคอล",
|
||||||
"breakEffect": "เอฟเฟกต์ทำลายล้าง",
|
"breakEffect": "เอฟเฟกต์ทำลายล้าง",
|
||||||
"effectRes": "ต้านทานสถานะ",
|
"effectRes": "ต้านทานสถานะ",
|
||||||
"energyRegenerationRate": "อัตราฟื้นฟูพลังงาน",
|
"energyRegenerationRate": "อัตราฟื้นฟูพลังงาน",
|
||||||
"effectHitRate": "อัตราสร้างสถานะ",
|
"effectHitRate": "อัตราสร้างสถานะ",
|
||||||
"outgoingHealingBoost": "โบนัสการรักษา",
|
"outgoingHealingBoost": "โบนัสการรักษา",
|
||||||
"fireDmgBoost": "โบนัสความเสียหายไฟ",
|
"fireDmgBoost": "โบนัสความเสียหายไฟ",
|
||||||
"iceDmgBoost": "โบนัสความเสียหายน้ำแข็ง",
|
"iceDmgBoost": "โบนัสความเสียหายน้ำแข็ง",
|
||||||
"imaginaryDmgBoost": "โบนัสความเสียหายจินตภาพ",
|
"imaginaryDmgBoost": "โบนัสความเสียหายจินตภาพ",
|
||||||
"physicalDmgBoost": "โบนัสความเสียหายกายภาพ",
|
"physicalDmgBoost": "โบนัสความเสียหายกายภาพ",
|
||||||
"quantumDmgBoost": "โบนัสความเสียหายควอนตัม",
|
"quantumDmgBoost": "โบนัสความเสียหายควอนตัม",
|
||||||
"thunderDmgBoost": "โบนัสความเสียหายสายฟ้า",
|
"thunderDmgBoost": "โบนัสความเสียหายสายฟ้า",
|
||||||
"windDmgBoost": "โบนัสความเสียหายลม",
|
"windDmgBoost": "โบนัสความเสียหายลม",
|
||||||
"pursued": "ความเสียหายเพิ่มเติม",
|
"pursued": "ความเสียหายเพิ่มเติม",
|
||||||
"true damage": "ความเสียหายจริง",
|
"true damage": "ความเสียหายจริง",
|
||||||
"elationdamage": "ความเสียหายปิติสุข",
|
"elationdamage": "ความเสียหายปิติสุข",
|
||||||
"follow-up": "ความเสียหายโจมตีต่อเนื่อง",
|
"follow-up": "ความเสียหายโจมตีต่อเนื่อง",
|
||||||
"elemental damage": "ความเสียหายทำลายล้างและซูเปอร์",
|
"elemental damage": "ความเสียหายทำลายล้างและซูเปอร์",
|
||||||
"dot": "ความเสียหายต่อเนื่อง",
|
"dot": "ความเสียหายต่อเนื่อง",
|
||||||
"qte": "สกิล QTE",
|
"qte": "สกิล QTE",
|
||||||
"level": "เลเวล",
|
"level": "เลเวล",
|
||||||
"relics": "รีลิกส์",
|
"relics": "รีลิกส์",
|
||||||
"eidolons": "Eidolon",
|
"eidolons": "Eidolon",
|
||||||
"lightcones": "Light Cone",
|
"lightcones": "Light Cone",
|
||||||
"loadData": "โหลดข้อมูล",
|
"loadData": "โหลดข้อมูล",
|
||||||
"exportData": "ส่งออกข้อมูล",
|
"exportData": "ส่งออกข้อมูล",
|
||||||
"connectSetting": "การตั้งค่าการเชื่อมต่อ",
|
"connectSetting": "การตั้งค่าการเชื่อมต่อ",
|
||||||
"connected": "เชื่อมต่อแล้ว",
|
"connected": "เชื่อมต่อแล้ว",
|
||||||
"unconnected": "ไม่ได้เชื่อมต่อ",
|
"unconnected": "ไม่ได้เชื่อมต่อ",
|
||||||
"psConnection": "การเชื่อมต่อ PS",
|
"psConnection": "การเชื่อมต่อ PS",
|
||||||
"connectionType": "ประเภทการเชื่อมต่อ",
|
"connectionType": "ประเภทการเชื่อมต่อ",
|
||||||
"status": "สถานะ",
|
"status": "สถานะ",
|
||||||
"connectPs": "เชื่อมต่อ PS",
|
"connectPs": "เชื่อมต่อ PS",
|
||||||
"disconnect": "ยกเลิกการเชื่อมต่อ",
|
"disconnect": "ยกเลิกการเชื่อมต่อ",
|
||||||
"other": "อื่นๆ",
|
"other": "อื่นๆ",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "ฐานข้อมูล",
|
"database": "ฐานข้อมูล",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "ตั้งค่ามอนสเตอร์",
|
"monsterSetting": "ตั้งค่ามอนสเตอร์",
|
||||||
"serverUrl": "URL เซิร์ฟเวอร์",
|
"serverUrl": "URL เซิร์ฟเวอร์",
|
||||||
"privateType": "ประเภทเซิร์ฟเวอร์ส่วนตัว",
|
"privateType": "ประเภทเซิร์ฟเวอร์ส่วนตัว",
|
||||||
"local": "ในเครื่อง",
|
"local": "ในเครื่อง",
|
||||||
"server": "เซิร์ฟเวอร์",
|
"server": "เซิร์ฟเวอร์",
|
||||||
"username": "ชื่อผู้ใช้",
|
"username": "ชื่อผู้ใช้",
|
||||||
"password": "รหัสผ่าน",
|
"password": "รหัสผ่าน",
|
||||||
"placeholderServerUrl": "ป้อน URL เซิร์ฟเวอร์",
|
"placeholderServerUrl": "ป้อน URL เซิร์ฟเวอร์",
|
||||||
"placeholderUsername": "ป้อนชื่อผู้ใช้",
|
"placeholderUsername": "ป้อนชื่อผู้ใช้",
|
||||||
"placeholderPassword": "ป้อนรหัสผ่าน",
|
"placeholderPassword": "ป้อนรหัสผ่าน",
|
||||||
"connectedSuccess": "เชื่อมต่อกับ PS สำเร็จ",
|
"connectedSuccess": "เชื่อมต่อกับ PS สำเร็จ",
|
||||||
"connectedFailed": "ล้มเหลวในการเชื่อมต่อกับ PS",
|
"connectedFailed": "ล้มเหลวในการเชื่อมต่อกับ PS",
|
||||||
"syncSuccess": "ซิงค์ข้อมูลกับ PS สำเร็จ",
|
"syncSuccess": "ซิงค์ข้อมูลกับ PS สำเร็จ",
|
||||||
"syncFailed": "ล้มเหลวในการซิงค์ข้อมูลกับ PS",
|
"syncFailed": "ล้มเหลวในการซิงค์ข้อมูลกับ PS",
|
||||||
"sync": "ซิงค์",
|
"sync": "ซิงค์",
|
||||||
"importSetting": "การตั้งค่าการนำเข้า",
|
"importSetting": "การตั้งค่าการนำเข้า",
|
||||||
"profile": "โปรไฟล์",
|
"profile": "โปรไฟล์",
|
||||||
"default": "ค่าเริ่มต้น",
|
"default": "ค่าเริ่มต้น",
|
||||||
"copyProfiles": "คัดลอกโปรไฟล์",
|
"copyProfiles": "คัดลอกโปรไฟล์",
|
||||||
"addNewProfile": "เพิ่มโปรไฟล์ใหม่",
|
"addNewProfile": "เพิ่มโปรไฟล์ใหม่",
|
||||||
"createNewProfile": "สร้างโปรไฟล์ใหม่",
|
"createNewProfile": "สร้างโปรไฟล์ใหม่",
|
||||||
"editProfile": "แก้ไขโปรไฟล์",
|
"editProfile": "แก้ไขโปรไฟล์",
|
||||||
"placeholderProfileName": "ป้อนชื่อโปรไฟล์",
|
"placeholderProfileName": "ป้อนชื่อโปรไฟล์",
|
||||||
"profileName": "ชื่อโปรไฟล์",
|
"profileName": "ชื่อโปรไฟล์",
|
||||||
"create": "สร้าง",
|
"create": "สร้าง",
|
||||||
"update": "อัปเดต",
|
"update": "อัปเดต",
|
||||||
"characterInformation": "ข้อมูลตัวละคร",
|
"characterInformation": "ข้อมูลตัวละคร",
|
||||||
"skills": "สกิล",
|
"skills": "สกิล",
|
||||||
"showcaseCard": "การ์ดแสดง",
|
"showcaseCard": "การ์ดแสดง",
|
||||||
"comingSoon": "เร็วๆ นี้",
|
"comingSoon": "เร็วๆ นี้",
|
||||||
"characterName": "ชื่อตัวละคร",
|
"characterName": "ชื่อตัวละคร",
|
||||||
"placeholderCharacter": "ป้อนชื่อตัวละคร",
|
"placeholderCharacter": "ป้อนชื่อตัวละคร",
|
||||||
"characterSettings": "การตั้งค่าตัวละคร",
|
"characterSettings": "การตั้งค่าตัวละคร",
|
||||||
"levelConfiguration": "การกำหนดระดับเลเวล",
|
"levelConfiguration": "การกำหนดระดับเลเวล",
|
||||||
"characterLevel": "เลเวลตัวละคร",
|
"characterLevel": "เลเวลตัวละคร",
|
||||||
"max": "สูงสุด",
|
"max": "สูงสุด",
|
||||||
"ultimateEnergy": "พลังงานท่าไม้ตาย",
|
"ultimateEnergy": "พลังงานท่าไม้ตาย",
|
||||||
"currentEnergy": "พลังงานปัจจุบัน",
|
"currentEnergy": "พลังงานปัจจุบัน",
|
||||||
"setTo50": "ตั้งค่าเป็น 50%",
|
"setTo50": "ตั้งค่าเป็น 50%",
|
||||||
"battleConfiguration": "การกำหนดค่าการต่อสู้",
|
"battleConfiguration": "การกำหนดค่าการต่อสู้",
|
||||||
"useTechnique": "ใช้เทคนิค",
|
"useTechnique": "ใช้เทคนิค",
|
||||||
"techniqueNote": "เปิดใช้เอฟเฟกต์เทคนิคก่อนการต่อสู้",
|
"techniqueNote": "เปิดใช้เอฟเฟกต์เทคนิคก่อนการต่อสู้",
|
||||||
"enhancement": "การเสริมพลัง",
|
"enhancement": "การเสริมพลัง",
|
||||||
"enhancementLevel": "ระดับการเสริมพลัง",
|
"enhancementLevel": "ระดับการเสริมพลัง",
|
||||||
"origin": "ดั้งเดิม",
|
"origin": "ดั้งเดิม",
|
||||||
"enhancedNote": "การเสริมพลังที่สูงขึ้นจะปลดล็อกความสามารถ",
|
"enhancedNote": "การเสริมพลังที่สูงขึ้นจะปลดล็อกความสามารถ",
|
||||||
"lightconeEquipment": "สวมใส่ Light Cone",
|
"lightconeEquipment": "สวมใส่ Light Cone",
|
||||||
"lightconeSettings": "การตั้งค่า Light Cone",
|
"lightconeSettings": "การตั้งค่า Light Cone",
|
||||||
"placeholderLevel": "ป้อนเลเวล",
|
"placeholderLevel": "ป้อนเลเวล",
|
||||||
"superimpositionRank": "ระดับการขัดเกลา",
|
"superimpositionRank": "ระดับการขัดเกลา",
|
||||||
"ranksNote": "ระดับที่สูงขึ้นให้เอฟเฟกต์ที่แข็งแกร่งกว่า",
|
"ranksNote": "ระดับที่สูงขึ้นให้เอฟเฟกต์ที่แข็งแกร่งกว่า",
|
||||||
"changeLightcone": "เปลี่ยน Light Cone",
|
"changeLightcone": "เปลี่ยน Light Cone",
|
||||||
"removeLightcone": "ถอด Light Cone",
|
"removeLightcone": "ถอด Light Cone",
|
||||||
"equipLightcone": "สวมใส่ Light Cone",
|
"equipLightcone": "สวมใส่ Light Cone",
|
||||||
"noLightconeEquipped": "ไม่ได้สวมใส่ Light Cone",
|
"noLightconeEquipped": "ไม่ได้สวมใส่ Light Cone",
|
||||||
"equipLightconeNote": "สวมใส่ Light Cone เพื่อเพิ่มพลัง",
|
"equipLightconeNote": "สวมใส่ Light Cone เพื่อเพิ่มพลัง",
|
||||||
"filter": "กรอง",
|
"filter": "กรอง",
|
||||||
"selectedCharacters": "ตัวละครที่เลือก",
|
"selectedCharacters": "ตัวละครที่เลือก",
|
||||||
"selectedProfiles": "โปรไฟล์ที่เลือก",
|
"selectedProfiles": "โปรไฟล์ที่เลือก",
|
||||||
"clearAll": "ล้างทั้งหมด",
|
"clearAll": "ล้างทั้งหมด",
|
||||||
"selectAll": "เลือกทั้งหมด",
|
"selectAll": "เลือกทั้งหมด",
|
||||||
"copy": "คัดลอก",
|
"copy": "คัดลอก",
|
||||||
"copied": "คัดลอกแล้ว",
|
"copied": "คัดลอกแล้ว",
|
||||||
"noAvatarSelected": "ไม่ได้เลือกตัวละคร",
|
"noAvatarSelected": "ไม่ได้เลือกตัวละคร",
|
||||||
"noAvatarToCopySelected": "ไม่ได้เลือกตัวละครที่จะคัดลอก",
|
"noAvatarToCopySelected": "ไม่ได้เลือกตัวละครที่จะคัดลอก",
|
||||||
"pleaseSelectAtLeastOneProfile": "โปรดเลือกอย่างน้อยหนึ่งโปรไฟล์",
|
"pleaseSelectAtLeastOneProfile": "โปรดเลือกอย่างน้อยหนึ่งโปรไฟล์",
|
||||||
"pleaseEnterUid": "โปรดป้อน UID",
|
"pleaseEnterUid": "โปรดป้อน UID",
|
||||||
"failedToFetchEnkaData": "ไม่สามารถดึงข้อมูล Enka ได้",
|
"failedToFetchEnkaData": "ไม่สามารถดึงข้อมูล Enka ได้",
|
||||||
"pleaseSelectAtLeastOneCharacter": "โปรดเลือกอย่างน้อยหนึ่งตัวละคร",
|
"pleaseSelectAtLeastOneCharacter": "โปรดเลือกอย่างน้อยหนึ่งตัวละคร",
|
||||||
"noDataToImport": "ไม่มีข้อมูลที่จะนำเข้า",
|
"noDataToImport": "ไม่มีข้อมูลที่จะนำเข้า",
|
||||||
"pleaseSelectAFile": "โปรดเลือกไฟล์",
|
"pleaseSelectAFile": "โปรดเลือกไฟล์",
|
||||||
"fileMustBeAValidJsonFile": "ไฟล์ต้องเป็นไฟล์ JSON ที่ถูกต้อง",
|
"fileMustBeAValidJsonFile": "ไฟล์ต้องเป็นไฟล์ JSON ที่ถูกต้อง",
|
||||||
"importEnkaDataSuccess": "นำเข้าข้อมูล Enka สำเร็จ",
|
"importEnkaDataSuccess": "นำเข้าข้อมูล Enka สำเร็จ",
|
||||||
"importFreeSRDataSuccess": "นำเข้าข้อมูล FreeSR สำเร็จ",
|
"importFreeSRDataSuccess": "นำเข้าข้อมูล FreeSR สำเร็จ",
|
||||||
"importDatabaseSuccess": "นำเข้าฐานข้อมูลสำเร็จ",
|
"importDatabaseSuccess": "นำเข้าฐานข้อมูลสำเร็จ",
|
||||||
"getData": "รับข้อมูล",
|
"getData": "รับข้อมูล",
|
||||||
"import": "นำเข้า",
|
"import": "นำเข้า",
|
||||||
"freeSRImport": "นำเข้า FreeSR",
|
"freeSRImport": "นำเข้า FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "รองรับเฉพาะไฟล์ JSON ของ FreeSR",
|
"onlySupportFreeSRJsonFile": "รองรับเฉพาะไฟล์ JSON ของ FreeSR",
|
||||||
"pickAFile": "เลือกไฟล์",
|
"pickAFile": "เลือกไฟล์",
|
||||||
"lightConeSetting": "การตั้งค่า Light Cone",
|
"lightConeSetting": "การตั้งค่า Light Cone",
|
||||||
"relicMaker": "สร้างรีลิกส์",
|
"relicMaker": "สร้างรีลิกส์",
|
||||||
"pleaseSelectAllOptions": "โปรดเลือกตัวเลือกทั้งหมด",
|
"pleaseSelectAllOptions": "โปรดเลือกตัวเลือกทั้งหมด",
|
||||||
"relicSavedSuccessfully": "บันทึกรีลิกส์สำเร็จ",
|
"relicSavedSuccessfully": "บันทึกรีลิกส์สำเร็จ",
|
||||||
"mainSettings": "การตั้งค่าหลัก",
|
"mainSettings": "การตั้งค่าหลัก",
|
||||||
"mainStat": "ค่าสเตตัสหลัก",
|
"mainStat": "ค่าสเตตัสหลัก",
|
||||||
"set": "เซ็ต",
|
"set": "เซ็ต",
|
||||||
"pleaseSelectASet": "โปรดเลือกเซ็ต",
|
"pleaseSelectASet": "โปรดเลือกเซ็ต",
|
||||||
"effectBonus": "โบนัสเอฟเฟกต์",
|
"effectBonus": "โบนัสเอฟเฟกต์",
|
||||||
"totalRoll": "จำนวนการอัปเกรด",
|
"totalRoll": "จำนวนการอัปเกรด",
|
||||||
"randomizeStats": "สุ่มสเตตัส",
|
"randomizeStats": "สุ่มสเตตัส",
|
||||||
"randomizeRolls": "สุ่มอัปเกรด",
|
"randomizeRolls": "สุ่มอัปเกรด",
|
||||||
"selectASubStat": "เลือกสเตตัสรอง",
|
"selectASubStat": "เลือกสเตตัสรอง",
|
||||||
"selectASet": "เลือกเซ็ต",
|
"selectASet": "เลือกเซ็ต",
|
||||||
"selectAMainStat": "เลือกสเตตัสหลัก",
|
"selectAMainStat": "เลือกสเตตัสหลัก",
|
||||||
"save": "บันทึก",
|
"save": "บันทึก",
|
||||||
"reset": "รีเซ็ต",
|
"reset": "รีเซ็ต",
|
||||||
"roll": "อัปเกรด",
|
"roll": "อัปเกรด",
|
||||||
"step": "ขั้น",
|
"step": "ขั้น",
|
||||||
"memoryOfChaos": "Memory of Chaos",
|
"memoryOfChaos": "Memory of Chaos",
|
||||||
"pureFiction": "Pure Fiction",
|
"pureFiction": "Pure Fiction",
|
||||||
"apocalypticShadow": "Apocalyptic Shadow",
|
"apocalypticShadow": "Apocalyptic Shadow",
|
||||||
"customEnemy": "ศัตรูกำหนดเอง",
|
"customEnemy": "ศัตรูกำหนดเอง",
|
||||||
"simulatedUniverse": "Simulated Universe",
|
"simulatedUniverse": "Simulated Universe",
|
||||||
"floor": "ชั้น",
|
"floor": "ชั้น",
|
||||||
"side": "ฝั่ง",
|
"side": "ฝั่ง",
|
||||||
"wave": "เวฟ",
|
"wave": "เวฟ",
|
||||||
"stage": "ด่าน",
|
"stage": "ด่าน",
|
||||||
"useCycleCount": "ใช้การนับรอบหรือไม่?",
|
"useCycleCount": "ใช้การนับรอบหรือไม่?",
|
||||||
"useTurbulenceBuff": "ใช้บัฟบรรยากาศหรือไม่?",
|
"useTurbulenceBuff": "ใช้บัฟบรรยากาศหรือไม่?",
|
||||||
"firstHalfEnemies": "ศัตรูครึ่งแรก",
|
"firstHalfEnemies": "ศัตรูครึ่งแรก",
|
||||||
"secondHalfEnemies": "ศัตรูครึ่งหลัง",
|
"secondHalfEnemies": "ศัตรูครึ่งหลัง",
|
||||||
"firstNodeEnemies": "ศัตรูโหนด 1",
|
"firstNodeEnemies": "ศัตรูโหนด 1",
|
||||||
"secondNodeEnemies": "ศัตรูโหนด 2",
|
"secondNodeEnemies": "ศัตรูโหนด 2",
|
||||||
"thirdNodeEnemies": "ศัตรูโหนด 3",
|
"thirdNodeEnemies": "ศัตรูโหนด 3",
|
||||||
"firstNode": "โหนด 1",
|
"firstNode": "โหนด 1",
|
||||||
"secondNode": "โหนด 2",
|
"secondNode": "โหนด 2",
|
||||||
"thirdNode": "โหนด 3",
|
"thirdNode": "โหนด 3",
|
||||||
"listEnemies": "รายการศัตรู",
|
"listEnemies": "รายการศัตรู",
|
||||||
"turbulenceBuff": "บัฟบรรยากาศ",
|
"turbulenceBuff": "บัฟบรรยากาศ",
|
||||||
"noEventSelected": "ไม่ได้เลือกอีเวนต์",
|
"noEventSelected": "ไม่ได้เลือกอีเวนต์",
|
||||||
"noTurbulenceBuff": "ไม่มีบัฟบรรยากาศ",
|
"noTurbulenceBuff": "ไม่มีบัฟบรรยากาศ",
|
||||||
"upper": "บน",
|
"upper": "บน",
|
||||||
"lower": "ล่าง",
|
"lower": "ล่าง",
|
||||||
"upperToLower": "บน -> ล่าง",
|
"upperToLower": "บน -> ล่าง",
|
||||||
"lowerToUpper": "ล่าง -> บน",
|
"lowerToUpper": "ล่าง -> บน",
|
||||||
"selectMOCEvent": "เลือกอีเวนต์ MOC",
|
"selectMOCEvent": "เลือกอีเวนต์ MOC",
|
||||||
"selectPFEvent": "เลือกอีเวนต์ PF",
|
"selectPFEvent": "เลือกอีเวนต์ PF",
|
||||||
"selectASEvent": "เลือกอีเวนต์ AS",
|
"selectASEvent": "เลือกอีเวนต์ AS",
|
||||||
"selectCEEvent": "เลือกอีเวนต์ CE",
|
"selectCEEvent": "เลือกอีเวนต์ CE",
|
||||||
"selectEvent": "เลือกอีเวนต์",
|
"selectEvent": "เลือกอีเวนต์",
|
||||||
"selectFloor": "เลือกชั้น",
|
"selectFloor": "เลือกชั้น",
|
||||||
"selectSide": "เลือกฝั่ง",
|
"selectSide": "เลือกฝั่ง",
|
||||||
"selectBuff": "เลือกบัฟ",
|
"selectBuff": "เลือกบัฟ",
|
||||||
"selectStage": "เลือกด่าน",
|
"selectStage": "เลือกด่าน",
|
||||||
"previous": "ก่อนหน้า",
|
"previous": "ก่อนหน้า",
|
||||||
"next": "ถัดไป",
|
"next": "ถัดไป",
|
||||||
"noMonstersFound": "ไม่พบมอนสเตอร์",
|
"noMonstersFound": "ไม่พบมอนสเตอร์",
|
||||||
"addNewWave": "เพิ่มเวฟใหม่",
|
"addNewWave": "เพิ่มเวฟใหม่",
|
||||||
"searchStage": "ค้นหาด่าน...",
|
"searchStage": "ค้นหาด่าน...",
|
||||||
"noStageFound": "ไม่พบด่าน",
|
"noStageFound": "ไม่พบด่าน",
|
||||||
"searchMonster": "ค้นหามอนสเตอร์...",
|
"searchMonster": "ค้นหามอนสเตอร์...",
|
||||||
"changeRelic": "เปลี่ยนรีลิกส์",
|
"changeRelic": "เปลี่ยนรีลิกส์",
|
||||||
"deleteRelic": "ลบรีลิกส์",
|
"deleteRelic": "ลบรีลิกส์",
|
||||||
"deleteRelicConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบรีลิกส์ในช่องนี้?",
|
"deleteRelicConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบรีลิกส์ในช่องนี้?",
|
||||||
"setEffects": "ตั้งค่าเอฟเฟกต์",
|
"setEffects": "ตั้งค่าเอฟเฟกต์",
|
||||||
"details": "รายละเอียด",
|
"details": "รายละเอียด",
|
||||||
"normal": "โจมตีปกติ",
|
"normal": "โจมตีปกติ",
|
||||||
"bpskill": "สกิลต่อสู้",
|
"bpskill": "สกิลต่อสู้",
|
||||||
"maze": "เทคนิค",
|
"maze": "เทคนิค",
|
||||||
"ultra": "ท่าไม้ตาย",
|
"ultra": "ท่าไม้ตาย",
|
||||||
"servantskill": "สกิล Memosprite",
|
"servantskill": "สกิล Memosprite",
|
||||||
"severaltalent": "พรสวรรค์ Memosprite",
|
"severaltalent": "พรสวรรค์ Memosprite",
|
||||||
"singleattack": "โจมตีเป้าหมายเดียว",
|
"singleattack": "โจมตีเป้าหมายเดียว",
|
||||||
"enhance": "เสริมพลัง",
|
"enhance": "เสริมพลัง",
|
||||||
"summon": "อัญเชิญ",
|
"summon": "อัญเชิญ",
|
||||||
"mazeattack": "โจมตีแบบเทคนิค",
|
"mazeattack": "โจมตีแบบเทคนิค",
|
||||||
"blast": "กระจาย",
|
"blast": "กระจาย",
|
||||||
"restore": "ฟื้นฟู",
|
"restore": "ฟื้นฟู",
|
||||||
"support": "สนับสนุน",
|
"support": "สนับสนุน",
|
||||||
"aoeattack": "โจมตีหมู่",
|
"aoeattack": "โจมตีหมู่",
|
||||||
"impair": "ป่วน",
|
"impair": "ป่วน",
|
||||||
"bounce": "ชิ่ง",
|
"bounce": "ชิ่ง",
|
||||||
"active": "เปิดใช้งาน",
|
"active": "เปิดใช้งาน",
|
||||||
"defence": "ป้องกัน",
|
"defence": "ป้องกัน",
|
||||||
"inactive": "ปิดใช้งาน",
|
"inactive": "ปิดใช้งาน",
|
||||||
"maxAll": "อัปเกรดทั้งหมด",
|
"maxAll": "อัปเกรดทั้งหมด",
|
||||||
"maxAllSuccess": "อัปเกรดสกิลถึงระดับสูงสุดสำเร็จ",
|
"maxAllSuccess": "อัปเกรดสกิลถึงระดับสูงสุดสำเร็จ",
|
||||||
"maxAllFailed": "อัปเกรดสกิลล้มเหลว",
|
"maxAllFailed": "อัปเกรดสกิลล้มเหลว",
|
||||||
"noRelicEquipped": "ไม่ได้สวมใส่รีลิกส์",
|
"noRelicEquipped": "ไม่ได้สวมใส่รีลิกส์",
|
||||||
"anomalyArbitration": "Anomaly Arbitration",
|
"anomalyArbitration": "Anomaly Arbitration",
|
||||||
"normalMode": "โหมดปกติ",
|
"normalMode": "โหมดปกติ",
|
||||||
"hardMode": "โหมดยาก",
|
"hardMode": "โหมดยาก",
|
||||||
"selectPEAKEvent": "เลือกอีเวนต์ PEAK",
|
"selectPEAKEvent": "เลือกอีเวนต์ PEAK",
|
||||||
"mode": "โหมด",
|
"mode": "โหมด",
|
||||||
"selectMode": "เลือกโหมด",
|
"selectMode": "เลือกโหมด",
|
||||||
"rollBack": "ย้อนกลับ",
|
"rollBack": "ย้อนกลับ",
|
||||||
"upRoll": "เพิ่มอัปเกรด",
|
"upRoll": "เพิ่มอัปเกรด",
|
||||||
"downRoll": "ลดอัปเกรด",
|
"downRoll": "ลดอัปเกรด",
|
||||||
"actions": "แอ็กชัน",
|
"actions": "แอ็กชัน",
|
||||||
"avatars": "อวตาร",
|
"avatars": "อวตาร",
|
||||||
"quickView": "ดูแบบด่วน",
|
"quickView": "ดูแบบด่วน",
|
||||||
"extraSetting": "การตั้งค่าเพิ่มเติม",
|
"extraSetting": "การตั้งค่าเพิ่มเติม",
|
||||||
"disableCensorship": "ปิดการเซ็นเซอร์",
|
"disableCensorship": "ปิดการเซ็นเซอร์",
|
||||||
"hideUI": "ซ่อน UI",
|
"hideUI": "ซ่อน UI",
|
||||||
"theoryCraftMode": "โหมด Theorycraft",
|
"theoryCraftMode": "โหมด Theorycraft",
|
||||||
"cycleCount": "จำนวนรอบ",
|
"cycleCount": "จำนวนรอบ",
|
||||||
"pleaseSelectAllSubStats": "โปรดเลือกสเตตัสรองทั้งหมด",
|
"pleaseSelectAllSubStats": "โปรดเลือกสเตตัสรองทั้งหมด",
|
||||||
"subStatRollCountCannotBeZero": "จำนวนการอัปเกรดสเตตัสรองต้องไม่เป็นศูนย์",
|
"subStatRollCountCannotBeZero": "จำนวนการอัปเกรดสเตตัสรองต้องไม่เป็นศูนย์",
|
||||||
"theoryCraft": "Theorycraft",
|
"theoryCraft": "Theorycraft",
|
||||||
"multipathCharacter": "ตัวละครหลาย Path",
|
"multipathCharacter": "ตัวละครหลาย Path",
|
||||||
"mainPath": "Path หลัก",
|
"mainPath": "Path หลัก",
|
||||||
"march7Path": "Path March 7th",
|
"march7Path": "Path March 7th",
|
||||||
"challenge": "ท้าทาย",
|
"challenge": "ท้าทาย",
|
||||||
"skipNode": "ข้ามโหนด",
|
"skipNode": "ข้ามโหนด",
|
||||||
"disableSkip": "ปิดใช้งานการข้าม",
|
"disableSkip": "ปิดใช้งานการข้าม",
|
||||||
"skipNode1": "ข้ามโหนด 1",
|
"skipNode1": "ข้ามโหนด 1",
|
||||||
"skipNode2": "ข้ามโหนด 2",
|
"skipNode2": "ข้ามโหนด 2",
|
||||||
"extraFeatures": "คุณสมบัติพิเศษ",
|
"extraFeatures": "คุณสมบัติพิเศษ",
|
||||||
"detailTheoryCraft": "การเปิดคุณสมบัตินี้จะช่วยให้คุณปรับแต่งจำนวนรอบและปรับ HP ของศัตรูได้",
|
"detailTheoryCraft": "การเปิดคุณสมบัตินี้จะช่วยให้คุณปรับแต่งจำนวนรอบและปรับ HP ของศัตรูได้",
|
||||||
"detailSkipNode": "ช่วยให้ข้าม (โหนด 1/โหนด 2) ใน Memory of Chaos หรือ Pure Fiction",
|
"detailSkipNode": "ช่วยให้ข้าม (โหนด 1/โหนด 2) ใน Memory of Chaos หรือ Pure Fiction",
|
||||||
"detailChallengePeak": "อนุญาตให้เปลี่ยนฤดูกาล Peak ใน Anomaly ปัจจุบัน",
|
"detailChallengePeak": "อนุญาตให้เปลี่ยนฤดูกาล Peak ใน Anomaly ปัจจุบัน",
|
||||||
"detailHiddenUi": "ซ่อนอินเทอร์เฟซของเกม",
|
"detailHiddenUi": "ซ่อนอินเทอร์เฟซของเกม",
|
||||||
"detailDisableCensorship": "ปิดการเซ็นเซอร์ในเกม",
|
"detailDisableCensorship": "ปิดการเซ็นเซอร์ในเกม",
|
||||||
"detailMultipathCharacter": "อนุญาตให้เปลี่ยน Path ของตัวละครบางตัว",
|
"detailMultipathCharacter": "อนุญาตให้เปลี่ยน Path ของตัวละครบางตัว",
|
||||||
"trailblazer": "ผู้บุกเบิก",
|
"trailblazer": "ผู้บุกเบิก",
|
||||||
"listExtraEffect": "รายการเอฟเฟกต์พิเศษ",
|
"listExtraEffect": "รายการเอฟเฟกต์พิเศษ",
|
||||||
"extra": "พิเศษ",
|
"extra": "พิเศษ",
|
||||||
"customLineup": "จัดทีมแบบกำหนดเอง"
|
"customLineup": "จัดทีมแบบกำหนดเอง"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+288
-288
@@ -1,289 +1,289 @@
|
|||||||
{
|
{
|
||||||
"TabTitle": {
|
"TabTitle": {
|
||||||
"title": "Firefly Tools",
|
"title": "Firefly Tools",
|
||||||
"description": "Firefly tools by Firefly Shelter"
|
"description": "Firefly tools by Firefly Shelter"
|
||||||
},
|
},
|
||||||
"DataPage": {
|
"DataPage": {
|
||||||
"skillType": "Loại kỹ năng",
|
"skillType": "Loại kỹ năng",
|
||||||
"skillName": "Tên kỹ năng",
|
"skillName": "Tên kỹ năng",
|
||||||
"character": "Nhân vật",
|
"character": "Nhân vật",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"path": "Vận mệnh",
|
"path": "Vận mệnh",
|
||||||
"rarity": "Độ hiếm",
|
"rarity": "Độ hiếm",
|
||||||
"element": "Nguyên tố",
|
"element": "Nguyên tố",
|
||||||
"technique": "Bí kỹ",
|
"technique": "Bí kỹ",
|
||||||
"talent": "Thiên phú",
|
"talent": "Thiên phú",
|
||||||
"basic": "Đánh thường",
|
"basic": "Đánh thường",
|
||||||
"skill": "Kỹ năng",
|
"skill": "Kỹ năng",
|
||||||
"ultimate": "Tuyệt kỹ",
|
"ultimate": "Tuyệt kỹ",
|
||||||
"servant": "Phụ trợ",
|
"servant": "Phụ trợ",
|
||||||
"damage": "Sát thương",
|
"damage": "Sát thương",
|
||||||
"type": "Loại",
|
"type": "Loại",
|
||||||
"warrior": "Hủy Diệt",
|
"warrior": "Hủy Diệt",
|
||||||
"knight": "Bảo Hộ",
|
"knight": "Bảo Hộ",
|
||||||
"mage": "Tri Thức",
|
"mage": "Tri Thức",
|
||||||
"priest": "Trù phú",
|
"priest": "Trù phú",
|
||||||
"rogue": "Săn Bắn",
|
"rogue": "Săn Bắn",
|
||||||
"shaman": "Hòa Hợp",
|
"shaman": "Hòa Hợp",
|
||||||
"warlock": "Hư Vô",
|
"warlock": "Hư Vô",
|
||||||
"memory": "Ký Ức",
|
"memory": "Ký Ức",
|
||||||
"elation": "Vui vẻ",
|
"elation": "Vui vẻ",
|
||||||
"fire": "Hỏa",
|
"fire": "Hỏa",
|
||||||
"ice": "Băng",
|
"ice": "Băng",
|
||||||
"imaginary": "Ảo Ảnh",
|
"imaginary": "Ảo Ảnh",
|
||||||
"physical": "Vật Lý",
|
"physical": "Vật Lý",
|
||||||
"quantum": "Lượng Tử",
|
"quantum": "Lượng Tử",
|
||||||
"thunder": "Lôi",
|
"thunder": "Lôi",
|
||||||
"wind": "Phong",
|
"wind": "Phong",
|
||||||
"hp": "HP",
|
"hp": "HP",
|
||||||
"atk": "Tấn công",
|
"atk": "Tấn công",
|
||||||
"speed": "Tốc độ",
|
"speed": "Tốc độ",
|
||||||
"critRate": "Tỷ lệ bạo",
|
"critRate": "Tỷ lệ bạo",
|
||||||
"critDmg": "ST bạo",
|
"critDmg": "ST bạo",
|
||||||
"breakEffect": "sát thương kích phá",
|
"breakEffect": "sát thương kích phá",
|
||||||
"effectRes": "Kháng hiệu ứng",
|
"effectRes": "Kháng hiệu ứng",
|
||||||
"energyRegenerationRate": "Tốc độ hồi năng lượng",
|
"energyRegenerationRate": "Tốc độ hồi năng lượng",
|
||||||
"effectHitRate": "Tỷ lệ trúng hiệu ứng",
|
"effectHitRate": "Tỷ lệ trúng hiệu ứng",
|
||||||
"outgoingHealingBoost": "Tăng hồi phục",
|
"outgoingHealingBoost": "Tăng hồi phục",
|
||||||
"fireDmgBoost": "Tăng sát thương Hỏa",
|
"fireDmgBoost": "Tăng sát thương Hỏa",
|
||||||
"iceDmgBoost": "Tăng sát thương Băng",
|
"iceDmgBoost": "Tăng sát thương Băng",
|
||||||
"imaginaryDmgBoost": "Tăng sát thương Ảo Ảnh",
|
"imaginaryDmgBoost": "Tăng sát thương Ảo Ảnh",
|
||||||
"physicalDmgBoost": "Tăng sát thương Vật Lý",
|
"physicalDmgBoost": "Tăng sát thương Vật Lý",
|
||||||
"quantumDmgBoost": "Tăng sát thương Lượng Tử",
|
"quantumDmgBoost": "Tăng sát thương Lượng Tử",
|
||||||
"thunderDmgBoost": "Tăng sát thương Lôi",
|
"thunderDmgBoost": "Tăng sát thương Lôi",
|
||||||
"windDmgBoost": "Tăng sát thương Phong",
|
"windDmgBoost": "Tăng sát thương Phong",
|
||||||
"pursued": "Sát thương thêm",
|
"pursued": "Sát thương thêm",
|
||||||
"true damage": "Sát thương chuẩn",
|
"true damage": "Sát thương chuẩn",
|
||||||
"elationdamage": "Sát thương vui vẻ",
|
"elationdamage": "Sát thương vui vẻ",
|
||||||
"follow-up": "Sát thương phản kích",
|
"follow-up": "Sát thương phản kích",
|
||||||
"elemental damage": "Sát thương kích phá và siêu kích phá",
|
"elemental damage": "Sát thương kích phá và siêu kích phá",
|
||||||
"dot": "Sát thương theo thời gian",
|
"dot": "Sát thương theo thời gian",
|
||||||
"qte": "Kỹ năng QTE",
|
"qte": "Kỹ năng QTE",
|
||||||
"level": "Cấp độ",
|
"level": "Cấp độ",
|
||||||
"relics": "Thánh di vật",
|
"relics": "Thánh di vật",
|
||||||
"eidolons": "Tinh hồn",
|
"eidolons": "Tinh hồn",
|
||||||
"lightcones": "Nón ánh sáng",
|
"lightcones": "Nón ánh sáng",
|
||||||
"loadData": "Tải dữ liệu",
|
"loadData": "Tải dữ liệu",
|
||||||
"exportData": "Xuất dữ liệu",
|
"exportData": "Xuất dữ liệu",
|
||||||
"connectSetting": "Cài đặt kết nối",
|
"connectSetting": "Cài đặt kết nối",
|
||||||
"connected": "Đã kết nối",
|
"connected": "Đã kết nối",
|
||||||
"unconnected": "Chưa kết nối",
|
"unconnected": "Chưa kết nối",
|
||||||
"psConnection": "Kết nối PS",
|
"psConnection": "Kết nối PS",
|
||||||
"connectionType": "Loại kết nối",
|
"connectionType": "Loại kết nối",
|
||||||
"status": "Trạng thái",
|
"status": "Trạng thái",
|
||||||
"connectPs": "Kết nối PS",
|
"connectPs": "Kết nối PS",
|
||||||
"disconnect": "Ngắt kết nối",
|
"disconnect": "Ngắt kết nối",
|
||||||
"other": "Khác",
|
"other": "Khác",
|
||||||
"freeSr": "FreeSR",
|
"freeSr": "FreeSR",
|
||||||
"database": "Database",
|
"database": "Database",
|
||||||
"enka": "Enka",
|
"enka": "Enka",
|
||||||
"monsterSetting": "Cài đặt quái",
|
"monsterSetting": "Cài đặt quái",
|
||||||
"serverUrl": "Địa chỉ server",
|
"serverUrl": "Địa chỉ server",
|
||||||
"privateType": "Loại riêng tư",
|
"privateType": "Loại riêng tư",
|
||||||
"local": "Cục bộ",
|
"local": "Cục bộ",
|
||||||
"server": "Máy chủ",
|
"server": "Máy chủ",
|
||||||
"username": "Tên người dùng",
|
"username": "Tên người dùng",
|
||||||
"password": "Mật khẩu",
|
"password": "Mật khẩu",
|
||||||
"placeholderServerUrl": "Nhập địa chỉ server",
|
"placeholderServerUrl": "Nhập địa chỉ server",
|
||||||
"placeholderUsername": "Nhập tên người dùng",
|
"placeholderUsername": "Nhập tên người dùng",
|
||||||
"placeholderPassword": "Nhập mật khẩu",
|
"placeholderPassword": "Nhập mật khẩu",
|
||||||
"connectedSuccess": "Kết nối PS thành công",
|
"connectedSuccess": "Kết nối PS thành công",
|
||||||
"connectedFailed": "Kết nối PS thất bại",
|
"connectedFailed": "Kết nối PS thất bại",
|
||||||
"syncSuccess": "Đồng bộ dữ liệu với PS thành công",
|
"syncSuccess": "Đồng bộ dữ liệu với PS thành công",
|
||||||
"syncFailed": "Đồng bộ dữ liệu với PS thất bại",
|
"syncFailed": "Đồng bộ dữ liệu với PS thất bại",
|
||||||
"sync": "Đồng bộ",
|
"sync": "Đồng bộ",
|
||||||
"importSetting": "Cài đặt nhập",
|
"importSetting": "Cài đặt nhập",
|
||||||
"profile": "Hồ sơ",
|
"profile": "Hồ sơ",
|
||||||
"default": "Mặc định",
|
"default": "Mặc định",
|
||||||
"copyProfiles": "Sao chép hồ sơ",
|
"copyProfiles": "Sao chép hồ sơ",
|
||||||
"addNewProfile": "Thêm hồ sơ mới",
|
"addNewProfile": "Thêm hồ sơ mới",
|
||||||
"createNewProfile": "Tạo hồ sơ mới",
|
"createNewProfile": "Tạo hồ sơ mới",
|
||||||
"editProfile": "Chỉnh sửa hồ sơ",
|
"editProfile": "Chỉnh sửa hồ sơ",
|
||||||
"placeholderProfileName": "Nhập tên hồ sơ",
|
"placeholderProfileName": "Nhập tên hồ sơ",
|
||||||
"profileName": "Tên hồ sơ",
|
"profileName": "Tên hồ sơ",
|
||||||
"create": "Tạo",
|
"create": "Tạo",
|
||||||
"update": "Cập nhật",
|
"update": "Cập nhật",
|
||||||
"characterInformation": "Thông tin nhân vật",
|
"characterInformation": "Thông tin nhân vật",
|
||||||
"skills": "Kỹ năng",
|
"skills": "Kỹ năng",
|
||||||
"showcaseCard": "Thẻ trưng bày",
|
"showcaseCard": "Thẻ trưng bày",
|
||||||
"comingSoon": "Sắp ra mắt",
|
"comingSoon": "Sắp ra mắt",
|
||||||
"characterName": "Tên nhân vật",
|
"characterName": "Tên nhân vật",
|
||||||
"placeholderCharacter": "Nhập tên nhân vật",
|
"placeholderCharacter": "Nhập tên nhân vật",
|
||||||
"characterSettings": "Cài đặt nhân vật",
|
"characterSettings": "Cài đặt nhân vật",
|
||||||
"levelConfiguration": "Cấu hình cấp độ",
|
"levelConfiguration": "Cấu hình cấp độ",
|
||||||
"characterLevel": "Cấp độ nhân vật",
|
"characterLevel": "Cấp độ nhân vật",
|
||||||
"max": "Tối đa",
|
"max": "Tối đa",
|
||||||
"ultimateEnergy": "Năng lượng tuyệt kỹ",
|
"ultimateEnergy": "Năng lượng tuyệt kỹ",
|
||||||
"currentEnergy": "Năng lượng hiện tại",
|
"currentEnergy": "Năng lượng hiện tại",
|
||||||
"setTo50": "Đặt thành 50%",
|
"setTo50": "Đặt thành 50%",
|
||||||
"battleConfiguration": "Cấu hình trận đấu",
|
"battleConfiguration": "Cấu hình trận đấu",
|
||||||
"useTechnique": "Dùng bí kỹ",
|
"useTechnique": "Dùng bí kỹ",
|
||||||
"techniqueNote": "Bật hiệu ứng bí kỹ trước trận",
|
"techniqueNote": "Bật hiệu ứng bí kỹ trước trận",
|
||||||
"enhancement": "Cường hóa",
|
"enhancement": "Cường hóa",
|
||||||
"enhancementLevel": "Cấp độ cường hóa",
|
"enhancementLevel": "Cấp độ cường hóa",
|
||||||
"origin": "Nguyên gốc",
|
"origin": "Nguyên gốc",
|
||||||
"enhancedNote": "Cường hóa cao mở thêm kỹ năng",
|
"enhancedNote": "Cường hóa cao mở thêm kỹ năng",
|
||||||
"lightconeEquipment": "Trang bị nón ánh sáng",
|
"lightconeEquipment": "Trang bị nón ánh sáng",
|
||||||
"lightconeSettings": "Cài đặt nón ánh sáng",
|
"lightconeSettings": "Cài đặt nón ánh sáng",
|
||||||
"placeholderLevel": "Nhập cấp độ",
|
"placeholderLevel": "Nhập cấp độ",
|
||||||
"superimpositionRank": "Bậc chồng kỹ năng",
|
"superimpositionRank": "Bậc chồng kỹ năng",
|
||||||
"ranksNote": "Bậc càng cao hiệu ứng càng mạnh",
|
"ranksNote": "Bậc càng cao hiệu ứng càng mạnh",
|
||||||
"changeLightcone": "Thay đổi nón ánh sáng",
|
"changeLightcone": "Thay đổi nón ánh sáng",
|
||||||
"removeLightcone": "Gỡ nón ánh sáng",
|
"removeLightcone": "Gỡ nón ánh sáng",
|
||||||
"equipLightcone": "Trang bị nón ánh sáng",
|
"equipLightcone": "Trang bị nón ánh sáng",
|
||||||
"noLightconeEquipped": "Chưa trang bị nón ánh sáng",
|
"noLightconeEquipped": "Chưa trang bị nón ánh sáng",
|
||||||
"equipLightconeNote": "Trang bị nón để tăng sức mạnh cho nhân vật",
|
"equipLightconeNote": "Trang bị nón để tăng sức mạnh cho nhân vật",
|
||||||
"filter": "Lọc",
|
"filter": "Lọc",
|
||||||
"selectedCharacters": "Nhân vật đã chọn",
|
"selectedCharacters": "Nhân vật đã chọn",
|
||||||
"selectedProfiles": "Hồ sơ đã chọn",
|
"selectedProfiles": "Hồ sơ đã chọn",
|
||||||
"clearAll": "Xóa tất cả",
|
"clearAll": "Xóa tất cả",
|
||||||
"selectAll": "Chọn tất cả",
|
"selectAll": "Chọn tất cả",
|
||||||
"copy": "Sao chép",
|
"copy": "Sao chép",
|
||||||
"copied": "Đã sao chép",
|
"copied": "Đã sao chép",
|
||||||
"noAvatarSelected": "Chưa chọn nhân vật",
|
"noAvatarSelected": "Chưa chọn nhân vật",
|
||||||
"noAvatarToCopySelected": "Chưa chọn nhân vật để sao chép",
|
"noAvatarToCopySelected": "Chưa chọn nhân vật để sao chép",
|
||||||
"pleaseSelectAtLeastOneProfile": "Vui lòng chọn ít nhất một hồ sơ",
|
"pleaseSelectAtLeastOneProfile": "Vui lòng chọn ít nhất một hồ sơ",
|
||||||
"pleaseEnterUid": "Vui lòng nhập UID",
|
"pleaseEnterUid": "Vui lòng nhập UID",
|
||||||
"failedToFetchEnkaData": "Lấy dữ liệu Enka thất bại",
|
"failedToFetchEnkaData": "Lấy dữ liệu Enka thất bại",
|
||||||
"pleaseSelectAtLeastOneCharacter": "Vui lòng chọn ít nhất một nhân vật",
|
"pleaseSelectAtLeastOneCharacter": "Vui lòng chọn ít nhất một nhân vật",
|
||||||
"noDataToImport": "Không có dữ liệu để nhập",
|
"noDataToImport": "Không có dữ liệu để nhập",
|
||||||
"pleaseSelectAFile": "Vui lòng chọn một tệp",
|
"pleaseSelectAFile": "Vui lòng chọn một tệp",
|
||||||
"fileMustBeAValidJsonFile": "Tệp phải là tệp JSON hợp lệ",
|
"fileMustBeAValidJsonFile": "Tệp phải là tệp JSON hợp lệ",
|
||||||
"importEnkaDataSuccess": "Nhập dữ liệu Enka thành công",
|
"importEnkaDataSuccess": "Nhập dữ liệu Enka thành công",
|
||||||
"importFreeSRDataSuccess": "Nhập dữ liệu FreeSR thành công",
|
"importFreeSRDataSuccess": "Nhập dữ liệu FreeSR thành công",
|
||||||
"importDatabaseSuccess": "Nhập cơ sở dữ liệu thành công",
|
"importDatabaseSuccess": "Nhập cơ sở dữ liệu thành công",
|
||||||
"getData": "Lấy dữ liệu",
|
"getData": "Lấy dữ liệu",
|
||||||
"import": "Nhập",
|
"import": "Nhập",
|
||||||
"freeSRImport": "Nhập FreeSR",
|
"freeSRImport": "Nhập FreeSR",
|
||||||
"onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR",
|
"onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR",
|
||||||
"pickAFile": "Chọn tệp",
|
"pickAFile": "Chọn tệp",
|
||||||
"lightConeSetting": "Cài đặt Nón Ánh Sáng",
|
"lightConeSetting": "Cài đặt Nón Ánh Sáng",
|
||||||
"relicMaker": "Trình tạo Thánh Di Vật",
|
"relicMaker": "Trình tạo Thánh Di Vật",
|
||||||
"pleaseSelectAllOptions": "Vui lòng chọn tất cả tùy chọn",
|
"pleaseSelectAllOptions": "Vui lòng chọn tất cả tùy chọn",
|
||||||
"relicSavedSuccessfully": "Lưu thánh di vật thành công",
|
"relicSavedSuccessfully": "Lưu thánh di vật thành công",
|
||||||
"mainSettings": "Cài đặt chính",
|
"mainSettings": "Cài đặt chính",
|
||||||
"mainStat": "Chỉ số chính",
|
"mainStat": "Chỉ số chính",
|
||||||
"set": "Bộ",
|
"set": "Bộ",
|
||||||
"pleaseSelectASet": "Vui lòng chọn một bộ",
|
"pleaseSelectASet": "Vui lòng chọn một bộ",
|
||||||
"effectBonus": "Hiệu ứng cộng thêm",
|
"effectBonus": "Hiệu ứng cộng thêm",
|
||||||
"totalRoll": "Tổng số dòng",
|
"totalRoll": "Tổng số dòng",
|
||||||
"randomizeStats": "Ngẫu nhiên chỉ số",
|
"randomizeStats": "Ngẫu nhiên chỉ số",
|
||||||
"randomizeRolls": "Ngẫu nhiên số dòng",
|
"randomizeRolls": "Ngẫu nhiên số dòng",
|
||||||
"selectASubStat": "Chọn chỉ số phụ",
|
"selectASubStat": "Chọn chỉ số phụ",
|
||||||
"selectASet": "Chọn một bộ",
|
"selectASet": "Chọn một bộ",
|
||||||
"selectAMainStat": "Chọn chỉ số chính",
|
"selectAMainStat": "Chọn chỉ số chính",
|
||||||
"save": "Lưu",
|
"save": "Lưu",
|
||||||
"reset": "Đặt lại toàn bộ",
|
"reset": "Đặt lại toàn bộ",
|
||||||
"roll": "Số dòng",
|
"roll": "Số dòng",
|
||||||
"step": "Bước nhảy",
|
"step": "Bước nhảy",
|
||||||
"memoryOfChaos": "Hồi ức hỗn độn",
|
"memoryOfChaos": "Hồi ức hỗn độn",
|
||||||
"pureFiction": "Kể chuyện hư cấu",
|
"pureFiction": "Kể chuyện hư cấu",
|
||||||
"apocalypticShadow": "Ảo ảnh tận thế",
|
"apocalypticShadow": "Ảo ảnh tận thế",
|
||||||
"customEnemy": "Kẻ địch tùy chỉnh",
|
"customEnemy": "Kẻ địch tùy chỉnh",
|
||||||
"simulatedUniverse": "Vũ trụ mô phỏng",
|
"simulatedUniverse": "Vũ trụ mô phỏng",
|
||||||
"floor": "Tầng",
|
"floor": "Tầng",
|
||||||
"side": "Nửa trận",
|
"side": "Nửa trận",
|
||||||
"wave": "Đợt",
|
"wave": "Đợt",
|
||||||
"stage": "Màn",
|
"stage": "Màn",
|
||||||
"useCycleCount": "Dùng dếm chu kỳ?",
|
"useCycleCount": "Dùng dếm chu kỳ?",
|
||||||
"useTurbulenceBuff": "Dùng buff hỗn loạn?",
|
"useTurbulenceBuff": "Dùng buff hỗn loạn?",
|
||||||
"firstHalfEnemies": "Địch nửa đầu",
|
"firstHalfEnemies": "Địch nửa đầu",
|
||||||
"secondHalfEnemies": "Địch nửa sau",
|
"secondHalfEnemies": "Địch nửa sau",
|
||||||
"firstNodeEnemies": "Địch Node 1",
|
"firstNodeEnemies": "Địch Node 1",
|
||||||
"secondNodeEnemies": "Địch Node 2",
|
"secondNodeEnemies": "Địch Node 2",
|
||||||
"thirdNodeEnemies": "Địch Node 3",
|
"thirdNodeEnemies": "Địch Node 3",
|
||||||
"firstNode": "Node 1",
|
"firstNode": "Node 1",
|
||||||
"secondNode": "Node 2",
|
"secondNode": "Node 2",
|
||||||
"thirdNode": "Node 3",
|
"thirdNode": "Node 3",
|
||||||
"turbulenceBuff": "Buff hỗn loạn",
|
"turbulenceBuff": "Buff hỗn loạn",
|
||||||
"noEventSelected": "Không có sự kiện",
|
"noEventSelected": "Không có sự kiện",
|
||||||
"noTurbulenceBuff": "Không có buff hỗn loạn",
|
"noTurbulenceBuff": "Không có buff hỗn loạn",
|
||||||
"upper": "Nửa trên",
|
"upper": "Nửa trên",
|
||||||
"lower": "Nửa dưới",
|
"lower": "Nửa dưới",
|
||||||
"upperToLower": "Nửa trên -> Nửa dưới",
|
"upperToLower": "Nửa trên -> Nửa dưới",
|
||||||
"lowerToUpper": "Nửa dưới -> Nửa trên",
|
"lowerToUpper": "Nửa dưới -> Nửa trên",
|
||||||
"selectMOCEvent": "Chọn sự kiện MOC",
|
"selectMOCEvent": "Chọn sự kiện MOC",
|
||||||
"selectPFEvent": "Chọn sự kiện PF",
|
"selectPFEvent": "Chọn sự kiện PF",
|
||||||
"selectASEvent": "Chọn sự kiện AS",
|
"selectASEvent": "Chọn sự kiện AS",
|
||||||
"selectCEEvent": "Chọn sự kiện CE",
|
"selectCEEvent": "Chọn sự kiện CE",
|
||||||
"selectPEAKEvent": "Chọn sự kiện PEAK",
|
"selectPEAKEvent": "Chọn sự kiện PEAK",
|
||||||
"selectEvent": "Chọn sự kiện",
|
"selectEvent": "Chọn sự kiện",
|
||||||
"selectFloor": "Chọn tầng",
|
"selectFloor": "Chọn tầng",
|
||||||
"selectSide": "Chọn nửa trận",
|
"selectSide": "Chọn nửa trận",
|
||||||
"selectBuff": "Chọn buff",
|
"selectBuff": "Chọn buff",
|
||||||
"selectStage": "Chọn màn",
|
"selectStage": "Chọn màn",
|
||||||
"previous": "Trước",
|
"previous": "Trước",
|
||||||
"next": "Tiếp",
|
"next": "Tiếp",
|
||||||
"noMonstersFound": "Không tìm thấy quái",
|
"noMonstersFound": "Không tìm thấy quái",
|
||||||
"addNewWave": "Thêm đợt mới",
|
"addNewWave": "Thêm đợt mới",
|
||||||
"searchStage": "Tìm màn...",
|
"searchStage": "Tìm màn...",
|
||||||
"noStageFound": "Không tìm thấy màn",
|
"noStageFound": "Không tìm thấy màn",
|
||||||
"searchMonster": "Tìm quái...",
|
"searchMonster": "Tìm quái...",
|
||||||
"changeRelic": "Thay đổi di vật",
|
"changeRelic": "Thay đổi di vật",
|
||||||
"deleteRelic": "Xóa di vật",
|
"deleteRelic": "Xóa di vật",
|
||||||
"deleteRelicConfirm": "Bạn có chắc chắn muốn xóa di vật trong ô này không?",
|
"deleteRelicConfirm": "Bạn có chắc chắn muốn xóa di vật trong ô này không?",
|
||||||
"setEffects": "Thiết lập hiệu ứng",
|
"setEffects": "Thiết lập hiệu ứng",
|
||||||
"details": "Chi tiết",
|
"details": "Chi tiết",
|
||||||
"normal": "Đánh thường",
|
"normal": "Đánh thường",
|
||||||
"bpskill": "Chiến kỹ",
|
"bpskill": "Chiến kỹ",
|
||||||
"maze": "Bí kỹ",
|
"maze": "Bí kỹ",
|
||||||
"ultra": "Tuyệt kỹ",
|
"ultra": "Tuyệt kỹ",
|
||||||
"servantskill": "Kỹ năng vật triệu hồi",
|
"servantskill": "Kỹ năng vật triệu hồi",
|
||||||
"severaltalent": "Thiên phú vật triệu hồi",
|
"severaltalent": "Thiên phú vật triệu hồi",
|
||||||
"singleattack": "Tấn công đơn",
|
"singleattack": "Tấn công đơn",
|
||||||
"enhance": "Cường hóa",
|
"enhance": "Cường hóa",
|
||||||
"summon": "Triệu hồi",
|
"summon": "Triệu hồi",
|
||||||
"blast": "Tấn công 3 mục tiêu",
|
"blast": "Tấn công 3 mục tiêu",
|
||||||
"restore": "Hồi phục",
|
"restore": "Hồi phục",
|
||||||
"support": "Hỗ trợ",
|
"support": "Hỗ trợ",
|
||||||
"aoeattack": "Tấn công đa mục tiêu",
|
"aoeattack": "Tấn công đa mục tiêu",
|
||||||
"mazeattack": "Bí kỹ tấn công",
|
"mazeattack": "Bí kỹ tấn công",
|
||||||
"impair": "Suy yếu",
|
"impair": "Suy yếu",
|
||||||
"bounce": "Nảy bật",
|
"bounce": "Nảy bật",
|
||||||
"active": "Kích hoạt",
|
"active": "Kích hoạt",
|
||||||
"inactive": "Không kích hoạt",
|
"inactive": "Không kích hoạt",
|
||||||
"defence": "Phòng thủ",
|
"defence": "Phòng thủ",
|
||||||
"maxAll": "Tối đa tất cả",
|
"maxAll": "Tối đa tất cả",
|
||||||
"maxAllSuccess": "Đã thiết lập cấp độ kỹ năng tối đa thành công.",
|
"maxAllSuccess": "Đã thiết lập cấp độ kỹ năng tối đa thành công.",
|
||||||
"maxAllFailed": "Thiết lập cấp độ kỹ năng tối đa thất bại.",
|
"maxAllFailed": "Thiết lập cấp độ kỹ năng tối đa thất bại.",
|
||||||
"noRelicEquipped": "Không có di vật",
|
"noRelicEquipped": "Không có di vật",
|
||||||
"anomalyArbitration": "Trọng tài dị tướng",
|
"anomalyArbitration": "Trọng tài dị tướng",
|
||||||
"normalMode": "Chế độ thường",
|
"normalMode": "Chế độ thường",
|
||||||
"hardMode": "Chế độ khó",
|
"hardMode": "Chế độ khó",
|
||||||
"mode": "Chế độ",
|
"mode": "Chế độ",
|
||||||
"selectMode": "Chọn chế độ",
|
"selectMode": "Chọn chế độ",
|
||||||
"rollBack": "Quay lại bước trước",
|
"rollBack": "Quay lại bước trước",
|
||||||
"upRoll": "Tăng dòng",
|
"upRoll": "Tăng dòng",
|
||||||
"downRoll": "Giảm dòng",
|
"downRoll": "Giảm dòng",
|
||||||
"actions": "Hành động",
|
"actions": "Hành động",
|
||||||
"avatars": "Nhân vật",
|
"avatars": "Nhân vật",
|
||||||
"quickView": "Xem nhanh",
|
"quickView": "Xem nhanh",
|
||||||
"extraSetting": "Cài đặt bổ sung",
|
"extraSetting": "Cài đặt bổ sung",
|
||||||
"disableCensorship": "Tắt kiểm duyệt",
|
"disableCensorship": "Tắt kiểm duyệt",
|
||||||
"hideUI": "Ẩn giao diện",
|
"hideUI": "Ẩn giao diện",
|
||||||
"theoryCraftMode": "Chế độ Theory Craft",
|
"theoryCraftMode": "Chế độ Theory Craft",
|
||||||
"cycleCount": "Số vòng",
|
"cycleCount": "Số vòng",
|
||||||
"pleaseSelectAllSubStats": "Vui lòng chọn tất cả chỉ số phụ",
|
"pleaseSelectAllSubStats": "Vui lòng chọn tất cả chỉ số phụ",
|
||||||
"subStatRollCountCannotBeZero": "Số dòng của chỉ số phụ không thể bằng 0",
|
"subStatRollCountCannotBeZero": "Số dòng của chỉ số phụ không thể bằng 0",
|
||||||
"theoryCraft": "Theory Craft",
|
"theoryCraft": "Theory Craft",
|
||||||
"multipathCharacter": "Nhân vật đa Vận Mệnh",
|
"multipathCharacter": "Nhân vật đa Vận Mệnh",
|
||||||
"mainPath": "Vận Mệnh Nhân Vật Chính",
|
"mainPath": "Vận Mệnh Nhân Vật Chính",
|
||||||
"march7Path": "Vận Mệnh March 7",
|
"march7Path": "Vận Mệnh March 7",
|
||||||
"challenge": "Thử thách",
|
"challenge": "Thử thách",
|
||||||
"skipNode": "Bỏ qua node",
|
"skipNode": "Bỏ qua node",
|
||||||
"disableSkip": "Tắt bỏ qua",
|
"disableSkip": "Tắt bỏ qua",
|
||||||
"skipNode1": "Bỏ qua node 1",
|
"skipNode1": "Bỏ qua node 1",
|
||||||
"skipNode2": "Bỏ qua node 2",
|
"skipNode2": "Bỏ qua node 2",
|
||||||
"extraFeatures": "Tính năng bổ sung",
|
"extraFeatures": "Tính năng bổ sung",
|
||||||
"detailTheoryCraft": "Khi bật tính năng này sẽ cho phép tùy chỉnh số cycle và trong mục kẻ địch tủy chỉnh sẽ cho phép điều chỉnh số hp.",
|
"detailTheoryCraft": "Khi bật tính năng này sẽ cho phép tùy chỉnh số cycle và trong mục kẻ địch tủy chỉnh sẽ cho phép điều chỉnh số hp.",
|
||||||
"detailSkipNode": "Khi bật tính năng này sẽ cho phép bỏ qua (node 1/node 2) của Hồi ức hỗn độn hoặc Kể chuyện hư cấu.",
|
"detailSkipNode": "Khi bật tính năng này sẽ cho phép bỏ qua (node 1/node 2) của Hồi ức hỗn độn hoặc Kể chuyện hư cấu.",
|
||||||
"detailChallengePeak": "Cho phép thay đổi mùa Trọng tại dị tướng hiện tại.",
|
"detailChallengePeak": "Cho phép thay đổi mùa Trọng tại dị tướng hiện tại.",
|
||||||
"detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.",
|
"detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.",
|
||||||
"detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.",
|
"detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.",
|
||||||
"detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.",
|
"detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.",
|
||||||
"trailblazer": "Nhà khai phá",
|
"trailblazer": "Nhà khai phá",
|
||||||
"listExtraEffect": "Danh sách hiệu ứng bổ sung",
|
"listExtraEffect": "Danh sách hiệu ứng bổ sung",
|
||||||
"extra": "Bổ sung",
|
"extra": "Bổ sung",
|
||||||
"customLineup": "Đội hình tùy chỉnh"
|
"customLineup": "Đội hình tùy chỉnh"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo [INFO] Create folder src\zod if not exist...
|
echo [INFO] Create folder src\zod if not exist...
|
||||||
if not exist src\zod (
|
if not exist src\zod (
|
||||||
mkdir src\zod
|
mkdir src\zod
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [INFO] Start convert file .ts from src\types to Zod schemas...
|
echo [INFO] Start convert file .ts from src\types to Zod schemas...
|
||||||
for %%f in (src\types\*.ts) do (
|
for %%f in (src\types\*.ts) do (
|
||||||
echo [ZOD] Chuyển %%f -> src\zod\%%~nf.zod.ts
|
echo [ZOD] Chuyển %%f -> src\zod\%%~nf.zod.ts
|
||||||
npx ts-to-zod src\types\%%~nxf src\zod\%%~nf.zod.ts
|
npx ts-to-zod src\types\%%~nxf src\zod\%%~nf.zod.ts
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [DONE] ✅ Done
|
echo [DONE] ✅ Done
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { getDataCache } from "@/lib/cache/cache"
|
import { getDataCache } from "@/lib/cache/cache"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: Request,
|
req: Request,
|
||||||
{ params }: { params: Promise<{ name: string }> }
|
{ params }: { params: Promise<{ name: string }> }
|
||||||
) {
|
) {
|
||||||
const { name } = await params
|
const { name } = await params
|
||||||
|
|
||||||
const item = getDataCache(name)
|
const item = getDataCache(name)
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return new Response("Not found", { status: 404 })
|
return new Response("Not found", { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Cache-Control": "public, max-age=3600"
|
"Cache-Control": "public, max-age=3600"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.type === "br") {
|
if (item.type === "br") {
|
||||||
headers["Content-Encoding"] = "br"
|
headers["Content-Encoding"] = "br"
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(new Uint8Array(item.buf), { headers })
|
return new Response(new Uint8Array(item.buf), { headers })
|
||||||
}
|
}
|
||||||
+70
-70
@@ -1,79 +1,79 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import net from 'net'
|
import net from 'net'
|
||||||
|
|
||||||
function isPrivateHost(hostname: string): boolean {
|
function isPrivateHost(hostname: string): boolean {
|
||||||
if (
|
if (
|
||||||
hostname === 'localhost' ||
|
hostname === 'localhost' ||
|
||||||
hostname.startsWith('127.') ||
|
hostname.startsWith('127.') ||
|
||||||
hostname.startsWith('10.') ||
|
hostname.startsWith('10.') ||
|
||||||
hostname.startsWith('192.168.') ||
|
hostname.startsWith('192.168.') ||
|
||||||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname)
|
/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname)
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (net.isIP(hostname)) {
|
if (net.isIP(hostname)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const targetUrl = searchParams.get("url")
|
const targetUrl = searchParams.get("url")
|
||||||
if (!targetUrl) {
|
if (!targetUrl) {
|
||||||
return NextResponse.json({ error: "Missing url" }, { status: 400 })
|
return NextResponse.json({ error: "Missing url" }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(targetUrl)
|
const response = await fetch(targetUrl)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return NextResponse.json({ error: "Failed to fetch image" }, { status: 500 })
|
return NextResponse.json({ error: "Failed to fetch image" }, { status: 500 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = await response.arrayBuffer()
|
const buffer = await response.arrayBuffer()
|
||||||
|
|
||||||
return new NextResponse(buffer, {
|
return new NextResponse(buffer, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": response.headers.get("content-type") || "image/png",
|
"Content-Type": response.headers.get("content-type") || "image/png",
|
||||||
"Cache-Control": "public, max-age=3600",
|
"Cache-Control": "public, max-age=3600",
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
return NextResponse.json({ error: (e as Error)?.message }, { status: 500 })
|
return NextResponse.json({ error: (e as Error)?.message }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
const { serverUrl, method, ...payload } = body
|
const { serverUrl, method, ...payload } = body
|
||||||
|
|
||||||
if (!serverUrl) {
|
if (!serverUrl) {
|
||||||
return NextResponse.json({ error: 'Missing serverUrl' }, { status: 400 })
|
return NextResponse.json({ error: 'Missing serverUrl' }, { status: 400 })
|
||||||
}
|
}
|
||||||
if (!method) {
|
if (!method) {
|
||||||
return NextResponse.json({ error: 'Missing method' }, { status: 400 })
|
return NextResponse.json({ error: 'Missing method' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = serverUrl.trim()
|
let url = serverUrl.trim()
|
||||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||||
url = `http://${url}`
|
url = `http://${url}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = new URL(url)
|
const parsed = new URL(url)
|
||||||
if (isPrivateHost(parsed.hostname)) {
|
if (isPrivateHost(parsed.hostname)) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: `Connection to private/internal address (${parsed.hostname}) is not allowed` },
|
{ error: `Connection to private/internal address (${parsed.hostname}) is not allowed` },
|
||||||
{ status: 403 }
|
{ status: 403 }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let response
|
let response
|
||||||
|
|
||||||
switch (method.toUpperCase()) {
|
switch (method.toUpperCase()) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import EidolonsInfo from "@/components/eidolonsInfo";
|
import EidolonsInfo from "@/components/eidolonsInfo";
|
||||||
export default function EidolonsInfoPage() {
|
export default function EidolonsInfoPage() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<EidolonsInfo/>
|
<EidolonsInfo/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import RelicsInfo from "@/components/relicsInfo";
|
import RelicsInfo from "@/components/relicsInfo";
|
||||||
|
|
||||||
export default function RelicsInfoPage() {
|
export default function RelicsInfoPage() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<RelicsInfo></RelicsInfo>
|
<RelicsInfo></RelicsInfo>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import ShowCaseInfo from "@/components/showcaseCard"
|
import ShowCaseInfo from "@/components/showcaseCard"
|
||||||
|
|
||||||
export default function ShowcaseCard() {
|
export default function ShowcaseCard() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ShowCaseInfo />
|
<ShowCaseInfo />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import SkillsInfo from "@/components/skillsInfo";
|
import SkillsInfo from "@/components/skillsInfo";
|
||||||
|
|
||||||
export default function SkillsInfoPage() {
|
export default function SkillsInfoPage() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<SkillsInfo></SkillsInfo>
|
<SkillsInfo></SkillsInfo>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+444
-444
@@ -1,445 +1,445 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import ParseText from "../parseText";
|
import ParseText from "../parseText";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import { getNameChar } from "@/helper/getName";
|
import { getNameChar } from "@/helper/getName";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import useModelStore from "@/stores/modelStore";
|
import useModelStore from "@/stores/modelStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { ModalConfig, RelicStore } from "@/types";
|
import { ModalConfig, RelicStore } from "@/types";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import useGlobalStore from "@/stores/globalStore";
|
import useGlobalStore from "@/stores/globalStore";
|
||||||
import { connectToPS, syncDataToPS } from "@/helper";
|
import { connectToPS, syncDataToPS } from "@/helper";
|
||||||
import CopyImport from "../importBar/copy";
|
import CopyImport from "../importBar/copy";
|
||||||
import useCopyProfileStore from "@/stores/copyProfile";
|
import useCopyProfileStore from "@/stores/copyProfile";
|
||||||
import AvatarBar from "../avatarBar";
|
import AvatarBar from "../avatarBar";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function ActionBar() {
|
export default function ActionBar() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { avatarSelected } = useCurrentDataStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { damageType, baseType } = useDetailDataStore()
|
const { damageType, baseType } = useDetailDataStore()
|
||||||
const { setResetData } = useCopyProfileStore()
|
const { setResetData } = useCopyProfileStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
isOpenCreateProfile,
|
isOpenCreateProfile,
|
||||||
setIsOpenCreateProfile,
|
setIsOpenCreateProfile,
|
||||||
isOpenCopy,
|
isOpenCopy,
|
||||||
setIsOpenCopy,
|
setIsOpenCopy,
|
||||||
isOpenAvatars,
|
isOpenAvatars,
|
||||||
setIsOpenAvatars
|
setIsOpenAvatars
|
||||||
} = useModelStore()
|
} = useModelStore()
|
||||||
const { avatars, setAvatar } = useUserDataStore()
|
const { avatars, setAvatar } = useUserDataStore()
|
||||||
const [profileName, setProfileName] = useState("");
|
const [profileName, setProfileName] = useState("");
|
||||||
const [formState, setFormState] = useState("EDIT");
|
const [formState, setFormState] = useState("EDIT");
|
||||||
const [profileEdit, setProfileEdit] = useState(-1);
|
const [profileEdit, setProfileEdit] = useState(-1);
|
||||||
const { isConnectPS } = useGlobalStore()
|
const { isConnectPS } = useGlobalStore()
|
||||||
|
|
||||||
const profileCurrent = useMemo(() => {
|
const profileCurrent = useMemo(() => {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
const avatar = avatars[avatarSelected.ID];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
return avatar?.profileList[avatar.profileSelect] || null;
|
return avatar?.profileList[avatar.profileSelect] || null;
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
const listProfile = useMemo(() => {
|
const listProfile = useMemo(() => {
|
||||||
if (!avatarSelected) return [];
|
if (!avatarSelected) return [];
|
||||||
const avatar = avatars[avatarSelected.ID];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
return avatar?.profileList || [];
|
return avatar?.profileList || [];
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
|
|
||||||
const handleUpdateProfile = () => {
|
const handleUpdateProfile = () => {
|
||||||
if (!profileName.trim()) return;
|
if (!profileName.trim()) return;
|
||||||
if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) {
|
if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
const newProfile = {
|
const newProfile = {
|
||||||
profile_name: profileName,
|
profile_name: profileName,
|
||||||
lightcone: null,
|
lightcone: null,
|
||||||
relics: {} as Record<string, RelicStore>
|
relics: {} as Record<string, RelicStore>
|
||||||
}
|
}
|
||||||
newListProfile.push(newProfile)
|
newListProfile.push(newProfile)
|
||||||
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 })
|
||||||
toast.success("Profile created successfully")
|
toast.success("Profile created successfully")
|
||||||
} else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) {
|
} else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
newListProfile[profileEdit].profile_name = profileName;
|
newListProfile[profileEdit].profile_name = profileName;
|
||||||
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile })
|
||||||
toast.success("Profile updated successfully")
|
toast.success("Profile updated successfully")
|
||||||
}
|
}
|
||||||
handleCloseModal("update_profile_modal");
|
handleCloseModal("update_profile_modal");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleShow = (modalId: string) => {
|
const handleShow = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.showModal();
|
modal.showModal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
// Close modal handler
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const actionMove = (path: string) => {
|
const actionMove = (path: string) => {
|
||||||
router.push(`/${path}`)
|
router.push(`/${path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleProfileSelect = (profileId: number) => {
|
const handleProfileSelect = (profileId: number) => {
|
||||||
if (!avatarSelected) return;
|
if (!avatarSelected) return;
|
||||||
if (avatars[avatarSelected.ID].profileSelect === profileId) return;
|
if (avatars[avatarSelected.ID].profileSelect === profileId) return;
|
||||||
setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId })
|
setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId })
|
||||||
toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`)
|
toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => {
|
const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (!avatarSelected || profileId == 0) return;
|
if (!avatarSelected || profileId == 0) return;
|
||||||
if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) {
|
if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
newListProfile.splice(profileId, 1)
|
newListProfile.splice(profileId, 1)
|
||||||
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 })
|
||||||
toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`)
|
toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleConnectOrSyncPS = async () => {
|
const handleConnectOrSyncPS = async () => {
|
||||||
if (isConnectPS) {
|
if (isConnectPS) {
|
||||||
const res = await syncDataToPS()
|
const res = await syncDataToPS()
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
toast.success(transI18n("syncSuccess"))
|
toast.success(transI18n("syncSuccess"))
|
||||||
} else {
|
} else {
|
||||||
toast.error(`${transI18n("syncFailed")}: ${res.message}`)
|
toast.error(`${transI18n("syncFailed")}: ${res.message}`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const res = await connectToPS()
|
const res = await connectToPS()
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
toast.success(transI18n("connectedSuccess"))
|
toast.success(transI18n("connectedSuccess"))
|
||||||
} else {
|
} else {
|
||||||
toast.error(`${transI18n("connectedFailed")}: ${res.message}`)
|
toast.error(`${transI18n("connectedFailed")}: ${res.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const modalConfigs: ModalConfig[] = [
|
const modalConfigs: ModalConfig[] = [
|
||||||
{
|
{
|
||||||
id: "update_profile_modal",
|
id: "update_profile_modal",
|
||||||
title: formState === "CREATE" ? transI18n("createNewProfile") : transI18n("editProfile"),
|
title: formState === "CREATE" ? transI18n("createNewProfile") : transI18n("editProfile"),
|
||||||
isOpen: isOpenCreateProfile,
|
isOpen: isOpenCreateProfile,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setIsOpenCreateProfile(false)
|
setIsOpenCreateProfile(false)
|
||||||
handleCloseModal("update_profile_modal")
|
handleCloseModal("update_profile_modal")
|
||||||
},
|
},
|
||||||
content: (
|
content: (
|
||||||
<div className="px-6 space-y-4">
|
<div className="px-6 space-y-4">
|
||||||
<div className="form-control">
|
<div className="form-control">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text text-primary font-semibold text-lg">
|
<span className="label-text text-primary font-semibold text-lg">
|
||||||
{transI18n("profileName")}
|
{transI18n("profileName")}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={transI18n("placeholderProfileName")}
|
placeholder={transI18n("placeholderProfileName")}
|
||||||
className="input input-warning mt-1 w-full"
|
className="input input-warning mt-1 w-full"
|
||||||
value={profileName}
|
value={profileName}
|
||||||
onChange={(e) => setProfileName(e.target.value)}
|
onChange={(e) => setProfileName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<button className="btn btn-success btn-sm sm:btn-md" onClick={handleUpdateProfile}>
|
<button className="btn btn-success btn-sm sm:btn-md" onClick={handleUpdateProfile}>
|
||||||
{formState === "CREATE" ? transI18n("create") : transI18n("update")}
|
{formState === "CREATE" ? transI18n("create") : transI18n("update")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "copy_profile_modal",
|
id: "copy_profile_modal",
|
||||||
title: transI18n("copyProfiles").toUpperCase(),
|
title: transI18n("copyProfiles").toUpperCase(),
|
||||||
isOpen: isOpenCopy,
|
isOpen: isOpenCopy,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setIsOpenCopy(false)
|
setIsOpenCopy(false)
|
||||||
handleCloseModal("copy_profile_modal")
|
handleCloseModal("copy_profile_modal")
|
||||||
},
|
},
|
||||||
content: <CopyImport />
|
content: <CopyImport />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "avatars_modal",
|
id: "avatars_modal",
|
||||||
title: transI18n("avatars").toUpperCase(),
|
title: transI18n("avatars").toUpperCase(),
|
||||||
isOpen: isOpenAvatars,
|
isOpen: isOpenAvatars,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setIsOpenAvatars(false)
|
setIsOpenAvatars(false)
|
||||||
handleCloseModal("avatars_modal")
|
handleCloseModal("avatars_modal")
|
||||||
},
|
},
|
||||||
content: <AvatarBar onClose={() => { setIsOpenAvatars(false); handleCloseModal("avatars_modal") }} />
|
content: <AvatarBar onClose={() => { setIsOpenAvatars(false); handleCloseModal("avatars_modal") }} />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Handle ESC key to close modal
|
// Handle ESC key to close modal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
for (const item of modalConfigs) {
|
for (const item of modalConfigs) {
|
||||||
if (!item?.isOpen) {
|
if (!item?.isOpen) {
|
||||||
handleCloseModal(item?.id || "")
|
handleCloseModal(item?.id || "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
for (const item of modalConfigs) {
|
for (const item of modalConfigs) {
|
||||||
handleCloseModal(item?.id || "")
|
handleCloseModal(item?.id || "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleEscKey);
|
window.addEventListener('keydown', handleEscKey);
|
||||||
return () => window.removeEventListener('keydown', handleEscKey);
|
return () => window.removeEventListener('keydown', handleEscKey);
|
||||||
}, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]);
|
}, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full px-4 pb-4 bg-base-200">
|
<div className="w-full px-4 pb-4 bg-base-200">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 items-center justify-items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 items-center justify-items-center">
|
||||||
|
|
||||||
<div className="flex flex-col justify-center w-full">
|
<div className="flex flex-col justify-center w-full">
|
||||||
<div className="flex flex-wrap items-center gap-2 ">
|
<div className="flex flex-wrap items-center gap-2 ">
|
||||||
<div className="flex flex-wrap items-center h-full opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
<div className="flex flex-wrap items-center h-full opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
||||||
{avatarSelected && (
|
{avatarSelected && (
|
||||||
<div className="flex flex-wrap items-center justify-start h-full w-full">
|
<div className="flex flex-wrap items-center justify-start h-full w-full">
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/${damageType?.[avatarSelected?.DamageType]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType?.[avatarSelected?.DamageType]?.Icon}`}
|
||||||
alt={'damage type'}
|
alt={'damage type'}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
className="h-10 w-10 object-contain"
|
className="h-10 w-10 object-contain"
|
||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
/>
|
/>
|
||||||
<p className="text-center font-bold text-lg">
|
<p className="text-center font-bold text-lg">
|
||||||
{transI18n(avatarSelected.BaseType.toLowerCase())}
|
{transI18n(avatarSelected.BaseType.toLowerCase())}
|
||||||
</p>
|
</p>
|
||||||
<div className="text-center font-bold text-lg">{" / "}</div>
|
<div className="text-center font-bold text-lg">{" / "}</div>
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={getNameChar(locale, transI18n, avatarSelected).toWellFormed()}
|
text={getNameChar(locale, transI18n, avatarSelected).toWellFormed()}
|
||||||
className={"font-bold text-lg"}
|
className={"font-bold text-lg"}
|
||||||
/>
|
/>
|
||||||
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.ID})`}</div>
|
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.ID})`}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<span className="text-base opacity-70 font-bold w-16">{transI18n("profile")}:</span>
|
<span className="text-base opacity-70 font-bold w-16">{transI18n("profile")}:</span>
|
||||||
<div className="dropdown dropdown-center md:dropdown-start">
|
<div className="dropdown dropdown-center md:dropdown-start">
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
className="btn btn-warning border-info btn-soft gap-1"
|
className="btn btn-warning border-info btn-soft gap-1"
|
||||||
>
|
>
|
||||||
<span className="truncate max-w-48 font-bold">
|
<span className="truncate max-w-48 font-bold">
|
||||||
{profileCurrent?.profile_name}
|
{profileCurrent?.profile_name}
|
||||||
</span>
|
</span>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto">
|
<ul className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto">
|
||||||
{listProfile.map((profile, index) => (
|
{listProfile.map((profile, index) => (
|
||||||
<li key={index} className="grid grid-cols-12">
|
<li key={index} className="grid grid-cols-12">
|
||||||
<button
|
<button
|
||||||
className={`col-span-8 btn btn-ghost`}
|
className={`col-span-8 btn btn-ghost`}
|
||||||
onClick={() => handleProfileSelect(index)}
|
onClick={() => handleProfileSelect(index)}
|
||||||
>
|
>
|
||||||
<span className="flex-1 truncate text-left">
|
<span className="flex-1 truncate text-left">
|
||||||
{profile.profile_name}
|
{profile.profile_name}
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<span className="text-xs text-base-content/60 ml-1">({transI18n("default")})</span>
|
<span className="text-xs text-base-content/60 ml-1">({transI18n("default")})</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className="col-span-2 flex items-center justify-center text-lg text-warning hover:bg-warning/20 z-20 w-full h-full"
|
className="col-span-2 flex items-center justify-center text-lg text-warning hover:bg-warning/20 z-20 w-full h-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFormState("EDIT");
|
setFormState("EDIT");
|
||||||
setProfileName(profile.profile_name)
|
setProfileName(profile.profile_name)
|
||||||
setProfileEdit(index)
|
setProfileEdit(index)
|
||||||
setIsOpenCreateProfile(true)
|
setIsOpenCreateProfile(true)
|
||||||
handleShow("update_profile_modal")
|
handleShow("update_profile_modal")
|
||||||
}}
|
}}
|
||||||
title="Edit Profile"
|
title="Edit Profile"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4 " fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 " fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536M4 20h4l10.293-10.293a1 1 0 000-1.414l-2.586-2.586a1 1 0 00-1.414 0L4 16v4z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536M4 20h4l10.293-10.293a1 1 0 000-1.414l-2.586-2.586a1 1 0 00-1.414 0L4 16v4z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="col-span-2 flex items-center justify-center text-error hover:bg-error/20 z-20 w-full h-full text-lg"
|
className="col-span-2 flex items-center justify-center text-error hover:bg-error/20 z-20 w-full h-full text-lg"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
handleDeleteProfile(index, e)
|
handleDeleteProfile(index, e)
|
||||||
}}
|
}}
|
||||||
title="Delete Profile"
|
title="Delete Profile"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4 " fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 " fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M9 7V4h6v3m5 0H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M9 7V4h6v3m5 0H4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<li className="border-t border-base-300 mt-2 pt-2 z-10">
|
<li className="border-t border-base-300 mt-2 pt-2 z-10">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpenCopy(true)
|
setIsOpenCopy(true)
|
||||||
setResetData(baseType, damageType)
|
setResetData(baseType, damageType)
|
||||||
handleShow("copy_profile_modal")
|
handleShow("copy_profile_modal")
|
||||||
}}
|
}}
|
||||||
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
{transI18n("copyProfiles")}
|
{transI18n("copyProfiles")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
||||||
setIsOpenCreateProfile(true)
|
setIsOpenCreateProfile(true)
|
||||||
setFormState("CREATE");
|
setFormState("CREATE");
|
||||||
setProfileName("")
|
setProfileName("")
|
||||||
handleShow("update_profile_modal")
|
handleShow("update_profile_modal")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
{transI18n("addNewProfile")}
|
{transI18n("addNewProfile")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className=" grid grid-cols-2 w-full sm:hidden gap-2">
|
<div className=" grid grid-cols-2 w-full sm:hidden gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpenAvatars(true)
|
setIsOpenAvatars(true)
|
||||||
handleShow("avatars_modal")
|
handleShow("avatars_modal")
|
||||||
}}
|
}}
|
||||||
className="col-span-1 btn btn-warning btn-sm w-full">
|
className="col-span-1 btn btn-warning btn-sm w-full">
|
||||||
{transI18n("avatars")}
|
{transI18n("avatars")}
|
||||||
</button>
|
</button>
|
||||||
<div className="col-span-1 dropdown dropdown-center w-full">
|
<div className="col-span-1 dropdown dropdown-center w-full">
|
||||||
<label tabIndex={0} className="btn btn-info btn-sm w-full">
|
<label tabIndex={0} className="btn btn-info btn-sm w-full">
|
||||||
{transI18n("actions")}
|
{transI18n("actions")}
|
||||||
</label>
|
</label>
|
||||||
<ul
|
<ul
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto"
|
className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box min-w-max w-full mt-1 border border-base-300 max-h-60 overflow-y-auto"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={() => actionMove('')}>
|
<button onClick={() => actionMove('')}>
|
||||||
{transI18n("characterInformation")}
|
{transI18n("characterInformation")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={() => actionMove('relics-info')}>
|
<button onClick={() => actionMove('relics-info')}>
|
||||||
{transI18n("relics")}
|
{transI18n("relics")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={() => actionMove('eidolons-info')}>
|
<button onClick={() => actionMove('eidolons-info')}>
|
||||||
{transI18n("eidolons")}
|
{transI18n("eidolons")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={() => actionMove('skills-info')}>
|
<button onClick={() => actionMove('skills-info')}>
|
||||||
{transI18n("skills")}
|
{transI18n("skills")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={() => actionMove('showcase-card')}>
|
<button onClick={() => actionMove('showcase-card')}>
|
||||||
{transI18n("showcaseCard")}
|
{transI18n("showcaseCard")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
||||||
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden sm:grid grid-cols-3 gap-2 w-full">
|
<div className="hidden sm:grid grid-cols-3 gap-2 w-full">
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove("")}>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("")}>
|
||||||
{transI18n("characterInformation")}
|
{transI18n("characterInformation")}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove("relics-info")}>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("relics-info")}>
|
||||||
{transI18n("relics")}
|
{transI18n("relics")}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove("eidolons-info")}>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("eidolons-info")}>
|
||||||
{transI18n("eidolons")}
|
{transI18n("eidolons")}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove("skills-info")}>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("skills-info")}>
|
||||||
{transI18n("skills")}
|
{transI18n("skills")}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn btn-success btn-sm" onClick={() => actionMove("showcase-card")}>
|
<button className="btn btn-success btn-sm" onClick={() => actionMove("showcase-card")}>
|
||||||
{transI18n("showcaseCard")}
|
{transI18n("showcaseCard")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
<button onClick={handleConnectOrSyncPS} className="btn btn-primary btn-sm">
|
||||||
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
{isConnectPS ? transI18n("sync") : transI18n("connectPs")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{modalConfigs.map(({ id, title, onClose, content }) => (
|
{modalConfigs.map(({ id, title, onClose, content }) => (
|
||||||
<dialog key={id} id={id} className="modal">
|
<dialog key={id} id={id} className="modal">
|
||||||
<div className="modal-box w-11/12 max-w-[90%] max-h-[85vh] bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
<div className="modal-box w-11/12 max-w-[90%] max-h-[85vh] bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
<div className="sticky top-0 z-10">
|
<div className="sticky top-0 z-10">
|
||||||
<motion.button
|
<motion.button
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+140
-140
@@ -1,140 +1,140 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import CharacterCard from "../card/characterCard"
|
import CharacterCard from "../card/characterCard"
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import useDetailDataStore from "@/stores/detailDataStore"
|
import useDetailDataStore from "@/stores/detailDataStore"
|
||||||
import useCurrentDataStore from '@/stores/currentDataStore';
|
import useCurrentDataStore from '@/stores/currentDataStore';
|
||||||
import { calcRarity, getNameChar } from "@/helper"
|
import { calcRarity, getNameChar } from "@/helper"
|
||||||
|
|
||||||
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
||||||
const {
|
const {
|
||||||
avatarSearch,
|
avatarSearch,
|
||||||
mapAvatarElementActive,
|
mapAvatarElementActive,
|
||||||
mapAvatarPathActive,
|
mapAvatarPathActive,
|
||||||
setAvatarSearch,
|
setAvatarSearch,
|
||||||
setAvatarSelected,
|
setAvatarSelected,
|
||||||
setMapAvatarElementActive,
|
setMapAvatarElementActive,
|
||||||
setMapAvatarPathActive,
|
setMapAvatarPathActive,
|
||||||
setSkillIDSelected,
|
setSkillIDSelected,
|
||||||
} = useCurrentDataStore()
|
} = useCurrentDataStore()
|
||||||
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const {locale} = useLocaleStore()
|
const {locale} = useLocaleStore()
|
||||||
|
|
||||||
const listAvatar = useMemo(() => {
|
const listAvatar = useMemo(() => {
|
||||||
if (!mapAvatar || !locale || !transI18n) return []
|
if (!mapAvatar || !locale || !transI18n) return []
|
||||||
|
|
||||||
let list = Object.values(mapAvatar)
|
let list = Object.values(mapAvatar)
|
||||||
|
|
||||||
if (avatarSearch) {
|
if (avatarSearch) {
|
||||||
list = list.filter(item =>
|
list = list.filter(item =>
|
||||||
getNameChar(locale, transI18n, item)
|
getNameChar(locale, transI18n, item)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(avatarSearch.toLowerCase())
|
.includes(avatarSearch.toLowerCase())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v)
|
const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v)
|
||||||
const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v)
|
const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v)
|
||||||
|
|
||||||
list = list.filter(item =>
|
list = list.filter(item =>
|
||||||
(allElementFalse || mapAvatarElementActive[item.DamageType]) &&
|
(allElementFalse || mapAvatarElementActive[item.DamageType]) &&
|
||||||
(allPathFalse || mapAvatarPathActive[item.BaseType])
|
(allPathFalse || mapAvatarPathActive[item.BaseType])
|
||||||
)
|
)
|
||||||
|
|
||||||
list.sort((a, b) => {
|
list.sort((a, b) => {
|
||||||
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
if (r !== 0) return r
|
if (r !== 0) return r
|
||||||
return b.ID - a.ID
|
return b.ID - a.ID
|
||||||
})
|
})
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n])
|
}, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-flow-row h-full auto-rows-max w-full">
|
<div className="grid grid-flow-row h-full auto-rows-max w-full">
|
||||||
<div className="h-full rounded-lg mx-2 py-2">
|
<div className="h-full rounded-lg mx-2 py-2">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="flex flex-col h-full gap-2">
|
<div className="flex flex-col h-full gap-2">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
placeholder={transI18n("placeholderCharacter")}
|
placeholder={transI18n("placeholderCharacter")}
|
||||||
className="input input-bordered input-primary w-full"
|
className="input input-bordered input-primary w-full"
|
||||||
value={avatarSearch}
|
value={avatarSearch}
|
||||||
onChange={(e) => setAvatarSearch(e.target.value)}
|
onChange={(e) => setAvatarSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-7 sm:grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
<div className="grid grid-cols-7 sm:grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
||||||
{Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => (
|
{Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] })
|
setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] })
|
||||||
}}
|
}}
|
||||||
className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg"
|
className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280"
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/${value.Icon}`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
height={200} />
|
height={200} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-9 sm:grid-cols-5 lg:grid-cols-9 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
<div className="grid grid-cols-9 sm:grid-cols-5 lg:grid-cols-9 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
||||||
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] })
|
setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] })
|
||||||
}}
|
}}
|
||||||
className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer"
|
className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/${value.Icon}`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
height={200} />
|
height={200} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start h-full">
|
<div className="flex items-start h-full">
|
||||||
<ul className="grid grid-cols-3 sm:grid-cols-2 lg:grid-cols-3 gap-2 w-full h-[65vh] overflow-y-scroll overflow-x-hidden">
|
<ul className="grid grid-cols-3 sm:grid-cols-2 lg:grid-cols-3 gap-2 w-full h-[65vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{listAvatar.map((item, index) => (
|
{listAvatar.map((item, index) => (
|
||||||
<div key={index} onClick={() => {
|
<div key={index} onClick={() => {
|
||||||
setAvatarSelected(item);
|
setAvatarSelected(item);
|
||||||
setSkillIDSelected(null)
|
setSkillIDSelected(null)
|
||||||
if (onClose) onClose()
|
if (onClose) onClose()
|
||||||
}}>
|
}}>
|
||||||
<CharacterCard data={item} />
|
<CharacterCard data={item} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+529
-529
File diff suppressed because it is too large
Load Diff
@@ -1,83 +1,83 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { getNameChar } from '@/helper';
|
import { getNameChar } from '@/helper';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { AvatarDetail } from '@/types';
|
import { AvatarDetail } from '@/types';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
|
||||||
interface CharacterCardProps {
|
interface CharacterCardProps {
|
||||||
data: AvatarDetail
|
data: AvatarDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CharacterCard({ data }: CharacterCardProps) {
|
export default function CharacterCard({ data }: CharacterCardProps) {
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
const transI18n = useTranslations("DataPage");
|
const transI18n = useTranslations("DataPage");
|
||||||
const { baseType, damageType } = useDetailDataStore()
|
const { baseType, damageType } = useDetailDataStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className="z-10 flex flex-col items-center rounded-xl shadow-xl
|
className="z-10 flex flex-col items-center rounded-xl shadow-xl
|
||||||
bg-linear-to-br from-base-300 via-base-100 to-warning/70
|
bg-linear-to-br from-base-300 via-base-100 to-warning/70
|
||||||
transform transition-transform duration-300 ease-in-out
|
transform transition-transform duration-300 ease-in-out
|
||||||
hover:scale-105 cursor-pointer min-h-45 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-65"
|
hover:scale-105 cursor-pointer min-h-45 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-65"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerAvatarRarityType5"
|
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerAvatarRarityType5"
|
||||||
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
||||||
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className="relative w-full h-32 lg:h-26 xl:h-36">
|
<div className="relative w-full h-32 lg:h-26 xl:h-36">
|
||||||
<Image
|
<Image
|
||||||
width={376}
|
width={376}
|
||||||
height={512}
|
height={512}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${data.Image.AvatarIconPath}`}
|
src={`${process.env.CDN_URL}/${data.Image.AvatarIconPath}`}
|
||||||
priority={true}
|
priority={true}
|
||||||
className="rounded-md w-full h-full object-contain"
|
className="rounded-md w-full h-full object-contain"
|
||||||
alt="ALT"
|
alt="ALT"
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType?.[data.DamageType].Icon}`}
|
src={`${process.env.CDN_URL}/${damageType?.[data.DamageType].Icon}`}
|
||||||
className="absolute top-0 left-0 w-6 h-6 rounded-full"
|
className="absolute top-0 left-0 w-6 h-6 rounded-full"
|
||||||
alt={data.DamageType.toLowerCase()}
|
alt={data.DamageType.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${baseType?.[data.BaseType].Icon}`}
|
src={`${process.env.CDN_URL}/${baseType?.[data.BaseType].Icon}`}
|
||||||
className="absolute top-0 right-0 w-6 h-6 rounded-full"
|
className="absolute top-0 right-0 w-6 h-6 rounded-full"
|
||||||
alt={data.BaseType.toLowerCase()}
|
alt={data.BaseType.toLowerCase()}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={getNameChar(locale, transI18n, data)}
|
text={getNameChar(locale, transI18n, data)}
|
||||||
className="
|
className="
|
||||||
w-full px-0.5
|
w-full px-0.5
|
||||||
my-1
|
my-1
|
||||||
text-center font-bold text-shadow-white
|
text-center font-bold text-shadow-white
|
||||||
leading-tight
|
leading-tight
|
||||||
wrap-break-word
|
wrap-break-word
|
||||||
text-sm sm:text-base 2xl:text-lg
|
text-sm sm:text-base 2xl:text-lg
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,125 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CharacterInfoCardType } from '@/types';
|
import { CharacterInfoCardType } from '@/types';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
import { getLocaleName } from '@/helper/getName';
|
import { getLocaleName } from '@/helper/getName';
|
||||||
|
|
||||||
export default function CharacterInfoCard({ character, selectedCharacters, onCharacterToggle }: { character: CharacterInfoCardType, selectedCharacters: CharacterInfoCardType[], onCharacterToggle: (characterId: CharacterInfoCardType) => void }) {
|
export default function CharacterInfoCard({ character, selectedCharacters, onCharacterToggle }: { character: CharacterInfoCardType, selectedCharacters: CharacterInfoCardType[], onCharacterToggle: (characterId: CharacterInfoCardType) => void }) {
|
||||||
const isSelected = selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id);
|
const isSelected = selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id);
|
||||||
const { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore();
|
const { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore();
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-base-200/60 rounded-xl p-4 border cursor-pointer transition-all duration-200 ${isSelected
|
className={`bg-base-200/60 rounded-xl p-4 border cursor-pointer transition-all duration-200 ${isSelected
|
||||||
? 'border-blue-400 ring-2 ring-blue-400/50'
|
? 'border-blue-400 ring-2 ring-blue-400/50'
|
||||||
: 'border-base-300/50 hover:border-base-300 opacity-75'
|
: 'border-base-300/50 hover:border-base-300 opacity-75'
|
||||||
}`}
|
}`}
|
||||||
|
|
||||||
onClick={() => onCharacterToggle(character)}
|
onClick={() => onCharacterToggle(character)}
|
||||||
>
|
>
|
||||||
|
|
||||||
{/* Character Portrait */}
|
{/* Character Portrait */}
|
||||||
<div className="relative mb-4">
|
<div className="relative mb-4">
|
||||||
<div className="w-full h-48 rounded-lg overflow-hidden relative">
|
<div className="w-full h-48 rounded-lg overflow-hidden relative">
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/${mapAvatar?.[character.avatar_id.toString()]?.Image?.AvatarIconPath}`}
|
src={`${process.env.CDN_URL}/${mapAvatar?.[character.avatar_id.toString()]?.Image?.AvatarIconPath}`}
|
||||||
alt={getLocaleName(locale, mapAvatar?.[character.avatar_id.toString()]?.Name)}
|
alt={getLocaleName(locale, mapAvatar?.[character.avatar_id.toString()]?.Name)}
|
||||||
width={376}
|
width={376}
|
||||||
height={512}
|
height={512}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType?.[mapAvatar?.[character.avatar_id.toString()]?.DamageType || ""]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType?.[mapAvatar?.[character.avatar_id.toString()]?.DamageType || ""]?.Icon}`}
|
||||||
className="absolute top-0 left-0 w-10 h-10 rounded-full"
|
className="absolute top-0 left-0 w-10 h-10 rounded-full"
|
||||||
alt={mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()}
|
alt={mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${baseType?.[mapAvatar?.[character.avatar_id.toString()]?.BaseType || ""]?.Icon}`}
|
src={`${process.env.CDN_URL}/${baseType?.[mapAvatar?.[character.avatar_id.toString()]?.BaseType || ""]?.Icon}`}
|
||||||
className="absolute top-0 right-0 w-10 h-10 rounded-full"
|
className="absolute top-0 right-0 w-10 h-10 rounded-full"
|
||||||
alt={mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()}
|
alt={mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Character Name and Level */}
|
{/* Character Name and Level */}
|
||||||
<div className="w-full rounded-lg flex items-center justify-center mb-2">
|
<div className="w-full rounded-lg flex items-center justify-center mb-2">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<ParseText className="text-lg font-bold"
|
<ParseText className="text-lg font-bold"
|
||||||
text={getLocaleName(locale, mapAvatar[character.avatar_id.toString()]?.Name)}
|
text={getLocaleName(locale, mapAvatar[character.avatar_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
<div className="text-base mb-1">Lv.{character.level} E{character.rank}</div>
|
<div className="text-base mb-1">Lv.{character.level} E{character.rank}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{character.relics.length > 0 && (
|
{character.relics.length > 0 && (
|
||||||
<div className="flex flex-wrap items-center justify-center gap-2 mb-4">
|
<div className="flex flex-wrap items-center justify-center gap-2 mb-4">
|
||||||
{character.relics.map((relic, index) => (
|
{character.relics.map((relic, index) => (
|
||||||
<div key={index} className="relative">
|
<div key={index} className="relative">
|
||||||
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
|
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
|
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
|
||||||
alt="Relic"
|
alt="Relic"
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
className="w-14 h-14 object-contain"
|
className="w-14 h-14 object-contain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute -bottom-2 left-1/2 transform -translate-x-1/2 bg-slate-800 text-white text-xs px-1 rounded">
|
<div className="absolute -bottom-2 left-1/2 transform -translate-x-1/2 bg-slate-800 text-white text-xs px-1 rounded">
|
||||||
+{relic.level}
|
+{relic.level}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Light Cone */}
|
{/* Light Cone */}
|
||||||
{character.lightcone.item_id && (
|
{character.lightcone.item_id && (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="rounded-lg h-42 flex items-center justify-center">
|
<div className="rounded-lg h-42 flex items-center justify-center">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapLightCone?.[character.lightcone.item_id.toString()]?.Image?.ImagePath}`}
|
src={`${process.env.CDN_URL}/${mapLightCone?.[character.lightcone.item_id.toString()]?.Image?.ImagePath}`}
|
||||||
alt={getLocaleName(locale, mapLightCone?.[character.lightcone.item_id.toString()]?.Name)}
|
alt={getLocaleName(locale, mapLightCone?.[character.lightcone.item_id.toString()]?.Name)}
|
||||||
width={348}
|
width={348}
|
||||||
height={408}
|
height={408}
|
||||||
className="w-full h-full object-contain rounded-lg"
|
className="w-full h-full object-contain rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-full rounded-lg flex items-center justify-center">
|
<div className="w-full h-full rounded-lg flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-lg font-bold">
|
<div className="text-lg font-bold">
|
||||||
<ParseText
|
<ParseText
|
||||||
text={getLocaleName(locale, mapLightCone[character.lightcone.item_id.toString()]?.Name)}
|
text={getLocaleName(locale, mapLightCone[character.lightcone.item_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-base mb-1">Lv.{character.lightcone.level} S{character.lightcone.rank}</div>
|
<div className="text-base mb-1">Lv.{character.lightcone.level} S{character.lightcone.rank}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,53 +1,53 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { getLocaleName } from '@/helper';
|
import { getLocaleName } from '@/helper';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { LightConeDetail } from '@/types';
|
import { LightConeDetail } from '@/types';
|
||||||
|
|
||||||
interface LightconeCardProps {
|
interface LightconeCardProps {
|
||||||
data: LightConeDetail
|
data: LightConeDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LightconeCard({ data }: LightconeCardProps) {
|
export default function LightconeCard({ data }: LightconeCardProps) {
|
||||||
|
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
const text = getLocaleName(locale, data.Name)
|
const text = getLocaleName(locale, data.Name)
|
||||||
return (
|
return (
|
||||||
<li className="z-10 flex flex-col items-center rounded-md shadow-lg
|
<li className="z-10 flex flex-col items-center rounded-md shadow-lg
|
||||||
bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300
|
bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300
|
||||||
hover:scale-105 cursor-pointer min-h-55"
|
hover:scale-105 cursor-pointer min-h-55"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerLightconeRarity5"
|
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerLightconeRarity5"
|
||||||
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
||||||
: data.Rarity === "CombatPowerLightconeRarity4" ? "from-purple-400 via-purple-600/70 to-purple-800/50" :
|
: data.Rarity === "CombatPowerLightconeRarity4" ? "from-purple-400 via-purple-600/70 to-purple-800/50" :
|
||||||
"from-blue-400 via-blue-600/70 to-blue-800/50"
|
"from-blue-400 via-blue-600/70 to-blue-800/50"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className="relative w-full h-full">
|
<div className="relative w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
src={`${process.env.CDN_URL}/${data?.Image?.ThumbnailPath}`}
|
src={`${process.env.CDN_URL}/${data?.Image?.ThumbnailPath}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={348}
|
width={348}
|
||||||
height={408}
|
height={408}
|
||||||
className="w-full h-full rounded-md object-cover"
|
className="w-full h-full rounded-md object-cover"
|
||||||
alt="ALT"
|
alt="ALT"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={text}
|
text={text}
|
||||||
className="mt-2 px-1 text-center text-sm sm:text-base font-bold leading-tight"
|
className="mt-2 px-1 text-center text-sm sm:text-base font-bold leading-tight"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,79 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AvatarProfileCardType } from '@/types';
|
import { AvatarProfileCardType } from '@/types';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
import { getLocaleName } from '@/helper/getName';
|
import { getLocaleName } from '@/helper/getName';
|
||||||
|
|
||||||
|
|
||||||
export default function ProfileCard({ profile, selectedProfile, onProfileToggle }: { profile: AvatarProfileCardType, selectedProfile: AvatarProfileCardType[], onProfileToggle: (profileId: AvatarProfileCardType) => void }) {
|
export default function ProfileCard({ profile, selectedProfile, onProfileToggle }: { profile: AvatarProfileCardType, selectedProfile: AvatarProfileCardType[], onProfileToggle: (profileId: AvatarProfileCardType) => void }) {
|
||||||
const isSelected = selectedProfile.some((selectedProfile) => selectedProfile.key === profile.key);
|
const isSelected = selectedProfile.some((selectedProfile) => selectedProfile.key === profile.key);
|
||||||
const { mapLightCone } = useDetailDataStore();
|
const { mapLightCone } = useDetailDataStore();
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-base-200/60 rounded-xl p-4 border cursor-pointer transition-all duration-200 ${isSelected
|
className={`bg-base-200/60 rounded-xl p-4 border cursor-pointer transition-all duration-200 ${isSelected
|
||||||
? 'border-blue-400 ring-2 ring-blue-400/50'
|
? 'border-blue-400 ring-2 ring-blue-400/50'
|
||||||
: 'border-base-300/50 hover:border-base-300 opacity-75'
|
: 'border-base-300/50 hover:border-base-300 opacity-75'
|
||||||
}`}
|
}`}
|
||||||
|
|
||||||
onClick={() => onProfileToggle(profile)}
|
onClick={() => onProfileToggle(profile)}
|
||||||
>
|
>
|
||||||
{/* Light Cone */}
|
{/* Light Cone */}
|
||||||
{profile.lightcone && (
|
{profile.lightcone && (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="rounded-lg h-42 flex items-center justify-center">
|
<div className="rounded-lg h-42 flex items-center justify-center">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${profile.lightcone.item_id}.png`}
|
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${profile.lightcone.item_id}.png`}
|
||||||
alt={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
alt={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
||||||
width={348}
|
width={348}
|
||||||
height={408}
|
height={408}
|
||||||
className="w-full h-full object-contain rounded-lg"
|
className="w-full h-full object-contain rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-full rounded-lg flex items-center justify-center">
|
<div className="w-full h-full rounded-lg flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-lg font-bold">
|
<div className="text-lg font-bold">
|
||||||
<ParseText
|
<ParseText
|
||||||
text={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
text={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-base mb-1">Lv.{profile.lightcone.level} S{profile.lightcone.rank}</div>
|
<div className="text-base mb-1">Lv.{profile.lightcone.level} S{profile.lightcone.rank}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{Object.keys(profile.relics).length > 0 && (
|
{Object.keys(profile.relics).length > 0 && (
|
||||||
<div className="flex flex-wrap items-center justify-center gap-2 mb-4">
|
<div className="flex flex-wrap items-center justify-center gap-2 mb-4">
|
||||||
{Object.values(profile.relics).map((relic, index) => (
|
{Object.values(profile.relics).map((relic, index) => (
|
||||||
<div key={index} className="relative">
|
<div key={index} className="relative">
|
||||||
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
|
<div className="w-9 h-9 rounded-lg flex items-center justify-center border border-amber-500/50">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
|
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
|
||||||
alt="Relic"
|
alt="Relic"
|
||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
className="w-14 h-14 object-contain"
|
className="w-14 h-14 object-contain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute -bottom-2 left-1/2 transform -translate-x-1/2 bg-slate-800 text-white text-xs px-1 rounded">
|
<div className="absolute -bottom-2 left-1/2 transform -translate-x-1/2 bg-slate-800 text-white text-xs px-1 rounded">
|
||||||
+{relic.level}
|
+{relic.level}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
+195
-195
@@ -1,196 +1,196 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import useRelicMakerStore from "@/stores/relicMakerStore";
|
import useRelicMakerStore from "@/stores/relicMakerStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
interface RelicCardProps {
|
interface RelicCardProps {
|
||||||
slot: string
|
slot: string
|
||||||
avatarId: string
|
avatarId: string
|
||||||
}
|
}
|
||||||
const getRarityColor = (rarity: string) => {
|
const getRarityColor = (rarity: string) => {
|
||||||
switch (rarity) {
|
switch (rarity) {
|
||||||
case '3': return 'border-green-500 shadow-green-500/50 bg-linear-to-br from-green-700 via-green-400 to-green-500';
|
case '3': return 'border-green-500 shadow-green-500/50 bg-linear-to-br from-green-700 via-green-400 to-green-500';
|
||||||
case '4': return 'border-blue-500 shadow-blue-500/50 bg-linear-to-br from-blue-700 via-blue-400 to-blue-500';
|
case '4': return 'border-blue-500 shadow-blue-500/50 bg-linear-to-br from-blue-700 via-blue-400 to-blue-500';
|
||||||
case '5': return 'border-purple-500 shadow-purple-500/50 bg-linear-to-br from-purple-700 via-purple-400 to-purple-500';
|
case '5': return 'border-purple-500 shadow-purple-500/50 bg-linear-to-br from-purple-700 via-purple-400 to-purple-500';
|
||||||
case '6': return 'border-yellow-500 shadow-yellow-500/50 bg-linear-to-br from-yellow-700 via-yellow-400 to-yellow-500';
|
case '6': return 'border-yellow-500 shadow-yellow-500/50 bg-linear-to-br from-yellow-700 via-yellow-400 to-yellow-500';
|
||||||
default: return 'border-gray-500 shadow-gray-500/50';
|
default: return 'border-gray-500 shadow-gray-500/50';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRarityName = (slot: string) => {
|
const getRarityName = (slot: string) => {
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case '1': return (
|
case '1': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/HEAD.png"
|
src="/relics/HEAD.png"
|
||||||
alt="Head"
|
alt="Head"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Head</h2>
|
<h2>Head</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case '2': return (
|
case '2': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/HAND.png"
|
src="/relics/HAND.png"
|
||||||
alt="Hand"
|
alt="Hand"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Hands</h2>
|
<h2>Hands</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case '3': return (
|
case '3': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/BODY.png"
|
src="/relics/BODY.png"
|
||||||
alt="Body"
|
alt="Body"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Body</h2>
|
<h2>Body</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case '4': return (
|
case '4': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/FOOT.png"
|
src="/relics/FOOT.png"
|
||||||
alt="Foot"
|
alt="Foot"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Feet</h2>
|
<h2>Feet</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case '5': return (
|
case '5': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/NECK.png"
|
src="/relics/NECK.png"
|
||||||
alt="Neck"
|
alt="Neck"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Planar sphere</h2>
|
<h2>Planar sphere</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case '6': return (
|
case '6': return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/relics/OBJECT.png"
|
src="/relics/OBJECT.png"
|
||||||
alt="Object"
|
alt="Object"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
className="bg-black/50 rounded-full"
|
className="bg-black/50 rounded-full"
|
||||||
/>
|
/>
|
||||||
<h2>Link rope</h2>
|
<h2>Link rope</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default function RelicCard({ slot, avatarId }: RelicCardProps) {
|
export default function RelicCard({ slot, avatarId }: RelicCardProps) {
|
||||||
const { avatars } = useUserDataStore()
|
const { avatars } = useUserDataStore()
|
||||||
const { selectedRelicSlot } = useRelicMakerStore()
|
const { selectedRelicSlot } = useRelicMakerStore()
|
||||||
|
|
||||||
const relicDetail = useMemo(() => {
|
const relicDetail = useMemo(() => {
|
||||||
const avatar = avatars[avatarId];
|
const avatar = avatars[avatarId];
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
if (avatar.profileList[avatar.profileSelect].relics[slot]) {
|
if (avatar.profileList[avatar.profileSelect].relics[slot]) {
|
||||||
return avatar.profileList[avatar.profileSelect].relics[slot];
|
return avatar.profileList[avatar.profileSelect].relics[slot];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [avatars, avatarId, slot]);
|
}, [avatars, avatarId, slot]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{relicDetail ? (
|
{relicDetail ? (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center cursor-pointer ">
|
className="flex flex-col items-center cursor-pointer ">
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
relative w-24 h-24 rounded-full
|
relative w-24 h-24 rounded-full
|
||||||
${getRarityColor(relicDetail.relic_id.toString()[0])}
|
${getRarityColor(relicDetail.relic_id.toString()[0])}
|
||||||
shadow-xl
|
shadow-xl
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
cursor-pointer transition-transform
|
cursor-pointer transition-transform
|
||||||
${selectedRelicSlot === slot ? 'ring-5 ring-success scale-105' : 'ring-3 ring-primary'}
|
${selectedRelicSlot === slot ? 'ring-5 ring-success scale-105' : 'ring-3 ring-primary'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relicDetail.relic_set_id}_${slot}.png`}
|
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relicDetail.relic_set_id}_${slot}.png`}
|
||||||
alt="Relic"
|
alt="Relic"
|
||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
className="w-14 h-14 object-contain"
|
className="w-14 h-14 object-contain"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Level Badge */}
|
{/* Level Badge */}
|
||||||
<div className="absolute -bottom-2 bg-base-100 border-2 border-base-300 rounded-full px-2 py-1">
|
<div className="absolute -bottom-2 bg-base-100 border-2 border-base-300 rounded-full px-2 py-1">
|
||||||
<span className="text-sm font-bold text-primary">+{relicDetail.level}</span>
|
<span className="text-sm font-bold text-primary">+{relicDetail.level}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3 text-center">
|
<div className="mt-3 text-center">
|
||||||
<div className="text-sm font-medium text-base-content">{getRarityName(slot)}</div>
|
<div className="text-sm font-medium text-base-content">{getRarityName(slot)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center cursor-pointer">
|
className="flex flex-col items-center cursor-pointer">
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
relative w-24 h-24 rounded-full border-4
|
relative w-24 h-24 rounded-full border-4
|
||||||
${getRarityColor("None")}
|
${getRarityColor("None")}
|
||||||
bg-base-300 shadow-xl
|
bg-base-300 shadow-xl
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
cursor-pointer hover:scale-105 transition-transform
|
cursor-pointer hover:scale-105 transition-transform
|
||||||
ring-4 ring-primary
|
ring-4 ring-primary
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<span className="text-3xl">
|
<span className="text-3xl">
|
||||||
<svg className="w-12 h-12 text-base-content/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-12 h-12 text-base-content/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Level Badge */}
|
{/* Level Badge */}
|
||||||
<div className="absolute -bottom-2 bg-base-100 border-2 border-base-300 rounded-full px-2 py-1">
|
<div className="absolute -bottom-2 bg-base-100 border-2 border-base-300 rounded-full px-2 py-1">
|
||||||
<span className="text-sm font-bold text-primary">+{0}</span>
|
<span className="text-sm font-bold text-primary">+{0}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3 text-center">
|
<div className="mt-3 text-center">
|
||||||
<div>{getRarityName(slot)}</div>
|
<div>{getRarityName(slot)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,69 +1,69 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { AvatarDetail } from '@/types';
|
import { AvatarDetail } from '@/types';
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
|
||||||
|
|
||||||
interface SimpleAvatarCardProps {
|
interface SimpleAvatarCardProps {
|
||||||
data: AvatarDetail;
|
data: AvatarDetail;
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
showRemoveHover?: boolean;
|
showRemoveHover?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SimpleAvatarCard = ({ data, isSelected, onClick, showRemoveHover }: SimpleAvatarCardProps) => {
|
export const SimpleAvatarCard = ({ data, isSelected, onClick, showRemoveHover }: SimpleAvatarCardProps) => {
|
||||||
const { baseType, damageType } = useDetailDataStore()
|
const { baseType, damageType } = useDetailDataStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`relative w-16 h-16 sm:w-20 sm:h-20 rounded-md cursor-pointer transition-transform duration-200 ease-in-out hover:scale-105 shadow-md shrink-0
|
className={`relative w-16 h-16 sm:w-20 sm:h-20 rounded-md cursor-pointer transition-transform duration-200 ease-in-out hover:scale-105 shadow-md shrink-0
|
||||||
${isSelected ? 'ring-2 ring-success opacity-60' : ''}
|
${isSelected ? 'ring-2 ring-success opacity-60' : ''}
|
||||||
bg-linear-to-br ${data.Rarity === "CombatPowerAvatarRarityType5"
|
bg-linear-to-br ${data.Rarity === "CombatPowerAvatarRarityType5"
|
||||||
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
||||||
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
width={80}
|
width={80}
|
||||||
height={80}
|
height={80}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${data.Image.ActionAvatarHeadIconPath}`}
|
src={`${process.env.CDN_URL}/${data.Image.ActionAvatarHeadIconPath}`}
|
||||||
priority={true}
|
priority={true}
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
alt="Avatar"
|
alt="Avatar"
|
||||||
/>
|
/>
|
||||||
<div className="absolute top-0 left-0 w-5 h-5 bg-black/40 rounded-full flex items-center justify-center p-0.5">
|
<div className="absolute top-0 left-0 w-5 h-5 bg-black/40 rounded-full flex items-center justify-center p-0.5">
|
||||||
<Image
|
<Image
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType?.[data.DamageType]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType?.[data.DamageType]?.Icon}`}
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
alt="Element"
|
alt="Element"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-0 right-0 w-5 h-5 bg-black/40 rounded-full flex items-center justify-center p-0.5">
|
<div className="absolute top-0 right-0 w-5 h-5 bg-black/40 rounded-full flex items-center justify-center p-0.5">
|
||||||
<Image
|
<Image
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${baseType?.[data.BaseType]?.Icon}`}
|
src={`${process.env.CDN_URL}/${baseType?.[data.BaseType]?.Icon}`}
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
alt="Path"
|
alt="Path"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showRemoveHover && (
|
{showRemoveHover && (
|
||||||
<div className="absolute inset-0 bg-error/80 rounded-md flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
|
<div className="absolute inset-0 bg-error/80 rounded-md flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,91 +1,91 @@
|
|||||||
|
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { Check, Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react';
|
import { Check, Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react';
|
||||||
|
|
||||||
export default function ChangelogBar() {
|
export default function ChangelogBar() {
|
||||||
const { changelog } = useLocaleStore()
|
const { changelog } = useLocaleStore()
|
||||||
|
|
||||||
const getIcon = (type: string) => {
|
const getIcon = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'feature':
|
case 'feature':
|
||||||
return <Sparkles className="w-3 h-3 md:w-4 md:h-4" />;
|
return <Sparkles className="w-3 h-3 md:w-4 md:h-4" />;
|
||||||
case 'fix':
|
case 'fix':
|
||||||
return <Bug className="w-3 h-3 md:w-4 md:h-4" />;
|
return <Bug className="w-3 h-3 md:w-4 md:h-4" />;
|
||||||
case 'improvement':
|
case 'improvement':
|
||||||
return <Zap className="w-3 h-3 md:w-4 md:h-4" />;
|
return <Zap className="w-3 h-3 md:w-4 md:h-4" />;
|
||||||
default:
|
default:
|
||||||
return <Package className="w-3 h-3 md:w-4 md:h-4" />;
|
return <Package className="w-3 h-3 md:w-4 md:h-4" />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBadgeClass = (type: string) => {
|
const getBadgeClass = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'feature':
|
case 'feature':
|
||||||
return 'badge-success';
|
return 'badge-success';
|
||||||
case 'fix':
|
case 'fix':
|
||||||
return 'badge-error';
|
return 'badge-error';
|
||||||
case 'improvement':
|
case 'improvement':
|
||||||
return 'badge-warning';
|
return 'badge-warning';
|
||||||
default:
|
default:
|
||||||
return 'badge-info';
|
return 'badge-info';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTypeLabel = (type: string) => {
|
const getTypeLabel = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'feature':
|
case 'feature':
|
||||||
return 'Feature';
|
return 'Feature';
|
||||||
case 'fix':
|
case 'fix':
|
||||||
return 'Fix';
|
return 'Fix';
|
||||||
case 'improvement':
|
case 'improvement':
|
||||||
return 'Improvement';
|
return 'Improvement';
|
||||||
default:
|
default:
|
||||||
return 'Update';
|
return 'Update';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="md:p-4">
|
<div className="md:p-4">
|
||||||
{/* Alert */}
|
{/* Alert */}
|
||||||
{/* <div className="alert alert-info mb-4 md:p-2 p-1">
|
{/* <div className="alert alert-info mb-4 md:p-2 p-1">
|
||||||
<AlertCircle className="w-6 h-6" />
|
<AlertCircle className="w-6 h-6" />
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
{/* Timeline */}
|
{/* Timeline */}
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{changelog.map((change, index) => (
|
{changelog.map((change, index) => (
|
||||||
<div key={index} className="bg-base-100 shadow-sm">
|
<div key={index} className="bg-base-100 shadow-sm">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{/* Version Header */}
|
{/* Version Header */}
|
||||||
<div className="flex flex-row items-center gap-2 md:gap-3">
|
<div className="flex flex-row items-center gap-2 md:gap-3">
|
||||||
<div className={`badge ${getBadgeClass(change.type)} gap-1 md:gap-2 p-1`}>
|
<div className={`badge ${getBadgeClass(change.type)} gap-1 md:gap-2 p-1`}>
|
||||||
{getIcon(change.type)}
|
{getIcon(change.type)}
|
||||||
{getTypeLabel(change.type)}
|
{getTypeLabel(change.type)}
|
||||||
</div>
|
</div>
|
||||||
<h2 className="card-title text-sm md:text-lg">
|
<h2 className="card-title text-sm md:text-lg">
|
||||||
Version {change.version}
|
Version {change.version}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center gap-1 md:gap-2 text-base-content/60 ml-auto">
|
<div className="flex items-center gap-1 md:gap-2 text-base-content/60 ml-auto">
|
||||||
<Calendar className="w-3 h-3 md:w-4 md:h-4" />
|
<Calendar className="w-3 h-3 md:w-4 md:h-4" />
|
||||||
<span className="text-sm">{change.date}</span>
|
<span className="text-sm">{change.date}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Changes List */}
|
{/* Changes List */}
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
{change.items.map((item, itemIndex) => (
|
{change.items.map((item, itemIndex) => (
|
||||||
<li key={itemIndex} className="flex items-start gap-3">
|
<li key={itemIndex} className="flex items-start gap-3">
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
<Check className="w-5 h-5 text-success" />
|
<Check className="w-5 h-5 text-success" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base-content">{item}</span>
|
<span className="text-base-content">{item}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,56 +1,56 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useFetchASGroupData,
|
useFetchASGroupData,
|
||||||
useFetchAvatarData,
|
useFetchAvatarData,
|
||||||
useFetchChangelog,
|
useFetchChangelog,
|
||||||
useFetchConfigData,
|
useFetchConfigData,
|
||||||
useFetchLightconeData,
|
useFetchLightconeData,
|
||||||
useFetchMOCGroupData,
|
useFetchMOCGroupData,
|
||||||
useFetchMonsterData,
|
useFetchMonsterData,
|
||||||
useFetchPeakGroupData,
|
useFetchPeakGroupData,
|
||||||
useFetchPFGroupData,
|
useFetchPFGroupData,
|
||||||
useFetchRelicSetData
|
useFetchRelicSetData
|
||||||
} from "@/lib/hooks"
|
} from "@/lib/hooks"
|
||||||
|
|
||||||
export default function ClientDataFetcher({
|
export default function ClientDataFetcher({
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const q1 = useFetchConfigData()
|
const q1 = useFetchConfigData()
|
||||||
const q2 = useFetchAvatarData()
|
const q2 = useFetchAvatarData()
|
||||||
const q3 = useFetchLightconeData()
|
const q3 = useFetchLightconeData()
|
||||||
const q4 = useFetchRelicSetData()
|
const q4 = useFetchRelicSetData()
|
||||||
const q5 = useFetchMonsterData()
|
const q5 = useFetchMonsterData()
|
||||||
const q6 = useFetchPFGroupData()
|
const q6 = useFetchPFGroupData()
|
||||||
const q7 = useFetchMOCGroupData()
|
const q7 = useFetchMOCGroupData()
|
||||||
const q8 = useFetchASGroupData()
|
const q8 = useFetchASGroupData()
|
||||||
const q9 = useFetchPeakGroupData()
|
const q9 = useFetchPeakGroupData()
|
||||||
const q10 = useFetchChangelog()
|
const q10 = useFetchChangelog()
|
||||||
|
|
||||||
const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10]
|
const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10]
|
||||||
|
|
||||||
const loading = queries.some(q => q.isLoading)
|
const loading = queries.some(q => q.isLoading)
|
||||||
|
|
||||||
const progress =
|
const progress =
|
||||||
(queries.filter(q => q.isSuccess).length / queries.length) * 100
|
(queries.filter(q => q.isSuccess).length / queries.length) * 100
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen flex-col items-center justify-center gap-4">
|
<div className="flex h-screen flex-col items-center justify-center gap-4">
|
||||||
<div className="text-lg font-semibold">Loading data...</div>
|
<div className="text-lg font-semibold">Loading data...</div>
|
||||||
|
|
||||||
<progress
|
<progress
|
||||||
className="progress progress-primary w-56"
|
className="progress progress-primary w-56"
|
||||||
value={progress}
|
value={progress}
|
||||||
max="100"
|
max="100"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>{Math.floor(progress)}%</div>
|
<div>{Math.floor(progress)}%</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>
|
return <>{children}</>
|
||||||
}
|
}
|
||||||
+168
-168
@@ -1,169 +1,169 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { connectToPS, syncDataToPS } from "@/helper"
|
import { connectToPS, syncDataToPS } from "@/helper"
|
||||||
import useConnectStore from "@/stores/connectStore"
|
import useConnectStore from "@/stores/connectStore"
|
||||||
import useGlobalStore from "@/stores/globalStore"
|
import useGlobalStore from "@/stores/globalStore"
|
||||||
import { PSConnectType } from "@/types"
|
import { PSConnectType } from "@/types"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
export default function ConnectBar() {
|
export default function ConnectBar() {
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const [message, setMessage] = useState({ text: '', type: '' });
|
const [message, setMessage] = useState({ text: '', type: '' });
|
||||||
const {
|
const {
|
||||||
connectionType,
|
connectionType,
|
||||||
privateType,
|
privateType,
|
||||||
serverUrl,
|
serverUrl,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
setConnectionType,
|
setConnectionType,
|
||||||
setPrivateType,
|
setPrivateType,
|
||||||
setServerUrl,
|
setServerUrl,
|
||||||
setUsername,
|
setUsername,
|
||||||
setPassword
|
setPassword
|
||||||
} = useConnectStore()
|
} = useConnectStore()
|
||||||
const { isConnectPS, setIsConnectPS } = useGlobalStore()
|
const { isConnectPS, setIsConnectPS } = useGlobalStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-6 py-4">
|
<div className="px-6 py-4">
|
||||||
<div className="form-control grid grid-cols-1 w-full mb-6">
|
<div className="form-control grid grid-cols-1 w-full mb-6">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-purple-300">{transI18n("connectionType")}</span>
|
<span className="label-text font-semibold text-purple-300">{transI18n("connectionType")}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
className="select w-full select-bordered border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
className="select w-full select-bordered border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
||||||
value={connectionType}
|
value={connectionType}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setIsConnectPS(false)
|
setIsConnectPS(false)
|
||||||
setConnectionType(e.target.value)
|
setConnectionType(e.target.value)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value={PSConnectType.FireflyGo}>FireflyGo</option>
|
<option value={PSConnectType.FireflyGo}>FireflyGo</option>
|
||||||
<option value={PSConnectType.RobinSR}>RobinSR</option>
|
<option value={PSConnectType.RobinSR}>RobinSR</option>
|
||||||
<option value={PSConnectType.Other}>{transI18n("other")}</option>
|
<option value={PSConnectType.Other}>{transI18n("other")}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{connectionType === PSConnectType.Other && (
|
{connectionType === PSConnectType.Other && (
|
||||||
<div className="flex flex-col md:space-x-4 mb-6 gap-2">
|
<div className="flex flex-col md:space-x-4 mb-6 gap-2">
|
||||||
<div className="form-control w-full mb-4 md:mb-0">
|
<div className="form-control w-full mb-4 md:mb-0">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-purple-300">{transI18n("serverUrl")}</span>
|
<span className="label-text font-semibold text-purple-300">{transI18n("serverUrl")}</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={transI18n("placeholderServerUrl")}
|
placeholder={transI18n("placeholderServerUrl")}
|
||||||
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
||||||
value={serverUrl}
|
value={serverUrl}
|
||||||
onChange={(e) => setServerUrl(e.target.value)}
|
onChange={(e) => setServerUrl(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-control w-full mb-4 md:mb-0">
|
<div className="form-control w-full mb-4 md:mb-0">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-purple-300">{transI18n("privateType")}</span>
|
<span className="label-text font-semibold text-purple-300">{transI18n("privateType")}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
className="select w-full select-bordered border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
className="select w-full select-bordered border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
||||||
value={privateType}
|
value={privateType}
|
||||||
onChange={(e) => setPrivateType(e.target.value)}
|
onChange={(e) => setPrivateType(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="Local">{transI18n("local")}</option>
|
<option value="Local">{transI18n("local")}</option>
|
||||||
<option value="Server">{transI18n("server")}</option>
|
<option value="Server">{transI18n("server")}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-control w-full mb-4 md:mb-0">
|
<div className="form-control w-full mb-4 md:mb-0">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-purple-300">{transI18n("username")}</span>
|
<span className="label-text font-semibold text-purple-300">{transI18n("username")}</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={transI18n("placeholderUsername")}
|
placeholder={transI18n("placeholderUsername")}
|
||||||
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-control w-full mb-4 md:mb-0">
|
<div className="form-control w-full mb-4 md:mb-0">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-purple-300">{transI18n("password")}</span>
|
<span className="label-text font-semibold text-purple-300">{transI18n("password")}</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={transI18n("placeholderPassword")}
|
placeholder={transI18n("placeholderPassword")}
|
||||||
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
className="input input-bordered w-full border-purple-500/30 focus:border-purple-500 bg-base-200 mt-1"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{message.text && (
|
{message.text && (
|
||||||
<div className={`alert ${message.type === 'success' ? 'alert-success' :
|
<div className={`alert ${message.type === 'success' ? 'alert-success' :
|
||||||
message.type === 'error' ? 'alert-error' : 'alert-info'
|
message.type === 'error' ? 'alert-error' : 'alert-info'
|
||||||
} mb-6`}>
|
} mb-6`}>
|
||||||
<span>{message.text}</span>
|
<span>{message.text}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6 mb-2">
|
||||||
<div className="flex items-center justify-center md:justify-start">
|
<div className="flex items-center justify-center md:justify-start">
|
||||||
<span className="text-md mr-2">{transI18n("status")}:</span>
|
<span className="text-md mr-2">{transI18n("status")}:</span>
|
||||||
<span
|
<span
|
||||||
className={`badge ${isConnectPS ? "badge-success" : "badge-error"
|
className={`badge ${isConnectPS ? "badge-success" : "badge-error"
|
||||||
} badge-lg`}
|
} badge-lg`}
|
||||||
>
|
>
|
||||||
{isConnectPS ? transI18n("connected") : transI18n("unconnected")}
|
{isConnectPS ? transI18n("connected") : transI18n("unconnected")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
<div className="flex flex-col sm:flex-row gap-2 w-full justify-center md:justify-end">
|
<div className="flex flex-col sm:flex-row gap-2 w-full justify-center md:justify-end">
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const response = await connectToPS();
|
const response = await connectToPS();
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: transI18n("connectedSuccess"),
|
text: transI18n("connectedSuccess"),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: response.message,
|
text: response.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn btn-primary w-full sm:w-auto"
|
className="btn btn-primary w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
{transI18n("connectPs")}
|
{transI18n("connectPs")}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isConnectPS && (
|
{isConnectPS && (
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const response = await syncDataToPS();
|
const response = await syncDataToPS();
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: transI18n("syncSuccess"),
|
text: transI18n("syncSuccess"),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: `${transI18n("syncFailed")}: ${response.message}`,
|
text: `${transI18n("syncFailed")}: ${response.message}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn btn-success w-full sm:w-auto"
|
className="btn btn-success w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
{transI18n("sync")}
|
{transI18n("sync")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,85 +1,85 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { replaceByParam, getLocaleName } from '@/helper';
|
import { replaceByParam, getLocaleName } from '@/helper';
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import ParseText from "../parseText";
|
import ParseText from "../parseText";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import ExtraEffectList from '../extraInfo';
|
import ExtraEffectList from '../extraInfo';
|
||||||
|
|
||||||
|
|
||||||
export default function EidolonsInfo() {
|
export default function EidolonsInfo() {
|
||||||
const { avatarSelected } = useCurrentDataStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { setAvatars, avatars } = useUserDataStore()
|
const { setAvatars, avatars } = useUserDataStore()
|
||||||
|
|
||||||
const charRank = useMemo(() => {
|
const charRank = useMemo(() => {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
const avatar = avatars[avatarSelected.ID];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
if (avatar?.enhanced != "") {
|
if (avatar?.enhanced != "") {
|
||||||
return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks
|
return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks
|
||||||
}
|
}
|
||||||
return avatarSelected?.Ranks
|
return avatarSelected?.Ranks
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("eidolons")}
|
{transI18n("eidolons")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{charRank && avatars[avatarSelected?.ID || ""] && (
|
{charRank && avatars[avatarSelected?.ID || ""] && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{Object.entries(charRank || {}).map(([key, rank]) => (
|
{Object.entries(charRank || {}).map(([key, rank]) => (
|
||||||
<div key={key}
|
<div key={key}
|
||||||
className="flex flex-col items-center"
|
className="flex flex-col items-center"
|
||||||
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
let newRank = Number(rank.Rank)
|
let newRank = Number(rank.Rank)
|
||||||
if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(rank.Rank)) {
|
if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(rank.Rank)) {
|
||||||
newRank = Number(rank.Rank) - 1
|
newRank = Number(rank.Rank) - 1
|
||||||
}
|
}
|
||||||
setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } })
|
setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
className={`w-60 object-contain mb-2 ${Number(rank.Rank) <= avatars[avatarSelected?.ID.toString() || ""]?.data?.rank ? "" : "grayscale"}`}
|
className={`w-60 object-contain mb-2 ${Number(rank.Rank) <= avatars[avatarSelected?.ID.toString() || ""]?.data?.rank ? "" : "grayscale"}`}
|
||||||
src={`${process.env.CDN_URL}/ui/ui3d/rank/_dependencies/textures/${avatarSelected?.ID}/${avatarSelected?.ID}_Rank_${rank.Rank}.png`}
|
src={`${process.env.CDN_URL}/ui/ui3d/rank/_dependencies/textures/${avatarSelected?.ID}/${avatarSelected?.ID}_Rank_${rank.Rank}.png`}
|
||||||
alt={`Rank ${rank.Rank} Image`}
|
alt={`Rank ${rank.Rank} Image`}
|
||||||
priority
|
priority
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={240}
|
width={240}
|
||||||
height={240}
|
height={240}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="text-lg pb-1 flex items-center justify-items-center gap-2">
|
<div className="text-lg pb-1 flex items-center justify-items-center gap-2">
|
||||||
<span className="inline-block text-indigo-500">{rank.Rank}.</span>
|
<span className="inline-block text-indigo-500">{rank.Rank}.</span>
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={getLocaleName(locale, rank.Name)}
|
text={getLocaleName(locale, rank.Name)}
|
||||||
className="text-center text-base font-normal leading-tight"
|
className="text-center text-base font-normal leading-tight"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-sm font-normal">
|
<div className="text-sm font-normal">
|
||||||
<div dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, rank.Desc), rank.Param) }} />
|
<div dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, rank.Desc), rank.Param) }} />
|
||||||
<ExtraEffectList extras={rank?.Extra} locale={locale} />
|
<ExtraEffectList extras={rank?.Extra} locale={locale} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +1,99 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { replaceByParam, getLocaleName } from "@/helper"
|
import { replaceByParam, getLocaleName } from "@/helper"
|
||||||
import { ExtraEffect } from "@/types"
|
import { ExtraEffect } from "@/types"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
extras: Record<string, ExtraEffect> | undefined
|
extras: Record<string, ExtraEffect> | undefined
|
||||||
locale: string
|
locale: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExtraEffectList({ extras, locale }: Props) {
|
export default function ExtraEffectList({ extras, locale }: Props) {
|
||||||
const [openList, setOpenList] = useState(false)
|
const [openList, setOpenList] = useState(false)
|
||||||
const [openId, setOpenId] = useState<number | null>(null)
|
const [openId, setOpenId] = useState<number | null>(null)
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
if (!extras || Object.keys(extras).length === 0) return null
|
if (!extras || Object.keys(extras).length === 0) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between cursor-pointer bg-primary/10 px-3 py-2 rounded-md"
|
className="flex items-center justify-between cursor-pointer bg-primary/10 px-3 py-2 rounded-md"
|
||||||
onClick={() => setOpenList(!openList)}
|
onClick={() => setOpenList(!openList)}
|
||||||
>
|
>
|
||||||
<span className="text-sm font-semibold text-primary">
|
<span className="text-sm font-semibold text-primary">
|
||||||
{transI18n("listExtraEffect")} ({Object.keys(extras).length})
|
{transI18n("listExtraEffect")} ({Object.keys(extras).length})
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={`transition-transform ${
|
className={`transition-transform ${
|
||||||
openList ? "rotate-90" : ""
|
openList ? "rotate-90" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
▶
|
▶
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`overflow-hidden transition-all duration-300 ${
|
className={`overflow-hidden transition-all duration-300 ${
|
||||||
openList ? "max-h-125 mt-2" : "max-h-0"
|
openList ? "max-h-125 mt-2" : "max-h-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{Object.values(extras).map((extra) => {
|
{Object.values(extras).map((extra) => {
|
||||||
const isOpen = openId === extra.ID
|
const isOpen = openId === extra.ID
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={extra.ID}
|
key={extra.ID}
|
||||||
className="bg-primary/5 rounded-md"
|
className="bg-primary/5 rounded-md"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between px-3 py-2 cursor-pointer"
|
className="flex items-center justify-between px-3 py-2 cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setOpenId(isOpen ? null : extra.ID)
|
setOpenId(isOpen ? null : extra.ID)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-[10px] uppercase font-bold bg-primary/50 px-1.5 py-0.5 rounded">
|
<span className="text-[10px] uppercase font-bold bg-primary/50 px-1.5 py-0.5 rounded">
|
||||||
{transI18n("extra")}
|
{transI18n("extra")}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium text-primary/80">
|
<span className="text-sm font-medium text-primary/80">
|
||||||
{getLocaleName(locale, extra.Name)}
|
{getLocaleName(locale, extra.Name)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={`transition-transform ${
|
className={`transition-transform ${
|
||||||
isOpen ? "rotate-90" : ""
|
isOpen ? "rotate-90" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
▶
|
▶
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`overflow-hidden transition-all duration-300 ${
|
className={`overflow-hidden transition-all duration-300 ${
|
||||||
isOpen ? "max-h-40 px-3 pb-2" : "max-h-0"
|
isOpen ? "max-h-40 px-3 pb-2" : "max-h-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="text-sm opacity-90"
|
className="text-sm opacity-90"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, extra.Desc),
|
getLocaleName(locale, extra.Desc),
|
||||||
extra.Param
|
extra.Param
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
|||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="footer footer-horizontal footer-center bg-base-200 text-base-content rounded p-10">
|
<footer className="footer footer-horizontal footer-center bg-base-200 text-base-content rounded p-10">
|
||||||
<aside>
|
<aside>
|
||||||
<p>Copyright © {new Date().getFullYear()} - Firefly Shelter (Powered by Nextjs & DaisyUi)</p>
|
<p>Copyright © {new Date().getFullYear()} - Firefly Shelter (Powered by Nextjs & DaisyUi)</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+609
-609
File diff suppressed because it is too large
Load Diff
+265
-265
@@ -1,74 +1,74 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import useCopyProfileStore from "@/stores/copyProfile";
|
import useCopyProfileStore from "@/stores/copyProfile";
|
||||||
import ProfileCard from "../card/profileCard";
|
import ProfileCard from "../card/profileCard";
|
||||||
import { AvatarProfileCardType, AvatarProfileStore } from "@/types";
|
import { AvatarProfileCardType, AvatarProfileStore } from "@/types";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { getNameChar, calcRarity } from "@/helper";
|
import { getNameChar, calcRarity } from "@/helper";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import SelectCustomImage from "../select/customSelectImage";
|
import SelectCustomImage from "../select/customSelectImage";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function CopyImport() {
|
export default function CopyImport() {
|
||||||
const { avatars, setAvatar } = useUserDataStore();
|
const { avatars, setAvatar } = useUserDataStore();
|
||||||
const { avatarSelected } = useCurrentDataStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
selectedProfiles,
|
selectedProfiles,
|
||||||
avatarCopySelected,
|
avatarCopySelected,
|
||||||
setSelectedProfiles,
|
setSelectedProfiles,
|
||||||
setAvatarCopySelected,
|
setAvatarCopySelected,
|
||||||
listElement,
|
listElement,
|
||||||
listPath,
|
listPath,
|
||||||
listRank,
|
listRank,
|
||||||
setListElement,
|
setListElement,
|
||||||
setListPath,
|
setListPath,
|
||||||
setListRank
|
setListRank
|
||||||
} = useCopyProfileStore()
|
} = useCopyProfileStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const [message, setMessage] = useState({
|
const [message, setMessage] = useState({
|
||||||
type: "",
|
type: "",
|
||||||
text: ""
|
text: ""
|
||||||
})
|
})
|
||||||
|
|
||||||
const listAvatar = useMemo(() => {
|
const listAvatar = useMemo(() => {
|
||||||
if (!mapAvatar || !locale || !transI18n) return []
|
if (!mapAvatar || !locale || !transI18n) return []
|
||||||
let list = Object.values(mapAvatar);
|
let list = Object.values(mapAvatar);
|
||||||
const allElementFalse = !Object.values(listElement).some(v => v)
|
const allElementFalse = !Object.values(listElement).some(v => v)
|
||||||
const allPathFalse = !Object.values(listPath).some(v => v)
|
const allPathFalse = !Object.values(listPath).some(v => v)
|
||||||
const allRarityFalse = !Object.values(listRank).some(v => v)
|
const allRarityFalse = !Object.values(listRank).some(v => v)
|
||||||
list = list.filter(item => (allElementFalse || listElement[item.DamageType]) && (allPathFalse || listPath[item.BaseType]) && (allRarityFalse || listRank[calcRarity(item.Rarity)]))
|
list = list.filter(item => (allElementFalse || listElement[item.DamageType]) && (allPathFalse || listPath[item.BaseType]) && (allRarityFalse || listRank[calcRarity(item.Rarity)]))
|
||||||
list.sort((a, b) => {
|
list.sort((a, b) => {
|
||||||
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
if (r !== 0) return r
|
if (r !== 0) return r
|
||||||
return a.ID - b.ID
|
return a.ID - b.ID
|
||||||
})
|
})
|
||||||
return list
|
return list
|
||||||
}, [mapAvatar, listElement, listPath, listRank, locale, transI18n])
|
}, [mapAvatar, listElement, listPath, listRank, locale, transI18n])
|
||||||
|
|
||||||
|
|
||||||
const handleProfileToggle = (profile: AvatarProfileCardType) => {
|
const handleProfileToggle = (profile: AvatarProfileCardType) => {
|
||||||
if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) {
|
if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) {
|
||||||
setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key));
|
setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSelectedProfiles([...selectedProfiles, profile]);
|
setSelectedProfiles([...selectedProfiles, profile]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
setSelectedProfiles([]);
|
setSelectedProfiles([]);
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: transI18n("clearAll")
|
text: transI18n("clearAll")
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAll = () => {
|
const selectAll = () => {
|
||||||
if (avatarCopySelected) {
|
if (avatarCopySelected) {
|
||||||
const sourceAvatar = avatars[avatarCopySelected.ID.toString()]
|
const sourceAvatar = avatars[avatarCopySelected.ID.toString()]
|
||||||
@@ -79,32 +79,32 @@ export default function CopyImport() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
key: index,
|
key: index,
|
||||||
...profile,
|
...profile,
|
||||||
} as AvatarProfileCardType
|
} as AvatarProfileCardType
|
||||||
}).filter((profile) => profile !== null));
|
}).filter((profile) => profile !== null));
|
||||||
}
|
}
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: transI18n("selectAll")
|
text: transI18n("selectAll")
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
if (selectedProfiles.length === 0) {
|
if (selectedProfiles.length === 0) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: transI18n("pleaseSelectAtLeastOneProfile")
|
text: transI18n("pleaseSelectAtLeastOneProfile")
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!avatarCopySelected) {
|
if (!avatarCopySelected) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: transI18n("noAvatarToCopySelected")
|
text: transI18n("noAvatarToCopySelected")
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!avatarSelected) {
|
if (!avatarSelected) {
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -130,180 +130,180 @@ export default function CopyImport() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...profile,
|
...profile,
|
||||||
profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`,
|
profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`,
|
||||||
} as AvatarProfileStore
|
} as AvatarProfileStore
|
||||||
}).filter((profile) => profile !== null);
|
}).filter((profile) => profile !== null);
|
||||||
|
|
||||||
const newAvatar = {
|
const newAvatar = {
|
||||||
...targetAvatar,
|
...targetAvatar,
|
||||||
profileList: targetAvatar.profileList.concat(newListProfile),
|
profileList: targetAvatar.profileList.concat(newListProfile),
|
||||||
profileSelect: targetAvatar.profileList.length + newListProfile.length - 1,
|
profileSelect: targetAvatar.profileList.length + newListProfile.length - 1,
|
||||||
}
|
}
|
||||||
setAvatar(newAvatar);
|
setAvatar(newAvatar);
|
||||||
setSelectedProfiles([]);
|
setSelectedProfiles([]);
|
||||||
setMessage({
|
setMessage({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: transI18n("copied")
|
text: transI18n("copied")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg px-2 min-h-[60vh]">
|
<div className="rounded-lg px-2 min-h-[60vh]">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
|
|
||||||
<div className="mt-2 w-full">
|
<div className="mt-2 w-full">
|
||||||
|
|
||||||
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
||||||
<div>{transI18n("filter")}</div>
|
<div>{transI18n("filter")}</div>
|
||||||
<div className="flex flex-wrap justify-start gap-5 md:gap-10 mt-2 w-full">
|
<div className="flex flex-wrap justify-start gap-5 md:gap-10 mt-2 w-full">
|
||||||
{/* Path */}
|
{/* Path */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-start items-center">
|
<div className="flex flex-wrap gap-2 justify-start items-center">
|
||||||
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListPath({ ...listPath, [key]: !listPath[key] })
|
setListPath({ ...listPath, [key]: !listPath[key] })
|
||||||
}}
|
}}
|
||||||
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listPath[key] ? "#374151" : "#6B7280"
|
backgroundColor: listPath[key] ? "#374151" : "#6B7280"
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${value.Icon}`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-8 w-8 object-contain rounded-md"
|
className="h-8 w-8 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Element */}
|
{/* Element */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-start items-center">
|
<div className="flex flex-wrap gap-2 justify-start items-center">
|
||||||
{Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => (
|
{Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListElement({ ...listElement, [key]: !listElement[key] })
|
setListElement({ ...listElement, [key]: !listElement[key] })
|
||||||
}}
|
}}
|
||||||
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listElement[key] ? "#374151" : "#6B7280"
|
backgroundColor: listElement[key] ? "#374151" : "#6B7280"
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${value.Icon}`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rank */}
|
{/* Rank */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-end items-center">
|
<div className="flex flex-wrap gap-2 justify-end items-center">
|
||||||
{Object.entries(listRank).map(([key], index) => (
|
{Object.entries(listRank).map(([key], index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListRank({ ...listRank, [key]: !listRank[key] })
|
setListRank({ ...listRank, [key]: !listRank[key] })
|
||||||
}}
|
}}
|
||||||
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listRank[key] ? "#374151" : "#6B7280"
|
backgroundColor: listRank[key] ? "#374151" : "#6B7280"
|
||||||
}}>
|
}}>
|
||||||
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">{key}*</div>
|
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">{key}*</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-2">
|
<div className="grid grid-cols-1 gap-2">
|
||||||
|
|
||||||
{listAvatar.length > 0 && (
|
{listAvatar.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div>{transI18n("characterName")}</div>
|
<div>{transI18n("characterName")}</div>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={listAvatar.map((avatar) => ({
|
customSet={listAvatar.map((avatar) => ({
|
||||||
value: avatar.ID.toString(),
|
value: avatar.ID.toString(),
|
||||||
label: getNameChar(locale, transI18n, avatar),
|
label: getNameChar(locale, transI18n, avatar),
|
||||||
imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}`
|
imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={avatarCopySelected?.ID.toString() || ""}
|
selectedCustomSet={avatarCopySelected?.ID.toString() || ""}
|
||||||
placeholder="Character Select"
|
placeholder="Character Select"
|
||||||
setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)}
|
setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Selection Info */}
|
{/* Selection Info */}
|
||||||
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
||||||
<span className="text-blue-400 font-medium">
|
<span className="text-blue-400 font-medium">
|
||||||
{transI18n("selectedProfiles")}: {selectedProfiles.length}
|
{transI18n("selectedProfiles")}: {selectedProfiles.length}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={clearSelection}
|
onClick={clearSelection}
|
||||||
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{transI18n("clearAll")}
|
{transI18n("clearAll")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={selectAll}
|
onClick={selectAll}
|
||||||
className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
||||||
{transI18n("selectAll")}
|
{transI18n("selectAll")}
|
||||||
</button>
|
</button>
|
||||||
{selectedProfiles.length > 0 && (
|
{selectedProfiles.length > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
className="text-success/75 hover:text-success text-sm font-medium px-3 py-1 border border-success/75 rounded hover:bg-success/10 transition-colors cursor-pointer">
|
className="text-success/75 hover:text-success text-sm font-medium px-3 py-1 border border-success/75 rounded hover:bg-success/10 transition-colors cursor-pointer">
|
||||||
{transI18n("copy")}
|
{transI18n("copy")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{message.type && message.text && (
|
{message.type && message.text && (
|
||||||
<div className={(message.type == "error" ? "text-error" : "text-success") + " text-lg mt-2"} >{message.type == "error" ? "😭" : "🎉"} {message.text}</div>
|
<div className={(message.type == "error" ? "text-error" : "text-success") + " text-lg mt-2"} >{message.type == "error" ? "😭" : "🎉"} {message.text}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Character Grid */}
|
{/* Character Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{avatarCopySelected && avatars[avatarCopySelected?.ID.toString()]?.profileList.map((profile, index) => {
|
{avatarCopySelected && avatars[avatarCopySelected?.ID.toString()]?.profileList.map((profile, index) => {
|
||||||
if (!profile.lightcone?.item_id && Object.keys(profile.relics ?? {}).length == 0) {
|
if (!profile.lightcone?.item_id && Object.keys(profile.relics ?? {}).length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ProfileCard
|
<ProfileCard
|
||||||
key={index}
|
key={index}
|
||||||
profile={{ ...profile, key: index }}
|
profile={{ ...profile, key: index }}
|
||||||
selectedProfile={selectedProfiles}
|
selectedProfile={selectedProfiles}
|
||||||
onProfileToggle={handleProfileToggle}
|
onProfileToggle={handleProfileToggle}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+124
-124
@@ -1,10 +1,10 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import CharacterInfoCard from "../card/characterInfoCard";
|
import CharacterInfoCard from "../card/characterInfoCard";
|
||||||
import useEnkaStore from "@/stores/enkaStore";
|
import useEnkaStore from "@/stores/enkaStore";
|
||||||
import { SendDataThroughProxy } from "@/lib/api/api";
|
import { SendDataThroughProxy } from "@/lib/api/api";
|
||||||
import { CharacterInfoCardType, EnkaResponse } from "@/types";
|
import { CharacterInfoCardType, EnkaResponse } from "@/types";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { converterOneEnkaDataToAvatarStore } from "@/helper";
|
import { converterOneEnkaDataToAvatarStore } from "@/helper";
|
||||||
import useModelStore from "@/stores/modelStore";
|
import useModelStore from "@/stores/modelStore";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -31,24 +31,24 @@ const toCharacterInfoCard = (character: AvatarEnkaDetailInput): CharacterInfoCar
|
|||||||
relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10),
|
relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function EnkaImport() {
|
export default function EnkaImport() {
|
||||||
const {
|
const {
|
||||||
uidInput,
|
uidInput,
|
||||||
setUidInput,
|
setUidInput,
|
||||||
enkaData,
|
enkaData,
|
||||||
selectedCharacters,
|
selectedCharacters,
|
||||||
setSelectedCharacters,
|
setSelectedCharacters,
|
||||||
setEnkaData,
|
setEnkaData,
|
||||||
} = useEnkaStore();
|
} = useEnkaStore();
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { avatars, setAvatar } = useUserDataStore();
|
const { avatars, setAvatar } = useUserDataStore();
|
||||||
const { setIsOpenImport } = useModelStore()
|
const { setIsOpenImport } = useModelStore()
|
||||||
const { mapAvatar } = useDetailDataStore()
|
const { mapAvatar } = useDetailDataStore()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [Error, setError] = useState("")
|
const [Error, setError] = useState("")
|
||||||
const validCharacters = enkaData?.detailInfo.avatarDetailList.filter((character) => mapAvatar?.[character.avatarId]) ?? []
|
const validCharacters = enkaData?.detailInfo.avatarDetailList.filter((character) => mapAvatar?.[character.avatarId]) ?? []
|
||||||
|
|
||||||
const handlerFetchData = async () => {
|
const handlerFetchData = async () => {
|
||||||
if (!uidInput) {
|
if (!uidInput) {
|
||||||
setError(transI18n("pleaseEnterUid"))
|
setError(transI18n("pleaseEnterUid"))
|
||||||
@@ -77,36 +77,36 @@ export default function EnkaImport() {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCharacterToggle = (character: CharacterInfoCardType) => {
|
const handleCharacterToggle = (character: CharacterInfoCardType) => {
|
||||||
if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) {
|
if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) {
|
||||||
setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key));
|
setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSelectedCharacters([...selectedCharacters, character]);
|
setSelectedCharacters([...selectedCharacters, character]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
setSelectedCharacters([]);
|
setSelectedCharacters([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAll = () => {
|
const selectAll = () => {
|
||||||
if (enkaData) {
|
if (enkaData) {
|
||||||
setSelectedCharacters(validCharacters.map(toCharacterInfoCard));
|
setSelectedCharacters(validCharacters.map(toCharacterInfoCard));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = () => {
|
const handleImport = () => {
|
||||||
if (selectedCharacters.length === 0) {
|
if (selectedCharacters.length === 0) {
|
||||||
setError(transI18n("pleaseSelectAtLeastOneCharacter"));
|
setError(transI18n("pleaseSelectAtLeastOneCharacter"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!enkaData) {
|
if (!enkaData) {
|
||||||
setError(transI18n("noDataToImport"));
|
setError(transI18n("noDataToImport"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setError("");
|
setError("");
|
||||||
const listAvatars = { ...avatars }
|
const listAvatars = { ...avatars }
|
||||||
const parsed = enkaResponseSchema.safeParse(enkaData)
|
const parsed = enkaResponseSchema.safeParse(enkaData)
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
setError(transI18n("failedToFetchEnkaData"));
|
setError(transI18n("failedToFetchEnkaData"));
|
||||||
@@ -125,80 +125,80 @@ export default function EnkaImport() {
|
|||||||
acc[skill.pointId] = skill.level;
|
acc[skill.pointId] = skill.level;
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>),
|
}, {} as Record<string, number>),
|
||||||
}
|
}
|
||||||
const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length)
|
const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length)
|
||||||
if (newProfile) {
|
if (newProfile) {
|
||||||
newAvatar.profileList.push(newProfile)
|
newAvatar.profileList.push(newProfile)
|
||||||
newAvatar.profileSelect = newAvatar.profileList.length - 1
|
newAvatar.profileSelect = newAvatar.profileList.length - 1
|
||||||
}
|
}
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
setIsOpenImport(false)
|
setIsOpenImport(false)
|
||||||
toast.success(transI18n("importEnkaDataSuccess"))
|
toast.success(transI18n("importEnkaDataSuccess"))
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-slate-900 p-6">
|
<div className="bg-slate-900 p-6">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-white text-2xl font-bold mb-6">HSR UID</h1>
|
<h1 className="text-white text-2xl font-bold mb-6">HSR UID</h1>
|
||||||
|
|
||||||
<div className="flex gap-4 items-center mb-6">
|
<div className="flex gap-4 items-center mb-6">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={uidInput}
|
value={uidInput}
|
||||||
onChange={(e) => setUidInput(e.target.value)}
|
onChange={(e) => setUidInput(e.target.value)}
|
||||||
className="bg-slate-800 text-white px-4 py-3 rounded-lg border border-slate-700 flex-1 max-w-md focus:outline-none focus:border-blue-500"
|
className="bg-slate-800 text-white px-4 py-3 rounded-lg border border-slate-700 flex-1 max-w-md focus:outline-none focus:border-blue-500"
|
||||||
placeholder="Enter UID"
|
placeholder="Enter UID"
|
||||||
/>
|
/>
|
||||||
<button onClick={handlerFetchData} className="btn btn-success rounded-lg transition-colors">
|
<button onClick={handlerFetchData} className="btn btn-success rounded-lg transition-colors">
|
||||||
{transI18n("getData")}
|
{transI18n("getData")}
|
||||||
</button>
|
</button>
|
||||||
{selectedCharacters.length > 0 && (
|
{selectedCharacters.length > 0 && (
|
||||||
<button onClick={handleImport} className="btn btn-primary rounded-lg transition-colors">
|
<button onClick={handleImport} className="btn btn-primary rounded-lg transition-colors">
|
||||||
{transI18n("import")}
|
{transI18n("import")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{Error && (
|
{Error && (
|
||||||
<div className="text-error text-base mt-2">😭 {Error}</div>
|
<div className="text-error text-base mt-2">😭 {Error}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Player Info */}
|
{/* Player Info */}
|
||||||
{enkaData && (
|
{enkaData && (
|
||||||
<div className="text-white space-y-2">
|
<div className="text-white space-y-2">
|
||||||
<div className="text-lg">Name: {enkaData.detailInfo.nickname}</div>
|
<div className="text-lg">Name: {enkaData.detailInfo.nickname}</div>
|
||||||
<div className="text-lg">UID: {enkaData.detailInfo.uid}</div>
|
<div className="text-lg">UID: {enkaData.detailInfo.uid}</div>
|
||||||
<div className="text-lg">Level: {enkaData.detailInfo.level}</div>
|
<div className="text-lg">Level: {enkaData.detailInfo.level}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Selection Info */}
|
{/* Selection Info */}
|
||||||
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
||||||
<span className="text-blue-400 font-medium">
|
<span className="text-blue-400 font-medium">
|
||||||
{transI18n("selectedCharacters")}: {selectedCharacters.length}
|
{transI18n("selectedCharacters")}: {selectedCharacters.length}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={clearSelection}
|
onClick={clearSelection}
|
||||||
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{transI18n("clearAll")}
|
{transI18n("clearAll")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={selectAll} className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
<button onClick={selectAll} className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
||||||
{transI18n("selectAll")}
|
{transI18n("selectAll")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="flex justify-center items-center h-64">
|
<div className="flex justify-center items-center h-64">
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white"></div>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Character Grid */}
|
{/* Character Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{validCharacters.map((character) => (
|
{validCharacters.map((character) => (
|
||||||
<CharacterInfoCard
|
<CharacterInfoCard
|
||||||
key={character.avatarId}
|
key={character.avatarId}
|
||||||
@@ -206,9 +206,9 @@ export default function EnkaImport() {
|
|||||||
selectedCharacters={selectedCharacters}
|
selectedCharacters={selectedCharacters}
|
||||||
onCharacterToggle={handleCharacterToggle}
|
onCharacterToggle={handleCharacterToggle}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+134
-134
@@ -1,15 +1,15 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import useFreeSRStore from "@/stores/freesrStore";
|
import useFreeSRStore from "@/stores/freesrStore";
|
||||||
import useModelStore from "@/stores/modelStore";
|
import useModelStore from "@/stores/modelStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { CharacterInfoCardType } from "@/types";
|
import { CharacterInfoCardType } from "@/types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import CharacterInfoCard from "../card/characterInfoCard";
|
import CharacterInfoCard from "../card/characterInfoCard";
|
||||||
import { freeSrJsonSchema } from "@/zod";
|
import { freeSrJsonSchema } from "@/zod";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { converterOneFreeSRDataToAvatarStore } from "@/helper";
|
import { converterOneFreeSRDataToAvatarStore } from "@/helper";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -36,55 +36,55 @@ const toCharacterInfoCard = (data: FreeSRJsonInput, character: FreeSRJsonInput["
|
|||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FreeSRImport() {
|
export default function FreeSRImport() {
|
||||||
const { avatars, setAvatar } = useUserDataStore();
|
const { avatars, setAvatar } = useUserDataStore();
|
||||||
const { mapAvatar } = useDetailDataStore();
|
const { mapAvatar } = useDetailDataStore();
|
||||||
const { setIsOpenImport } = useModelStore()
|
const { setIsOpenImport } = useModelStore()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [Error, setError] = useState("")
|
const [Error, setError] = useState("")
|
||||||
const { freeSRData, setFreeSRData, selectedCharacters, setSelectedCharacters } = useFreeSRStore()
|
const { freeSRData, setFreeSRData, selectedCharacters, setSelectedCharacters } = useFreeSRStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const parsedFreeSRData = freeSrJsonSchema.safeParse(freeSRData)
|
const parsedFreeSRData = freeSrJsonSchema.safeParse(freeSRData)
|
||||||
const validFreeSRData = parsedFreeSRData.success ? parsedFreeSRData.data : null
|
const validFreeSRData = parsedFreeSRData.success ? parsedFreeSRData.data : null
|
||||||
|
|
||||||
const handleCharacterToggle = (character: CharacterInfoCardType) => {
|
const handleCharacterToggle = (character: CharacterInfoCardType) => {
|
||||||
if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) {
|
if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) {
|
||||||
setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key));
|
setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSelectedCharacters([...selectedCharacters, character]);
|
setSelectedCharacters([...selectedCharacters, character]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
setSelectedCharacters([]);
|
setSelectedCharacters([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAll = () => {
|
const selectAll = () => {
|
||||||
const parsed = freeSrJsonSchema.safeParse(freeSRData)
|
const parsed = freeSrJsonSchema.safeParse(freeSRData)
|
||||||
if (parsed.success) {
|
if (parsed.success) {
|
||||||
setSelectedCharacters(Object.values(parsed.data.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => toCharacterInfoCard(parsed.data, character)));
|
setSelectedCharacters(Object.values(parsed.data.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => toCharacterInfoCard(parsed.data, character)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlerReadFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handlerReadFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
setSelectedCharacters([])
|
setSelectedCharacters([])
|
||||||
setFreeSRData(null)
|
setFreeSRData(null)
|
||||||
setError(transI18n("pleaseSelectAFile"))
|
setError(transI18n("pleaseSelectAFile"))
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!file.name.endsWith(".json") || file.type !== "application/json") {
|
if (!file.name.endsWith(".json") || file.type !== "application/json") {
|
||||||
setSelectedCharacters([])
|
setSelectedCharacters([])
|
||||||
setFreeSRData(null)
|
setFreeSRData(null)
|
||||||
setError(transI18n("fileMustBeAValidJsonFile"))
|
setError(transI18n("fileMustBeAValidJsonFile"))
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -121,18 +121,18 @@ export default function FreeSRImport() {
|
|||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = () => {
|
const handleImport = () => {
|
||||||
if (selectedCharacters.length === 0) {
|
if (selectedCharacters.length === 0) {
|
||||||
setError(transI18n("pleaseSelectAtLeastOneCharacter"));
|
setError(transI18n("pleaseSelectAtLeastOneCharacter"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!freeSRData) {
|
if (!freeSRData) {
|
||||||
setError(transI18n("noDataToImport"));
|
setError(transI18n("noDataToImport"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
const parsed = freeSrJsonSchema.safeParse(freeSRData)
|
const parsed = freeSrJsonSchema.safeParse(freeSRData)
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
setError(transI18n("fileMustBeAValidJsonFile"));
|
setError(transI18n("fileMustBeAValidJsonFile"));
|
||||||
@@ -144,73 +144,73 @@ export default function FreeSRImport() {
|
|||||||
filterData.forEach((character) => {
|
filterData.forEach((character) => {
|
||||||
const newAvatar = { ...listAvatars[character.avatar_id] }
|
const newAvatar = { ...listAvatars[character.avatar_id] }
|
||||||
if (Object.keys(newAvatar).length !== 0) {
|
if (Object.keys(newAvatar).length !== 0) {
|
||||||
newAvatar.level = character.level
|
newAvatar.level = character.level
|
||||||
newAvatar.promotion = character.promotion
|
newAvatar.promotion = character.promotion
|
||||||
newAvatar.data = {
|
newAvatar.data = {
|
||||||
rank: character.data.rank ?? 0,
|
rank: character.data.rank ?? 0,
|
||||||
skills: character.data.skills
|
skills: character.data.skills
|
||||||
}
|
}
|
||||||
const newProfile = converterOneFreeSRDataToAvatarStore(parsed.data, newAvatar.profileList.length, character.avatar_id)
|
const newProfile = converterOneFreeSRDataToAvatarStore(parsed.data, newAvatar.profileList.length, character.avatar_id)
|
||||||
if (newProfile) {
|
if (newProfile) {
|
||||||
newAvatar.profileList.push(newProfile)
|
newAvatar.profileList.push(newProfile)
|
||||||
newAvatar.profileSelect = newAvatar.profileList.length - 1
|
newAvatar.profileSelect = newAvatar.profileList.length - 1
|
||||||
}
|
}
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
setIsOpenImport(false)
|
setIsOpenImport(false)
|
||||||
toast.success(transI18n("importFreeSRDataSuccess"))
|
toast.success(transI18n("importFreeSRDataSuccess"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-slate-900 p-6">
|
<div className="bg-slate-900 p-6">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-white text-2xl font-bold mb-6">{transI18n("freeSRImport")}</h1>
|
<h1 className="text-white text-2xl font-bold mb-6">{transI18n("freeSRImport")}</h1>
|
||||||
|
|
||||||
<div className="flex gap-4 items-center mb-6">
|
<div className="flex gap-4 items-center mb-6">
|
||||||
<fieldset className="fieldset">
|
<fieldset className="fieldset">
|
||||||
<legend className="fieldset-legend">{transI18n("pickAFile")}</legend>
|
<legend className="fieldset-legend">{transI18n("pickAFile")}</legend>
|
||||||
<input type="file" accept=".json" className="file-input file-input-info" onChange={handlerReadFile} />
|
<input type="file" accept=".json" className="file-input file-input-info" onChange={handlerReadFile} />
|
||||||
<label className="label">{transI18n("onlySupportFreeSRJsonFile")}</label>
|
<label className="label">{transI18n("onlySupportFreeSRJsonFile")}</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
{selectedCharacters.length > 0 && (
|
{selectedCharacters.length > 0 && (
|
||||||
<button onClick={handleImport} className="btn btn-primary rounded-lg transition-colors">
|
<button onClick={handleImport} className="btn btn-primary rounded-lg transition-colors">
|
||||||
{transI18n("import")}
|
{transI18n("import")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{Error && (
|
{Error && (
|
||||||
<div className="text-error text-base mt-2">😭 {Error}</div>
|
<div className="text-error text-base mt-2">😭 {Error}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* Selection Info */}
|
{/* Selection Info */}
|
||||||
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
<div className="mt-6 flex items-center gap-4 bg-slate-800/50 p-4 rounded-lg">
|
||||||
<span className="text-blue-400 font-medium">
|
<span className="text-blue-400 font-medium">
|
||||||
{transI18n("selectedCharacters")}: {selectedCharacters.length}
|
{transI18n("selectedCharacters")}: {selectedCharacters.length}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={clearSelection}
|
onClick={clearSelection}
|
||||||
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
className="text-error/75 hover:text-error text-sm font-medium px-3 py-1 border border-error/75 rounded hover:bg-error/10 transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{transI18n("clearAll")}
|
{transI18n("clearAll")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={selectAll} className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
<button onClick={selectAll} className="text-primary/75 hover:text-primary text-sm font-medium px-3 py-1 border border-primary/75 rounded hover:bg-primary/10 transition-colors cursor-pointer">
|
||||||
{transI18n("selectAll")}
|
{transI18n("selectAll")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="flex justify-center items-center h-64">
|
<div className="flex justify-center items-center h-64">
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white"></div>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Character Grid */}
|
{/* Character Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{validFreeSRData && Object.values(validFreeSRData.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => {
|
{validFreeSRData && Object.values(validFreeSRData.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => {
|
||||||
return (
|
return (
|
||||||
<CharacterInfoCard
|
<CharacterInfoCard
|
||||||
@@ -219,10 +219,10 @@ export default function FreeSRImport() {
|
|||||||
selectedCharacters={selectedCharacters}
|
selectedCharacters={selectedCharacters}
|
||||||
onCharacterToggle={handleCharacterToggle}
|
onCharacterToggle={handleCharacterToggle}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,145 +1,145 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import LightconeCard from "../card/lightconeCard";
|
import LightconeCard from "../card/lightconeCard";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useModelStore from "@/stores/modelStore";
|
import useModelStore from "@/stores/modelStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import { calcRarity, getLocaleName } from "@/helper";
|
import { calcRarity, getLocaleName } from "@/helper";
|
||||||
|
|
||||||
export default function LightconeBar() {
|
export default function LightconeBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
avatarSelected,
|
avatarSelected,
|
||||||
mapLightconePathActive,
|
mapLightconePathActive,
|
||||||
mapLightconeRankActive,
|
mapLightconeRankActive,
|
||||||
setMapLightconePathActive,
|
setMapLightconePathActive,
|
||||||
setMapLightconeRankActive,
|
setMapLightconeRankActive,
|
||||||
lightconeSearch,
|
lightconeSearch,
|
||||||
setLightconeSearch
|
setLightconeSearch
|
||||||
} = useCurrentDataStore()
|
} = useCurrentDataStore()
|
||||||
const { setAvatar, avatars } = useUserDataStore()
|
const { setAvatar, avatars } = useUserDataStore()
|
||||||
const { setIsOpenLightcone } = useModelStore()
|
const { setIsOpenLightcone } = useModelStore()
|
||||||
const { mapLightCone, baseType } = useDetailDataStore()
|
const { mapLightCone, baseType } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const listLightcone = useMemo(() => {
|
const listLightcone = useMemo(() => {
|
||||||
if (!mapLightCone || !locale) return []
|
if (!mapLightCone || !locale) return []
|
||||||
|
|
||||||
let list = Object.values(mapLightCone)
|
let list = Object.values(mapLightCone)
|
||||||
|
|
||||||
if (lightconeSearch) {
|
if (lightconeSearch) {
|
||||||
list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase()))
|
list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v)
|
const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v)
|
||||||
const allPathFalse = !Object.values(mapLightconePathActive).some(v => v)
|
const allPathFalse = !Object.values(mapLightconePathActive).some(v => v)
|
||||||
|
|
||||||
list = list.filter(item =>
|
list = list.filter(item =>
|
||||||
(allRankFalse || mapLightconeRankActive[item.Rarity]) &&
|
(allRankFalse || mapLightconeRankActive[item.Rarity]) &&
|
||||||
(allPathFalse || mapLightconePathActive[item.BaseType])
|
(allPathFalse || mapLightconePathActive[item.BaseType])
|
||||||
)
|
)
|
||||||
|
|
||||||
list.sort((a, b) => {
|
list.sort((a, b) => {
|
||||||
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
if (r !== 0) return r
|
if (r !== 0) return r
|
||||||
return b.ID - a.ID
|
return b.ID - a.ID
|
||||||
})
|
})
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale])
|
}, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
||||||
{transI18n("lightConeSetting")}
|
{transI18n("lightConeSetting")}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 w-full">
|
<div className="mt-2 w-full">
|
||||||
<div className="flex items-start flex-col gap-2">
|
<div className="flex items-start flex-col gap-2">
|
||||||
<div>Search</div>
|
<div>Search</div>
|
||||||
<input
|
<input
|
||||||
value={lightconeSearch}
|
value={lightconeSearch}
|
||||||
onChange={(e) => setLightconeSearch(e.target.value)}
|
onChange={(e) => setLightconeSearch(e.target.value)}
|
||||||
type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full"
|
type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
<div className="flex items-start flex-col gap-2 mt-2 w-full">
|
||||||
<div>Filter</div>
|
<div>Filter</div>
|
||||||
<div className="flex flex-row flex-wrap justify-between mt-1 w-full">
|
<div className="flex flex-row flex-wrap justify-between mt-1 w-full">
|
||||||
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
||||||
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
{Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMapLightconePathActive({ ...mapLightconePathActive, [key]: !mapLightconePathActive[key] })
|
setMapLightconePathActive({ ...mapLightconePathActive, [key]: !mapLightconePathActive[key] })
|
||||||
}}
|
}}
|
||||||
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${value.Icon}`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-7 w-7 md:h-8 md:w-8 object-contain rounded-md"
|
className="h-7 w-7 md:h-8 md:w-8 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
||||||
{Object.keys(mapLightconeRankActive).map((key, index) => (
|
{Object.keys(mapLightconeRankActive).map((key, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMapLightconeRankActive({ ...mapLightconeRankActive, [key]: !mapLightconeRankActive[key] })
|
setMapLightconeRankActive({ ...mapLightconeRankActive, [key]: !mapLightconeRankActive[key] })
|
||||||
}}
|
}}
|
||||||
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">
|
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">
|
||||||
{key}*
|
{key}*
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 md:grid-cols-6 lg:grid-cols-8 mt-2 gap-2">
|
<div className="grid grid-cols-3 md:grid-cols-6 lg:grid-cols-8 mt-2 gap-2">
|
||||||
{listLightcone.map((item, index) => (
|
{listLightcone.map((item, index) => (
|
||||||
<div key={index} onClick={() => {
|
<div key={index} onClick={() => {
|
||||||
if (avatarSelected) {
|
if (avatarSelected) {
|
||||||
const avatar = avatars[avatarSelected?.ID?.toString()]
|
const avatar = avatars[avatarSelected?.ID?.toString()]
|
||||||
avatar.profileList[avatar.profileSelect].lightcone = {
|
avatar.profileList[avatar.profileSelect].lightcone = {
|
||||||
level: 80,
|
level: 80,
|
||||||
item_id: item.ID,
|
item_id: item.ID,
|
||||||
rank: 1,
|
rank: 1,
|
||||||
promotion: 6
|
promotion: 6
|
||||||
}
|
}
|
||||||
setAvatar({ ...avatar })
|
setAvatar({ ...avatar })
|
||||||
setIsOpenLightcone(false)
|
setIsOpenLightcone(false)
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<LightconeCard data={item} />
|
<LightconeCard data={item} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+340
-340
@@ -1,341 +1,341 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ASEvent, MonsterStore } from "@/types";
|
import { ASEvent, MonsterStore } from "@/types";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function AsBar() {
|
export default function AsBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
as_config,
|
as_config,
|
||||||
setAsConfig
|
setAsConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id)
|
return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id)
|
||||||
}, [as_config, mapAS])
|
}, [as_config, mapAS])
|
||||||
|
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapAS[as_config.event_id.toString()]
|
return mapAS[as_config.event_id.toString()]
|
||||||
}, [as_config, mapAS])
|
}, [as_config, mapAS])
|
||||||
|
|
||||||
|
|
||||||
const floorSideList = useMemo(() => {
|
const floorSideList = useMemo(() => {
|
||||||
if (!eventSelected) return [];
|
if (!eventSelected) return [];
|
||||||
|
|
||||||
const floorList = [
|
const floorList = [
|
||||||
{
|
{
|
||||||
id: "firstNode",
|
id: "firstNode",
|
||||||
name: transI18n("firstNode"),
|
name: transI18n("firstNode"),
|
||||||
wave: transI18n("firstNodeEnemies")
|
wave: transI18n("firstNodeEnemies")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "secondNode",
|
id: "secondNode",
|
||||||
name: transI18n("secondNode"),
|
name: transI18n("secondNode"),
|
||||||
wave: transI18n("secondNodeEnemies")
|
wave: transI18n("secondNodeEnemies")
|
||||||
},
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === as_config.challenge_id) {
|
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === as_config.challenge_id) {
|
||||||
floorList.push({
|
floorList.push({
|
||||||
id: "thirdNode",
|
id: "thirdNode",
|
||||||
name: transI18n("thirdNode"),
|
name: transI18n("thirdNode"),
|
||||||
wave: transI18n("thirdNodeEnemies")
|
wave: transI18n("thirdNodeEnemies")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return floorList
|
return floorList
|
||||||
}, [as_config.challenge_id, eventSelected, transI18n])
|
}, [as_config.challenge_id, eventSelected, transI18n])
|
||||||
|
|
||||||
const buffList = useMemo(() => {
|
const buffList = useMemo(() => {
|
||||||
if (!eventSelected) return [];
|
if (!eventSelected) return [];
|
||||||
|
|
||||||
if (as_config.floor_side === "firstNode") {
|
if (as_config.floor_side === "firstNode") {
|
||||||
return eventSelected?.BuffList1 ?? [];
|
return eventSelected?.BuffList1 ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (as_config.floor_side === "secondNode") {
|
if (as_config.floor_side === "secondNode") {
|
||||||
return eventSelected?.BuffList2 ?? [];
|
return eventSelected?.BuffList2 ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (as_config.floor_side === "thirdNode" && eventSelected?.BuffList3) {
|
if (as_config.floor_side === "thirdNode" && eventSelected?.BuffList3) {
|
||||||
return eventSelected?.BuffList3 ?? [];
|
return eventSelected?.BuffList3 ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}, [as_config.floor_side, eventSelected]);
|
}, [as_config.floor_side, eventSelected]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return
|
if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return
|
||||||
const newBattleConfig = structuredClone(as_config)
|
const newBattleConfig = structuredClone(as_config)
|
||||||
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
|
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (as_config.buff_id !== 0) {
|
if (as_config.buff_id !== 0) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: as_config.buff_id,
|
id: as_config.buff_id,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (challengeSelected) {
|
if (challengeSelected) {
|
||||||
challengeSelected.MazeBuff.map((item) => {
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: item.ID,
|
id: item.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = 0
|
newBattleConfig.stage_id = 0
|
||||||
|
|
||||||
let targetEventList: ASEvent[] = []
|
let targetEventList: ASEvent[] = []
|
||||||
if (as_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
if (as_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList1
|
targetEventList = challengeSelected.EventList1
|
||||||
} else if (as_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
} else if (as_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList2
|
targetEventList = challengeSelected.EventList2
|
||||||
} else if (as_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
} else if (as_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
||||||
targetEventList = eventSelected.Tierce.EventList
|
targetEventList = eventSelected.Tierce.EventList
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetEventList.length > 0) {
|
if (targetEventList.length > 0) {
|
||||||
newBattleConfig.stage_id = targetEventList[0].ID
|
newBattleConfig.stage_id = targetEventList[0].ID
|
||||||
for (const wave of targetEventList[0].MonsterList) {
|
for (const wave of targetEventList[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: value,
|
monster_id: value,
|
||||||
level: targetEventList[0].Level,
|
level: targetEventList[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setAsConfig(newBattleConfig)
|
setAsConfig(newBattleConfig)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
challengeSelected,
|
challengeSelected,
|
||||||
eventSelected,
|
eventSelected,
|
||||||
mapAS,
|
mapAS,
|
||||||
as_config.event_id,
|
as_config.event_id,
|
||||||
as_config.challenge_id,
|
as_config.challenge_id,
|
||||||
as_config.floor_side,
|
as_config.floor_side,
|
||||||
as_config.buff_id,
|
as_config.buff_id,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!mapAS) return null
|
if (!mapAS) return null
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
{/* Title Card */}
|
{/* Title Card */}
|
||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={Object.values(mapAS).sort((a, b) => b.ID - a.ID).map((as) => ({
|
customSet={Object.values(mapAS).sort((a, b) => b.ID - a.ID).map((as) => ({
|
||||||
id: as.ID.toString(),
|
id: as.ID.toString(),
|
||||||
name: getLocaleName(locale, as.Name),
|
name: getLocaleName(locale, as.Name),
|
||||||
time: `${as.BeginTime} - ${as.EndTime}`,
|
time: `${as.BeginTime} - ${as.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={as_config.event_id.toString()}
|
selectedCustomSet={as_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectASEvent")}
|
placeholder={transI18n("selectASEvent")}
|
||||||
setSelectedCustomSet={(id) => setAsConfig({
|
setSelectedCustomSet={(id) => setAsConfig({
|
||||||
...as_config,
|
...as_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0,
|
challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0,
|
||||||
buff_id: 0
|
buff_id: 0
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={as_config.challenge_id}
|
value={as_config.challenge_id}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setAsConfig({ ...as_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setAsConfig({ ...as_config, challenge_id: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{eventSelected?.Level.map((as) => (
|
{eventSelected?.Level.map((as) => (
|
||||||
<option key={as.ID} value={as.ID}>{getLocaleName(locale, as.Name)}</option>
|
<option key={as.ID} value={as.ID}>{getLocaleName(locale, as.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={as_config.floor_side}
|
value={as_config.floor_side}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setAsConfig({ ...as_config, floor_side: e.target.value })}
|
onChange={(e) => setAsConfig({ ...as_config, floor_side: e.target.value })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
||||||
{floorSideList.map((side) => {
|
{floorSideList.map((side) => {
|
||||||
return <option key={side.id} value={side.id}>{side.name}</option>
|
return <option key={side.id} value={side.id}>{side.name}</option>
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="label-text font-bold text-success mb-2">StageId: {as_config?.stage_id}</div>
|
<div className="label-text font-bold text-success mb-2">StageId: {as_config?.stage_id}</div>
|
||||||
{eventSelected && (
|
{eventSelected && (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={buffList.map((buff) => ({
|
customSet={buffList.map((buff) => ({
|
||||||
id: buff.ID?.toString() || "",
|
id: buff.ID?.toString() || "",
|
||||||
name: getLocaleName(locale, buff?.Name) || "",
|
name: getLocaleName(locale, buff?.Name) || "",
|
||||||
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={as_config?.buff_id?.toString()}
|
selectedCustomSet={as_config?.buff_id?.toString()}
|
||||||
placeholder={transI18n("selectBuff")}
|
placeholder={transI18n("selectBuff")}
|
||||||
setSelectedCustomSet={(id) => setAsConfig({ ...as_config, buff_id: Number(id) })}
|
setSelectedCustomSet={(id) => setAsConfig({ ...as_config, buff_id: Number(id) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Turbulence Buff */}
|
{/* Turbulence Buff */}
|
||||||
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
||||||
{challengeSelected ? (
|
{challengeSelected ? (
|
||||||
challengeSelected.MazeBuff.map((buff, i) => (
|
challengeSelected.MazeBuff.map((buff, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, buff?.Desc) || "",
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
buff?.Param || []
|
buff?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enemy Waves */}
|
{/* Enemy Waves */}
|
||||||
{(as_config?.challenge_id ?? 0) !== 0 && (
|
{(as_config?.challenge_id ?? 0) !== 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{floorSideList.map((side, i) => {
|
{floorSideList.map((side, i) => {
|
||||||
const eventList = side.id === "firstNode"
|
const eventList = side.id === "firstNode"
|
||||||
? challengeSelected?.EventList1
|
? challengeSelected?.EventList1
|
||||||
: side.id === "secondNode"
|
: side.id === "secondNode"
|
||||||
? challengeSelected?.EventList2
|
? challengeSelected?.EventList2
|
||||||
: side.id === "thirdNode"
|
: side.id === "thirdNode"
|
||||||
? eventSelected?.Tierce?.EventList
|
? eventSelected?.Tierce?.EventList
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (!eventList || eventList.length === 0) return null;
|
if (!eventList || eventList.length === 0) return null;
|
||||||
const targetEvent = eventList[0];
|
const targetEvent = eventList[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{side.wave}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{side.wave}</h2>
|
||||||
|
|
||||||
{targetEvent?.MonsterList?.map((wave, waveIndex) => (
|
{targetEvent?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => {
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
const monsterStats = calcMonsterStats(
|
const monsterStats = calcMonsterStats(
|
||||||
mapMonster?.[waveValue.toString()],
|
mapMonster?.[waveValue.toString()],
|
||||||
targetEvent?.EliteGroup,
|
targetEvent?.EliteGroup,
|
||||||
targetEvent?.HardLevelGroup,
|
targetEvent?.HardLevelGroup,
|
||||||
targetEvent?.Level,
|
targetEvent?.Level,
|
||||||
hardLevelConfig,
|
hardLevelConfig,
|
||||||
eliteConfig
|
eliteConfig
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={enemyIndex}
|
key={enemyIndex}
|
||||||
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
>
|
>
|
||||||
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
Lv. {targetEvent.Level}
|
Lv. {targetEvent.Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={150}
|
width={150}
|
||||||
height={150}
|
height={150}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-1 pb-2 pt-2">
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
<div className="flex flex-col space-y-1.5">
|
<div className="flex flex-col space-y-1.5">
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-error">HP</span>
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-info">Speed</span>
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3 pt-2 border-t border-base-300 flex flex-col items-center">
|
<div className="mt-3 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
Weakness
|
Weakness
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
<Image
|
<Image
|
||||||
key={iconIndex}
|
key={iconIndex}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
alt={icon}
|
alt={icon}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)})}
|
)})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+533
-533
File diff suppressed because it is too large
Load Diff
@@ -1,100 +1,100 @@
|
|||||||
import MocBar from "./moc";
|
import MocBar from "./moc";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import PfBar from "./pf";
|
import PfBar from "./pf";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import AsBar from "./as";
|
import AsBar from "./as";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import CeBar from "./ce";
|
import CeBar from "./ce";
|
||||||
import PeakBar from "./peak";
|
import PeakBar from "./peak";
|
||||||
|
|
||||||
export default function MonsterBar() {
|
export default function MonsterBar() {
|
||||||
const { battle_type, setBattleType } = useUserDataStore()
|
const { battle_type, setBattleType } = useUserDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: transI18n("memoryOfChaos"), icon: 'AbyssIcon01', value: 'MOC' },
|
{ name: transI18n("memoryOfChaos"), icon: 'AbyssIcon01', value: 'MOC' },
|
||||||
{ name: transI18n("pureFiction"), icon: 'ChallengeStory', value: 'PF' },
|
{ name: transI18n("pureFiction"), icon: 'ChallengeStory', value: 'PF' },
|
||||||
{ name: transI18n("apocalypticShadow"), icon: 'ChallengeBoss', value: 'AS' },
|
{ name: transI18n("apocalypticShadow"), icon: 'ChallengeBoss', value: 'AS' },
|
||||||
{ name: transI18n("anomalyArbitration"), icon: 'ChallengePeakIcon', value: 'PEAK' },
|
{ name: transI18n("anomalyArbitration"), icon: 'ChallengePeakIcon', value: 'PEAK' },
|
||||||
{ name: transI18n("customEnemy"), icon: 'MonsterIcon', value: 'CE' },
|
{ name: transI18n("customEnemy"), icon: 'MonsterIcon', value: 'CE' },
|
||||||
{ name: transI18n("simulatedUniverse"), icon: 'SimulatedUniverse', value: 'SU' },
|
{ name: transI18n("simulatedUniverse"), icon: 'SimulatedUniverse', value: 'SU' },
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen">
|
<div className="min-h-screen">
|
||||||
{/* Header Navigation */}
|
{/* Header Navigation */}
|
||||||
<nav className="border-b border-warning/30 relative pb-2">
|
<nav className="border-b border-warning/30 relative pb-2">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
{/* Mobile Select */}
|
{/* Mobile Select */}
|
||||||
<div className="block md:hidden w-full">
|
<div className="block md:hidden w-full">
|
||||||
<select
|
<select
|
||||||
className="select select-bordered w-full"
|
className="select select-bordered w-full"
|
||||||
value={battle_type.toUpperCase()}
|
value={battle_type.toUpperCase()}
|
||||||
onChange={(e) => setBattleType(e.target.value.toUpperCase())}
|
onChange={(e) => setBattleType(e.target.value.toUpperCase())}
|
||||||
>
|
>
|
||||||
{navItems.map((item) => (
|
{navItems.map((item) => (
|
||||||
<option key={item.name} value={item.value.toUpperCase()}>
|
<option key={item.name} value={item.value.toUpperCase()}>
|
||||||
{item.name}
|
{item.name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop Tabs */}
|
{/* Desktop Tabs */}
|
||||||
<div className="hidden md:grid grid-cols-3 lg:grid-cols-6 gap-1">
|
<div className="hidden md:grid grid-cols-3 lg:grid-cols-6 gap-1">
|
||||||
{navItems.map((item) => (
|
{navItems.map((item) => (
|
||||||
<button
|
<button
|
||||||
key={item.name}
|
key={item.name}
|
||||||
onClick={() => setBattleType(item.value.toUpperCase())}
|
onClick={() => setBattleType(item.value.toUpperCase())}
|
||||||
className={`px-4 py-2 rounded-lg transition-all cursor-pointer duration-300 flex items-center space-x-2 ${battle_type.toUpperCase() === item.value.toUpperCase()
|
className={`px-4 py-2 rounded-lg transition-all cursor-pointer duration-300 flex items-center space-x-2 ${battle_type.toUpperCase() === item.value.toUpperCase()
|
||||||
? 'bg-success/30 shadow-lg'
|
? 'bg-success/30 shadow-lg'
|
||||||
: 'bg-base-200/20 hover:bg-base-200/40 '
|
: 'bg-base-200/20 hover:bg-base-200/40 '
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={
|
style={
|
||||||
battle_type.toUpperCase() === item.value.toUpperCase()
|
battle_type.toUpperCase() === item.value.toUpperCase()
|
||||||
? {
|
? {
|
||||||
filter:
|
filter:
|
||||||
'brightness(0) saturate(100%) invert(63%) sepia(78%) saturate(643%) hue-rotate(1deg) brightness(93%) contrast(89%)',
|
'brightness(0) saturate(100%) invert(63%) sepia(78%) saturate(643%) hue-rotate(1deg) brightness(93%) contrast(89%)',
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${item.icon}.webp`}
|
src={`/icon/${item.icon}.webp`}
|
||||||
alt={item.name}
|
alt={item.name}
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>{item.name}</span>
|
<span>{item.name}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{(battle_type.toUpperCase() === "DEFAULT" || battle_type.toUpperCase() === "") && (
|
{(battle_type.toUpperCase() === "DEFAULT" || battle_type.toUpperCase() === "") && (
|
||||||
<div className="container mx-auto px-4 py-8 text-center font-bold text-3xl">
|
<div className="container mx-auto px-4 py-8 text-center font-bold text-3xl">
|
||||||
{transI18n("noEventSelected")}
|
{transI18n("noEventSelected")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{battle_type.toUpperCase() === 'MOC' && <MocBar />}
|
{battle_type.toUpperCase() === 'MOC' && <MocBar />}
|
||||||
{battle_type.toUpperCase() === 'PF' && <PfBar />}
|
{battle_type.toUpperCase() === 'PF' && <PfBar />}
|
||||||
{battle_type.toUpperCase() === 'AS' && <AsBar />}
|
{battle_type.toUpperCase() === 'AS' && <AsBar />}
|
||||||
{battle_type.toUpperCase() === 'CE' && <CeBar />}
|
{battle_type.toUpperCase() === 'CE' && <CeBar />}
|
||||||
{battle_type.toUpperCase() === 'PEAK' && <PeakBar />}
|
{battle_type.toUpperCase() === 'PEAK' && <PeakBar />}
|
||||||
{battle_type.toUpperCase() === 'SU' && (
|
{battle_type.toUpperCase() === 'SU' && (
|
||||||
<div className="container mx-auto px-4 py-8 text-center font-bold text-3xl">
|
<div className="container mx-auto px-4 py-8 text-center font-bold text-3xl">
|
||||||
{transI18n("comingSoon")}
|
{transI18n("comingSoon")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+341
-341
@@ -1,342 +1,342 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { MoCEvent, MonsterStore } from "@/types";
|
import { MoCEvent, MonsterStore } from "@/types";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function MocBar() {
|
export default function MocBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
moc_config,
|
moc_config,
|
||||||
setMocConfig
|
setMocConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id)
|
return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id)
|
||||||
}, [moc_config, mapMoc])
|
}, [moc_config, mapMoc])
|
||||||
|
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapMoc[moc_config.event_id.toString()]
|
return mapMoc[moc_config.event_id.toString()]
|
||||||
}, [moc_config, mapMoc])
|
}, [moc_config, mapMoc])
|
||||||
|
|
||||||
const floorSideList = useMemo(() => {
|
const floorSideList = useMemo(() => {
|
||||||
if (!eventSelected) return [];
|
if (!eventSelected) return [];
|
||||||
|
|
||||||
const floorList = [
|
const floorList = [
|
||||||
{
|
{
|
||||||
id: "firstNode",
|
id: "firstNode",
|
||||||
name: transI18n("firstNode"),
|
name: transI18n("firstNode"),
|
||||||
wave: transI18n("firstNodeEnemies")
|
wave: transI18n("firstNodeEnemies")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "secondNode",
|
id: "secondNode",
|
||||||
name: transI18n("secondNode"),
|
name: transI18n("secondNode"),
|
||||||
wave: transI18n("secondNodeEnemies")
|
wave: transI18n("secondNodeEnemies")
|
||||||
},
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === moc_config.challenge_id) {
|
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === moc_config.challenge_id) {
|
||||||
floorList.push({
|
floorList.push({
|
||||||
id: "thirdNode",
|
id: "thirdNode",
|
||||||
name: transI18n("thirdNode"),
|
name: transI18n("thirdNode"),
|
||||||
wave: transI18n("thirdNodeEnemies")
|
wave: transI18n("thirdNodeEnemies")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return floorList
|
return floorList
|
||||||
}, [moc_config.challenge_id, eventSelected, transI18n])
|
}, [moc_config.challenge_id, eventSelected, transI18n])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return
|
if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return
|
||||||
|
|
||||||
const newBattleConfig = structuredClone(moc_config)
|
const newBattleConfig = structuredClone(moc_config)
|
||||||
newBattleConfig.cycle_count = 0
|
newBattleConfig.cycle_count = 0
|
||||||
if (moc_config.use_cycle_count) {
|
if (moc_config.use_cycle_count) {
|
||||||
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
}
|
}
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (moc_config.use_turbulence_buff && challengeSelected) {
|
if (moc_config.use_turbulence_buff && challengeSelected) {
|
||||||
challengeSelected.MazeBuff.map((item) => {
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: item.ID,
|
id: item.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = 0
|
newBattleConfig.stage_id = 0
|
||||||
|
|
||||||
let targetEventList: MoCEvent[] = []
|
let targetEventList: MoCEvent[] = []
|
||||||
if (moc_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
if (moc_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList1
|
targetEventList = challengeSelected.EventList1
|
||||||
} else if (moc_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
} else if (moc_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList2
|
targetEventList = challengeSelected.EventList2
|
||||||
} else if (moc_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
} else if (moc_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
||||||
targetEventList = eventSelected.Tierce.EventList
|
targetEventList = eventSelected.Tierce.EventList
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetEventList.length > 0) {
|
if (targetEventList.length > 0) {
|
||||||
newBattleConfig.stage_id = targetEventList[0].ID
|
newBattleConfig.stage_id = targetEventList[0].ID
|
||||||
for (const wave of targetEventList[0].MonsterList) {
|
for (const wave of targetEventList[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: value,
|
monster_id: value,
|
||||||
level: targetEventList[0].Level,
|
level: targetEventList[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setMocConfig(newBattleConfig)
|
setMocConfig(newBattleConfig)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
challengeSelected,
|
challengeSelected,
|
||||||
eventSelected,
|
eventSelected,
|
||||||
moc_config.event_id,
|
moc_config.event_id,
|
||||||
moc_config.challenge_id,
|
moc_config.challenge_id,
|
||||||
moc_config.floor_side,
|
moc_config.floor_side,
|
||||||
moc_config.use_cycle_count,
|
moc_config.use_cycle_count,
|
||||||
moc_config.use_turbulence_buff,
|
moc_config.use_turbulence_buff,
|
||||||
mapMoc,
|
mapMoc,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!mapMoc) return null
|
if (!mapMoc) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
{/* Title Card */}
|
{/* Title Card */}
|
||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={Object.values(mapMoc).sort((a, b) => b.ID - a.ID).map((moc) => ({
|
customSet={Object.values(mapMoc).sort((a, b) => b.ID - a.ID).map((moc) => ({
|
||||||
id: moc.ID.toString(),
|
id: moc.ID.toString(),
|
||||||
name: getLocaleName(locale, moc.Name),
|
name: getLocaleName(locale, moc.Name),
|
||||||
time: `${moc.BeginTime} - ${moc.EndTime}`,
|
time: `${moc.BeginTime} - ${moc.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={moc_config.event_id.toString()}
|
selectedCustomSet={moc_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectMOCEvent")}
|
placeholder={transI18n("selectMOCEvent")}
|
||||||
setSelectedCustomSet={(id) => setMocConfig({
|
setSelectedCustomSet={(id) => setMocConfig({
|
||||||
...moc_config,
|
...moc_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0,
|
challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={moc_config.challenge_id}
|
value={moc_config.challenge_id}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setMocConfig({
|
onChange={(e) => setMocConfig({
|
||||||
...moc_config,
|
...moc_config,
|
||||||
challenge_id: Number(e.target.value)
|
challenge_id: Number(e.target.value)
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>Select a Floor</option>
|
<option value={0} disabled={true}>Select a Floor</option>
|
||||||
{eventSelected?.Level?.map((moc) => (
|
{eventSelected?.Level?.map((moc) => (
|
||||||
<option key={moc.ID} value={moc.ID}>{getLocaleName(locale, moc.Name)}</option>
|
<option key={moc.ID} value={moc.ID}>{getLocaleName(locale, moc.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={moc_config.floor_side}
|
value={moc_config.floor_side}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setMocConfig({ ...moc_config, floor_side: e.target.value })}
|
onChange={(e) => setMocConfig({ ...moc_config, floor_side: e.target.value })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
||||||
{floorSideList.map((side) => (
|
{floorSideList.map((side) => (
|
||||||
<option key={side.id} value={side.id}>{side.name}</option>
|
<option key={side.id} value={side.id}>{side.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label cursor-pointer">
|
<label className="label cursor-pointer">
|
||||||
<span
|
<span
|
||||||
onClick={() => setMocConfig({ ...moc_config, use_cycle_count: !moc_config.use_cycle_count })}
|
onClick={() => setMocConfig({ ...moc_config, use_cycle_count: !moc_config.use_cycle_count })}
|
||||||
className="label-text font-bold text-success cursor-pointer"
|
className="label-text font-bold text-success cursor-pointer"
|
||||||
>
|
>
|
||||||
{transI18n("useCycleCount")} {" "}
|
{transI18n("useCycleCount")} {" "}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={moc_config.use_cycle_count}
|
checked={moc_config.use_cycle_count}
|
||||||
onChange={(e) => setMocConfig({ ...moc_config, use_cycle_count: e.target.checked })}
|
onChange={(e) => setMocConfig({ ...moc_config, use_cycle_count: e.target.checked })}
|
||||||
className="checkbox checkbox-primary"
|
className="checkbox checkbox-primary"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="label-text font-bold text-success mb-2">StageId: {moc_config?.stage_id}</div>
|
<div className="label-text font-bold text-success mb-2">StageId: {moc_config?.stage_id}</div>
|
||||||
|
|
||||||
{/* Turbulence Buff */}
|
{/* Turbulence Buff */}
|
||||||
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={moc_config.use_turbulence_buff}
|
checked={moc_config.use_turbulence_buff}
|
||||||
onChange={(e) => setMocConfig({ ...moc_config, use_turbulence_buff: e.target.checked })}
|
onChange={(e) => setMocConfig({ ...moc_config, use_turbulence_buff: e.target.checked })}
|
||||||
className="checkbox checkbox-primary"
|
className="checkbox checkbox-primary"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
onClick={() => setMocConfig({ ...moc_config, use_turbulence_buff: !moc_config.use_turbulence_buff })}
|
onClick={() => setMocConfig({ ...moc_config, use_turbulence_buff: !moc_config.use_turbulence_buff })}
|
||||||
className="font-bold text-success cursor-pointer">
|
className="font-bold text-success cursor-pointer">
|
||||||
{transI18n("useTurbulenceBuff")}
|
{transI18n("useTurbulenceBuff")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{challengeSelected ? (
|
{challengeSelected ? (
|
||||||
challengeSelected.MazeBuff.map((buff, i) => (
|
challengeSelected.MazeBuff.map((buff, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, buff?.Desc) || "",
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
buff?.Param || []
|
buff?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enemy Waves */}
|
{/* Enemy Waves */}
|
||||||
{(moc_config?.challenge_id ?? 0) !== 0 && (
|
{(moc_config?.challenge_id ?? 0) !== 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{floorSideList.map((side, i) => {
|
{floorSideList.map((side, i) => {
|
||||||
const eventList = side.id === "firstNode"
|
const eventList = side.id === "firstNode"
|
||||||
? challengeSelected?.EventList1
|
? challengeSelected?.EventList1
|
||||||
: side.id === "secondNode"
|
: side.id === "secondNode"
|
||||||
? challengeSelected?.EventList2
|
? challengeSelected?.EventList2
|
||||||
: side.id === "thirdNode"
|
: side.id === "thirdNode"
|
||||||
? eventSelected?.Tierce?.EventList
|
? eventSelected?.Tierce?.EventList
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (!eventList || eventList.length === 0) return null;
|
if (!eventList || eventList.length === 0) return null;
|
||||||
const targetEvent = eventList[0];
|
const targetEvent = eventList[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{side.wave}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{side.wave}</h2>
|
||||||
|
|
||||||
{targetEvent?.MonsterList?.map((wave, waveIndex) => (
|
{targetEvent?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => {
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
const monsterStats = calcMonsterStats(
|
const monsterStats = calcMonsterStats(
|
||||||
mapMonster?.[waveValue.toString()],
|
mapMonster?.[waveValue.toString()],
|
||||||
targetEvent?.EliteGroup,
|
targetEvent?.EliteGroup,
|
||||||
targetEvent?.HardLevelGroup,
|
targetEvent?.HardLevelGroup,
|
||||||
targetEvent?.Level,
|
targetEvent?.Level,
|
||||||
hardLevelConfig,
|
hardLevelConfig,
|
||||||
eliteConfig
|
eliteConfig
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={enemyIndex}
|
key={enemyIndex}
|
||||||
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
>
|
>
|
||||||
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
Lv. {targetEvent.Level}
|
Lv. {targetEvent.Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={150}
|
width={150}
|
||||||
height={150}
|
height={150}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-1 pb-2 pt-2">
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
<div className="flex flex-col space-y-1.5">
|
<div className="flex flex-col space-y-1.5">
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-error">HP</span>
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-info">Speed</span>
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
Weakness
|
Weakness
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
<Image
|
<Image
|
||||||
key={iconIndex}
|
key={iconIndex}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
alt={icon}
|
alt={icon}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+307
-307
@@ -1,308 +1,308 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";;
|
import useUserDataStore from "@/stores/userDataStore";;
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { MonsterStore } from "@/types";
|
import { MonsterStore } from "@/types";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function PeakBar() {
|
export default function PeakBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
peak_config,
|
peak_config,
|
||||||
setPeakConfig
|
setPeakConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore()
|
const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const listFloor = useMemo(() => {
|
const listFloor = useMemo(() => {
|
||||||
const peak = mapPeak?.[peak_config?.event_id?.toString()]
|
const peak = mapPeak?.[peak_config?.event_id?.toString()]
|
||||||
if (!peak) return []
|
if (!peak) return []
|
||||||
|
|
||||||
return [...peak.PreLevel, peak.BossLevel].filter(it => it != null)
|
return [...peak.PreLevel, peak.BossLevel].filter(it => it != null)
|
||||||
}, [peak_config, mapPeak])
|
}, [peak_config, mapPeak])
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapPeak?.[peak_config?.event_id?.toString()]
|
return mapPeak?.[peak_config?.event_id?.toString()]
|
||||||
}, [peak_config, mapPeak])
|
}, [peak_config, mapPeak])
|
||||||
|
|
||||||
const bossConfig = useMemo(() => {
|
const bossConfig = useMemo(() => {
|
||||||
return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig;
|
return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig;
|
||||||
}, [peak_config, mapPeak])
|
}, [peak_config, mapPeak])
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id))
|
const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id))
|
||||||
if (
|
if (
|
||||||
challenge
|
challenge
|
||||||
&& challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID
|
&& challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID
|
||||||
&& bossConfig
|
&& bossConfig
|
||||||
&& peak_config?.boss_mode === "Hard"
|
&& peak_config?.boss_mode === "Hard"
|
||||||
) {
|
) {
|
||||||
return { challenge: bossConfig, isBoss: true }
|
return { challenge: bossConfig, isBoss: true }
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
challenge: challenge,
|
challenge: challenge,
|
||||||
isBoss: challenge?.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID,
|
isBoss: challenge?.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID,
|
||||||
}
|
}
|
||||||
}, [peak_config, listFloor, mapPeak, bossConfig])
|
}, [peak_config, listFloor, mapPeak, bossConfig])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected.challenge) return
|
if (!challengeSelected.challenge) return
|
||||||
if (peak_config.event_id !== 0 && peak_config.challenge_id !== 0 && challengeSelected) {
|
if (peak_config.event_id !== 0 && peak_config.challenge_id !== 0 && challengeSelected) {
|
||||||
const newBattleConfig = structuredClone(peak_config)
|
const newBattleConfig = structuredClone(peak_config)
|
||||||
newBattleConfig.cycle_count = 6
|
newBattleConfig.cycle_count = 6
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
for (const value of challengeSelected?.challenge?.MazeBuff) {
|
for (const value of challengeSelected?.challenge?.MazeBuff) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: value.ID,
|
id: value.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (peak_config.buff_id !== 0 && challengeSelected.isBoss) {
|
if (peak_config.buff_id !== 0 && challengeSelected.isBoss) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: peak_config.buff_id,
|
id: peak_config.buff_id,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = challengeSelected.challenge.EventList[0].ID
|
newBattleConfig.stage_id = challengeSelected.challenge.EventList[0].ID
|
||||||
for (const wave of challengeSelected.challenge.EventList[0].MonsterList) {
|
for (const wave of challengeSelected.challenge.EventList[0].MonsterList) {
|
||||||
if (!wave) continue
|
if (!wave) continue
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
if (!value) continue
|
if (!value) continue
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: value,
|
monster_id: value,
|
||||||
level: challengeSelected.challenge.EventList[0].Level,
|
level: challengeSelected.challenge.EventList[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
|
|
||||||
setPeakConfig(newBattleConfig)
|
setPeakConfig(newBattleConfig)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
peak_config.event_id,
|
peak_config.event_id,
|
||||||
peak_config.challenge_id,
|
peak_config.challenge_id,
|
||||||
peak_config.buff_id,
|
peak_config.buff_id,
|
||||||
peak_config.boss_mode,
|
peak_config.boss_mode,
|
||||||
mapPeak,
|
mapPeak,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!mapPeak) return null
|
if (!mapPeak) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
{/* Title Card */}
|
{/* Title Card */}
|
||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={Object.values(mapPeak).sort((a, b) => b.ID - a.ID).map((peak) => ({
|
customSet={Object.values(mapPeak).sort((a, b) => b.ID - a.ID).map((peak) => ({
|
||||||
id: peak.ID.toString(),
|
id: peak.ID.toString(),
|
||||||
name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`,
|
name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={peak_config.event_id.toString()}
|
selectedCustomSet={peak_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectPEAKEvent")}
|
placeholder={transI18n("selectPEAKEvent")}
|
||||||
setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })}
|
setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<div className={
|
<div className={
|
||||||
`grid grid-cols-1
|
`grid grid-cols-1
|
||||||
${eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id ? "md:grid-cols-2" : ""}
|
${eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id ? "md:grid-cols-2" : ""}
|
||||||
gap-4 mb-2 justify-items-center items-center w-full`}
|
gap-4 mb-2 justify-items-center items-center w-full`}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 w-full">
|
<div className="flex items-center gap-2 w-full">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={peak_config.challenge_id}
|
value={peak_config.challenge_id}
|
||||||
className="select select-success w-full"
|
className="select select-success w-full"
|
||||||
onChange={(e) => setPeakConfig({ ...peak_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setPeakConfig({ ...peak_config, challenge_id: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{listFloor.map((peak) => (
|
{listFloor.map((peak) => (
|
||||||
<option key={peak.ID} value={peak.ID}>{getLocaleName(locale, peak.Name)}</option>
|
<option key={peak.ID} value={peak.ID}>{getLocaleName(locale, peak.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && (
|
{eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && (
|
||||||
<div className="flex items-center gap-2 w-full">
|
<div className="flex items-center gap-2 w-full">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("mode")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("mode")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={peak_config.boss_mode}
|
value={peak_config.boss_mode}
|
||||||
className="select select-success w-full"
|
className="select select-success w-full"
|
||||||
onChange={(e) => setPeakConfig({ ...peak_config, boss_mode: e.target.value })}
|
onChange={(e) => setPeakConfig({ ...peak_config, boss_mode: e.target.value })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
||||||
<option value="Normal">{transI18n("normalMode")}</option>
|
<option value="Normal">{transI18n("normalMode")}</option>
|
||||||
<option value="Hard">{transI18n("hardMode")}</option>
|
<option value="Hard">{transI18n("hardMode")}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="label-text font-bold text-success mb-2">StageId: {peak_config?.stage_id}</div>
|
<div className="label-text font-bold text-success mb-2">StageId: {peak_config?.stage_id}</div>
|
||||||
{
|
{
|
||||||
eventSelected
|
eventSelected
|
||||||
&& eventSelected.BossLevel?.ID === peak_config.challenge_id
|
&& eventSelected.BossLevel?.ID === peak_config.challenge_id
|
||||||
&& bossConfig
|
&& bossConfig
|
||||||
&& (
|
&& (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={
|
customSet={
|
||||||
bossConfig.BuffList.map((buff) => ({
|
bossConfig.BuffList.map((buff) => ({
|
||||||
id: buff.ID.toString(),
|
id: buff.ID.toString(),
|
||||||
name: getLocaleName(locale, buff?.Name || ""),
|
name: getLocaleName(locale, buff?.Name || ""),
|
||||||
description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []),
|
description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={peak_config?.buff_id?.toString()}
|
selectedCustomSet={peak_config?.buff_id?.toString()}
|
||||||
placeholder={transI18n("selectBuff")}
|
placeholder={transI18n("selectBuff")}
|
||||||
setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, buff_id: Number(id) })}
|
setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, buff_id: Number(id) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{/* Turbulence Buff */}
|
{/* Turbulence Buff */}
|
||||||
|
|
||||||
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">
|
<h2 className="text-2xl font-bold mb-2 text-info">
|
||||||
{transI18n("turbulenceBuff")}
|
{transI18n("turbulenceBuff")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{challengeSelected?.challenge && challengeSelected?.challenge?.MazeBuff?.length > 0 ? (
|
{challengeSelected?.challenge && challengeSelected?.challenge?.MazeBuff?.length > 0 ? (
|
||||||
challengeSelected.challenge.MazeBuff.map((subOption, index) => (
|
challengeSelected.challenge.MazeBuff.map((subOption, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">
|
<span className="label-text font-bold text-success">
|
||||||
{index + 1}. {getLocaleName(locale, subOption.Name)}
|
{index + 1}. {getLocaleName(locale, subOption.Name)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, subOption.Desc),
|
getLocaleName(locale, subOption.Desc),
|
||||||
subOption.Param || []
|
subOption.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enemy Waves */}
|
{/* Enemy Waves */}
|
||||||
|
|
||||||
{(peak_config?.challenge_id ?? 0) !== 0 && (
|
{(peak_config?.challenge_id ?? 0) !== 0 && (
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
|
||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">{getLocaleName(locale, challengeSelected?.challenge?.Name)}</h2>
|
<h2 className="text-2xl font-bold mb-2 text-info">{getLocaleName(locale, challengeSelected?.challenge?.Name)}</h2>
|
||||||
|
|
||||||
|
|
||||||
{challengeSelected?.challenge && Object.values(challengeSelected?.challenge?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => (
|
{challengeSelected?.challenge && Object.values(challengeSelected?.challenge?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
||||||
const monsterStats = calcMonsterStats(
|
const monsterStats = calcMonsterStats(
|
||||||
mapMonster?.[monsterId.toString()],
|
mapMonster?.[monsterId.toString()],
|
||||||
waveValue.EliteGroup,
|
waveValue.EliteGroup,
|
||||||
challengeSelected?.challenge?.EventList?.[0]?.HardLevelGroup || 0,
|
challengeSelected?.challenge?.EventList?.[0]?.HardLevelGroup || 0,
|
||||||
challengeSelected?.challenge?.EventList?.[0]?.Level || 0,
|
challengeSelected?.challenge?.EventList?.[0]?.Level || 0,
|
||||||
hardLevelConfig,
|
hardLevelConfig,
|
||||||
eliteConfig
|
eliteConfig
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={enemyIndex}
|
key={enemyIndex}
|
||||||
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
>
|
>
|
||||||
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
Lv. {challengeSelected?.challenge?.EventList?.[0]?.Level}
|
Lv. {challengeSelected?.challenge?.EventList?.[0]?.Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
||||||
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={150}
|
width={150}
|
||||||
height={150}
|
height={150}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-1 pb-2 pt-2">
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
<div className="flex flex-col space-y-1.5">
|
<div className="flex flex-col space-y-1.5">
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-error">HP</span>
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-info">Speed</span>
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
Weakness
|
Weakness
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
<Image
|
<Image
|
||||||
key={iconIndex}
|
key={iconIndex}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
alt={icon}
|
alt={icon}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+340
-340
@@ -1,341 +1,341 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { MonsterStore, PFEvent } from "@/types";
|
import { MonsterStore, PFEvent } from "@/types";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function PfBar() {
|
export default function PfBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
pf_config,
|
pf_config,
|
||||||
setPfConfig
|
setPfConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id)
|
return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id)
|
||||||
}, [pf_config, mapPF])
|
}, [pf_config, mapPF])
|
||||||
|
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapPF[pf_config.event_id.toString()]
|
return mapPF[pf_config.event_id.toString()]
|
||||||
}, [pf_config, mapPF])
|
}, [pf_config, mapPF])
|
||||||
|
|
||||||
const floorSideList = useMemo(() => {
|
const floorSideList = useMemo(() => {
|
||||||
if (!eventSelected) return [];
|
if (!eventSelected) return [];
|
||||||
|
|
||||||
const floorList = [
|
const floorList = [
|
||||||
{
|
{
|
||||||
id: "firstNode",
|
id: "firstNode",
|
||||||
name: transI18n("firstNode"),
|
name: transI18n("firstNode"),
|
||||||
wave: transI18n("firstNodeEnemies")
|
wave: transI18n("firstNodeEnemies")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "secondNode",
|
id: "secondNode",
|
||||||
name: transI18n("secondNode"),
|
name: transI18n("secondNode"),
|
||||||
wave: transI18n("secondNodeEnemies")
|
wave: transI18n("secondNodeEnemies")
|
||||||
},
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === pf_config.challenge_id) {
|
if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === pf_config.challenge_id) {
|
||||||
floorList.push({
|
floorList.push({
|
||||||
id: "thirdNode",
|
id: "thirdNode",
|
||||||
name: transI18n("thirdNode"),
|
name: transI18n("thirdNode"),
|
||||||
wave: transI18n("thirdNodeEnemies")
|
wave: transI18n("thirdNodeEnemies")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return floorList
|
return floorList
|
||||||
}, [pf_config.challenge_id, eventSelected, transI18n])
|
}, [pf_config.challenge_id, eventSelected, transI18n])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) {
|
if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newBattleConfig = structuredClone(pf_config)
|
const newBattleConfig = structuredClone(pf_config)
|
||||||
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (pf_config.buff_id !== 0) {
|
if (pf_config.buff_id !== 0) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: pf_config.buff_id,
|
id: pf_config.buff_id,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (challengeSelected) {
|
if (challengeSelected) {
|
||||||
challengeSelected.MazeBuff.map((item) => {
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: item.ID,
|
id: item.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = 0
|
newBattleConfig.stage_id = 0
|
||||||
|
|
||||||
let targetEventList: PFEvent[] = []
|
let targetEventList: PFEvent[] = []
|
||||||
if (pf_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
if (pf_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList1
|
targetEventList = challengeSelected.EventList1
|
||||||
} else if (pf_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
} else if (pf_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) {
|
||||||
targetEventList = challengeSelected.EventList2
|
targetEventList = challengeSelected.EventList2
|
||||||
} else if (pf_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
} else if (pf_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) {
|
||||||
targetEventList = eventSelected.Tierce.EventList
|
targetEventList = eventSelected.Tierce.EventList
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetEventList.length > 0) {
|
if (targetEventList.length > 0) {
|
||||||
newBattleConfig.stage_id = targetEventList[0].ID
|
newBattleConfig.stage_id = targetEventList[0].ID
|
||||||
for (const wave of targetEventList[0].MonsterList) {
|
for (const wave of targetEventList[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: value as number,
|
monster_id: value as number,
|
||||||
level: targetEventList[0].Level,
|
level: targetEventList[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPfConfig(newBattleConfig)
|
setPfConfig(newBattleConfig)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
challengeSelected,
|
challengeSelected,
|
||||||
eventSelected,
|
eventSelected,
|
||||||
pf_config.event_id,
|
pf_config.event_id,
|
||||||
pf_config.challenge_id,
|
pf_config.challenge_id,
|
||||||
pf_config.floor_side,
|
pf_config.floor_side,
|
||||||
pf_config.buff_id,
|
pf_config.buff_id,
|
||||||
mapPF,
|
mapPF,
|
||||||
])
|
])
|
||||||
if (!mapPF) return null
|
if (!mapPF) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
{/* Title Card */}
|
{/* Title Card */}
|
||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={Object.values(mapPF).sort((a, b) => b.ID - a.ID).map((pf) => ({
|
customSet={Object.values(mapPF).sort((a, b) => b.ID - a.ID).map((pf) => ({
|
||||||
id: pf.ID.toString(),
|
id: pf.ID.toString(),
|
||||||
name: getLocaleName(locale, pf.Name),
|
name: getLocaleName(locale, pf.Name),
|
||||||
time: `${pf.BeginTime} - ${pf.EndTime}`,
|
time: `${pf.BeginTime} - ${pf.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={pf_config.event_id.toString()}
|
selectedCustomSet={pf_config.event_id.toString()}
|
||||||
placeholder={transI18n("selectPFEvent")}
|
placeholder={transI18n("selectPFEvent")}
|
||||||
setSelectedCustomSet={(id) => setPfConfig({
|
setSelectedCustomSet={(id) => setPfConfig({
|
||||||
...pf_config,
|
...pf_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0,
|
challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0,
|
||||||
buff_id: 0
|
buff_id: 0
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("floor")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={pf_config.challenge_id}
|
value={pf_config.challenge_id}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setPfConfig({ ...pf_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setPfConfig({ ...pf_config, challenge_id: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{eventSelected?.Level.map((pf) => (
|
{eventSelected?.Level.map((pf) => (
|
||||||
<option key={pf.ID} value={pf.ID}>{getLocaleName(locale, pf.Name)}</option>
|
<option key={pf.ID} value={pf.ID}>{getLocaleName(locale, pf.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("side")}:{" "}</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={pf_config.floor_side}
|
value={pf_config.floor_side}
|
||||||
className="select select-success"
|
className="select select-success"
|
||||||
onChange={(e) => setPfConfig({ ...pf_config, floor_side: e.target.value })}
|
onChange={(e) => setPfConfig({ ...pf_config, floor_side: e.target.value })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
<option value={0} disabled={true}>{transI18n("selectSide")}</option>
|
||||||
{floorSideList.map((side) => (
|
{floorSideList.map((side) => (
|
||||||
<option key={side.id} value={side.id}>{side.name}</option>
|
<option key={side.id} value={side.id}>{side.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="label-text font-bold text-success mb-2">StageId: {pf_config?.stage_id}</div>
|
<div className="label-text font-bold text-success mb-2">StageId: {pf_config?.stage_id}</div>
|
||||||
{eventSelected && (
|
{eventSelected && (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={eventSelected.Option.map((buff) => ({
|
customSet={eventSelected.Option.map((buff) => ({
|
||||||
id: buff.ID?.toString() || "",
|
id: buff.ID?.toString() || "",
|
||||||
name: getLocaleName(locale, buff?.Name) || "",
|
name: getLocaleName(locale, buff?.Name) || "",
|
||||||
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={pf_config?.buff_id?.toString()}
|
selectedCustomSet={pf_config?.buff_id?.toString()}
|
||||||
placeholder={transI18n("selectBuff")}
|
placeholder={transI18n("selectBuff")}
|
||||||
setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, buff_id: Number(id) })}
|
setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, buff_id: Number(id) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Turbulence Buff */}
|
{/* Turbulence Buff */}
|
||||||
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
||||||
{eventSelected && eventSelected.SubOption.length > 0 ? (
|
{eventSelected && eventSelected.SubOption.length > 0 ? (
|
||||||
eventSelected.SubOption.map((subOption, index) => (
|
eventSelected.SubOption.map((subOption, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{index + 1}. {getLocaleName(locale, subOption.Name)}</span>
|
<span className="label-text font-bold text-success">{index + 1}. {getLocaleName(locale, subOption.Name)}</span>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, subOption.Desc) || "",
|
getLocaleName(locale, subOption.Desc) || "",
|
||||||
subOption.Param || []
|
subOption.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? (
|
) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? (
|
||||||
challengeSelected?.MazeBuff?.map((buff, i) => (
|
challengeSelected?.MazeBuff?.map((buff, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, buff?.Desc) || "",
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
buff?.Param || []
|
buff?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enemy Waves */}
|
{/* Enemy Waves */}
|
||||||
|
|
||||||
{(pf_config?.challenge_id ?? 0) !== 0 && (
|
{(pf_config?.challenge_id ?? 0) !== 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{floorSideList.map((side, i) => {
|
{floorSideList.map((side, i) => {
|
||||||
const eventList = side.id === "firstNode"
|
const eventList = side.id === "firstNode"
|
||||||
? challengeSelected?.EventList1
|
? challengeSelected?.EventList1
|
||||||
: side.id === "secondNode"
|
: side.id === "secondNode"
|
||||||
? challengeSelected?.EventList2
|
? challengeSelected?.EventList2
|
||||||
: side.id === "thirdNode"
|
: side.id === "thirdNode"
|
||||||
? eventSelected?.Tierce?.EventList
|
? eventSelected?.Tierce?.EventList
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (!eventList || eventList.length === 0) return null;
|
if (!eventList || eventList.length === 0) return null;
|
||||||
const targetEvent = eventList[0];
|
const targetEvent = eventList[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
<div key={i} className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">{side.wave}</h2>
|
<h2 className="text-2xl font-bold mb-2 text-info">{side.wave}</h2>
|
||||||
|
|
||||||
{targetEvent && Object.values(targetEvent.Infinite || []).map((waveValue, waveIndex) => (
|
{targetEvent && Object.values(targetEvent.Infinite || []).map((waveValue, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="flex flex-wrap gap-2 mt-2">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
||||||
const monsterStats = calcMonsterStats(
|
const monsterStats = calcMonsterStats(
|
||||||
mapMonster?.[monsterId.toString()],
|
mapMonster?.[monsterId.toString()],
|
||||||
waveValue.EliteGroup,
|
waveValue.EliteGroup,
|
||||||
targetEvent?.HardLevelGroup,
|
targetEvent?.HardLevelGroup,
|
||||||
targetEvent?.Level,
|
targetEvent?.Level,
|
||||||
hardLevelConfig,
|
hardLevelConfig,
|
||||||
eliteConfig
|
eliteConfig
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={enemyIndex}
|
key={enemyIndex}
|
||||||
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
>
|
>
|
||||||
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
Lv. {targetEvent.Level}
|
Lv. {targetEvent.Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
||||||
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={150}
|
width={150}
|
||||||
height={150}
|
height={150}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-1 pb-2 pt-2">
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
<div className="flex flex-col space-y-1.5">
|
<div className="flex flex-col space-y-1.5">
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-error">HP</span>
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-info">Speed</span>
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
Weakness
|
Weakness
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
<Image
|
<Image
|
||||||
key={iconIndex}
|
key={iconIndex}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
alt={icon}
|
alt={icon}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
import { parseRuby } from "@/helper";
|
import { parseRuby } from "@/helper";
|
||||||
|
|
||||||
interface TextProps {
|
interface TextProps {
|
||||||
text: string;
|
text: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ParseText({ text, locale, className }: TextProps) {
|
export default function ParseText({ text, locale, className }: TextProps) {
|
||||||
if (locale === "ja") {
|
if (locale === "ja") {
|
||||||
return <div className={className} dangerouslySetInnerHTML={{ __html: parseRuby(text) }} />;
|
return <div className={className} dangerouslySetInnerHTML={{ __html: parseRuby(text) }} />;
|
||||||
}
|
}
|
||||||
return <div className={className}>{text}</div>;
|
return <div className={className}>{text}</div>;
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function QueryProviderWrapper({ children }: { children: React.ReactNode }) {
|
export default function QueryProviderWrapper({ children }: { children: React.ReactNode }) {
|
||||||
const [queryClient] = React.useState(() => new QueryClient());
|
const [queryClient] = React.useState(() => new QueryClient());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+472
-472
@@ -1,473 +1,473 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import NextImage from "next/image"
|
import NextImage from "next/image"
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper";
|
import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { mappingStats } from "@/constant/constant";
|
import { mappingStats } from "@/constant/constant";
|
||||||
import RelicShowcase from "../showcaseCard/relicShowcase";
|
import RelicShowcase from "../showcaseCard/relicShowcase";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
|
||||||
export default function QuickView() {
|
export default function QuickView() {
|
||||||
const { avatars } = useUserDataStore()
|
const { avatars } = useUserDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const { avatarSelected, } = useCurrentDataStore()
|
const { avatarSelected, } = useCurrentDataStore()
|
||||||
const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore()
|
const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore()
|
||||||
|
|
||||||
|
|
||||||
const avatarSkillTree = useMemo(() => {
|
const avatarSkillTree = useMemo(() => {
|
||||||
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
||||||
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
||||||
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
||||||
}
|
}
|
||||||
return avatarSelected?.SkillTrees || {}
|
return avatarSelected?.SkillTrees || {}
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarData = useMemo(() => {
|
const avatarData = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return avatars[avatarSelected?.ID?.toString()]
|
return avatars[avatarSelected?.ID?.toString()]
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarProfile = useMemo(() => {
|
const avatarProfile = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarData) return
|
if (!avatarSelected || !avatarData) return
|
||||||
return avatarData?.profileList?.[avatarData?.profileSelect]
|
return avatarData?.profileList?.[avatarData?.profileSelect]
|
||||||
}, [avatarSelected, avatarData])
|
}, [avatarSelected, avatarData])
|
||||||
|
|
||||||
const relicEffects = useMemo(() => {
|
const relicEffects = useMemo(() => {
|
||||||
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
||||||
const relicCount: { [key: string]: number } = {};
|
const relicCount: { [key: string]: number } = {};
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
if (relicCount[relic.relic_set_id]) {
|
if (relicCount[relic.relic_set_id]) {
|
||||||
relicCount[relic.relic_set_id]++;
|
relicCount[relic.relic_set_id]++;
|
||||||
} else {
|
} else {
|
||||||
relicCount[relic.relic_set_id] = 1;
|
relicCount[relic.relic_set_id] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const listEffects: { key: string, count: number }[] = [];
|
const listEffects: { key: string, count: number }[] = [];
|
||||||
Object.entries(relicCount).forEach(([key, value]) => {
|
Object.entries(relicCount).forEach(([key, value]) => {
|
||||||
if (value >= 2) {
|
if (value >= 2) {
|
||||||
listEffects.push({ key: key, count: value });
|
listEffects.push({ key: key, count: value });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return listEffects;
|
return listEffects;
|
||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const relicStats = useMemo(() => {
|
const relicStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return
|
if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return
|
||||||
|
|
||||||
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
||||||
const mainAffixMap = mainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = subAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
return {
|
return {
|
||||||
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
||||||
mainAffix: {
|
mainAffix: {
|
||||||
property: mainAffixMap?.[value?.main_affix_id]?.Property,
|
property: mainAffixMap?.[value?.main_affix_id]?.Property,
|
||||||
level: value?.level,
|
level: value?.level,
|
||||||
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
||||||
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property]
|
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property]
|
||||||
},
|
},
|
||||||
subAffix: value?.sub_affixes?.map((subValue) => {
|
subAffix: value?.sub_affixes?.map((subValue) => {
|
||||||
return {
|
return {
|
||||||
property: subAffixMap?.[subValue?.sub_affix_id]?.Property,
|
property: subAffixMap?.[subValue?.sub_affix_id]?.Property,
|
||||||
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
||||||
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property],
|
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property],
|
||||||
step: subValue?.step,
|
step: subValue?.step,
|
||||||
count: subValue?.count
|
count: subValue?.count
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [avatarSelected, avatarProfile, mainAffix, subAffix])
|
}, [avatarSelected, avatarProfile, mainAffix, subAffix])
|
||||||
|
|
||||||
const characterStats = useMemo(() => {
|
const characterStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarData) return
|
if (!avatarSelected || !avatarData) return
|
||||||
const charPromotion = calcPromotion(avatarData.level)
|
const charPromotion = calcPromotion(avatarData.level)
|
||||||
|
|
||||||
const statsData: Record<string, {
|
const statsData: Record<string, {
|
||||||
value: number,
|
value: number,
|
||||||
base: number,
|
base: number,
|
||||||
name: string,
|
name: string,
|
||||||
icon: string,
|
icon: string,
|
||||||
unit: string,
|
unit: string,
|
||||||
round: number
|
round: number
|
||||||
}> = {
|
}> = {
|
||||||
HP: {
|
HP: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "HP",
|
name: "HP",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
ATK: {
|
ATK: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "ATK",
|
name: "ATK",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconAttack.png",
|
icon: "spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
DEF: {
|
DEF: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "DEF",
|
name: "DEF",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconDefence.png",
|
icon: "spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
SPD: {
|
SPD: {
|
||||||
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
name: "SPD",
|
name: "SPD",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconSpeed.png",
|
icon: "spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITRate: {
|
CRITRate: {
|
||||||
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
name: "CRIT Rate",
|
name: "CRIT Rate",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITDmg: {
|
CRITDmg: {
|
||||||
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
name: "CRIT DMG",
|
name: "CRIT DMG",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
BreakEffect: {
|
BreakEffect: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Break Effect",
|
name: "Break Effect",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
EffectRES: {
|
EffectRES: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect RES",
|
name: "Effect RES",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
EnergyRate: {
|
EnergyRate: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Energy Rate",
|
name: "Energy Rate",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
EffectHitRate: {
|
EffectHitRate: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect Hit Rate",
|
name: "Effect Hit Rate",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
HealBoost: {
|
HealBoost: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Healing Boost",
|
name: "Healing Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
PhysicalAdd: {
|
PhysicalAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Physical Boost",
|
name: "Physical Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
FireAdd: {
|
FireAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Fire Boost",
|
name: "Fire Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
IceAdd: {
|
IceAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Ice Boost",
|
name: "Ice Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
ThunderAdd: {
|
ThunderAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Thunder Boost",
|
name: "Thunder Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
WindAdd: {
|
WindAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Wind Boost",
|
name: "Wind Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
QuantumAdd: {
|
QuantumAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Quantum Boost",
|
name: "Quantum Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
ImaginaryAdd: {
|
ImaginaryAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Imaginary Boost",
|
name: "Imaginary Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
ElationAdd: {
|
ElationAdd: {
|
||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Elation Boost",
|
name: "Elation Boost",
|
||||||
icon: "spriteoutput/ui/avatar/icon/IconJoy.png",
|
icon: "spriteoutput/ui/avatar/icon/IconJoy.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) {
|
if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) {
|
||||||
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
||||||
statsData.HP.value += calcBaseStatRaw(
|
statsData.HP.value += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.HP.base += calcBaseStatRaw(
|
statsData.HP.base += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.value += calcBaseStatRaw(
|
statsData.ATK.value += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.base += calcBaseStatRaw(
|
statsData.ATK.base += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.value += calcBaseStatRaw(
|
statsData.DEF.value += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.base += calcBaseStatRaw(
|
statsData.DEF.base += calcBaseStatRaw(
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence,
|
||||||
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
|
|
||||||
const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus
|
const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus
|
||||||
if (bonusData && bonusData.length > 0) {
|
if (bonusData && bonusData.length > 0) {
|
||||||
const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed")
|
const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed")
|
||||||
const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed")
|
const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed")
|
||||||
bonusSpd.forEach((bonus) => {
|
bonusSpd.forEach((bonus) => {
|
||||||
statsData.SPD.value += bonus.Value
|
statsData.SPD.value += bonus.Value
|
||||||
statsData.SPD.base += bonus.Value
|
statsData.SPD.base += bonus.Value
|
||||||
})
|
})
|
||||||
bonusOther.forEach((bonus) => {
|
bonusOther.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (avatarSkillTree) {
|
if (avatarSkillTree) {
|
||||||
Object.values(avatarSkillTree).forEach((value) => {
|
Object.values(avatarSkillTree).forEach((value) => {
|
||||||
if (value?.["1"]
|
if (value?.["1"]
|
||||||
&& value?.["1"]?.PointID
|
&& value?.["1"]?.PointID
|
||||||
&& typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number"
|
&& typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number"
|
||||||
&& avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0
|
&& avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0
|
||||||
&& value?.["1"]?.StatusAddList
|
&& value?.["1"]?.StatusAddList
|
||||||
&& value?.["1"].StatusAddList.length > 0) {
|
&& value?.["1"].StatusAddList.length > 0) {
|
||||||
value?.["1"]?.StatusAddList.forEach((status) => {
|
value?.["1"]?.StatusAddList.forEach((status) => {
|
||||||
const statsBase = mappingStats?.[status?.PropertyType]?.baseStat
|
const statsBase = mappingStats?.[status?.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value)
|
statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (avatarProfile?.relics && mainAffix && subAffix) {
|
if (avatarProfile?.relics && mainAffix && subAffix) {
|
||||||
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
||||||
const mainAffixMap = mainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = subAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat
|
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat
|
||||||
if (mainStats && statsData[mainStats]) {
|
if (mainStats && statsData[mainStats]) {
|
||||||
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
||||||
}
|
}
|
||||||
value?.sub_affixes.forEach((subValue) => {
|
value?.sub_affixes.forEach((subValue) => {
|
||||||
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat
|
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat
|
||||||
if (subStats && statsData[subStats]) {
|
if (subStats && statsData[subStats]) {
|
||||||
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relicEffects && relicEffects.length > 0) {
|
if (relicEffects && relicEffects.length > 0) {
|
||||||
relicEffects.forEach((relic) => {
|
relicEffects.forEach((relic) => {
|
||||||
const dataBonus = mapRelicSet?.[relic.key]?.Skills
|
const dataBonus = mapRelicSet?.[relic.key]?.Skills
|
||||||
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
||||||
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
||||||
if (relic.count < Number(key)) return
|
if (relic.count < Number(key)) return
|
||||||
value.Bonus.forEach((bonus) => {
|
value.Bonus.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return statsData
|
return statsData
|
||||||
}, [
|
}, [
|
||||||
avatarSelected,
|
avatarSelected,
|
||||||
avatarData,
|
avatarData,
|
||||||
mapAvatar,
|
mapAvatar,
|
||||||
avatarProfile?.lightcone,
|
avatarProfile?.lightcone,
|
||||||
avatarProfile?.relics,
|
avatarProfile?.relics,
|
||||||
mapLightCone,
|
mapLightCone,
|
||||||
mainAffix,
|
mainAffix,
|
||||||
subAffix,
|
subAffix,
|
||||||
relicEffects,
|
relicEffects,
|
||||||
mapRelicSet,
|
mapRelicSet,
|
||||||
avatarSkillTree
|
avatarSkillTree
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div className="col-span-1 md:col-span-2 flex flex-col justify-between py-3">
|
<div className="col-span-1 md:col-span-2 flex flex-col justify-between py-3">
|
||||||
<div className="flex w-full flex-col justify-between gap-y-0.5 text-base">
|
<div className="flex w-full flex-col justify-between gap-y-0.5 text-base">
|
||||||
{Object.entries(characterStats || {})?.map(([key, stat], index) => {
|
{Object.entries(characterStats || {})?.map(([key, stat], index) => {
|
||||||
if (!stat || (key.includes("Add") && stat.value === 0)) return null
|
if (!stat || (key.includes("Add") && stat.value === 0)) return null
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex flex-row items-center justify-between">
|
<div key={index} className="flex flex-row items-center justify-between">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<NextImage
|
<NextImage
|
||||||
src={`${process.env.CDN_URL}/${stat?.icon}`}
|
src={`${process.env.CDN_URL}/${stat?.icon}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt="Stat Icon"
|
alt="Stat Icon"
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-10 w-10 p-1 mx-1 bg-black/20 rounded-full"
|
className="h-10 w-10 p-1 mx-1 bg-black/20 rounded-full"
|
||||||
/>
|
/>
|
||||||
<div className="font-bold">{stat.name}</div>
|
<div className="font-bold">{stat.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 mr-3 grow border rounded opacity-50" />
|
<div className="ml-3 mr-3 grow border rounded opacity-50" />
|
||||||
<div className="flex cursor-default flex-col text-right font-bold">{
|
<div className="flex cursor-default flex-col text-right font-bold">{
|
||||||
stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0
|
stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0
|
||||||
}{stat.unit}</div>
|
}{stat.unit}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-center gap-1 w-full my-2">
|
<div className="flex flex-col items-center gap-1 w-full my-2">
|
||||||
{relicEffects.map((setEffect, index) => {
|
{relicEffects.map((setEffect, index) => {
|
||||||
const relicInfo = mapRelicSet[setEffect.key];
|
const relicInfo = mapRelicSet[setEffect.key];
|
||||||
if (!relicInfo) return null;
|
if (!relicInfo) return null;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex w-full flex-row justify-between text-left">
|
<div key={index} className="flex w-full flex-row justify-between text-left">
|
||||||
<div
|
<div
|
||||||
className="font-bold truncate max-w-full mr-1"
|
className="font-bold truncate max-w-full mr-1"
|
||||||
style={{
|
style={{
|
||||||
fontSize: 'clamp(0.5rem, 2vw, 1rem)'
|
fontSize: 'clamp(0.5rem, 2vw, 1rem)'
|
||||||
}}
|
}}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, relicInfo.Name),
|
getLocaleName(locale, relicInfo.Name),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<span className="black-blur bg-black/20 flex w-5 justify-center rounded px-1.5 py-0.5">{setEffect.count}</span>
|
<span className="black-blur bg-black/20 flex w-5 justify-center rounded px-1.5 py-0.5">{setEffect.count}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-2 justify-between py-3 text-lg">
|
<div className="grid grid-cols-1 gap-2 justify-between py-3 text-lg">
|
||||||
|
|
||||||
{relicStats?.map((relic, index) => {
|
{relicStats?.map((relic, index) => {
|
||||||
if (!relic || !avatarSelected) return null
|
if (!relic || !avatarSelected) return null
|
||||||
return (
|
return (
|
||||||
<RelicShowcase key={index} relic={relic} avatarInfo={avatarSelected} />
|
<RelicShowcase key={index} relic={relic} avatarInfo={avatarSelected} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{(!relicStats || !relicStats?.length) && (
|
{(!relicStats || !relicStats?.length) && (
|
||||||
<div className="flex flex-col items-center justify-center">
|
<div className="flex flex-col items-center justify-center">
|
||||||
<div className="text-center p-6 rounded-lg bg-black/40 backdrop-blur-sm border border-white/10">
|
<div className="text-center p-6 rounded-lg bg-black/40 backdrop-blur-sm border border-white/10">
|
||||||
<span className="text-lg text-gray-400">{transI18n("noRelicEquipped")}</span>
|
<span className="text-lg text-gray-400">{transI18n("noRelicEquipped")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+498
-498
@@ -1,498 +1,498 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import useUserDataStore from '@/stores/userDataStore';
|
import useUserDataStore from '@/stores/userDataStore';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import SelectCustomImage from '../select/customSelectImage';
|
import SelectCustomImage from '../select/customSelectImage';
|
||||||
import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper';
|
import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper';
|
||||||
import { mappingStats } from '@/constant/constant';
|
import { mappingStats } from '@/constant/constant';
|
||||||
import useModelStore from '@/stores/modelStore';
|
import useModelStore from '@/stores/modelStore';
|
||||||
import useRelicMakerStore from '@/stores/relicMakerStore';
|
import useRelicMakerStore from '@/stores/relicMakerStore';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
import useCurrentDataStore from '@/stores/currentDataStore';
|
import useCurrentDataStore from '@/stores/currentDataStore';
|
||||||
import { RelicSetDetail, SubAffixData } from '@/types';
|
import { RelicSetDetail, SubAffixData } from '@/types';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { mappingRelicSlot } from "@/constant/constant";
|
import { mappingRelicSlot } from "@/constant/constant";
|
||||||
|
|
||||||
export default function RelicMaker() {
|
export default function RelicMaker() {
|
||||||
const { avatars, setAvatars } = useUserDataStore()
|
const { avatars, setAvatars } = useUserDataStore()
|
||||||
const { avatarSelected } = useCurrentDataStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { setIsOpenRelic } = useModelStore()
|
const { setIsOpenRelic } = useModelStore()
|
||||||
const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore()
|
const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const {
|
const {
|
||||||
selectedRelicSlot,
|
selectedRelicSlot,
|
||||||
selectedRelicSet,
|
selectedRelicSet,
|
||||||
selectedMainStat,
|
selectedMainStat,
|
||||||
listSelectedSubStats,
|
listSelectedSubStats,
|
||||||
selectedRelicLevel,
|
selectedRelicLevel,
|
||||||
preSelectedSubStats,
|
preSelectedSubStats,
|
||||||
setSelectedRelicSet,
|
setSelectedRelicSet,
|
||||||
setSelectedMainStat,
|
setSelectedMainStat,
|
||||||
setSelectedRelicLevel,
|
setSelectedRelicLevel,
|
||||||
setListSelectedSubStats,
|
setListSelectedSubStats,
|
||||||
resetHistory,
|
resetHistory,
|
||||||
popHistory,
|
popHistory,
|
||||||
addHistory,
|
addHistory,
|
||||||
} = useRelicMakerStore()
|
} = useRelicMakerStore()
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
const relicSets = useMemo(() => {
|
const relicSets = useMemo(() => {
|
||||||
const listSet: Record<string, RelicSetDetail> = {};
|
const listSet: Record<string, RelicSetDetail> = {};
|
||||||
for (const [key, value] of Object.entries(mapRelicSet || {})) {
|
for (const [key, value] of Object.entries(mapRelicSet || {})) {
|
||||||
let isOk = false;
|
let isOk = false;
|
||||||
for (const key2 of Object.keys(value.Parts)) {
|
for (const key2 of Object.keys(value.Parts)) {
|
||||||
if (key2 == mappingRelicSlot?.[selectedRelicSlot]) {
|
if (key2 == mappingRelicSlot?.[selectedRelicSlot]) {
|
||||||
isOk = true;
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isOk) {
|
if (isOk) {
|
||||||
listSet[key] = value;
|
listSet[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [mapRelicSet , selectedRelicSlot]);
|
}, [mapRelicSet , selectedRelicSlot]);
|
||||||
|
|
||||||
const subAffixOptions = useMemo(() => {
|
const subAffixOptions = useMemo(() => {
|
||||||
const listSet: Record<string, SubAffixData> = {};
|
const listSet: Record<string, SubAffixData> = {};
|
||||||
const subAffixMap = subAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mainAffix["5" + selectedRelicSlot]
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot]
|
||||||
|
|
||||||
if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet;
|
if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet;
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(subAffixMap)) {
|
for (const [key, value] of Object.entries(subAffixMap)) {
|
||||||
if (value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
if (value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
||||||
listSet[key] = value;
|
listSet[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]);
|
}, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subAffixMap = subAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
||||||
|
|
||||||
if (!subAffixMap || !mainAffixMap) return;
|
if (!subAffixMap || !mainAffixMap) return;
|
||||||
|
|
||||||
const mainProp = mainAffixMap[selectedMainStat]?.Property;
|
const mainProp = mainAffixMap[selectedMainStat]?.Property;
|
||||||
if (!mainProp) return;
|
if (!mainProp) return;
|
||||||
|
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
let updated = false;
|
let updated = false;
|
||||||
|
|
||||||
for (let i = 0; i < newSubAffixes.length; i++) {
|
for (let i = 0; i < newSubAffixes.length; i++) {
|
||||||
if (newSubAffixes[i].property === mainProp) {
|
if (newSubAffixes[i].property === mainProp) {
|
||||||
newSubAffixes[i].affixId = "";
|
newSubAffixes[i].affixId = "";
|
||||||
newSubAffixes[i].property = "";
|
newSubAffixes[i].property = "";
|
||||||
newSubAffixes[i].rollCount = 0;
|
newSubAffixes[i].rollCount = 0;
|
||||||
newSubAffixes[i].stepCount = 0;
|
newSubAffixes[i].stepCount = 0;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updated) setListSelectedSubStats(newSubAffixes);
|
if (updated) setListSelectedSubStats(newSubAffixes);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]);
|
}, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]);
|
||||||
|
|
||||||
const exSubAffixOptions = useMemo(() => {
|
const exSubAffixOptions = useMemo(() => {
|
||||||
const listSet: Record<string, SubAffixData> = {};
|
const listSet: Record<string, SubAffixData> = {};
|
||||||
const subAffixMap = subAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
||||||
|
|
||||||
if (!subAffixMap || !mainAffixMap) return listSet;
|
if (!subAffixMap || !mainAffixMap) return listSet;
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(subAffixMap)) {
|
for (const [key, value] of Object.entries(subAffixMap)) {
|
||||||
const subAffix = listSelectedSubStats.find((item) => item.property === value.Property);
|
const subAffix = listSelectedSubStats.find((item) => item.property === value.Property);
|
||||||
if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
||||||
listSet[key] = value;
|
listSet[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]);
|
}, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]);
|
||||||
|
|
||||||
const effectBonus = useMemo(() => {
|
const effectBonus = useMemo(() => {
|
||||||
const affixSet = mainAffix?.["5" + selectedRelicSlot];
|
const affixSet = mainAffix?.["5" + selectedRelicSlot];
|
||||||
if (!affixSet) return 0;
|
if (!affixSet) return 0;
|
||||||
|
|
||||||
const data = affixSet[selectedMainStat];
|
const data = affixSet[selectedMainStat];
|
||||||
if (!data) return 0;
|
if (!data) return 0;
|
||||||
|
|
||||||
return calcMainAffixBonus(data, selectedRelicLevel);
|
return calcMainAffixBonus(data, selectedRelicLevel);
|
||||||
}, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]);
|
}, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]);
|
||||||
|
|
||||||
const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => {
|
const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => {
|
||||||
setError("");
|
setError("");
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
if (!subAffixOptions[key]) {
|
if (!subAffixOptions[key]) {
|
||||||
newSubAffixes[index].affixId = "";
|
newSubAffixes[index].affixId = "";
|
||||||
newSubAffixes[index].property = "";
|
newSubAffixes[index].property = "";
|
||||||
newSubAffixes[index].rollCount = rollCount;
|
newSubAffixes[index].rollCount = rollCount;
|
||||||
newSubAffixes[index].stepCount = stepCount;
|
newSubAffixes[index].stepCount = stepCount;
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
addHistory(index, newSubAffixes[index]);
|
addHistory(index, newSubAffixes[index]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newSubAffixes[index].affixId = key;
|
newSubAffixes[index].affixId = key;
|
||||||
newSubAffixes[index].property = subAffixOptions[key].Property;
|
newSubAffixes[index].property = subAffixOptions[key].Property;
|
||||||
newSubAffixes[index].rollCount = rollCount;
|
newSubAffixes[index].rollCount = rollCount;
|
||||||
newSubAffixes[index].stepCount = stepCount;
|
newSubAffixes[index].stepCount = stepCount;
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
addHistory(index, newSubAffixes[index]);
|
addHistory(index, newSubAffixes[index]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlerRollback = (index: number) => {
|
const handlerRollback = (index: number) => {
|
||||||
setError("");
|
setError("");
|
||||||
if (!preSelectedSubStats[index]) return;
|
if (!preSelectedSubStats[index]) return;
|
||||||
|
|
||||||
const keys = Object.keys(preSelectedSubStats[index]);
|
const keys = Object.keys(preSelectedSubStats[index]);
|
||||||
if (keys.length <= 1) return;
|
if (keys.length <= 1) return;
|
||||||
|
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
const listHistory = structuredClone(preSelectedSubStats[index]);
|
const listHistory = structuredClone(preSelectedSubStats[index]);
|
||||||
const secondLastKey = listHistory.length - 2;
|
const secondLastKey = listHistory.length - 2;
|
||||||
const preSubAffixes = { ...listHistory[secondLastKey] };
|
const preSubAffixes = { ...listHistory[secondLastKey] };
|
||||||
newSubAffixes[index].rollCount = preSubAffixes.rollCount;
|
newSubAffixes[index].rollCount = preSubAffixes.rollCount;
|
||||||
newSubAffixes[index].stepCount = preSubAffixes.stepCount;
|
newSubAffixes[index].stepCount = preSubAffixes.stepCount;
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
popHistory(index);
|
popHistory(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetSubStat = (index: number) => {
|
const resetSubStat = (index: number) => {
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
resetHistory(index);
|
resetHistory(index);
|
||||||
newSubAffixes[index].affixId = "";
|
newSubAffixes[index].affixId = "";
|
||||||
newSubAffixes[index].property = "";
|
newSubAffixes[index].property = "";
|
||||||
newSubAffixes[index].rollCount = 0;
|
newSubAffixes[index].rollCount = 0;
|
||||||
newSubAffixes[index].stepCount = 0;
|
newSubAffixes[index].stepCount = 0;
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
};
|
};
|
||||||
|
|
||||||
const randomizeStats = () => {
|
const randomizeStats = () => {
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
const exKeys = Object.keys(exSubAffixOptions);
|
const exKeys = Object.keys(exSubAffixOptions);
|
||||||
for (let i = 0; i < newSubAffixes.length; i++) {
|
for (let i = 0; i < newSubAffixes.length; i++) {
|
||||||
const keys = Object.keys(subAffixOptions).filter((key) => !exKeys.includes(key));
|
const keys = Object.keys(subAffixOptions).filter((key) => !exKeys.includes(key));
|
||||||
const randomKey = keys[Math.floor(Math.random() * keys.length)];
|
const randomKey = keys[Math.floor(Math.random() * keys.length)];
|
||||||
exKeys.push(randomKey);
|
exKeys.push(randomKey);
|
||||||
const randomValue = subAffixOptions[randomKey];
|
const randomValue = subAffixOptions[randomKey];
|
||||||
newSubAffixes[i].affixId = randomKey;
|
newSubAffixes[i].affixId = randomKey;
|
||||||
newSubAffixes[i].property = randomValue.Property;
|
newSubAffixes[i].property = randomValue.Property;
|
||||||
newSubAffixes[i].rollCount = 0;
|
newSubAffixes[i].rollCount = 0;
|
||||||
newSubAffixes[i].stepCount = 0;
|
newSubAffixes[i].stepCount = 0;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < newSubAffixes.length; i++) {
|
for (let i = 0; i < newSubAffixes.length; i++) {
|
||||||
addHistory(i, newSubAffixes[i]);
|
addHistory(i, newSubAffixes[i]);
|
||||||
}
|
}
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const randomizeRolls = () => {
|
const randomizeRolls = () => {
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
const randomRolls = randomPartition(9, listSelectedSubStats.length);
|
const randomRolls = randomPartition(9, listSelectedSubStats.length);
|
||||||
for (let i = 0; i < listSelectedSubStats.length; i++) {
|
for (let i = 0; i < listSelectedSubStats.length; i++) {
|
||||||
newSubAffixes[i].rollCount = randomRolls[i];
|
newSubAffixes[i].rollCount = randomRolls[i];
|
||||||
newSubAffixes[i].stepCount = randomStep(randomRolls[i]);
|
newSubAffixes[i].stepCount = randomStep(randomRolls[i]);
|
||||||
}
|
}
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
for (let i = 0; i < newSubAffixes.length; i++) {
|
for (let i = 0; i < newSubAffixes.length; i++) {
|
||||||
addHistory(i, newSubAffixes[i]);
|
addHistory(i, newSubAffixes[i]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlerSaveRelic = () => {
|
const handlerSaveRelic = () => {
|
||||||
setError("");
|
setError("");
|
||||||
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
||||||
if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) {
|
if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) {
|
||||||
setError(transI18n("pleaseSelectAllOptions"));
|
setError(transI18n("pleaseSelectAllOptions"));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (listSelectedSubStats.find((item) => item.affixId === "")) {
|
if (listSelectedSubStats.find((item) => item.affixId === "")) {
|
||||||
setError(transI18n("pleaseSelectAllSubStats"));
|
setError(transI18n("pleaseSelectAllSubStats"));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
avatar.profileList[avatar.profileSelect].relics[selectedRelicSlot] = {
|
avatar.profileList[avatar.profileSelect].relics[selectedRelicSlot] = {
|
||||||
level: selectedRelicLevel,
|
level: selectedRelicLevel,
|
||||||
relic_id: Number(`6${selectedRelicSet}${selectedRelicSlot}`),
|
relic_id: Number(`6${selectedRelicSet}${selectedRelicSlot}`),
|
||||||
relic_set_id: Number(selectedRelicSet),
|
relic_set_id: Number(selectedRelicSet),
|
||||||
main_affix_id: Number(selectedMainStat),
|
main_affix_id: Number(selectedMainStat),
|
||||||
sub_affixes: listSelectedSubStats.map((item) => {
|
sub_affixes: listSelectedSubStats.map((item) => {
|
||||||
return {
|
return {
|
||||||
sub_affix_id: Number(item.affixId),
|
sub_affix_id: Number(item.affixId),
|
||||||
count: item.rollCount,
|
count: item.rollCount,
|
||||||
step: item.stepCount
|
step: item.stepCount
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setAvatars({ ...avatars });
|
setAvatars({ ...avatars });
|
||||||
setIsOpenRelic(false);
|
setIsOpenRelic(false);
|
||||||
|
|
||||||
toast.success(transI18n("relicSavedSuccessfully"));
|
toast.success(transI18n("relicSavedSuccessfully"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
||||||
{transI18n("relicMaker")}
|
{transI18n("relicMaker")}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||||
|
|
||||||
{/* Left Panel */}
|
{/* Left Panel */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
||||||
{/* Set Configuration */}
|
{/* Set Configuration */}
|
||||||
<div className="bg-base-100 rounded-xl p-6 border border-slate-700">
|
<div className="bg-base-100 rounded-xl p-6 border border-slate-700">
|
||||||
<h2 className="text-xl font-bold mb-6 text-warning">{transI18n("mainSettings")}</h2>
|
<h2 className="text-xl font-bold mb-6 text-warning">{transI18n("mainSettings")}</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||||
{/* Main Stat */}
|
{/* Main Stat */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("mainStat")}</label>
|
<label className="block text-lg font-medium mb-2">{transI18n("mainStat")}</label>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(mainAffix["5" + selectedRelicSlot] || {}).map(([key, value]) => ({
|
customSet={Object.entries(mainAffix["5" + selectedRelicSlot] || {}).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={selectedMainStat}
|
selectedCustomSet={selectedMainStat}
|
||||||
placeholder={transI18n("selectAMainStat")}
|
placeholder={transI18n("selectAMainStat")}
|
||||||
setSelectedCustomSet={setSelectedMainStat}
|
setSelectedCustomSet={setSelectedMainStat}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Relic Set Selection */}
|
{/* Relic Set Selection */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("set")}</label>
|
<label className="block text-lg font-medium mb-2">{transI18n("set")}</label>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(relicSets).map(([key, value]) => ({
|
customSet={Object.entries(relicSets).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: getLocaleName(locale, value.Name),
|
label: getLocaleName(locale, value.Name),
|
||||||
imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}`
|
imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={selectedRelicSet}
|
selectedCustomSet={selectedRelicSet}
|
||||||
placeholder={transI18n("selectASet")}
|
placeholder={transI18n("selectASet")}
|
||||||
setSelectedCustomSet={setSelectedRelicSet}
|
setSelectedCustomSet={setSelectedRelicSet}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Set Bonus Display */}
|
{/* Set Bonus Display */}
|
||||||
<div className="mb-6 py-4 bg-base-100 rounded-lg">
|
<div className="mb-6 py-4 bg-base-100 rounded-lg">
|
||||||
{selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => (
|
{selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => (
|
||||||
<div key={key} className="text-blue-300 text-sm mb-1">
|
<div key={key} className="text-blue-300 text-sm mb-1">
|
||||||
<span className="text-info font-bold">{key}-Pc:
|
<span className="text-info font-bold">{key}-Pc:
|
||||||
<div
|
<div
|
||||||
className="text-warning leading-relaxed font-bold"
|
className="text-warning leading-relaxed font-bold"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, value.Desc),
|
getLocaleName(locale, value.Desc),
|
||||||
value.Param || []
|
value.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/> </span>
|
/> </span>
|
||||||
</div>
|
</div>
|
||||||
)) : <p className="text-blue-300 text-sm font-bold mb-1">{transI18n("pleaseSelectASet")}</p>}
|
)) : <p className="text-blue-300 text-sm font-bold mb-1">{transI18n("pleaseSelectASet")}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* Rarity */}
|
{/* Rarity */}
|
||||||
<div className="grid grid-cols-2 items-center gap-4 mb-6">
|
<div className="grid grid-cols-2 items-center gap-4 mb-6">
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("rarity")}: {5} ⭐</label>
|
<label className="block text-lg font-medium mb-2">{transI18n("rarity")}: {5} ⭐</label>
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("effectBonus")}: <span className="text-warning font-bold">{effectBonus}</span></label>
|
<label className="block text-lg font-medium mb-2">{transI18n("effectBonus")}: <span className="text-warning font-bold">{effectBonus}</span></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Level */}
|
{/* Level */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("level")}</label>
|
<label className="block text-lg font-medium mb-2">{transI18n("level")}</label>
|
||||||
<div className="bg-base-200 rounded-lg p-4">
|
<div className="bg-base-200 rounded-lg p-4">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max="15"
|
max="15"
|
||||||
value={selectedRelicLevel}
|
value={selectedRelicLevel}
|
||||||
onChange={(e) => setSelectedRelicLevel(parseInt(e.target.value))}
|
onChange={(e) => setSelectedRelicLevel(parseInt(e.target.value))}
|
||||||
className="range range-primary w-full"
|
className="range range-primary w-full"
|
||||||
/>
|
/>
|
||||||
<div className="text-center text-2xl font-bold mt-2">{selectedRelicLevel}</div>
|
<div className="text-center text-2xl font-bold mt-2">{selectedRelicLevel}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{error && (
|
{error && (
|
||||||
<motion.p
|
<motion.p
|
||||||
key="error"
|
key="error"
|
||||||
initial={{ x: 0 }}
|
initial={{ x: 0 }}
|
||||||
animate={{ x: [0, -2, 2, -2, 2, 0] }}
|
animate={{ x: [0, -2, 2, -2, 2, 0] }}
|
||||||
transition={{
|
transition={{
|
||||||
duration: 6,
|
duration: 6,
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
ease: "easeInOut",
|
ease: "easeInOut",
|
||||||
repeatDelay: 1.5,
|
repeatDelay: 1.5,
|
||||||
}}
|
}}
|
||||||
className="text-error my-2 text-center font-bold"
|
className="text-error my-2 text-center font-bold"
|
||||||
>
|
>
|
||||||
{error}!
|
{error}!
|
||||||
</motion.p>
|
</motion.p>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Save Button */}
|
{/* Save Button */}
|
||||||
<button onClick={handlerSaveRelic} className="btn btn-success w-full">
|
<button onClick={handlerSaveRelic} className="btn btn-success w-full">
|
||||||
{transI18n("save")}
|
{transI18n("save")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel - Sub Stats */}
|
{/* Right Panel - Sub Stats */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Total Roll */}
|
{/* Total Roll */}
|
||||||
<div className="bg-base-100 rounded-xl p-4 border border-slate-700 z-1">
|
<div className="bg-base-100 rounded-xl p-4 border border-slate-700 z-1">
|
||||||
<h3 className="text-lg font-bold mb-4">{transI18n("totalRoll")} {listSelectedSubStats.reduce((a, b) => a + b.rollCount, 0)}</h3>
|
<h3 className="text-lg font-bold mb-4">{transI18n("totalRoll")} {listSelectedSubStats.reduce((a, b) => a + b.rollCount, 0)}</h3>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline btn-success sm:btn-sm"
|
className="btn btn-outline btn-success sm:btn-sm"
|
||||||
onClick={randomizeStats}
|
onClick={randomizeStats}
|
||||||
>
|
>
|
||||||
{transI18n("randomizeStats")}
|
{transI18n("randomizeStats")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline btn-success sm:btn-sm"
|
className="btn btn-outline btn-success sm:btn-sm"
|
||||||
onClick={randomizeRolls}
|
onClick={randomizeRolls}
|
||||||
>
|
>
|
||||||
{transI18n("randomizeRolls")}
|
{transI18n("randomizeRolls")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{listSelectedSubStats.map((v, index) => (
|
{listSelectedSubStats.map((v, index) => (
|
||||||
<div key={index} className={`bg-base-100 rounded-xl p-4 border border-slate-700`}>
|
<div key={index} className={`bg-base-100 rounded-xl p-4 border border-slate-700`}>
|
||||||
<div className="grid grid-cols-12 gap-2 items-center">
|
<div className="grid grid-cols-12 gap-2 items-center">
|
||||||
|
|
||||||
{/* Stat Selection */}
|
{/* Stat Selection */}
|
||||||
<div className="col-span-8">
|
<div className="col-span-8">
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(subAffixOptions).map(([key, value]) => ({
|
customSet={Object.entries(subAffixOptions).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({
|
excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
selectedCustomSet={v.affixId}
|
selectedCustomSet={v.affixId}
|
||||||
placeholder={transI18n("selectASubStat")}
|
placeholder={transI18n("selectASubStat")}
|
||||||
setSelectedCustomSet={(key) => handleSubStatChange(key, index, 0, 0)}
|
setSelectedCustomSet={(key) => handleSubStatChange(key, index, 0, 0)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Current Value */}
|
{/* Current Value */}
|
||||||
<div className="col-span-4 text-center flex items-center justify-center gap-2">
|
<div className="col-span-4 text-center flex items-center justify-center gap-2">
|
||||||
<span className="text-2xl font-mono">+{ }</span>
|
<span className="text-2xl font-mono">+{ }</span>
|
||||||
<div className="text-xl font-bold text-info">{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}</div>
|
<div className="text-xl font-bold text-info">{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Up Roll Values */}
|
{/* Up Roll Values */}
|
||||||
<div className="col-span-12">
|
<div className="col-span-12">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<ChevronUp className="w-4 h-4 text-success" />
|
<ChevronUp className="w-4 h-4 text-success" />
|
||||||
<span className="text-sm font-semibold text-success">{transI18n("upRoll")}</span>
|
<span className="text-sm font-semibold text-success">{transI18n("upRoll")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-1">
|
<div className="grid grid-cols-3 gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Down Roll Values */}
|
{/* Down Roll Values */}
|
||||||
<div className="col-span-12">
|
<div className="col-span-12">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<ChevronDown className="w-4 h-4 text-error" />
|
<ChevronDown className="w-4 h-4 text-error" />
|
||||||
<span className="text-sm font-semibold text-error">{transI18n("downRoll")}</span>
|
<span className="text-sm font-semibold text-error">{transI18n("downRoll")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-1">
|
<div className="grid grid-cols-3 gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Reset Button & Roll Info */}
|
{/* Reset Button & Roll Info */}
|
||||||
<div className="col-span-12 text-center w-full">
|
<div className="col-span-12 text-center w-full">
|
||||||
<div className="grid grid-rows-2 gap-1 items-center justify-items-start w-full">
|
<div className="grid grid-rows-2 gap-1 items-center justify-items-start w-full">
|
||||||
<div className="grid grid-cols-2 gap-2 items-center w-full">
|
<div className="grid grid-cols-2 gap-2 items-center w-full">
|
||||||
<button
|
<button
|
||||||
className="btn btn-error btn-sm mb-1"
|
className="btn btn-error btn-sm mb-1"
|
||||||
onClick={() => resetSubStat(index)}
|
onClick={() => resetSubStat(index)}
|
||||||
>
|
>
|
||||||
{transI18n("reset")}
|
{transI18n("reset")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-warning btn-sm mb-1"
|
className="btn btn-warning btn-sm mb-1"
|
||||||
onClick={() => handlerRollback(index)}
|
onClick={() => handlerRollback(index)}
|
||||||
>
|
>
|
||||||
{transI18n("rollBack")}
|
{transI18n("rollBack")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-2 items-center w-full">
|
<div className="grid grid-cols-2 gap-2 items-center w-full">
|
||||||
<span className="font-bold">{transI18n("roll")}: <span className="text-info">{v.rollCount}</span></span>
|
<span className="font-bold">{transI18n("roll")}: <span className="text-info">{v.rollCount}</span></span>
|
||||||
<span className="font-bold">{transI18n("step")}: <span className="text-info">{v.stepCount}</span></span>
|
<span className="font-bold">{transI18n("step")}: <span className="text-info">{v.stepCount}</span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+366
-366
@@ -1,366 +1,366 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client";
|
"use client";
|
||||||
import { useCallback, useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import RelicMaker from "../relicBar";
|
import RelicMaker from "../relicBar";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import RelicCard from "../card/relicCard";
|
import RelicCard from "../card/relicCard";
|
||||||
import useModelStore from '@/stores/modelStore';
|
import useModelStore from '@/stores/modelStore';
|
||||||
import { replaceByParam } from '@/helper';
|
import { replaceByParam } from '@/helper';
|
||||||
import useRelicMakerStore from '@/stores/relicMakerStore';
|
import useRelicMakerStore from '@/stores/relicMakerStore';
|
||||||
import QuickView from "../quickView";
|
import QuickView from "../quickView";
|
||||||
import { ModalConfig } from "@/types";
|
import { ModalConfig } from "@/types";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import { getLocaleName } from '@/helper';
|
import { getLocaleName } from '@/helper';
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
|
|
||||||
export default function RelicsInfo() {
|
export default function RelicsInfo() {
|
||||||
const { avatars, setAvatars } = useUserDataStore()
|
const { avatars, setAvatars } = useUserDataStore()
|
||||||
const {
|
const {
|
||||||
setSelectedRelicSlot,
|
setSelectedRelicSlot,
|
||||||
selectedRelicSlot,
|
selectedRelicSlot,
|
||||||
setSelectedMainStat,
|
setSelectedMainStat,
|
||||||
setSelectedRelicSet,
|
setSelectedRelicSet,
|
||||||
setSelectedRelicLevel,
|
setSelectedRelicLevel,
|
||||||
setListSelectedSubStats,
|
setListSelectedSubStats,
|
||||||
resetHistory,
|
resetHistory,
|
||||||
resetSubStat,
|
resetSubStat,
|
||||||
listSelectedSubStats,
|
listSelectedSubStats,
|
||||||
} = useRelicMakerStore()
|
} = useRelicMakerStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpenRelic,
|
isOpenRelic,
|
||||||
setIsOpenRelic,
|
setIsOpenRelic,
|
||||||
isOpenQuickView,
|
isOpenQuickView,
|
||||||
setIsOpenQuickView
|
setIsOpenQuickView
|
||||||
} = useModelStore()
|
} = useModelStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { avatarSelected } = useCurrentDataStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { mapRelicSet, subAffix } = useDetailDataStore()
|
const { mapRelicSet, subAffix } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
|
|
||||||
const handleShow = (modalId: string) => {
|
const handleShow = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.showModal();
|
modal.showModal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close modal handler
|
// Close modal handler
|
||||||
const handleCloseModal = (modalId: string) => {
|
const handleCloseModal = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getRelic = useCallback((slot: string) => {
|
const getRelic = useCallback((slot: string) => {
|
||||||
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
return avatar.profileList[avatar.profileSelect]?.relics[slot] || null;
|
return avatar.profileList[avatar.profileSelect]?.relics[slot] || null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const handlerDeleteRelic = (slot: string) => {
|
const handlerDeleteRelic = (slot: string) => {
|
||||||
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
delete avatar.profileList[avatar.profileSelect].relics[slot]
|
delete avatar.profileList[avatar.profileSelect].relics[slot]
|
||||||
setAvatars({ ...avatars });
|
setAvatars({ ...avatars });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlerChangeRelic = (slot: string) => {
|
const handlerChangeRelic = (slot: string) => {
|
||||||
const relic = getRelic(slot)
|
const relic = getRelic(slot)
|
||||||
setSelectedRelicSlot(slot)
|
setSelectedRelicSlot(slot)
|
||||||
resetSubStat()
|
resetSubStat()
|
||||||
resetHistory(null)
|
resetHistory(null)
|
||||||
if (relic) {
|
if (relic) {
|
||||||
setSelectedMainStat(relic.main_affix_id.toString())
|
setSelectedMainStat(relic.main_affix_id.toString())
|
||||||
setSelectedRelicSet(relic.relic_set_id.toString())
|
setSelectedRelicSet(relic.relic_set_id.toString())
|
||||||
setSelectedRelicLevel(relic.level)
|
setSelectedRelicLevel(relic.level)
|
||||||
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
||||||
relic.sub_affixes.forEach((item, index) => {
|
relic.sub_affixes.forEach((item, index) => {
|
||||||
newSubAffixes[index].affixId = item.sub_affix_id.toString();
|
newSubAffixes[index].affixId = item.sub_affix_id.toString();
|
||||||
newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || "";
|
newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || "";
|
||||||
newSubAffixes[index].rollCount = item.count || 0;
|
newSubAffixes[index].rollCount = item.count || 0;
|
||||||
newSubAffixes[index].stepCount = item.step || 0;
|
newSubAffixes[index].stepCount = item.step || 0;
|
||||||
})
|
})
|
||||||
setListSelectedSubStats(newSubAffixes)
|
setListSelectedSubStats(newSubAffixes)
|
||||||
} else {
|
} else {
|
||||||
setSelectedMainStat("")
|
setSelectedMainStat("")
|
||||||
setSelectedRelicSet("")
|
setSelectedRelicSet("")
|
||||||
setSelectedRelicLevel(15)
|
setSelectedRelicLevel(15)
|
||||||
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
||||||
newSubAffixes.forEach((item) => {
|
newSubAffixes.forEach((item) => {
|
||||||
item.affixId = ""
|
item.affixId = ""
|
||||||
item.property = ""
|
item.property = ""
|
||||||
item.rollCount = 0
|
item.rollCount = 0
|
||||||
item.stepCount = 0
|
item.stepCount = 0
|
||||||
})
|
})
|
||||||
|
|
||||||
setListSelectedSubStats(newSubAffixes)
|
setListSelectedSubStats(newSubAffixes)
|
||||||
}
|
}
|
||||||
setIsOpenRelic(true)
|
setIsOpenRelic(true)
|
||||||
handleShow("action_detail_modal")
|
handleShow("action_detail_modal")
|
||||||
}
|
}
|
||||||
|
|
||||||
const relicEffects = useMemo(() => {
|
const relicEffects = useMemo(() => {
|
||||||
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
const relicCount: { [key: string]: number } = {};
|
const relicCount: { [key: string]: number } = {};
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
if (relicCount[relic.relic_set_id]) {
|
if (relicCount[relic.relic_set_id]) {
|
||||||
relicCount[relic.relic_set_id]++;
|
relicCount[relic.relic_set_id]++;
|
||||||
} else {
|
} else {
|
||||||
relicCount[relic.relic_set_id] = 1;
|
relicCount[relic.relic_set_id] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const listEffects: { key: string, count: number }[] = [];
|
const listEffects: { key: string, count: number }[] = [];
|
||||||
Object.entries(relicCount).forEach(([key, value]) => {
|
Object.entries(relicCount).forEach(([key, value]) => {
|
||||||
if (value >= 2) {
|
if (value >= 2) {
|
||||||
listEffects.push({ key: key, count: value });
|
listEffects.push({ key: key, count: value });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return listEffects;
|
return listEffects;
|
||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const modalConfigs: ModalConfig[] = [
|
const modalConfigs: ModalConfig[] = [
|
||||||
{
|
{
|
||||||
id: "action_detail_modal",
|
id: "action_detail_modal",
|
||||||
title: "",
|
title: "",
|
||||||
isOpen: isOpenRelic,
|
isOpen: isOpenRelic,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setIsOpenRelic(false)
|
setIsOpenRelic(false)
|
||||||
handleCloseModal("action_detail_modal")
|
handleCloseModal("action_detail_modal")
|
||||||
},
|
},
|
||||||
content: <RelicMaker />
|
content: <RelicMaker />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "quick_view_modal",
|
id: "quick_view_modal",
|
||||||
title: transI18n("quickView").toUpperCase(),
|
title: transI18n("quickView").toUpperCase(),
|
||||||
isOpen: isOpenQuickView,
|
isOpen: isOpenQuickView,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
setIsOpenQuickView(false)
|
setIsOpenQuickView(false)
|
||||||
handleCloseModal("quick_view_modal")
|
handleCloseModal("quick_view_modal")
|
||||||
},
|
},
|
||||||
content: <QuickView />
|
content: <QuickView />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Handle ESC key to close modal
|
// Handle ESC key to close modal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
for (const item of modalConfigs) {
|
for (const item of modalConfigs) {
|
||||||
if (!item?.isOpen) {
|
if (!item?.isOpen) {
|
||||||
handleCloseModal(item?.id || "")
|
handleCloseModal(item?.id || "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleEscKey = (event: KeyboardEvent) => {
|
const handleEscKey = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
for (const item of modalConfigs) {
|
for (const item of modalConfigs) {
|
||||||
handleCloseModal(item?.id || "")
|
handleCloseModal(item?.id || "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleEscKey);
|
window.addEventListener('keydown', handleEscKey);
|
||||||
return () => window.removeEventListener('keydown', handleEscKey);
|
return () => window.removeEventListener('keydown', handleEscKey);
|
||||||
}, [isOpenRelic]);
|
}, [isOpenRelic]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
<div className="max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
|
|
||||||
{/* Left Section - Items Grid */}
|
{/* Left Section - Items Grid */}
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("relics")}
|
{transI18n("relics")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
|
||||||
{["1", "2", "3", "4", "5", "6"].map((item, index) => (
|
{["1", "2", "3", "4", "5", "6"].map((item, index) => (
|
||||||
<div key={index} className="relative group">
|
<div key={index} className="relative group">
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item === selectedRelicSlot) {
|
if (item === selectedRelicSlot) {
|
||||||
setSelectedRelicSlot("")
|
setSelectedRelicSlot("")
|
||||||
} else {
|
} else {
|
||||||
setSelectedRelicSlot(item)
|
setSelectedRelicSlot(item)
|
||||||
}
|
}
|
||||||
handlerChangeRelic(item)
|
handlerChangeRelic(item)
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
<RelicCard
|
<RelicCard
|
||||||
slot={item}
|
slot={item}
|
||||||
avatarId={avatarSelected?.ID.toString() || ""}
|
avatarId={avatarSelected?.ID.toString() || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex gap-1">
|
<div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
handlerChangeRelic(item)
|
handlerChangeRelic(item)
|
||||||
}}
|
}}
|
||||||
className="btn btn-info p-1.5 rounded-full shadow-lg transition-colors duration-200"
|
className="btn btn-info p-1.5 rounded-full shadow-lg transition-colors duration-200"
|
||||||
title={transI18n("changeRelic")}
|
title={transI18n("changeRelic")}
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{getRelic(item) && (
|
{getRelic(item) && (
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (window.confirm(`${transI18n("deleteRelicConfirm")} ${item}?`)) {
|
if (window.confirm(`${transI18n("deleteRelicConfirm")} ${item}?`)) {
|
||||||
handlerDeleteRelic(item)
|
handlerDeleteRelic(item)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn btn-error p-1.5 rounded-full shadow-lg transition-colors duration-200"
|
className="btn btn-error p-1.5 rounded-full shadow-lg transition-colors duration-200"
|
||||||
title={transI18n("deleteRelic")}
|
title={transI18n("deleteRelic")}
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-2 mt-10">
|
<div className="grid grid-cols-2 gap-2 mt-10">
|
||||||
<button
|
<button
|
||||||
disabled={!selectedRelicSlot}
|
disabled={!selectedRelicSlot}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlerChangeRelic(selectedRelicSlot)
|
handlerChangeRelic(selectedRelicSlot)
|
||||||
}}
|
}}
|
||||||
className="btn btn-info"
|
className="btn btn-info"
|
||||||
>
|
>
|
||||||
{transI18n("changeRelic")}
|
{transI18n("changeRelic")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
disabled={!selectedRelicSlot}
|
disabled={!selectedRelicSlot}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (window.confirm(`${transI18n("deleteRelicConfirm")} ${selectedRelicSlot}?`)) {
|
if (window.confirm(`${transI18n("deleteRelicConfirm")} ${selectedRelicSlot}?`)) {
|
||||||
handlerDeleteRelic(selectedRelicSlot)
|
handlerDeleteRelic(selectedRelicSlot)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn btn-error"
|
className="btn btn-error"
|
||||||
>
|
>
|
||||||
{transI18n("deleteRelic")}
|
{transI18n("deleteRelic")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpenQuickView(true)
|
setIsOpenQuickView(true)
|
||||||
handleShow("quick_view_modal")
|
handleShow("quick_view_modal")
|
||||||
}}
|
}}
|
||||||
className="btn btn-info w-full mt-2"
|
className="btn btn-info w-full mt-2"
|
||||||
>
|
>
|
||||||
{transI18n("quickView")}
|
{transI18n("quickView")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Section - Stats and Set Effects */}
|
{/* Right Section - Stats and Set Effects */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
||||||
{/* Set Effects Panel */}
|
{/* Set Effects Panel */}
|
||||||
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
<h3 className="flex items-center gap-2 text-xl font-bold mb-4 text-base-content">
|
<h3 className="flex items-center gap-2 text-xl font-bold mb-4 text-base-content">
|
||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("setEffects")}
|
{transI18n("setEffects")}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{relicEffects.map((setEffect, index) => {
|
{relicEffects.map((setEffect, index) => {
|
||||||
const relicInfo = mapRelicSet[setEffect.key];
|
const relicInfo = mapRelicSet[setEffect.key];
|
||||||
if (!relicInfo) return null;
|
if (!relicInfo) return null;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="space-y-3">
|
<div key={index} className="space-y-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
className="font-bold text-warning"
|
className="font-bold text-warning"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, relicInfo.Name),
|
getLocaleName(locale, relicInfo.Name),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{setEffect.count && (
|
{setEffect.count && (
|
||||||
<span className={`text-sm text-info`}>
|
<span className={`text-sm text-info`}>
|
||||||
({setEffect.count})
|
({setEffect.count})
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 pl-4">
|
<div className="space-y-2 pl-4">
|
||||||
{Object.entries(relicInfo.Skills).map(([requireNum, value]) => {
|
{Object.entries(relicInfo.Skills).map(([requireNum, value]) => {
|
||||||
if (Number(requireNum) > Number(setEffect.count)) return null;
|
if (Number(requireNum) > Number(setEffect.count)) return null;
|
||||||
return (
|
return (
|
||||||
<div key={requireNum} className="space-y-1">
|
<div key={requireNum} className="space-y-1">
|
||||||
<div className={`font-medium text-success`}>
|
<div className={`font-medium text-success`}>
|
||||||
{requireNum}-PC:
|
{requireNum}-PC:
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="text-sm text-base-content/80 leading-relaxed pl-4"
|
className="text-sm text-base-content/80 leading-relaxed pl-4"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, value.Desc),
|
getLocaleName(locale, value.Desc),
|
||||||
value.Param || []
|
value.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{modalConfigs.map(({ id, title, onClose, content }) => (
|
{modalConfigs.map(({ id, title, onClose, content }) => (
|
||||||
<dialog key={id} id={id} className="modal">
|
<dialog key={id} id={id} className="modal">
|
||||||
<div className="modal-box w-11/12 max-w-[90%] max-h-[85vh] bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
<div className="modal-box w-11/12 max-w-[90%] max-h-[85vh] bg-base-100 text-base-content border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
<div className="sticky top-0 z-10">
|
<div className="sticky top-0 z-10">
|
||||||
<motion.button
|
<motion.button
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
className="btn btn-circle btn-md absolute right-2 top-2 bg-red-600 hover:bg-red-700 text-white border-none"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{title && (
|
{title && (
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-linear-to-r from-pink-400 to-cyan-400">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,112 +1,112 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import dynamic from "next/dynamic"
|
import dynamic from "next/dynamic"
|
||||||
import type { SingleValue } from "react-select"
|
import type { SingleValue } from "react-select"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import ParseText from "../parseText"
|
import ParseText from "../parseText"
|
||||||
import { themeColors } from "@/constant/constant"
|
import { themeColors } from "@/constant/constant"
|
||||||
import type { Props as SelectProps } from "react-select"
|
import type { Props as SelectProps } from "react-select"
|
||||||
import { JSX } from "react"
|
import { JSX } from "react"
|
||||||
|
|
||||||
const Select = dynamic(
|
const Select = dynamic(
|
||||||
() => import("react-select").then(m => m.default),
|
() => import("react-select").then(m => m.default),
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
) as <Option, IsMulti extends boolean = false>(
|
) as <Option, IsMulti extends boolean = false>(
|
||||||
props: SelectProps<Option, IsMulti>
|
props: SelectProps<Option, IsMulti>
|
||||||
) => JSX.Element
|
) => JSX.Element
|
||||||
|
|
||||||
export type SelectOption = {
|
export type SelectOption = {
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
imageUrl: string
|
imageUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectCustomProp = {
|
type SelectCustomProp = {
|
||||||
customSet: SelectOption[]
|
customSet: SelectOption[]
|
||||||
excludeSet: SelectOption[]
|
excludeSet: SelectOption[]
|
||||||
selectedCustomSet: string
|
selectedCustomSet: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
setSelectedCustomSet: (value: string) => void
|
setSelectedCustomSet: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
|
export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
|
||||||
const options: SelectOption[] = customSet
|
const options: SelectOption[] = customSet
|
||||||
const { locale, theme } = useLocaleStore()
|
const { locale, theme } = useLocaleStore()
|
||||||
|
|
||||||
const c = themeColors[theme] || themeColors.winter
|
const c = themeColors[theme] || themeColors.winter
|
||||||
|
|
||||||
const customStyles = {
|
const customStyles = {
|
||||||
option: (p: any, s: any) => ({
|
option: (p: any, s: any) => ({
|
||||||
...p,
|
...p,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '8px',
|
gap: '8px',
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
backgroundColor: s.isFocused ? c.bgHover : c.bg,
|
backgroundColor: s.isFocused ? c.bgHover : c.bg,
|
||||||
color: c.text,
|
color: c.text,
|
||||||
cursor: 'pointer'
|
cursor: 'pointer'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
singleValue: (p: any) => ({
|
singleValue: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '8px',
|
gap: '8px',
|
||||||
color: c.text
|
color: c.text
|
||||||
}),
|
}),
|
||||||
|
|
||||||
control: (p: any) => ({
|
control: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
backgroundColor: c.bg,
|
backgroundColor: c.bg,
|
||||||
borderColor: c.border,
|
borderColor: c.border,
|
||||||
boxShadow: 'none'
|
boxShadow: 'none'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
menu: (p: any) => ({
|
menu: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
backgroundColor: c.bg,
|
backgroundColor: c.bg,
|
||||||
color: c.text,
|
color: c.text,
|
||||||
zIndex: 9999
|
zIndex: 9999
|
||||||
}),
|
}),
|
||||||
|
|
||||||
menuPortal: (p: any) => ({
|
menuPortal: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
zIndex: 9999
|
zIndex: 9999
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatOptionLabel = (option: SelectOption) => (
|
const formatOptionLabel = (option: SelectOption) => (
|
||||||
<div className="flex items-center gap-1 w-full h-full">
|
<div className="flex items-center gap-1 w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={option.imageUrl}
|
src={option.imageUrl}
|
||||||
alt=""
|
alt=""
|
||||||
width={125}
|
width={125}
|
||||||
height={125}
|
height={125}
|
||||||
className="w-8 h-8 object-contain bg-warning-content rounded-full"
|
className="w-8 h-8 object-contain bg-warning-content rounded-full"
|
||||||
/>
|
/>
|
||||||
<ParseText className='font-bold' text={option.label} locale={locale} />
|
<ParseText className='font-bold' text={option.label} locale={locale} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
options={options.filter(opt => !excludeSet.some(ex => ex.value === opt.value))}
|
options={options.filter(opt => !excludeSet.some(ex => ex.value === opt.value))}
|
||||||
value={options.find(opt => {
|
value={options.find(opt => {
|
||||||
return opt.value === selectedCustomSet
|
return opt.value === selectedCustomSet
|
||||||
}) || null}
|
}) || null}
|
||||||
onChange={(selected: SingleValue<SelectOption>) => {
|
onChange={(selected: SingleValue<SelectOption>) => {
|
||||||
setSelectedCustomSet(selected?.value || '')
|
setSelectedCustomSet(selected?.value || '')
|
||||||
}}
|
}}
|
||||||
formatOptionLabel={formatOptionLabel}
|
formatOptionLabel={formatOptionLabel}
|
||||||
styles={customStyles}
|
styles={customStyles}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className="my-react-select-container"
|
className="my-react-select-container"
|
||||||
classNamePrefix="my-react-select"
|
classNamePrefix="my-react-select"
|
||||||
isSearchable
|
isSearchable
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,118 +1,118 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import dynamic from "next/dynamic"
|
import dynamic from "next/dynamic"
|
||||||
import type { SingleValue } from "react-select"
|
import type { SingleValue } from "react-select"
|
||||||
import { replaceByParam } from '@/helper'
|
import { replaceByParam } from '@/helper'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
import useLocaleStore from '@/stores/localeStore'
|
||||||
import { themeColors } from '@/constant/constant'
|
import { themeColors } from '@/constant/constant'
|
||||||
import type { Props as SelectProps } from "react-select"
|
import type { Props as SelectProps } from "react-select"
|
||||||
import { JSX } from "react"
|
import { JSX } from "react"
|
||||||
|
|
||||||
const Select = dynamic(
|
const Select = dynamic(
|
||||||
() => import("react-select").then(m => m.default),
|
() => import("react-select").then(m => m.default),
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
) as <Option, IsMulti extends boolean = false>(
|
) as <Option, IsMulti extends boolean = false>(
|
||||||
props: SelectProps<Option, IsMulti>
|
props: SelectProps<Option, IsMulti>
|
||||||
) => JSX.Element
|
) => JSX.Element
|
||||||
|
|
||||||
export type SelectOption = {
|
export type SelectOption = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
time?: string
|
time?: string
|
||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectCustomProp = {
|
type SelectCustomProp = {
|
||||||
customSet: SelectOption[]
|
customSet: SelectOption[]
|
||||||
excludeSet: SelectOption[]
|
excludeSet: SelectOption[]
|
||||||
selectedCustomSet: string
|
selectedCustomSet: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
setSelectedCustomSet: (value: string) => void
|
setSelectedCustomSet: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SelectCustomText({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
|
export default function SelectCustomText({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) {
|
||||||
const options: SelectOption[] = customSet
|
const options: SelectOption[] = customSet
|
||||||
const { theme } = useLocaleStore()
|
const { theme } = useLocaleStore()
|
||||||
const c = themeColors[theme] || themeColors.winter
|
const c = themeColors[theme] || themeColors.winter
|
||||||
|
|
||||||
const customStyles = {
|
const customStyles = {
|
||||||
option: (p: any, s: any) => ({
|
option: (p: any, s: any) => ({
|
||||||
...p,
|
...p,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '8px',
|
gap: '8px',
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
backgroundColor: s.isFocused ? c.bgHover : c.bg,
|
backgroundColor: s.isFocused ? c.bgHover : c.bg,
|
||||||
color: c.text,
|
color: c.text,
|
||||||
cursor: 'pointer'
|
cursor: 'pointer'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
singleValue: (p: any) => ({
|
singleValue: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '8px',
|
gap: '8px',
|
||||||
color: c.text
|
color: c.text
|
||||||
}),
|
}),
|
||||||
|
|
||||||
control: (p: any) => ({
|
control: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
backgroundColor: c.bg,
|
backgroundColor: c.bg,
|
||||||
borderColor: c.border,
|
borderColor: c.border,
|
||||||
boxShadow: 'none'
|
boxShadow: 'none'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
menu: (p: any) => ({
|
menu: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
backgroundColor: c.bg,
|
backgroundColor: c.bg,
|
||||||
color: c.text,
|
color: c.text,
|
||||||
zIndex: 9999
|
zIndex: 9999
|
||||||
}),
|
}),
|
||||||
|
|
||||||
menuPortal: (p: any) => ({
|
menuPortal: (p: any) => ({
|
||||||
...p,
|
...p,
|
||||||
zIndex: 9999
|
zIndex: 9999
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const formatOptionLabel = (option: SelectOption) => (
|
const formatOptionLabel = (option: SelectOption) => (
|
||||||
<div className="flex flex-col gap-1 w-full h-full">
|
<div className="flex flex-col gap-1 w-full h-full">
|
||||||
<div
|
<div
|
||||||
className="font-bold text-lg"
|
className="font-bold text-lg"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
option.name,
|
option.name,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{option.time && <div className='text-base'>{option.time}</div>}
|
{option.time && <div className='text-base'>{option.time}</div>}
|
||||||
{option.description && <div
|
{option.description && <div
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: option.description
|
__html: option.description
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
options={options.filter(opt => !excludeSet.some(ex => ex.id === opt.id))}
|
options={options.filter(opt => !excludeSet.some(ex => ex.id === opt.id))}
|
||||||
value={options.find(opt => {
|
value={options.find(opt => {
|
||||||
return opt.id === selectedCustomSet
|
return opt.id === selectedCustomSet
|
||||||
}) || null}
|
}) || null}
|
||||||
onChange={(selected: SingleValue<SelectOption>) => {
|
onChange={(selected: SingleValue<SelectOption>) => {
|
||||||
setSelectedCustomSet(selected?.id || '')
|
setSelectedCustomSet(selected?.id || '')
|
||||||
}}
|
}}
|
||||||
formatOptionLabel={formatOptionLabel}
|
formatOptionLabel={formatOptionLabel}
|
||||||
styles={customStyles}
|
styles={customStyles}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className="my-react-select-container"
|
className="my-react-select-container"
|
||||||
classNamePrefix="my-react-select"
|
classNamePrefix="my-react-select"
|
||||||
isSearchable
|
isSearchable
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1006
-1006
File diff suppressed because it is too large
Load Diff
@@ -1,108 +1,108 @@
|
|||||||
|
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import NextImage from "next/image"
|
import NextImage from "next/image"
|
||||||
import { AvatarDetail, RelicShowcaseType } from "@/types";
|
import { AvatarDetail, RelicShowcaseType } from "@/types";
|
||||||
|
|
||||||
export default function RelicShowcase({
|
export default function RelicShowcase({
|
||||||
relic,
|
relic,
|
||||||
avatarInfo,
|
avatarInfo,
|
||||||
}: {
|
}: {
|
||||||
relic: RelicShowcaseType;
|
relic: RelicShowcaseType;
|
||||||
avatarInfo: AvatarDetail;
|
avatarInfo: AvatarDetail;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className="relative w-full flex flex-row items-center rounded-s-lg border-l-2 p-1 border-yellow-600/60 bg-linear-to-r from-yellow-600/20 to-transparent"
|
className="relative w-full flex flex-row items-center rounded-s-lg border-l-2 p-1 border-yellow-600/60 bg-linear-to-r from-yellow-600/20 to-transparent"
|
||||||
>
|
>
|
||||||
{/* Subtle glow overlay */}
|
{/* Subtle glow overlay */}
|
||||||
<div className="absolute inset-0 rounded-s-lg pointer-events-none"></div>
|
<div className="absolute inset-0 rounded-s-lg pointer-events-none"></div>
|
||||||
|
|
||||||
<div className="flex relative">
|
<div className="flex relative">
|
||||||
<div className="absolute inset-0 rounded-lg blur-lg -z-10"></div>
|
<div className="absolute inset-0 rounded-lg blur-lg -z-10"></div>
|
||||||
<NextImage
|
<NextImage
|
||||||
src={relic?.img || ""}
|
src={relic?.img || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={78}
|
width={78}
|
||||||
height={78}
|
height={78}
|
||||||
alt="Relic Icon"
|
alt="Relic Icon"
|
||||||
className="h-19.5 w-19.5 rounded-lg"
|
className="h-19.5 w-19.5 rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="absolute text-yellow-400 font-bold z-10 drop-shadow-[0_0_6px_rgba(251,191,36,0.8)]"
|
className="absolute text-yellow-400 font-bold z-10 drop-shadow-[0_0_6px_rgba(251,191,36,0.8)]"
|
||||||
style={{
|
style={{
|
||||||
left: '0.65rem',
|
left: '0.65rem',
|
||||||
bottom: '-0.45rem',
|
bottom: '-0.45rem',
|
||||||
fontSize: '1.05rem',
|
fontSize: '1.05rem',
|
||||||
letterSpacing: '-0.1em',
|
letterSpacing: '-0.1em',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
✦✦✦✦✦
|
✦✦✦✦✦
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className=" flex w-1/6 flex-col items-center justify-center">
|
<div className=" flex w-1/6 flex-col items-center justify-center">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
|
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
|
||||||
<NextImage
|
<NextImage
|
||||||
src={`${process.env.CDN_URL}/${relic?.mainAffix?.detail?.icon}` || ""}
|
src={`${process.env.CDN_URL}/${relic?.mainAffix?.detail?.icon}` || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={35}
|
width={35}
|
||||||
height={35}
|
height={35}
|
||||||
alt="Main Affix Icon"
|
alt="Main Affix Icon"
|
||||||
className="h-8.75 w-8.75"
|
className="h-8.75 w-8.75"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
|
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
|
||||||
{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}
|
{relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit}
|
||||||
</span>
|
</span>
|
||||||
<span className="bg-black/20 backdrop-blur-sm rounded px-1.5 py-0.5 text-xs border border-white/10">
|
<span className="bg-black/20 backdrop-blur-sm rounded px-1.5 py-0.5 text-xs border border-white/10">
|
||||||
+{relic?.mainAffix?.level}
|
+{relic?.mainAffix?.level}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ opacity: 0.3, height: '78px', borderLeftWidth: '1px' }}></div>
|
<div style={{ opacity: 0.3, height: '78px', borderLeftWidth: '1px' }}></div>
|
||||||
|
|
||||||
<div className="grid w-[65%] m-2 grid-cols-2 gap-1">
|
<div className="grid w-[65%] m-2 grid-cols-2 gap-1">
|
||||||
{relic?.subAffix?.map((subAffix, index) => {
|
{relic?.subAffix?.map((subAffix, index) => {
|
||||||
if (!subAffix) return null
|
if (!subAffix) return null
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex flex-col">
|
<div key={index} className="flex flex-col">
|
||||||
<div className="relative flex flex-row items-center bg-black/20 backdrop-blur-sm rounded-md p-1 border border-white/5 min-w-0">
|
<div className="relative flex flex-row items-center bg-black/20 backdrop-blur-sm rounded-md p-1 border border-white/5 min-w-0">
|
||||||
{subAffix?.detail?.icon ? (
|
{subAffix?.detail?.icon ? (
|
||||||
<NextImage
|
<NextImage
|
||||||
src={`${process.env.CDN_URL}/${subAffix?.detail?.icon}` || ""}
|
src={`${process.env.CDN_URL}/${subAffix?.detail?.icon}` || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
alt="Sub Affix Icon"
|
alt="Sub Affix Icon"
|
||||||
className="h-6 w-6 shrink-0"
|
className="h-6 w-6 shrink-0"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-6 w-6 bg-black/60 rounded flex items-center justify-center border border-white/10 shrink-0">
|
<div className="h-6 w-6 bg-black/60 rounded flex items-center justify-center border border-white/10 shrink-0">
|
||||||
<span className="text-xs text-white/50">?</span>
|
<span className="text-xs text-white/50">?</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span className="text-xs text-gray-200 ml-0.5 truncate flex-1 min-w-0">
|
<span className="text-xs text-gray-200 ml-0.5 truncate flex-1 min-w-0">
|
||||||
+{subAffix?.valueAffix + subAffix?.detail?.unit}
|
+{subAffix?.valueAffix + subAffix?.detail?.unit}
|
||||||
</span>
|
</span>
|
||||||
{
|
{
|
||||||
(avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && (
|
(avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && (
|
||||||
<span className="ml-1 bg-yellow-600/20 text-yellow-400 rounded-full px-1 py-0.5 text-[10px] font-semibold border border-yellow-600/30 shrink-0 leading-none">
|
<span className="ml-1 bg-yellow-600/20 text-yellow-400 rounded-full px-1 py-0.5 text-[10px] font-semibold border border-yellow-600/30 shrink-0 leading-none">
|
||||||
{subAffix?.count}
|
{subAffix?.count}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+384
-384
@@ -1,385 +1,385 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
|
import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { replaceByParam, getLocaleName } from '@/helper';
|
import { replaceByParam, getLocaleName } from '@/helper';
|
||||||
import { mappingStats } from "@/constant/constant";
|
import { mappingStats } from "@/constant/constant";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import useCurrentDataStore from "@/stores/currentDataStore";
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
import { StatusAdd } from '@/types/avatarDetail';
|
import { StatusAdd } from '@/types/avatarDetail';
|
||||||
import { SkillDescription } from "./skillDescription";
|
import { SkillDescription } from "./skillDescription";
|
||||||
|
|
||||||
export default function SkillsInfo() {
|
export default function SkillsInfo() {
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { theme } = useLocaleStore()
|
const { theme } = useLocaleStore()
|
||||||
const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore()
|
const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore()
|
||||||
const { avatars, setAvatar } = useUserDataStore()
|
const { avatars, setAvatar } = useUserDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const traceButtons = useMemo(() => {
|
const traceButtons = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return traceButtonsInfo[avatarSelected.BaseType]
|
return traceButtonsInfo[avatarSelected.BaseType]
|
||||||
}, [avatarSelected])
|
}, [avatarSelected])
|
||||||
|
|
||||||
const avatarData = useMemo(() => {
|
const avatarData = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return avatars[avatarSelected?.ID?.toString()]
|
return avatars[avatarSelected?.ID?.toString()]
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarSkillTree = useMemo(() => {
|
const avatarSkillTree = useMemo(() => {
|
||||||
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
||||||
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
||||||
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
||||||
}
|
}
|
||||||
return avatarSelected?.SkillTrees || {}
|
return avatarSelected?.SkillTrees || {}
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const skillInfo = useMemo(() => {
|
const skillInfo = useMemo(() => {
|
||||||
if (!avatarSelected || !skillIDSelected) return
|
if (!avatarSelected || !skillIDSelected) return
|
||||||
return avatarSkillTree?.[skillIDSelected || ""]?.["1"]
|
return avatarSkillTree?.[skillIDSelected || ""]?.["1"]
|
||||||
}, [avatarSelected, avatarSkillTree, skillIDSelected])
|
}, [avatarSelected, avatarSkillTree, skillIDSelected])
|
||||||
|
|
||||||
const getTraceBuffDisplay = (status: StatusAdd) => {
|
const getTraceBuffDisplay = (status: StatusAdd) => {
|
||||||
const dataDisplay = mappingStats[status.PropertyType]
|
const dataDisplay = mappingStats[status.PropertyType]
|
||||||
if (!dataDisplay) return ""
|
if (!dataDisplay) return ""
|
||||||
if (dataDisplay.unit === "%") {
|
if (dataDisplay.unit === "%") {
|
||||||
return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}`
|
return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}`
|
||||||
}
|
}
|
||||||
if (dataDisplay.name === "SPD") {
|
if (dataDisplay.name === "SPD") {
|
||||||
return `${status.Value.toFixed(1)}${dataDisplay.unit}`
|
return `${status.Value.toFixed(1)}${dataDisplay.unit}`
|
||||||
}
|
}
|
||||||
return `${status.Value.toFixed(0)}${dataDisplay.unit}`
|
return `${status.Value.toFixed(0)}${dataDisplay.unit}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataLevelUpSkill = useMemo(() => {
|
const dataLevelUpSkill = useMemo(() => {
|
||||||
const skillIds: number[] = skillInfo?.LevelUpSkillID || []
|
const skillIds: number[] = skillInfo?.LevelUpSkillID || []
|
||||||
if (!avatarSelected || !avatarData) return undefined
|
if (!avatarSelected || !avatarData) return undefined
|
||||||
let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
if (avatarData.enhanced) {
|
if (avatarData.enhanced) {
|
||||||
result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result && result.length > 0) {
|
if (result && result.length > 0) {
|
||||||
return {
|
return {
|
||||||
isServant: false,
|
isServant: false,
|
||||||
data: result,
|
data: result,
|
||||||
servantData: null,
|
servantData: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {})
|
const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {})
|
||||||
?.filter((skill) => skillIds.includes(skill.ID))
|
?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
|
|
||||||
if (resultServant && resultServant.length > 0) {
|
if (resultServant && resultServant.length > 0) {
|
||||||
return {
|
return {
|
||||||
isServant: true,
|
isServant: true,
|
||||||
data: resultServant,
|
data: resultServant,
|
||||||
servantData: avatarSelected.Memosprite,
|
servantData: avatarSelected.Memosprite,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData])
|
}, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData])
|
||||||
|
|
||||||
|
|
||||||
const handlerMaxAll = () => {
|
const handlerMaxAll = () => {
|
||||||
if (!avatarData || !avatarSkillTree) {
|
if (!avatarData || !avatarSkillTree) {
|
||||||
toast.error(transI18n("maxAllFailed"))
|
toast.error(transI18n("maxAllFailed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => {
|
newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => {
|
||||||
const firstEntry = Object.values(dataPointEntry)[0];
|
const firstEntry = Object.values(dataPointEntry)[0];
|
||||||
if (firstEntry) {
|
if (firstEntry) {
|
||||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>)
|
}, {} as Record<string, number>)
|
||||||
toast.success(transI18n("maxAllSuccess"))
|
toast.success(transI18n("maxAllSuccess"))
|
||||||
setAvatar(newData)
|
setAvatar(newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlerChangeStatusTrace = (status: boolean) => {
|
const handlerChangeStatusTrace = (status: boolean) => {
|
||||||
if (!avatarData || !skillInfo) return
|
if (!avatarData || !skillInfo) return
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills[skillInfo?.PointID] = status ? 1 : 0
|
newData.data.skills[skillInfo?.PointID] = status ? 1 : 0
|
||||||
|
|
||||||
if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) {
|
if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) {
|
||||||
traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => {
|
traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => {
|
||||||
if (avatarSkillTree?.[pointId]?.["1"]) {
|
if (avatarSkillTree?.[pointId]?.["1"]) {
|
||||||
newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0
|
newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setAvatar(newData)
|
setAvatar(newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<div className="rounded-xl p-6 shadow-lg">
|
<div className="rounded-xl p-6 shadow-lg">
|
||||||
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("skills")}
|
{transI18n("skills")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<button className="btn btn-success" onClick={handlerMaxAll}>{transI18n("maxAll")}</button>
|
<button className="btn btn-success" onClick={handlerMaxAll}>{transI18n("maxAll")}</button>
|
||||||
{traceButtons && avatarSelected && (
|
{traceButtons && avatarSelected && (
|
||||||
<div className="grid col-span-4 relative w-full aspect-square">
|
<div className="grid col-span-4 relative w-full aspect-square">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/skilltree/${avatarSelected?.BaseType?.toUpperCase()}.webp`}
|
src={`/skilltree/${avatarSelected?.BaseType?.toUpperCase()}.webp`}
|
||||||
alt=""
|
alt=""
|
||||||
width={312}
|
width={312}
|
||||||
priority={true}
|
priority={true}
|
||||||
height={312}
|
height={312}
|
||||||
style={{
|
style={{
|
||||||
filter: (theme === "winter" || theme === "cupcake") ? "invert(1)" : "none"
|
filter: (theme === "winter" || theme === "cupcake") ? "invert(1)" : "none"
|
||||||
}}
|
}}
|
||||||
className={`w-full h-full object-cover rounded-xl`}
|
className={`w-full h-full object-cover rounded-xl`}
|
||||||
/>
|
/>
|
||||||
{traceButtons.map((btn, index) => {
|
{traceButtons.map((btn, index) => {
|
||||||
if (!avatarSelected?.SkillTrees?.[btn.id]) {
|
if (!avatarSelected?.SkillTrees?.[btn.id]) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${btn.id} + ${index}`}
|
key={`${btn.id} + ${index}`}
|
||||||
id={btn.id}
|
id={btn.id}
|
||||||
className={`
|
className={`
|
||||||
absolute rounded-full border border-black
|
absolute rounded-full border border-black
|
||||||
bg-no-repeat bg-contain
|
bg-no-repeat bg-contain
|
||||||
cursor-pointer transition-all duration-200 ease-in-out
|
cursor-pointer transition-all duration-200 ease-in-out
|
||||||
shadow-[0_0_5px_white] flex justify-center items-center
|
shadow-[0_0_5px_white] flex justify-center items-center
|
||||||
hover:scale-110z-10
|
hover:scale-110z-10
|
||||||
${btn.size === "small" ? "w-[6vw] h-[6vw] md:w-[2vw] md:h-[2vw] bg-white" : ""}
|
${btn.size === "small" ? "w-[6vw] h-[6vw] md:w-[2vw] md:h-[2vw] bg-white" : ""}
|
||||||
${btn.size === "medium" ? "w-[8vw] h-[8vw] md:w-[3vw] md:h-[3vw] bg-white" : ""}
|
${btn.size === "medium" ? "w-[8vw] h-[8vw] md:w-[3vw] md:h-[3vw] bg-white" : ""}
|
||||||
${btn.size === "big" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
${btn.size === "big" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${btn.size === "special" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-white" : ""}
|
${btn.size === "special" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-white" : ""}
|
||||||
${btn.size === "memory" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
${btn.size === "memory" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${btn.size === "elation" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
${btn.size === "elation" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${skillIDSelected === btn.id ? "border-4 border-primary" : ""}
|
${skillIDSelected === btn.id ? "border-4 border-primary" : ""}
|
||||||
${!avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]
|
${!avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]
|
||||||
? "opacity-50 cursor-not-allowed"
|
? "opacity-50 cursor-not-allowed"
|
||||||
: ""}
|
: ""}
|
||||||
`}
|
`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id)
|
setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id)
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
left: btn.left,
|
left: btn.left,
|
||||||
top: btn.top,
|
top: btn.top,
|
||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/${avatarSelected?.SkillTrees?.[btn.id]?.["1"]?.Icon}`}
|
src={`${process.env.CDN_URL}/${avatarSelected?.SkillTrees?.[btn.id]?.["1"]?.Icon}`}
|
||||||
alt={btn.id.replaceAll("Point", "")}
|
alt={btn.id.replaceAll("Point", "")}
|
||||||
priority={true}
|
priority={true}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
style={{
|
style={{
|
||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
padding: "2px",
|
padding: "2px",
|
||||||
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
|
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && (
|
{(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && (
|
||||||
<p className="
|
<p className="
|
||||||
z-12 text-sm sm:text-xs lg:text-sm xl:text-base 2xl:text-2xl
|
z-12 text-sm sm:text-xs lg:text-sm xl:text-base 2xl:text-2xl
|
||||||
font-bold text-center rounded-full absolute
|
font-bold text-center rounded-full absolute
|
||||||
translate-y-full mt-1
|
translate-y-full mt-1
|
||||||
bg-base-300 px-1
|
bg-base-300 px-1
|
||||||
left-1/2 transform -translate-x-1/2
|
left-1/2 transform -translate-x-1/2
|
||||||
">
|
">
|
||||||
{`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] || 0}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`}
|
{`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] || 0}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
{btn.size === "special" && (
|
{btn.size === "special" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
backgroundColor: "#4a6eff",
|
backgroundColor: "#4a6eff",
|
||||||
mixBlendMode: "screen",
|
mixBlendMode: "screen",
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
borderRadius: "50%"
|
borderRadius: "50%"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{btn.size === "memory" && (
|
{btn.size === "memory" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
backgroundColor: "#9a89ff",
|
backgroundColor: "#9a89ff",
|
||||||
mixBlendMode: "screen",
|
mixBlendMode: "screen",
|
||||||
opacity: 0.4,
|
opacity: 0.4,
|
||||||
borderRadius: "50%"
|
borderRadius: "50%"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{btn.size === "elation" && (
|
{btn.size === "elation" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
backgroundColor: "#ff8c00",
|
backgroundColor: "#ff8c00",
|
||||||
mixBlendMode: "screen",
|
mixBlendMode: "screen",
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
borderRadius: "50%"
|
borderRadius: "50%"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{btn.size === "big" && (
|
{btn.size === "big" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
backgroundColor: "#f5e4b0",
|
backgroundColor: "#f5e4b0",
|
||||||
mixBlendMode: "screen",
|
mixBlendMode: "screen",
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
borderRadius: "50%"
|
borderRadius: "50%"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!traceButtons && avatarSelected && (
|
{!traceButtons && avatarSelected && (
|
||||||
<div className="flex flex-col relative w-full aspect-square">
|
<div className="flex flex-col relative w-full aspect-square">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
<h2 className="flex items-center gap-2 text-2xl font-bold mb-6 text-base-content">
|
||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("details")}
|
{transI18n("details")}
|
||||||
</h2>
|
</h2>
|
||||||
{skillIDSelected && avatarSelected?.SkillTrees && avatarData && (
|
{skillIDSelected && avatarSelected?.SkillTrees && avatarData && (
|
||||||
<div>
|
<div>
|
||||||
{skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? (
|
{skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? (
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-success">{transI18n("level")}</div>
|
<div className="font-bold text-success">{transI18n("level")}</div>
|
||||||
<div className="w-full max-w-xs">
|
<div className="w-full max-w-xs">
|
||||||
<input type="range"
|
<input type="range"
|
||||||
min={1}
|
min={1}
|
||||||
max={skillInfo?.MaxLevel || 1}
|
max={skillInfo?.MaxLevel || 1}
|
||||||
value={avatarData?.data.skills?.[skillInfo?.PointID] || 1}
|
value={avatarData?.data.skills?.[skillInfo?.PointID] || 1}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value)
|
newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value)
|
||||||
setAvatar(newData)
|
setAvatar(newData)
|
||||||
}}
|
}}
|
||||||
className="range range-success"
|
className="range range-success"
|
||||||
step="1" />
|
step="1" />
|
||||||
<div className="flex justify-between px-2.5 mt-2 text-xs">
|
<div className="flex justify-between px-2.5 mt-2 text-xs">
|
||||||
{Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => (
|
{Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => (
|
||||||
<span key={index}>{index}</span>
|
<span key={index}>{index}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? (
|
) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={avatarData?.data.skills?.[skillInfo?.PointID] === 1}
|
checked={avatarData?.data.skills?.[skillInfo?.PointID] === 1}
|
||||||
className="toggle toggle-success"
|
className="toggle toggle-success"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") {
|
if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills[skillInfo?.PointID] = 1
|
newData.data.skills[skillInfo?.PointID] = 1
|
||||||
setAvatar(newData)
|
setAvatar(newData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
delete newData.data.skills[skillInfo?.PointID]
|
delete newData.data.skills[skillInfo?.PointID]
|
||||||
setAvatar(newData)
|
setAvatar(newData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handlerChangeStatusTrace(e.target.checked)
|
handlerChangeStatusTrace(e.target.checked)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="font-bold text-success">
|
<div className="font-bold text-success">
|
||||||
{avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")}
|
{avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
null
|
null
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{((skillInfo?.PointName && skillInfo?.PointDesc) ||
|
{((skillInfo?.PointName && skillInfo?.PointDesc) ||
|
||||||
(skillInfo?.PointName && skillInfo?.StatusAddList.length > 0))
|
(skillInfo?.PointName && skillInfo?.StatusAddList.length > 0))
|
||||||
&& (
|
&& (
|
||||||
<div className="text-xl font-bold flex items-center gap-2 mt-2">
|
<div className="text-xl font-bold flex items-center gap-2 mt-2">
|
||||||
{getLocaleName(locale, skillInfo.PointName)}
|
{getLocaleName(locale, skillInfo.PointName)}
|
||||||
{skillInfo.StatusAddList.length > 0 && (
|
{skillInfo.StatusAddList.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
{skillInfo.StatusAddList.map((status, index) => (
|
{skillInfo.StatusAddList.map((status, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<div className="text-xl font-bold">{getTraceBuffDisplay(status)}</div>
|
<div className="text-xl font-bold">{getTraceBuffDisplay(status)}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{skillInfo?.PointDesc && (
|
{skillInfo?.PointDesc && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
getLocaleName(locale, skillInfo?.PointDesc) || "",
|
getLocaleName(locale, skillInfo?.PointDesc) || "",
|
||||||
skillInfo?.Param || []
|
skillInfo?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{skillInfo?.LevelUpSkillID
|
{skillInfo?.LevelUpSkillID
|
||||||
&& skillInfo?.LevelUpSkillID.length > 0
|
&& skillInfo?.LevelUpSkillID.length > 0
|
||||||
&& dataLevelUpSkill
|
&& dataLevelUpSkill
|
||||||
&& (
|
&& (
|
||||||
<div className="mt-2 flex flex-col gap-2">
|
<div className="mt-2 flex flex-col gap-2">
|
||||||
|
|
||||||
{dataLevelUpSkill?.data?.map((skill, index) => (
|
{dataLevelUpSkill?.data?.map((skill, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
|
|
||||||
<div className="text-xl font-bold text-primary">
|
<div className="text-xl font-bold text-primary">
|
||||||
{transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)}
|
{transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)}
|
||||||
{` (${transI18n(skill?.SkillEffect?.toLowerCase())})`}
|
{` (${transI18n(skill?.SkillEffect?.toLowerCase())})`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SkillDescription
|
<SkillDescription
|
||||||
skill={skill}
|
skill={skill}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
avatarData={avatarData}
|
avatarData={avatarData}
|
||||||
skillInfo={skillInfo}
|
skillInfo={skillInfo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,41 +1,41 @@
|
|||||||
import { getLocaleName, replaceByParam } from "@/helper";
|
import { getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { AvatarStore, SkillDetail, SkillTreePoint } from "@/types";
|
import { AvatarStore, SkillDetail, SkillTreePoint } from "@/types";
|
||||||
import ExtraEffectList from "../extraInfo";
|
import ExtraEffectList from "../extraInfo";
|
||||||
|
|
||||||
export const SkillDescription = ({ skill, locale, avatarData, skillInfo }: {
|
export const SkillDescription = ({ skill, locale, avatarData, skillInfo }: {
|
||||||
skill: SkillDetail,
|
skill: SkillDetail,
|
||||||
locale: string,
|
locale: string,
|
||||||
avatarData: AvatarStore,
|
avatarData: AvatarStore,
|
||||||
skillInfo: SkillTreePoint
|
skillInfo: SkillTreePoint
|
||||||
}) => {
|
}) => {
|
||||||
const levelKey = avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || "";
|
const levelKey = avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || "";
|
||||||
const params = skill.Level[levelKey]?.Param || [];
|
const params = skill.Level[levelKey]?.Param || [];
|
||||||
const descHtml = getLocaleName(locale, skill.Desc) || getLocaleName(locale, skill.SimpleDesc);
|
const descHtml = getLocaleName(locale, skill.Desc) || getLocaleName(locale, skill.SimpleDesc);
|
||||||
|
|
||||||
const extraList = Object.values(skill.Extra).length > 0
|
const extraList = Object.values(skill.Extra).length > 0
|
||||||
? skill.Extra
|
? skill.Extra
|
||||||
: skill?.SimpleExtra || {};
|
: skill?.SimpleExtra || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="space-y-2 pb-2">
|
<div className="space-y-2 pb-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-1 h-5 bg-primary/80 rounded-sm" />
|
<div className="w-1 h-5 bg-primary/80 rounded-sm" />
|
||||||
<div
|
<div
|
||||||
className="text-lg font-bold tracking-wide text-foreground uppercase"
|
className="text-lg font-bold tracking-wide text-foreground uppercase"
|
||||||
dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, skill.Name), []) }}
|
dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, skill.Name), []) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="text-[15px] leading-relaxed text-foreground/90 pl-3 border-l border-transparent"
|
className="text-[15px] leading-relaxed text-foreground/90 pl-3 border-l border-transparent"
|
||||||
dangerouslySetInnerHTML={{ __html: replaceByParam(descHtml, params) }}
|
dangerouslySetInnerHTML={{ __html: replaceByParam(descHtml, params) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Object.keys(extraList).length > 0 && (
|
{Object.keys(extraList).length > 0 && (
|
||||||
<ExtraEffectList extras={extraList} locale={locale} />
|
<ExtraEffectList extras={extraList} locale={locale} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { PropsWithChildren, useContext } from "react";
|
import { PropsWithChildren, useContext } from "react";
|
||||||
import { ThemeContext } from "./themeContext";
|
import { ThemeContext } from "./themeContext";
|
||||||
|
|
||||||
export function ClientThemeWrapper({ children }: PropsWithChildren) {
|
export function ClientThemeWrapper({ children }: PropsWithChildren) {
|
||||||
const { theme } = useContext(ThemeContext);
|
const { theme } = useContext(ThemeContext);
|
||||||
return <div data-theme={theme} className="h-full">{children}</div>;
|
return <div data-theme={theme} className="h-full">{children}</div>;
|
||||||
}
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./clientThemeWrapper"
|
export * from "./clientThemeWrapper"
|
||||||
export * from "./themeContext"
|
export * from "./themeContext"
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { createContext, PropsWithChildren, useEffect } from "react";
|
import { createContext, PropsWithChildren, useEffect } from "react";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
|
|
||||||
interface ThemeContextType {
|
interface ThemeContextType {
|
||||||
theme?: string;
|
theme?: string;
|
||||||
changeTheme?: (nextTheme: string | null) => void;
|
changeTheme?: (nextTheme: string | null) => void;
|
||||||
}
|
}
|
||||||
export const ThemeContext = createContext<ThemeContextType>({});
|
export const ThemeContext = createContext<ThemeContextType>({});
|
||||||
|
|
||||||
export const ThemeProvider = ({ children }: PropsWithChildren) => {
|
export const ThemeProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
|
||||||
const { theme, setTheme } = useLocaleStore()
|
const { theme, setTheme } = useLocaleStore()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const storedTheme = localStorage.getItem("theme");
|
const storedTheme = localStorage.getItem("theme");
|
||||||
if (storedTheme) setTheme(storedTheme);
|
if (storedTheme) setTheme(storedTheme);
|
||||||
}
|
}
|
||||||
}, [setTheme]);
|
}, [setTheme]);
|
||||||
|
|
||||||
const changeTheme = (nextTheme: string | null) => {
|
const changeTheme = (nextTheme: string | null) => {
|
||||||
if (nextTheme) {
|
if (nextTheme) {
|
||||||
setTheme(nextTheme);
|
setTheme(nextTheme);
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem("theme", nextTheme);
|
localStorage.setItem("theme", nextTheme);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setTheme(theme === "winter" ? "night" : "winter");
|
setTheme(theme === "winter" ? "night" : "winter");
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem("theme", theme);
|
localStorage.setItem("theme", theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={{ theme, changeTheme }}>
|
<ThemeContext.Provider value={{ theme, changeTheme }}>
|
||||||
{children}
|
{children}
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
+220
-220
@@ -1,220 +1,220 @@
|
|||||||
export const listCurrentLanguage = {
|
export const listCurrentLanguage = {
|
||||||
en: { label: "English", flag: "🇬🇧" },
|
en: { label: "English", flag: "🇬🇧" },
|
||||||
vi: { label: "Tiếng Việt", flag: "🇻🇳" },
|
vi: { label: "Tiếng Việt", flag: "🇻🇳" },
|
||||||
ja: { label: "日本語", flag: "🇯🇵" },
|
ja: { label: "日本語", flag: "🇯🇵" },
|
||||||
ko: { label: "한국어", flag: "🇰🇷" },
|
ko: { label: "한국어", flag: "🇰🇷" },
|
||||||
zh: { label: "中文", flag: "🇨🇳" },
|
zh: { label: "中文", flag: "🇨🇳" },
|
||||||
de: { label: "Deutsch", flag: "🇩🇪" },
|
de: { label: "Deutsch", flag: "🇩🇪" },
|
||||||
es: { label: "Español", flag: "🇪🇸" },
|
es: { label: "Español", flag: "🇪🇸" },
|
||||||
fr: { label: "Français", flag: "🇫🇷" },
|
fr: { label: "Français", flag: "🇫🇷" },
|
||||||
id: { label: "Bahasa Indonesia", flag: "🇮🇩" },
|
id: { label: "Bahasa Indonesia", flag: "🇮🇩" },
|
||||||
pt: { label: "Português", flag: "🇵🇹" },
|
pt: { label: "Português", flag: "🇵🇹" },
|
||||||
ru: { label: "Русский", flag: "🇷🇺" },
|
ru: { label: "Русский", flag: "🇷🇺" },
|
||||||
th: { label: "ไทย", flag: "🇹🇭" }
|
th: { label: "ไทย", flag: "🇹🇭" }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listCurrentLanguageApi : Record<string, string> = {
|
export const listCurrentLanguageApi : Record<string, string> = {
|
||||||
ja: "jp",
|
ja: "jp",
|
||||||
ko: "kr",
|
ko: "kr",
|
||||||
en: "en",
|
en: "en",
|
||||||
vi: "vi",
|
vi: "vi",
|
||||||
zh: "cn",
|
zh: "cn",
|
||||||
de: "de",
|
de: "de",
|
||||||
es: "es",
|
es: "es",
|
||||||
fr: "fr",
|
fr: "fr",
|
||||||
id: "id",
|
id: "id",
|
||||||
pt: "pt",
|
pt: "pt",
|
||||||
ru: "ru",
|
ru: "ru",
|
||||||
th: "th"
|
th: "th"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mappingStats = <Record<string, {name: string, icon: string, unit: string, baseStat: string}> > {
|
export const mappingStats = <Record<string, {name: string, icon: string, unit: string, baseStat: string}> > {
|
||||||
"HPDelta": {
|
"HPDelta": {
|
||||||
name:"HP",
|
name:"HP",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "HP"
|
baseStat: "HP"
|
||||||
},
|
},
|
||||||
"AttackDelta": {
|
"AttackDelta": {
|
||||||
name:"ATK",
|
name:"ATK",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "ATK"
|
baseStat: "ATK"
|
||||||
},
|
},
|
||||||
"HPAddedRatio": {
|
"HPAddedRatio": {
|
||||||
name:"HP",
|
name:"HP",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "HP"
|
baseStat: "HP"
|
||||||
},
|
},
|
||||||
"AttackAddedRatio": {
|
"AttackAddedRatio": {
|
||||||
name:"ATK",
|
name:"ATK",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ATK"
|
baseStat: "ATK"
|
||||||
},
|
},
|
||||||
"DefenceDelta": {
|
"DefenceDelta": {
|
||||||
name:"DEF",
|
name:"DEF",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "DEF"
|
baseStat: "DEF"
|
||||||
},
|
},
|
||||||
"DefenceAddedRatio": {
|
"DefenceAddedRatio": {
|
||||||
name:"DEF",
|
name:"DEF",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "DEF"
|
baseStat: "DEF"
|
||||||
},
|
},
|
||||||
"SpeedAddedRatio": {
|
"SpeedAddedRatio": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"BaseSpeed": {
|
"BaseSpeed": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"CriticalChanceBase": {
|
"CriticalChanceBase": {
|
||||||
name:"CRIT Rate",
|
name:"CRIT Rate",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "CRITRate"
|
baseStat: "CRITRate"
|
||||||
},
|
},
|
||||||
"CriticalDamageBase": {
|
"CriticalDamageBase": {
|
||||||
name:"CRIT DMG",
|
name:"CRIT DMG",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "CRITDmg"
|
baseStat: "CRITDmg"
|
||||||
},
|
},
|
||||||
"HealRatioBase": {
|
"HealRatioBase": {
|
||||||
name:"Outgoing Healing Boost",
|
name:"Outgoing Healing Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "HealBoost"
|
baseStat: "HealBoost"
|
||||||
},
|
},
|
||||||
"StatusProbabilityBase": {
|
"StatusProbabilityBase": {
|
||||||
name:"Effect Hit Rate",
|
name:"Effect Hit Rate",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EffectHitRate"
|
baseStat: "EffectHitRate"
|
||||||
},
|
},
|
||||||
"StatusResistanceBase": {
|
"StatusResistanceBase": {
|
||||||
name:"Effect RES",
|
name:"Effect RES",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EffectRES"
|
baseStat: "EffectRES"
|
||||||
},
|
},
|
||||||
"BreakDamageAddedRatioBase": {
|
"BreakDamageAddedRatioBase": {
|
||||||
name:"Break Effect",
|
name:"Break Effect",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "BreakEffect"
|
baseStat: "BreakEffect"
|
||||||
},
|
},
|
||||||
"SpeedDelta": {
|
"SpeedDelta": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"PhysicalAddedRatio": {
|
"PhysicalAddedRatio": {
|
||||||
name:"Physical DMG Boost",
|
name:"Physical DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "PhysicalAdd"
|
baseStat: "PhysicalAdd"
|
||||||
},
|
},
|
||||||
"FireAddedRatio": {
|
"FireAddedRatio": {
|
||||||
name:"Fire DMG Boost",
|
name:"Fire DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "FireAdd"
|
baseStat: "FireAdd"
|
||||||
},
|
},
|
||||||
"IceAddedRatio": {
|
"IceAddedRatio": {
|
||||||
name:"Ice DMG Boost",
|
name:"Ice DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "IceAdd"
|
baseStat: "IceAdd"
|
||||||
},
|
},
|
||||||
"ThunderAddedRatio": {
|
"ThunderAddedRatio": {
|
||||||
name:"Thunder DMG Boost",
|
name:"Thunder DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ThunderAdd"
|
baseStat: "ThunderAdd"
|
||||||
},
|
},
|
||||||
"WindAddedRatio": {
|
"WindAddedRatio": {
|
||||||
name:"Wind DMG Boost",
|
name:"Wind DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "WindAdd"
|
baseStat: "WindAdd"
|
||||||
},
|
},
|
||||||
"QuantumAddedRatio": {
|
"QuantumAddedRatio": {
|
||||||
name:"Quantum DMG Boost",
|
name:"Quantum DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "QuantumAdd"
|
baseStat: "QuantumAdd"
|
||||||
},
|
},
|
||||||
"ImaginaryAddedRatio": {
|
"ImaginaryAddedRatio": {
|
||||||
name:"Imaginary DMG Boost",
|
name:"Imaginary DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ImaginaryAdd"
|
baseStat: "ImaginaryAdd"
|
||||||
},
|
},
|
||||||
"ElationDamageAddedRatioBase": {
|
"ElationDamageAddedRatioBase": {
|
||||||
name:"Elation DMG Boost",
|
name:"Elation DMG Boost",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconJoy.png",
|
icon:"spriteoutput/ui/avatar/icon/IconJoy.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ElationAdd"
|
baseStat: "ElationAdd"
|
||||||
},
|
},
|
||||||
"SPRatioBase": {
|
"SPRatioBase": {
|
||||||
name:"Energy Regeneration Rate",
|
name:"Energy Regeneration Rate",
|
||||||
icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EnergyRate"
|
baseStat: "EnergyRate"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const ratioStats = [
|
export const ratioStats = [
|
||||||
"HPAddedRatio",
|
"HPAddedRatio",
|
||||||
"AttackAddedRatio",
|
"AttackAddedRatio",
|
||||||
"DefenceAddedRatio",
|
"DefenceAddedRatio",
|
||||||
"SpeedAddedRatio",
|
"SpeedAddedRatio",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const mappingRelicSlot: Record<string, string> = {
|
export const mappingRelicSlot: Record<string, string> = {
|
||||||
"1": "HEAD",
|
"1": "HEAD",
|
||||||
"2": "HAND",
|
"2": "HAND",
|
||||||
"3": "BODY",
|
"3": "BODY",
|
||||||
"4": "FOOT",
|
"4": "FOOT",
|
||||||
"5": "NECK",
|
"5": "NECK",
|
||||||
"6": "OBJECT",
|
"6": "OBJECT",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const themeColors: Record<string, { bg: string; bgHover: string; text: string; border: string }> = {
|
export const themeColors: Record<string, { bg: string; bgHover: string; text: string; border: string }> = {
|
||||||
winter: {
|
winter: {
|
||||||
bg: '#ffffff',
|
bg: '#ffffff',
|
||||||
bgHover: '#f1f5f9',
|
bgHover: '#f1f5f9',
|
||||||
text: '#3a4f6b',
|
text: '#3a4f6b',
|
||||||
border: '#cbd5e1'
|
border: '#cbd5e1'
|
||||||
},
|
},
|
||||||
night: {
|
night: {
|
||||||
bg: '#1d232a',
|
bg: '#1d232a',
|
||||||
bgHover: '#2a323c',
|
bgHover: '#2a323c',
|
||||||
text: '#cbcdd1',
|
text: '#cbcdd1',
|
||||||
border: '#3f3f46'
|
border: '#3f3f46'
|
||||||
},
|
},
|
||||||
cupcake: {
|
cupcake: {
|
||||||
bg: '#faf7f5',
|
bg: '#faf7f5',
|
||||||
bgHover: '#f3eae6',
|
bgHover: '#f3eae6',
|
||||||
text: '#281333',
|
text: '#281333',
|
||||||
border: '#e5d3cb'
|
border: '#e5d3cb'
|
||||||
},
|
},
|
||||||
coffee: {
|
coffee: {
|
||||||
bg: '#20161f',
|
bg: '#20161f',
|
||||||
bgHover: '#2a1d29',
|
bgHover: '#2a1d29',
|
||||||
text: '#c4a051',
|
text: '#c4a051',
|
||||||
border: '#3a2a36'
|
border: '#3a2a36'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1147
-1147
File diff suppressed because it is too large
Load Diff
+104
-104
@@ -1,105 +1,105 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { SendDataThroughProxy, SendDataToServer } from "@/lib/api/api"
|
import { SendDataThroughProxy, SendDataToServer } from "@/lib/api/api"
|
||||||
import useConnectStore from "@/stores/connectStore"
|
import useConnectStore from "@/stores/connectStore"
|
||||||
import useUserDataStore from "@/stores/userDataStore"
|
import useUserDataStore from "@/stores/userDataStore"
|
||||||
import { converterToFreeSRJson } from "./converterToFreeSRJson"
|
import { converterToFreeSRJson } from "./converterToFreeSRJson"
|
||||||
import { psResponseSchema } from "@/zod"
|
import { psResponseSchema } from "@/zod"
|
||||||
import useGlobalStore from "@/stores/globalStore"
|
import useGlobalStore from "@/stores/globalStore"
|
||||||
import { ActionResult, ExtraData, ProxyPayload, ProxyResponse, PSConnectType, PSResponse } from "@/types"
|
import { ActionResult, ExtraData, ProxyPayload, ProxyResponse, PSConnectType, PSResponse } from "@/types"
|
||||||
|
|
||||||
|
|
||||||
const getUrlQuery = (connectionType: PSConnectType | string, serverUrl: string): string => {
|
const getUrlQuery = (connectionType: PSConnectType | string, serverUrl: string): string => {
|
||||||
if (connectionType === PSConnectType.FireflyGo) return "http://localhost:21000/sync"
|
if (connectionType === PSConnectType.FireflyGo) return "http://localhost:21000/sync"
|
||||||
if (connectionType === PSConnectType.RobinSR) return "http://localhost:21000/srtools"
|
if (connectionType === PSConnectType.RobinSR) return "http://localhost:21000/srtools"
|
||||||
|
|
||||||
if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) {
|
if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) {
|
||||||
return `http://${serverUrl}`
|
return `http://${serverUrl}`
|
||||||
}
|
}
|
||||||
return serverUrl
|
return serverUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleProxyRequest = async (payload: ProxyPayload): Promise<ActionResult> => {
|
const handleProxyRequest = async (payload: ProxyPayload): Promise<ActionResult> => {
|
||||||
const response = await SendDataThroughProxy({
|
const response = await SendDataThroughProxy({
|
||||||
data: { ...payload, method: "POST" }
|
data: { ...payload, method: "POST" }
|
||||||
}) as ProxyResponse | Error
|
}) as ProxyResponse | Error
|
||||||
|
|
||||||
if (response instanceof Error) {
|
if (response instanceof Error) {
|
||||||
return { success: false, message: response.message }
|
return { success: false, message: response.message }
|
||||||
}
|
}
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
return { success: false, message: response.error }
|
return { success: false, message: response.error }
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = psResponseSchema.safeParse(response.data)
|
const parsed = psResponseSchema.safeParse(response.data)
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
return { success: false, message: "Invalid response schema" }
|
return { success: false, message: "Invalid response schema" }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: true, message: "" }
|
return { success: true, message: "" }
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDirectServerResponse = (
|
const handleDirectServerResponse = (
|
||||||
response: PSResponse | string,
|
response: PSResponse | string,
|
||||||
setIsConnectPS: (val: boolean) => void,
|
setIsConnectPS: (val: boolean) => void,
|
||||||
onSuccess: (extraData?: ExtraData) => void
|
onSuccess: (extraData?: ExtraData) => void
|
||||||
): ActionResult => {
|
): ActionResult => {
|
||||||
if (typeof response === "string") {
|
if (typeof response === "string") {
|
||||||
setIsConnectPS(false)
|
setIsConnectPS(false)
|
||||||
return { success: false, message: response }
|
return { success: false, message: response }
|
||||||
}
|
}
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
setIsConnectPS(false)
|
setIsConnectPS(false)
|
||||||
return { success: false, message: response.message }
|
return { success: false, message: response.message }
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsConnectPS(true)
|
setIsConnectPS(true)
|
||||||
onSuccess(response?.extra_data)
|
onSuccess(response?.extra_data)
|
||||||
return { success: true, message: "" }
|
return { success: true, message: "" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const connectToPS = async (): Promise<ActionResult> => {
|
export const connectToPS = async (): Promise<ActionResult> => {
|
||||||
const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState()
|
const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState()
|
||||||
const { setExtraData, setIsConnectPS } = useGlobalStore.getState()
|
const { setExtraData, setIsConnectPS } = useGlobalStore.getState()
|
||||||
|
|
||||||
if (connectionType === "Other" && privateType === "Server") {
|
if (connectionType === "Other" && privateType === "Server") {
|
||||||
return handleProxyRequest({ username, password, serverUrl, data: undefined })
|
return handleProxyRequest({ username, password, serverUrl, data: undefined })
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlQuery = getUrlQuery(connectionType, serverUrl)
|
const urlQuery = getUrlQuery(connectionType, serverUrl)
|
||||||
const response = await SendDataToServer(username, password, urlQuery, undefined)
|
const response = await SendDataToServer(username, password, urlQuery, undefined)
|
||||||
|
|
||||||
return handleDirectServerResponse(response, setIsConnectPS, (extraData) => {
|
return handleDirectServerResponse(response, setIsConnectPS, (extraData) => {
|
||||||
setExtraData(extraData)
|
setExtraData(extraData)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const syncDataToPS = async (): Promise<ActionResult> => {
|
export const syncDataToPS = async (): Promise<ActionResult> => {
|
||||||
const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState()
|
const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState()
|
||||||
const { extraData, setIsConnectPS, setExtraData, isEnableChangePath, isEnableLua } = useGlobalStore.getState()
|
const { extraData, setIsConnectPS, setExtraData, isEnableChangePath, isEnableLua } = useGlobalStore.getState()
|
||||||
const { avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config } = useUserDataStore.getState()
|
const { avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config } = useUserDataStore.getState()
|
||||||
|
|
||||||
const data = converterToFreeSRJson(avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config)
|
const data = converterToFreeSRJson(avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config)
|
||||||
|
|
||||||
if (connectionType === "Other" && privateType === "Server") {
|
if (connectionType === "Other" && privateType === "Server") {
|
||||||
return handleProxyRequest({ username, password, serverUrl, data })
|
return handleProxyRequest({ username, password, serverUrl, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlQuery = getUrlQuery(connectionType, serverUrl)
|
const urlQuery = getUrlQuery(connectionType, serverUrl)
|
||||||
|
|
||||||
const payloadExtra: PSResponse['extra_data'] = structuredClone(extraData)
|
const payloadExtra: PSResponse['extra_data'] = structuredClone(extraData)
|
||||||
if (payloadExtra) {
|
if (payloadExtra) {
|
||||||
if (!isEnableChangePath) payloadExtra.multi_path = undefined
|
if (!isEnableChangePath) payloadExtra.multi_path = undefined
|
||||||
if (!isEnableLua) payloadExtra.lua = null
|
if (!isEnableLua) payloadExtra.lua = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await SendDataToServer(username, password, urlQuery, data, payloadExtra)
|
const response = await SendDataToServer(username, password, urlQuery, data, payloadExtra)
|
||||||
|
|
||||||
return handleDirectServerResponse(response, setIsConnectPS, (responseExtraData) => {
|
return handleDirectServerResponse(response, setIsConnectPS, (responseExtraData) => {
|
||||||
const newData = structuredClone(responseExtraData)
|
const newData = structuredClone(responseExtraData)
|
||||||
if (newData) {
|
if (newData) {
|
||||||
newData.lua = extraData?.lua || null
|
newData.lua = extraData?.lua || null
|
||||||
}
|
}
|
||||||
setExtraData(newData)
|
setExtraData(newData)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
+14
-14
@@ -1,15 +1,15 @@
|
|||||||
export function convertToRoman(num: number): string {
|
export function convertToRoman(num: number): string {
|
||||||
const roman: [number, string][] = [
|
const roman: [number, string][] = [
|
||||||
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
|
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
|
||||||
[100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
|
[100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
|
||||||
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']
|
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']
|
||||||
];
|
];
|
||||||
let result = '';
|
let result = '';
|
||||||
for (const [val, sym] of roman) {
|
for (const [val, sym] of roman) {
|
||||||
while (num >= val) {
|
while (num >= val) {
|
||||||
result += sym;
|
result += sym;
|
||||||
num -= val;
|
num -= val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
+119
-119
@@ -1,89 +1,89 @@
|
|||||||
|
|
||||||
import useConnectStore from "@/stores/connectStore";
|
import useConnectStore from "@/stores/connectStore";
|
||||||
import useDetailDataStore from "@/stores/detailDataStore";
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, PSConnectType, RelicJson } from "@/types";
|
import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, PSConnectType, RelicJson } from "@/types";
|
||||||
|
|
||||||
|
|
||||||
export function converterToFreeSRJson(
|
export function converterToFreeSRJson(
|
||||||
avatars: Record<string, AvatarStore>,
|
avatars: Record<string, AvatarStore>,
|
||||||
battle_type: string,
|
battle_type: string,
|
||||||
moc_config: MOCConfigStore,
|
moc_config: MOCConfigStore,
|
||||||
pf_config: PFConfigStore,
|
pf_config: PFConfigStore,
|
||||||
as_config: ASConfigStore,
|
as_config: ASConfigStore,
|
||||||
ce_config: CEConfigStore,
|
ce_config: CEConfigStore,
|
||||||
peak_config: PEAKConfigStore,
|
peak_config: PEAKConfigStore,
|
||||||
): FreeSRJson {
|
): FreeSRJson {
|
||||||
const { skillConfig } = useDetailDataStore.getState()
|
const { skillConfig } = useDetailDataStore.getState()
|
||||||
const { connectionType } = useConnectStore.getState()
|
const { connectionType } = useConnectStore.getState()
|
||||||
const lightcones: LightconeJson[] = []
|
const lightcones: LightconeJson[] = []
|
||||||
const relics: RelicJson[] = []
|
const relics: RelicJson[] = []
|
||||||
let battleJson: BattleConfigJson
|
let battleJson: BattleConfigJson
|
||||||
if (battle_type === "MOC") {
|
if (battle_type === "MOC") {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: battle_type,
|
battle_type: battle_type,
|
||||||
blessings: moc_config.blessings,
|
blessings: moc_config.blessings,
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: moc_config.cycle_count,
|
cycle_count: moc_config.cycle_count,
|
||||||
stage_id: moc_config.stage_id,
|
stage_id: moc_config.stage_id,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: moc_config.monsters,
|
monsters: moc_config.monsters,
|
||||||
}
|
}
|
||||||
} else if (battle_type === "PF") {
|
} else if (battle_type === "PF") {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: battle_type,
|
battle_type: battle_type,
|
||||||
blessings: pf_config.blessings,
|
blessings: pf_config.blessings,
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: pf_config.cycle_count,
|
cycle_count: pf_config.cycle_count,
|
||||||
stage_id: pf_config.stage_id,
|
stage_id: pf_config.stage_id,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: pf_config.monsters,
|
monsters: pf_config.monsters,
|
||||||
}
|
}
|
||||||
} else if (battle_type === "AS") {
|
} else if (battle_type === "AS") {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: battle_type,
|
battle_type: battle_type,
|
||||||
blessings: as_config.blessings,
|
blessings: as_config.blessings,
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: as_config.cycle_count,
|
cycle_count: as_config.cycle_count,
|
||||||
stage_id: as_config.stage_id,
|
stage_id: as_config.stage_id,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: as_config.monsters,
|
monsters: as_config.monsters,
|
||||||
}
|
}
|
||||||
} else if (battle_type === "CE") {
|
} else if (battle_type === "CE") {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT",
|
battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT",
|
||||||
blessings: ce_config.blessings,
|
blessings: ce_config.blessings,
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: ce_config.cycle_count,
|
cycle_count: ce_config.cycle_count,
|
||||||
stage_id: ce_config.stage_id,
|
stage_id: ce_config.stage_id,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: ce_config.monsters,
|
monsters: ce_config.monsters,
|
||||||
}
|
}
|
||||||
} else if (battle_type === "PEAK") {
|
} else if (battle_type === "PEAK") {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT",
|
battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT",
|
||||||
blessings: peak_config.blessings,
|
blessings: peak_config.blessings,
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: peak_config.cycle_count,
|
cycle_count: peak_config.cycle_count,
|
||||||
stage_id: peak_config.stage_id,
|
stage_id: peak_config.stage_id,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: peak_config.monsters,
|
monsters: peak_config.monsters,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
battleJson = {
|
battleJson = {
|
||||||
battle_type: battle_type,
|
battle_type: battle_type,
|
||||||
blessings: [],
|
blessings: [],
|
||||||
custom_stats: [],
|
custom_stats: [],
|
||||||
cycle_count: 0,
|
cycle_count: 0,
|
||||||
stage_id: 0,
|
stage_id: 0,
|
||||||
path_resonance_id: 0,
|
path_resonance_id: 0,
|
||||||
monsters: [],
|
monsters: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatarsJson: { [key: string]: AvatarJson } = {}
|
const avatarsJson: { [key: string]: AvatarJson } = {}
|
||||||
let internalUidLightcone = 0
|
let internalUidLightcone = 0
|
||||||
let internalUidRelic = 0
|
let internalUidRelic = 0
|
||||||
|
|
||||||
Object.entries(avatars ?? {}).forEach(([avatarId, avatar]) => {
|
Object.entries(avatars ?? {}).forEach(([avatarId, avatar]) => {
|
||||||
if (!avatar) return
|
if (!avatar) return
|
||||||
|
|
||||||
@@ -91,10 +91,10 @@ export function converterToFreeSRJson(
|
|||||||
for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) {
|
for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) {
|
||||||
if (skillConfig?.[skillId]) {
|
if (skillConfig?.[skillId]) {
|
||||||
skillsByAnchorType[skillConfig[skillId].IndexSlot] = level > skillConfig[skillId].MaxLevel ? skillConfig[skillId].MaxLevel : level
|
skillsByAnchorType[skillConfig[skillId].IndexSlot] = level > skillConfig[skillId].MaxLevel ? skillConfig[skillId].MaxLevel : level
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avatarsJson[avatarId] = {
|
avatarsJson[avatarId] = {
|
||||||
owner_uid: Number(avatar.owner_uid || 0),
|
owner_uid: Number(avatar.owner_uid || 0),
|
||||||
avatar_id: Number(avatar.avatar_id || 0),
|
avatar_id: Number(avatar.avatar_id || 0),
|
||||||
data: {
|
data: {
|
||||||
rank: Number(avatar.data?.rank || 0),
|
rank: Number(avatar.data?.rank || 0),
|
||||||
@@ -115,21 +115,21 @@ export function converterToFreeSRJson(
|
|||||||
const newLightcone: LightconeJson = {
|
const newLightcone: LightconeJson = {
|
||||||
level: Number(currentProfile.lightcone.level || 0),
|
level: Number(currentProfile.lightcone.level || 0),
|
||||||
item_id: Number(currentProfile.lightcone.item_id || 0),
|
item_id: Number(currentProfile.lightcone.item_id || 0),
|
||||||
rank: Number(currentProfile.lightcone.rank || 0),
|
rank: Number(currentProfile.lightcone.rank || 0),
|
||||||
promotion: Number(currentProfile.lightcone.promotion || 0),
|
promotion: Number(currentProfile.lightcone.promotion || 0),
|
||||||
internal_uid: internalUidLightcone,
|
internal_uid: internalUidLightcone,
|
||||||
equip_avatar: Number(avatar.avatar_id || 0),
|
equip_avatar: Number(avatar.avatar_id || 0),
|
||||||
}
|
}
|
||||||
internalUidLightcone++
|
internalUidLightcone++
|
||||||
lightcones.push(newLightcone)
|
lightcones.push(newLightcone)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentProfile.relics) {
|
if (currentProfile.relics) {
|
||||||
["1", "2", "3", "4", "5", "6"].forEach(slot => {
|
["1", "2", "3", "4", "5", "6"].forEach(slot => {
|
||||||
const relic = currentProfile.relics[slot]
|
const relic = currentProfile.relics[slot]
|
||||||
if (relic && relic.relic_id !== 0) {
|
if (relic && relic.relic_id !== 0) {
|
||||||
const newRelic: RelicJson = {
|
const newRelic: RelicJson = {
|
||||||
level: Number(relic.level || 0),
|
level: Number(relic.level || 0),
|
||||||
relic_id: Number(relic.relic_id || 0),
|
relic_id: Number(relic.relic_id || 0),
|
||||||
relic_set_id: Number(relic.relic_set_id || 0),
|
relic_set_id: Number(relic.relic_set_id || 0),
|
||||||
main_affix_id: Number(relic.main_affix_id || 0),
|
main_affix_id: Number(relic.main_affix_id || 0),
|
||||||
@@ -137,18 +137,18 @@ export function converterToFreeSRJson(
|
|||||||
internal_uid: internalUidRelic,
|
internal_uid: internalUidRelic,
|
||||||
equip_avatar: Number(avatar.avatar_id || 0),
|
equip_avatar: Number(avatar.avatar_id || 0),
|
||||||
}
|
}
|
||||||
internalUidRelic++
|
internalUidRelic++
|
||||||
relics.push(newRelic)
|
relics.push(newRelic)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lightcones,
|
lightcones,
|
||||||
relics,
|
relics,
|
||||||
avatars: avatarsJson,
|
avatars: avatarsJson,
|
||||||
battle_config: battleJson,
|
battle_config: battleJson,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-61
@@ -1,62 +1,62 @@
|
|||||||
import { listCurrentLanguageApi } from "@/constant/constant";
|
import { listCurrentLanguageApi } from "@/constant/constant";
|
||||||
import { AvatarDetail } from "@/types";
|
import { AvatarDetail } from "@/types";
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
|
||||||
type TFunc = ReturnType<typeof useTranslations>
|
type TFunc = ReturnType<typeof useTranslations>
|
||||||
function cleanText(text: string): string {
|
function cleanText(text: string): string {
|
||||||
if (!text) return ""
|
if (!text) return ""
|
||||||
return text.replace(/<unbreak>(.*?)<\/unbreak>/g, "$1")
|
return text.replace(/<unbreak>(.*?)<\/unbreak>/g, "$1")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNameChar(
|
export function getNameChar(
|
||||||
locale: string,
|
locale: string,
|
||||||
t: TFunc,
|
t: TFunc,
|
||||||
data: AvatarDetail | undefined
|
data: AvatarDetail | undefined
|
||||||
): string {
|
): string {
|
||||||
if (!data) return "";
|
if (!data) return "";
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) {
|
if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase();
|
const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase();
|
||||||
|
|
||||||
let text = data.Name[langKey] ?? "";
|
let text = data.Name[langKey] ?? "";
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
text = data.Name["en"] ?? "";
|
text = data.Name["en"] ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.ID > 8000) {
|
if (data.ID > 8000) {
|
||||||
text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`;
|
text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanText(text)
|
return cleanText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocaleName(locale: string, data: Record<string, string> | undefined | null): string {
|
export function getLocaleName(locale: string, data: Record<string, string> | undefined | null): string {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) {
|
if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase();
|
const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase();
|
||||||
|
|
||||||
let text = data[langKey] ?? "";
|
let text = data[langKey] ?? "";
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
text = data["en"] ?? "";
|
text = data["en"] ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanText(text)
|
return cleanText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseRuby(text: string): string {
|
export function parseRuby(text: string): string {
|
||||||
const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs;
|
const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs;
|
||||||
return text.replace(rubyRegex, (_match, furigana, kanji) => {
|
return text.replace(rubyRegex, (_match, furigana, kanji) => {
|
||||||
return `<ruby>${kanji}<rt>${furigana}</rt></ruby>`;
|
return `<ruby>${kanji}<rt>${furigana}</rt></ruby>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
+19
-19
@@ -1,23 +1,23 @@
|
|||||||
import { AvatarDetail } from "@/types";
|
import { AvatarDetail } from "@/types";
|
||||||
|
|
||||||
export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) {
|
export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) {
|
if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) {
|
||||||
return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => {
|
return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => {
|
||||||
const firstEntry = Object.values(dataPointEntry)[0];
|
const firstEntry = Object.values(dataPointEntry)[0];
|
||||||
if (firstEntry) {
|
if (firstEntry) {
|
||||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>)
|
}, {} as Record<string, number>)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.values(avatarSelected?.SkillTrees ?? {}).reduce((acc, dataPointEntry) => {
|
return Object.values(avatarSelected?.SkillTrees ?? {}).reduce((acc, dataPointEntry) => {
|
||||||
const firstEntry = Object.values(dataPointEntry)[0];
|
const firstEntry = Object.values(dataPointEntry)[0];
|
||||||
if (firstEntry) {
|
if (firstEntry) {
|
||||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -1,8 +1,8 @@
|
|||||||
export * from "./getName"
|
export * from "./getName"
|
||||||
export * from "./replaceByParam"
|
export * from "./replaceByParam"
|
||||||
export * from "./converterToAvatarStore"
|
export * from "./converterToAvatarStore"
|
||||||
export * from "./calcData"
|
export * from "./calcData"
|
||||||
export * from "./random"
|
export * from "./random"
|
||||||
export * from "./json"
|
export * from "./json"
|
||||||
export * from "./convertData"
|
export * from "./convertData"
|
||||||
export * from "./connect"
|
export * from "./connect"
|
||||||
+23
-23
@@ -8,27 +8,27 @@ export function randomPartition(sum: number, parts: number): number[] {
|
|||||||
|
|
||||||
const raw = Array.from({ length: parts }, () => Math.random());
|
const raw = Array.from({ length: parts }, () => Math.random());
|
||||||
const total = raw.reduce((a, b) => a + b, 0);
|
const total = raw.reduce((a, b) => a + b, 0);
|
||||||
const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1);
|
const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1);
|
||||||
let diff = sum - result.reduce((a, b) => a + b, 0);
|
let diff = sum - result.reduce((a, b) => a + b, 0);
|
||||||
while (diff !== 0) {
|
while (diff !== 0) {
|
||||||
for (let i = 0; i < result.length && diff !== 0; i++) {
|
for (let i = 0; i < result.length && diff !== 0; i++) {
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
result[i]++;
|
result[i]++;
|
||||||
diff--;
|
diff--;
|
||||||
} else if (result[i] > 1) {
|
} else if (result[i] > 1) {
|
||||||
result[i]--;
|
result[i]--;
|
||||||
diff++;
|
diff++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function randomStep(x: number): number {
|
export function randomStep(x: number): number {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (let i = 0; i < x; i++) {
|
for (let i = 0; i < x; i++) {
|
||||||
total += Math.floor(Math.random() * 3);
|
total += Math.floor(Math.random() * 3);
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
const formatValue = (value: number, format: string, floatDigits?: string, hasPercent?: boolean): string => {
|
const formatValue = (value: number, format: string, floatDigits?: string, hasPercent?: boolean): string => {
|
||||||
if (format.startsWith('f')) {
|
if (format.startsWith('f')) {
|
||||||
const digits = parseInt(floatDigits || "1", 10);
|
const digits = parseInt(floatDigits || "1", 10);
|
||||||
const num = hasPercent ? value * 100 : value;
|
const num = hasPercent ? value * 100 : value;
|
||||||
return `${num.toFixed(digits)}${hasPercent ? "%" : ""}`;
|
return `${num.toFixed(digits)}${hasPercent ? "%" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format === 'i') {
|
if (format === 'i') {
|
||||||
const num = hasPercent ? value * 100 : value;
|
const num = hasPercent ? value * 100 : value;
|
||||||
return `${Math.round(num)}${hasPercent ? "%" : ""}`;
|
return `${Math.round(num)}${hasPercent ? "%" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(value);
|
return String(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function replaceByParam(desc?: string, params: number[] = []): string {
|
export function replaceByParam(desc?: string, params: number[] = []): string {
|
||||||
if (!desc) return "";
|
if (!desc) return "";
|
||||||
|
|
||||||
const PARAM_REGEX = /#(\d+)\[(f(\d+)|i)\](%)?/g;
|
const PARAM_REGEX = /#(\d+)\[(f(\d+)|i)\](%)?/g;
|
||||||
|
|
||||||
const processor = (_match: string, index: string, format: string, digits?: string, percent?: string): string => {
|
const processor = (_match: string, index: string, format: string, digits?: string, percent?: string): string => {
|
||||||
const i = parseInt(index, 10) - 1;
|
const i = parseInt(index, 10) - 1;
|
||||||
const val = params[i];
|
const val = params[i];
|
||||||
return val !== undefined ? formatValue(val, format, digits, !!percent) : "";
|
return val !== undefined ? formatValue(val, format, digits, !!percent) : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = desc.replace(/<color=(#[0-9a-fA-F]{8})>(.*?)<\/color>/g, (_, color, inner) => {
|
let result = desc.replace(/<color=(#[0-9a-fA-F]{8})>(.*?)<\/color>/g, (_, color, inner) => {
|
||||||
const processedInner = inner.replace(PARAM_REGEX, processor);
|
const processedInner = inner.replace(PARAM_REGEX, processor);
|
||||||
return `<span style="color: ${color}">${processedInner}</span>`;
|
return `<span style="color: ${color}">${processedInner}</span>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
result = result.replace(/<unbreak>(.*?)<\/unbreak>/g, (_, inner) => {
|
result = result.replace(/<unbreak>(.*?)<\/unbreak>/g, (_, inner) => {
|
||||||
return inner.replace(PARAM_REGEX, processor);
|
return inner.replace(PARAM_REGEX, processor);
|
||||||
});
|
});
|
||||||
|
|
||||||
result = result.replace(PARAM_REGEX, processor);
|
result = result.replace(PARAM_REGEX, processor);
|
||||||
|
|
||||||
return result.split("\\n").join("<br/>");
|
return result.split("\\n").join("<br/>");
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
export * from "./useChangeTheme";
|
export * from "./useChangeTheme";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { ThemeContext } from "@/components/themeController";
|
import { ThemeContext } from "@/components/themeController";
|
||||||
|
|
||||||
export const useChangeTheme = () => useContext(ThemeContext);
|
export const useChangeTheme = () => useContext(ThemeContext);
|
||||||
+141
-141
@@ -1,142 +1,142 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types";
|
import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types";
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { psResponseSchema } from "@/zod";
|
import { psResponseSchema } from "@/zod";
|
||||||
import { ExtraData, Metadata } from "@/types";
|
import { ExtraData, Metadata } from "@/types";
|
||||||
|
|
||||||
export async function getMetadataApi(): Promise<Metadata> {
|
export async function getMetadataApi(): Promise<Metadata> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Metadata>(`/api/data/metadata`);
|
const res = await axios.get<Metadata>(`/api/data/metadata`);
|
||||||
return res.data as Metadata;
|
return res.data as Metadata;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Failed to fetch metadata:', error);
|
console.error('Failed to fetch metadata:', error);
|
||||||
return {
|
return {
|
||||||
BaseType: {},
|
BaseType: {},
|
||||||
DamageType: {},
|
DamageType: {},
|
||||||
MainAffix: {},
|
MainAffix: {},
|
||||||
SubAffix: {},
|
SubAffix: {},
|
||||||
SkillConfig: {},
|
SkillConfig: {},
|
||||||
Stage: {},
|
Stage: {},
|
||||||
HardLevelConfig: {},
|
HardLevelConfig: {},
|
||||||
EliteConfig: {}
|
EliteConfig: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAvatarListApi(): Promise<Record<string, AvatarDetail>> {
|
export async function getAvatarListApi(): Promise<Record<string, AvatarDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, AvatarDetail>>(`/api/data/avatar`);
|
const res = await axios.get<Record<string, AvatarDetail>>(`/api/data/avatar`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch Avatars:', error);
|
console.error('Failed to fetch Avatars:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLightconeListApi(): Promise<Record<string, LightConeDetail>> {
|
export async function getLightconeListApi(): Promise<Record<string, LightConeDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, LightConeDetail>>(`/api/data/lightcone`);
|
const res = await axios.get<Record<string, LightConeDetail>>(`/api/data/lightcone`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch lightcones:', error);
|
console.error('Failed to fetch lightcones:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRelicSetListApi(): Promise<Record<string, RelicSetDetail>> {
|
export async function getRelicSetListApi(): Promise<Record<string, RelicSetDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, RelicSetDetail>>(`/api/data/relic`);
|
const res = await axios.get<Record<string, RelicSetDetail>>(`/api/data/relic`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch relics:', error);
|
console.error('Failed to fetch relics:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMonsterListApi(): Promise<Record<string, MonsterDetail>> {
|
export async function getMonsterListApi(): Promise<Record<string, MonsterDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, MonsterDetail>>(`/api/data/monster`);
|
const res = await axios.get<Record<string, MonsterDetail>>(`/api/data/monster`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch monster:', error);
|
console.error('Failed to fetch monster:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getASEventListApi(): Promise<Record<string, ASGroupDetail>> {
|
export async function getASEventListApi(): Promise<Record<string, ASGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, ASGroupDetail>>(`/api/data/as`);
|
const res = await axios.get<Record<string, ASGroupDetail>>(`/api/data/as`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch AS:', error);
|
console.error('Failed to fetch AS:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPFEventListApi(): Promise<Record<string, PFGroupDetail>> {
|
export async function getPFEventListApi(): Promise<Record<string, PFGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, PFGroupDetail>>(`/api/data/pf`);
|
const res = await axios.get<Record<string, PFGroupDetail>>(`/api/data/pf`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch PF:', error);
|
console.error('Failed to fetch PF:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMOCEventListApi(): Promise<Record<string, MOCGroupDetail>> {
|
export async function getMOCEventListApi(): Promise<Record<string, MOCGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, MOCGroupDetail>>(`/api/data/moc`);
|
const res = await axios.get<Record<string, MOCGroupDetail>>(`/api/data/moc`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch MOC:', error);
|
console.error('Failed to fetch MOC:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPeakEventListApi(): Promise<Record<string, PeakGroupDetail>> {
|
export async function getPeakEventListApi(): Promise<Record<string, PeakGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, PeakGroupDetail>>(`/api/data/peak`);
|
const res = await axios.get<Record<string, PeakGroupDetail>>(`/api/data/peak`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch peak:', error);
|
console.error('Failed to fetch peak:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getChangelog(): Promise<ChangelogItemType[]> {
|
export async function getChangelog(): Promise<ChangelogItemType[]> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<ChangelogItemType[]>(`/api/data/changelog`);
|
const res = await axios.get<ChangelogItemType[]>(`/api/data/changelog`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch monster:', error);
|
console.error('Failed to fetch monster:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function SendDataToServer(
|
export async function SendDataToServer(
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
serverUrl: string,
|
serverUrl: string,
|
||||||
data?: FreeSRJson,
|
data?: FreeSRJson,
|
||||||
extraData?: ExtraData
|
extraData?: ExtraData
|
||||||
): Promise<PSResponse | string> {
|
): Promise<PSResponse | string> {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${serverUrl}`, { username, password, data, extra_data: extraData })
|
const response = await axios.post(`${serverUrl}`, { username, password, data, extra_data: extraData })
|
||||||
const parsed = psResponseSchema.safeParse(response.data)
|
const parsed = psResponseSchema.safeParse(response.data)
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
return "Invalid response schema";
|
return "Invalid response schema";
|
||||||
}
|
}
|
||||||
return parsed.data;
|
return parsed.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return error?.message || "Unknown error";
|
return error?.message || "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function SendDataThroughProxy({ data }: { data: any }) {
|
export async function SendDataThroughProxy({ data }: { data: any }) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`/api/proxy`, { ...data })
|
const response = await axios.post(`/api/proxy`, { ...data })
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vendored
+37
-37
@@ -1,38 +1,38 @@
|
|||||||
import { readFileSync, readdirSync } from "fs"
|
import { readFileSync, readdirSync } from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
type CacheItem = {
|
type CacheItem = {
|
||||||
buf: Uint8Array
|
buf: Uint8Array
|
||||||
type: "json" | "br"
|
type: "json" | "br"
|
||||||
}
|
}
|
||||||
|
|
||||||
const cache = new Map<string, CacheItem>()
|
const cache = new Map<string, CacheItem>()
|
||||||
|
|
||||||
const dir = path.join(process.cwd(), "data")
|
const dir = path.join(process.cwd(), "data")
|
||||||
|
|
||||||
for (const f of readdirSync(dir)) {
|
for (const f of readdirSync(dir)) {
|
||||||
const file = path.join(dir, f)
|
const file = path.join(dir, f)
|
||||||
|
|
||||||
if (f.endsWith(".json.br")) {
|
if (f.endsWith(".json.br")) {
|
||||||
const name = f.replace(".json.br", "")
|
const name = f.replace(".json.br", "")
|
||||||
const buf = new Uint8Array(readFileSync(file))
|
const buf = new Uint8Array(readFileSync(file))
|
||||||
cache.set(name, {
|
cache.set(name, {
|
||||||
buf,
|
buf,
|
||||||
type: "br"
|
type: "br"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.endsWith(".json")) {
|
if (f.endsWith(".json")) {
|
||||||
const name = f.replace(".json", "")
|
const name = f.replace(".json", "")
|
||||||
const buf = new Uint8Array(readFileSync(file))
|
const buf = new Uint8Array(readFileSync(file))
|
||||||
|
|
||||||
cache.set(name, {
|
cache.set(name, {
|
||||||
buf,
|
buf,
|
||||||
type: "json"
|
type: "json"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDataCache(name: string) {
|
export function getDataCache(name: string) {
|
||||||
return cache.get(name)
|
return cache.get(name)
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
export * from "./useFetchMetaData";
|
export * from "./useFetchMetaData";
|
||||||
export * from "./useFetchAvatarData";
|
export * from "./useFetchAvatarData";
|
||||||
export * from "./useFetchLightconeData";
|
export * from "./useFetchLightconeData";
|
||||||
export * from "./useFetchRelicData";
|
export * from "./useFetchRelicData";
|
||||||
export * from "./useFetchMonsterData";
|
export * from "./useFetchMonsterData";
|
||||||
export * from "./useFetchPFData";
|
export * from "./useFetchPFData";
|
||||||
export * from "./useFetchMOCData";
|
export * from "./useFetchMOCData";
|
||||||
export * from "./useFetchASData";
|
export * from "./useFetchASData";
|
||||||
export * from "./useFetchPEAKData";
|
export * from "./useFetchPEAKData";
|
||||||
export * from "./useFetchChangelog";
|
export * from "./useFetchChangelog";
|
||||||
@@ -1,26 +1,26 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getASEventListApi } from '@/lib/api'
|
import { getASEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
|
|
||||||
export const useFetchASGroupData = () => {
|
export const useFetchASGroupData = () => {
|
||||||
const { setMapAS } = useDetailDataStore()
|
const { setMapAS } = useDetailDataStore()
|
||||||
|
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['ASGroupData'],
|
queryKey: ['ASGroupData'],
|
||||||
queryFn: getASEventListApi,
|
queryFn: getASEventListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data) {
|
if (query.data) {
|
||||||
setMapAS(query.data)
|
setMapAS(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load ASGroup data")
|
toast.error("Failed to load ASGroup data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapAS])
|
}, [query.data, query.error, setMapAS])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getAvatarListApi } from '@/lib/api'
|
import { getAvatarListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import useCurrentDataStore from '@/stores/currentDataStore'
|
import useCurrentDataStore from '@/stores/currentDataStore'
|
||||||
import useUserDataStore from '@/stores/userDataStore'
|
import useUserDataStore from '@/stores/userDataStore'
|
||||||
import { converterToAvatarStore } from '@/helper'
|
import { converterToAvatarStore } from '@/helper'
|
||||||
|
|
||||||
export const useFetchAvatarData = () => {
|
export const useFetchAvatarData = () => {
|
||||||
const { setMapAvatar, mapAvatar } = useDetailDataStore()
|
const { setMapAvatar, mapAvatar } = useDetailDataStore()
|
||||||
const { setAvatar, avatars } = useUserDataStore()
|
const { setAvatar, avatars } = useUserDataStore()
|
||||||
const { avatarSelected, setAvatarSelected } = useCurrentDataStore()
|
const { avatarSelected, setAvatarSelected } = useCurrentDataStore()
|
||||||
|
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['AvatarData'],
|
queryKey: ['AvatarData'],
|
||||||
queryFn: getAvatarListApi,
|
queryFn: getAvatarListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listAvatarId = Object.keys(avatars)
|
const listAvatarId = Object.keys(avatars)
|
||||||
const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId))
|
const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId))
|
||||||
const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist))
|
const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist))
|
||||||
if (Object.keys(avatarStore).length === 0) return
|
if (Object.keys(avatarStore).length === 0) return
|
||||||
for (const avatar of Object.values(avatarStore)) {
|
for (const avatar of Object.values(avatarStore)) {
|
||||||
setAvatar(avatar)
|
setAvatar(avatar)
|
||||||
}
|
}
|
||||||
}, [mapAvatar])
|
}, [mapAvatar])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data) {
|
if (query.data) {
|
||||||
setMapAvatar(query.data)
|
setMapAvatar(query.data)
|
||||||
if (!avatarSelected) {
|
if (!avatarSelected) {
|
||||||
setAvatarSelected(Object.values(query.data)[0])
|
setAvatarSelected(Object.values(query.data)[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query.error) toast.error("Failed to load Avatar data")
|
if (query.error) toast.error("Failed to load Avatar data")
|
||||||
}, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected])
|
}, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,32 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getChangelog } from '@/lib/api'
|
import { getChangelog } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useModelStore from '@/stores/modelStore'
|
import useModelStore from '@/stores/modelStore'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
import useLocaleStore from '@/stores/localeStore'
|
||||||
|
|
||||||
export const useFetchChangelog = () => {
|
export const useFetchChangelog = () => {
|
||||||
const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore()
|
const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore()
|
||||||
const { setIsChangelog } = useModelStore()
|
const { setIsChangelog } = useModelStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['changelog'],
|
queryKey: ['changelog'],
|
||||||
queryFn: getChangelog,
|
queryFn: getChangelog,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setChangelog(query.data)
|
setChangelog(query.data)
|
||||||
if (query.data?.[0] && query.data[0].version != currentVersion) {
|
if (query.data?.[0] && query.data[0].version != currentVersion) {
|
||||||
setIsChangelog(true)
|
setIsChangelog(true)
|
||||||
setCurrentVersion(query.data[0].version)
|
setCurrentVersion(query.data[0].version)
|
||||||
}
|
}
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load changelog data")
|
toast.error("Failed to load changelog data")
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog])
|
}, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getLightconeListApi } from '@/lib/api';
|
import { getLightconeListApi } from '@/lib/api';
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore';
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
|
||||||
export const useFetchLightconeData = () => {
|
export const useFetchLightconeData = () => {
|
||||||
const { setMapLightCone } = useDetailDataStore()
|
const { setMapLightCone } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['lightconeData'],
|
queryKey: ['lightconeData'],
|
||||||
queryFn: getLightconeListApi,
|
queryFn: getLightconeListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapLightCone(query.data)
|
setMapLightCone(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load lightcone data")
|
toast.error("Failed to load lightcone data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapLightCone])
|
}, [query.data, query.error, setMapLightCone])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getMOCEventListApi } from '@/lib/api'
|
import { getMOCEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchMOCGroupData = () => {
|
export const useFetchMOCGroupData = () => {
|
||||||
const { setMapMoc } = useDetailDataStore()
|
const { setMapMoc } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['MOCGroupData'],
|
queryKey: ['MOCGroupData'],
|
||||||
queryFn: getMOCEventListApi,
|
queryFn: getMOCEventListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapMoc(query.data)
|
setMapMoc(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load MOCGroup data")
|
toast.error("Failed to load MOCGroup data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapMoc])
|
}, [query.data, query.error, setMapMoc])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { getMetadataApi } from '@/lib/api'
|
import { getMetadataApi } from '@/lib/api'
|
||||||
import useCurrentDataStore from '@/stores/currentDataStore'
|
import useCurrentDataStore from '@/stores/currentDataStore'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
|
|
||||||
export const useFetchConfigData = () => {
|
export const useFetchConfigData = () => {
|
||||||
const { setMetaData } = useDetailDataStore()
|
const { setMetaData } = useDetailDataStore()
|
||||||
const { setSettingData } = useCurrentDataStore()
|
const { setSettingData } = useCurrentDataStore()
|
||||||
|
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['initialConfigData'],
|
queryKey: ['initialConfigData'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const metaData = await getMetadataApi()
|
const metaData = await getMetadataApi()
|
||||||
return { metaData }
|
return { metaData }
|
||||||
},
|
},
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setSettingData(query.data.metaData)
|
setSettingData(query.data.metaData)
|
||||||
setMetaData(query.data.metaData)
|
setMetaData(query.data.metaData)
|
||||||
}
|
}
|
||||||
else if (query.error) {
|
else if (query.error) {
|
||||||
toast.error("Failed to load initial config data")
|
toast.error("Failed to load initial config data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMetaData, setSettingData])
|
}, [query.data, query.error, setMetaData, setSettingData])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getMonsterListApi } from '@/lib/api'
|
import { getMonsterListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchMonsterData = () => {
|
export const useFetchMonsterData = () => {
|
||||||
const { setMapMonster } = useDetailDataStore()
|
const { setMapMonster } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['MonsterData'],
|
queryKey: ['MonsterData'],
|
||||||
queryFn: getMonsterListApi,
|
queryFn: getMonsterListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapMonster(query.data)
|
setMapMonster(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load Monster data")
|
toast.error("Failed to load Monster data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapMonster])
|
}, [query.data, query.error, setMapMonster])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getPeakEventListApi } from '@/lib/api'
|
import { getPeakEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchPeakGroupData = () => {
|
export const useFetchPeakGroupData = () => {
|
||||||
const { setMapPeak } = useDetailDataStore()
|
const { setMapPeak } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['PeakGroupData'],
|
queryKey: ['PeakGroupData'],
|
||||||
queryFn: getPeakEventListApi,
|
queryFn: getPeakEventListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapPeak(query.data)
|
setMapPeak(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load PeakGroup data")
|
toast.error("Failed to load PeakGroup data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapPeak])
|
}, [query.data, query.error, setMapPeak])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getPFEventListApi } from '@/lib/api'
|
import { getPFEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchPFGroupData = () => {
|
export const useFetchPFGroupData = () => {
|
||||||
const { setMapPF } = useDetailDataStore()
|
const { setMapPF } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['PFGroupData'],
|
queryKey: ['PFGroupData'],
|
||||||
queryFn: getPFEventListApi,
|
queryFn: getPFEventListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapPF(query.data)
|
setMapPF(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load PFGroup data")
|
toast.error("Failed to load PFGroup data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapPF])
|
}, [query.data, query.error, setMapPF])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getRelicSetListApi } from '@/lib/api'
|
import { getRelicSetListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useDetailDataStore from '@/stores/detailDataStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchRelicSetData = () => {
|
export const useFetchRelicSetData = () => {
|
||||||
const { setMapRelicSet } = useDetailDataStore()
|
const { setMapRelicSet } = useDetailDataStore()
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['RelicSetData'],
|
queryKey: ['RelicSetData'],
|
||||||
queryFn: getRelicSetListApi,
|
queryFn: getRelicSetListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.data && !query.error) {
|
if (query.data && !query.error) {
|
||||||
setMapRelicSet(query.data)
|
setMapRelicSet(query.data)
|
||||||
} else if (query.error) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load RelicSet data")
|
toast.error("Failed to load RelicSet data")
|
||||||
}
|
}
|
||||||
}, [query.data, query.error, setMapRelicSet])
|
}, [query.data, query.error, setMapRelicSet])
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-24
@@ -2,19 +2,19 @@ import { create } from 'zustand'
|
|||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import { connectPersistedSchema } from '@/zod';
|
import { connectPersistedSchema } from '@/zod';
|
||||||
import { createValidatedJSONStorage } from './persistStorage';
|
import { createValidatedJSONStorage } from './persistStorage';
|
||||||
|
|
||||||
|
|
||||||
interface ConnectState {
|
interface ConnectState {
|
||||||
connectionType: string;
|
connectionType: string;
|
||||||
privateType: string;
|
privateType: string;
|
||||||
serverUrl: string;
|
serverUrl: string;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
setConnectionType: (newConnectionType: string) => void;
|
setConnectionType: (newConnectionType: string) => void;
|
||||||
setPrivateType: (newPrivateType: string) => void;
|
setPrivateType: (newPrivateType: string) => void;
|
||||||
setServerUrl: (newServerUrl: string) => void;
|
setServerUrl: (newServerUrl: string) => void;
|
||||||
setUsername: (newUsername: string) => void;
|
setUsername: (newUsername: string) => void;
|
||||||
setPassword: (newPassword: string) => void;
|
setPassword: (newPassword: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectPersistedState = Pick<ConnectState, "connectionType" | "privateType" | "serverUrl" | "username" | "password">;
|
type ConnectPersistedState = Pick<ConnectState, "connectionType" | "privateType" | "serverUrl" | "username" | "password">;
|
||||||
@@ -22,17 +22,17 @@ type ConnectPersistedState = Pick<ConnectState, "connectionType" | "privateType"
|
|||||||
const useConnectStore = create<ConnectState>()(
|
const useConnectStore = create<ConnectState>()(
|
||||||
persist<ConnectState, [], [], ConnectPersistedState>(
|
persist<ConnectState, [], [], ConnectPersistedState>(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
connectionType: "FireflyGo",
|
connectionType: "FireflyGo",
|
||||||
privateType: "Local",
|
privateType: "Local",
|
||||||
serverUrl: "http://localhost:21000",
|
serverUrl: "http://localhost:21000",
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
setConnectionType: (newConnectionType: string) => set({ connectionType: newConnectionType }),
|
setConnectionType: (newConnectionType: string) => set({ connectionType: newConnectionType }),
|
||||||
setPrivateType: (newPrivateType: string) => set({ privateType: newPrivateType }),
|
setPrivateType: (newPrivateType: string) => set({ privateType: newPrivateType }),
|
||||||
setServerUrl: (newServerUrl: string) => set({ serverUrl: newServerUrl }),
|
setServerUrl: (newServerUrl: string) => set({ serverUrl: newServerUrl }),
|
||||||
setUsername: (newUsername: string) => set({ username: newUsername }),
|
setUsername: (newUsername: string) => set({ username: newUsername }),
|
||||||
setPassword: (newPassword: string) => set({ password: newPassword }),
|
setPassword: (newPassword: string) => set({ password: newPassword }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'connect-storage',
|
name: 'connect-storage',
|
||||||
storage: createValidatedJSONStorage(() => localStorage, connectPersistedSchema),
|
storage: createValidatedJSONStorage(() => localStorage, connectPersistedSchema),
|
||||||
@@ -46,5 +46,5 @@ const useConnectStore = create<ConnectState>()(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export default useConnectStore;
|
export default useConnectStore;
|
||||||
|
|||||||
+40
-40
@@ -1,41 +1,41 @@
|
|||||||
import { AvatarDetail, AvatarProfileCardType, BaseTypeData, DamageTypeData } from '@/types';
|
import { AvatarDetail, AvatarProfileCardType, BaseTypeData, DamageTypeData } from '@/types';
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
|
||||||
interface CopyProfileState {
|
interface CopyProfileState {
|
||||||
selectedProfiles: AvatarProfileCardType[];
|
selectedProfiles: AvatarProfileCardType[];
|
||||||
avatarCopySelected: AvatarDetail | null;
|
avatarCopySelected: AvatarDetail | null;
|
||||||
listElement: Record<string, boolean>;
|
listElement: Record<string, boolean>;
|
||||||
listPath: Record<string, boolean>;
|
listPath: Record<string, boolean>;
|
||||||
listRank: Record<string, boolean>;
|
listRank: Record<string, boolean>;
|
||||||
setListElement: (newListElement: Record<string, boolean>) => void;
|
setListElement: (newListElement: Record<string, boolean>) => void;
|
||||||
setListPath: (newListPath: Record<string, boolean>) => void;
|
setListPath: (newListPath: Record<string, boolean>) => void;
|
||||||
setListRank: (newListRank: Record<string, boolean>) => void;
|
setListRank: (newListRank: Record<string, boolean>) => void;
|
||||||
setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void;
|
setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void;
|
||||||
setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => void;
|
setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => void;
|
||||||
setResetData: (baseType: Record<string, BaseTypeData>, damageType: Record<string, DamageTypeData>) => void;
|
setResetData: (baseType: Record<string, BaseTypeData>, damageType: Record<string, DamageTypeData>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCopyProfileStore = create<CopyProfileState>((set) => ({
|
const useCopyProfileStore = create<CopyProfileState>((set) => ({
|
||||||
selectedProfiles: [],
|
selectedProfiles: [],
|
||||||
avatarCopySelected: null,
|
avatarCopySelected: null,
|
||||||
listElement: {},
|
listElement: {},
|
||||||
listPath: {},
|
listPath: {},
|
||||||
listRank: { "4": false, "5": false },
|
listRank: { "4": false, "5": false },
|
||||||
listCopyAvatar: [],
|
listCopyAvatar: [],
|
||||||
listRawCopyAvatar: [],
|
listRawCopyAvatar: [],
|
||||||
|
|
||||||
setListElement: (newListElement: Record<string, boolean>) => set({ listElement: newListElement }),
|
setListElement: (newListElement: Record<string, boolean>) => set({ listElement: newListElement }),
|
||||||
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
|
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
|
||||||
setListRank: (newListRank: Record<string, boolean>) => set({ listRank: newListRank }),
|
setListRank: (newListRank: Record<string, boolean>) => set({ listRank: newListRank }),
|
||||||
setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => set({ avatarCopySelected: newAvatarSelected }),
|
setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => set({ avatarCopySelected: newAvatarSelected }),
|
||||||
setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => set({ selectedProfiles: newListAvatar }),
|
setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => set({ selectedProfiles: newListAvatar }),
|
||||||
setResetData: (baseType: Record<string, BaseTypeData>, damageType: Record<string, DamageTypeData>) => {
|
setResetData: (baseType: Record<string, BaseTypeData>, damageType: Record<string, DamageTypeData>) => {
|
||||||
set({
|
set({
|
||||||
listElement: Object.fromEntries(Object.keys(damageType).map(key => [key, false])) as Record<string, boolean>,
|
listElement: Object.fromEntries(Object.keys(damageType).map(key => [key, false])) as Record<string, boolean>,
|
||||||
listPath: Object.fromEntries(Object.keys(baseType).map(key => [key, false])) as Record<string, boolean>,
|
listPath: Object.fromEntries(Object.keys(baseType).map(key => [key, false])) as Record<string, boolean>,
|
||||||
listRank: { "4": false, "5": false }
|
listRank: { "4": false, "5": false }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useCopyProfileStore;
|
export default useCopyProfileStore;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user