diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 0f1b04d..6dac2c7 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,15 +1,15 @@ -name: Gitea Auto Deploy -run-name: ${{ gitea.actor }} pushed code 🚀 - -on: [push] - -jobs: - Deploy-Container: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Deploy to Container - run: | +name: Gitea Auto Deploy +run-name: ${{ gitea.actor }} pushed code 🚀 + +on: [push] + +jobs: + Deploy-Container: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Deploy to Container + run: | docker compose up -d --build --remove-orphans \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7d301fa..134ebbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,55 +1,55 @@ -FROM oven/bun:canary-alpine AS base - -FROM base AS deps - -WORKDIR /app - -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Disable telemetry during the build -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN bun run build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV=production - -# Disable telemetry -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder /app/public ./public -COPY --from=builder /app/messages ./messages - -# Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:bun .next - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:bun /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static - -USER nextjs - -EXPOSE 3000 - -ENV PORT=3000 - -# Set hostname to localhost -ENV HOSTNAME=0.0.0.0 - -CMD ["bun", "server.js"] +FROM oven/bun:canary-alpine AS base + +FROM base AS deps + +WORKDIR /app + +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Disable telemetry during the build +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN bun run build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# Disable telemetry +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder /app/messages ./messages + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:bun .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:bun /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +# Set hostname to localhost +ENV HOSTNAME=0.0.0.0 + +CMD ["bun", "server.js"] diff --git a/README.md b/README.md index 0326a51..f019531 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,62 @@ -# Firefly SrTools - -![Next.js](https://img.shields.io/badge/Next.js-black?style=for-the-badge&logo=next.js&logoColor=white) -![Bun](https://img.shields.io/badge/Bun-%23000000.svg?style=for-the-badge&logo=bun&logoColor=white) -![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white) -![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) - -**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 - -The application is deployed and available at the following URLs: - -| Role | URL | -| :--- | :--- | -| **Main Site** | [srtools.punklorde.org](https://srtools.punklorde.org) | - ---- - -## Key Features - -* **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. -* **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. -* **Modular Design:** Clean separation of concerns with reusable components and utilities. - -## Technology Stack - -* **Frontend Framework:** [Next.js 15](https://nextjs.org/) (React) -* **Runtime & Package Manager:** [Bun](https://bun.sh/) -* **Language:** TypeScript -* **Styling:** (Tailwind CSS, DaisyUI) -* **State Management:** (Zustand) - ---- - -## Prerequisites - -Ensure you have **Bun** installed on your system. - -Install Bun (macOS, WSL, Linux): -```bash -curl -fsSL [https://bun.sh/install](https://bun.sh/install) | bash -``` - -## Development - -```bash -bun install -``` -```bash -bun run dev -``` -```bash -bun run build -bun run start -``` - - +# Firefly SrTools + +![Next.js](https://img.shields.io/badge/Next.js-black?style=for-the-badge&logo=next.js&logoColor=white) +![Bun](https://img.shields.io/badge/Bun-%23000000.svg?style=for-the-badge&logo=bun&logoColor=white) +![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white) +![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) + +**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 + +The application is deployed and available at the following URLs: + +| Role | URL | +| :--- | :--- | +| **Main Site** | [srtools.punklorde.org](https://srtools.punklorde.org) | + +--- + +## Key Features + +* **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. +* **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. +* **Modular Design:** Clean separation of concerns with reusable components and utilities. + +## Technology Stack + +* **Frontend Framework:** [Next.js 15](https://nextjs.org/) (React) +* **Runtime & Package Manager:** [Bun](https://bun.sh/) +* **Language:** TypeScript +* **Styling:** (Tailwind CSS, DaisyUI) +* **State Management:** (Zustand) + +--- + +## Prerequisites + +Ensure you have **Bun** installed on your system. + +Install Bun (macOS, WSL, Linux): +```bash +curl -fsSL [https://bun.sh/install](https://bun.sh/install) | bash +``` + +## Development + +```bash +bun install +``` +```bash +bun run dev +``` +```bash +bun run build +bun run start +``` + + diff --git a/data/as.json.br b/data/as.json.br index d749a79..a5b9fe6 100644 Binary files a/data/as.json.br and b/data/as.json.br differ diff --git a/data/avatar.json.br b/data/avatar.json.br index b4374d1..3d3b457 100644 Binary files a/data/avatar.json.br and b/data/avatar.json.br differ diff --git a/data/changelog.json b/data/changelog.json index ad42486..d67e6ea 100644 --- a/data/changelog.json +++ b/data/changelog.json @@ -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", "type": "update", "items": [ - "New data for 4.2.5" + "New data for 4.3.0" ] }, { diff --git a/data/lightcone.json.br b/data/lightcone.json.br index 7d08e39..504c4ba 100644 Binary files a/data/lightcone.json.br and b/data/lightcone.json.br differ diff --git a/data/metadata.json.br b/data/metadata.json.br index 5f55f90..92b3e71 100644 Binary files a/data/metadata.json.br and b/data/metadata.json.br differ diff --git a/data/moc.json.br b/data/moc.json.br index bd2389c..5155f7f 100644 Binary files a/data/moc.json.br and b/data/moc.json.br differ diff --git a/data/monster.json.br b/data/monster.json.br index de75de6..fb200ba 100644 Binary files a/data/monster.json.br and b/data/monster.json.br differ diff --git a/data/peak.json.br b/data/peak.json.br index 1ec6c45..d4e28bf 100644 Binary files a/data/peak.json.br and b/data/peak.json.br differ diff --git a/data/pf.json.br b/data/pf.json.br index 714e9f5..d2e3562 100644 Binary files a/data/pf.json.br and b/data/pf.json.br differ diff --git a/data/relic.json.br b/data/relic.json.br index 19d4a63..1058839 100644 Binary files a/data/relic.json.br and b/data/relic.json.br differ diff --git a/docker-compose.yml b/docker-compose.yml index 8d37027..7515e72 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,15 @@ -services: - srtools-live: - build: - context: . - dockerfile: Dockerfile - container_name: srtools-live - restart: unless-stopped - ports: - - "3009:3000" - networks: - - srtools-live-network - -networks: - srtools-live-network: - driver: bridge +services: + srtools-live: + build: + context: . + dockerfile: Dockerfile + container_name: srtools-live + restart: unless-stopped + ports: + - "3009:3000" + networks: + - srtools-live-network + +networks: + srtools-live-network: + driver: bridge diff --git a/messages/de.json b/messages/de.json index d857abb..f6f080b 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Firefly-Tools von Firefly Shelter" - }, - "DataPage": { - "skillType": "Fähigkeitstyp", - "skillName": "Fähigkeitsname", - "character": "Charakter", - "id": "ID", - "path": "Pfad", - "rarity": "Seltenheit", - "element": "Element", - "technique": "Technik", - "talent": "Talent", - "basic": "Standardangriff", - "skill": "Fähigkeit", - "ultimate": "Ultimate", - "servant": "Diener", - "damage": "Schaden", - "type": "Typ", - "warrior": "Die Zerstörung", - "knight": "Die Bewahrung", - "mage": "Die Gelehrsamkeit", - "priest": "Der Überfluss", - "rogue": "Die Jagd", - "shaman": "Die Harmonie", - "warlock": "Die Nichtigkeit", - "memory": "Die Erinnerung", - "elation": "Die Freude", - "fire": "Feuer", - "ice": "Eis", - "imaginary": "Imaginär", - "physical": "Physisch", - "quantum": "Quanten", - "thunder": "Blitz", - "wind": "Wind", - "hp": "LP", - "atk": "ANG", - "speed": "GES", - "critRate": "Krit. Rate", - "critDmg": "Krit. SCH", - "breakEffect": "Brucheffekt", - "effectRes": "Effekt-WDS", - "energyRegenerationRate": "Energie-Regenerationsrate", - "effectHitRate": "Effekttrefferquote", - "outgoingHealingBoost": "Ausgehender Heilungsbonus", - "fireDmgBoost": "Feuer-SCH-Bonus", - "iceDmgBoost": "Eis-SCH-Bonus", - "imaginaryDmgBoost": "Imaginär-SCH-Bonus", - "physicalDmgBoost": "Physisch-SCH-Bonus", - "quantumDmgBoost": "Quanten-SCH-Bonus", - "thunderDmgBoost": "Blitz-SCH-Bonus", - "windDmgBoost": "Wind-SCH-Bonus", - "pursued": "Zusätzlicher Schaden", - "true damage": "Wahrer Schaden", - "elationdamage": "Freude-Schaden", - "follow-up": "Folgeangriff-Schaden", - "elemental damage": "Bruch- und Superbruch-Schaden", - "dot": "Schaden über Zeit", - "qte": "QTE-Fähigkeit", - "level": "Stufe", - "relics": "Relikte", - "eidolons": "Eidolons", - "lightcones": "Lichtkegel", - "loadData": "Daten laden", - "exportData": "Daten exportieren", - "connectSetting": "Verbindungseinstellungen", - "connected": "Verbunden", - "unconnected": "Nicht verbunden", - "psConnection": "PS-Verbindung", - "connectionType": "Verbindungstyp", - "status": "Status", - "connectPs": "Mit PS verbinden", - "disconnect": "Trennen", - "other": "Andere", - "freeSr": "FreeSR", - "database": "Datenbank", - "enka": "Enka", - "monsterSetting": "Monstereinstellungen", - "serverUrl": "Server-URL", - "privateType": "Privater Typ", - "local": "Lokal", - "server": "Server", - "username": "Benutzername", - "password": "Passwort", - "placeholderServerUrl": "Server-URL eingeben", - "placeholderUsername": "Benutzernamen eingeben", - "placeholderPassword": "Passwort eingeben", - "connectedSuccess": "Erfolgreich mit PS verbunden", - "connectedFailed": "Verbindung zu PS fehlgeschlagen", - "syncSuccess": "Daten erfolgreich mit PS synchronisiert", - "syncFailed": "Fehler beim Synchronisieren", - "sync": "Synchronisieren", - "importSetting": "Import-Einstellungen", - "profile": "Profil", - "default": "Standard", - "copyProfiles": "Profile kopieren", - "addNewProfile": "Neues Profil hinzufügen", - "createNewProfile": "Neues Profil erstellen", - "editProfile": "Profil bearbeiten", - "placeholderProfileName": "Profilnamen eingeben", - "profileName": "Profilname", - "create": "Erstellen", - "update": "Aktualisieren", - "characterInformation": "Charakterinformationen", - "skills": "Fähigkeiten", - "showcaseCard": "Showcase-Karte", - "comingSoon": "Demnächst", - "characterName": "Charaktername", - "placeholderCharacter": "Charakternamen eingeben", - "characterSettings": "Charaktereinstellungen", - "levelConfiguration": "Stufenkonfiguration", - "characterLevel": "Charakterstufe", - "max": "MAX", - "ultimateEnergy": "Ultimate-Energie", - "currentEnergy": "Aktuelle Energie", - "setTo50": "Auf 50% setzen", - "battleConfiguration": "Kampfkonfiguration", - "useTechnique": "Technik verwenden", - "techniqueNote": "Technikeffekte vor dem Kampf aktivieren", - "enhancement": "Verbesserung", - "enhancementLevel": "Verbesserungsstufe", - "origin": "Ursprung", - "enhancedNote": "Höhere Verbesserungen schalten Fähigkeiten frei", - "lightconeEquipment": "Lichtkegel-Ausrüstung", - "lightconeSettings": "Lichtkegel-Einstellungen", - "placeholderLevel": "Stufe eingeben", - "superimpositionRank": "Überlagerungsrang", - "ranksNote": "Höhere Ränge bieten stärkere Effekte", - "changeLightcone": "Lichtkegel wechseln", - "removeLightcone": "Lichtkegel entfernen", - "equipLightcone": "Lichtkegel ausrüsten", - "noLightconeEquipped": "Kein Lichtkegel ausgerüstet", - "equipLightconeNote": "Rüste einen Lichtkegel aus, um deinen Charakter zu stärken", - "filter": "Filter", - "selectedCharacters": "Ausgewählte Charaktere", - "selectedProfiles": "Ausgewählte Profile", - "clearAll": "Alles löschen", - "selectAll": "Alles auswählen", - "copy": "Kopieren", - "copied": "Kopiert", - "noAvatarSelected": "Kein Charakter ausgewählt", - "noAvatarToCopySelected": "Kein Charakter zum Kopieren ausgewählt", - "pleaseSelectAtLeastOneProfile": "Bitte wähle mindestens ein Profil aus", - "pleaseEnterUid": "Bitte UID eingeben", - "failedToFetchEnkaData": "Fehler beim Abrufen der Enka-Daten", - "pleaseSelectAtLeastOneCharacter": "Bitte wähle mindestens einen Charakter aus", - "noDataToImport": "Keine Daten zum Importieren", - "pleaseSelectAFile": "Bitte wähle eine Datei", - "fileMustBeAValidJsonFile": "Die Datei muss eine gültige JSON-Datei sein", - "importEnkaDataSuccess": "Enka-Daten erfolgreich importiert", - "importFreeSRDataSuccess": "FreeSR-Daten erfolgreich importiert", - "importDatabaseSuccess": "Datenbank erfolgreich importiert", - "getData": "Daten abrufen", - "import": "Importieren", - "freeSRImport": "FreeSR-Import", - "onlySupportFreeSRJsonFile": "Unterstützt nur FreeSR-JSON-Dateien", - "pickAFile": "Datei auswählen", - "lightConeSetting": "Lichtkegel-Einstellung", - "relicMaker": "Relikt-Ersteller", - "pleaseSelectAllOptions": "Bitte wähle alle Optionen", - "relicSavedSuccessfully": "Relikt erfolgreich gespeichert", - "mainSettings": "Haupteinstellungen", - "mainStat": "Hauptwert", - "set": "Set", - "pleaseSelectASet": "Bitte wähle ein Set", - "effectBonus": "Effektbonus", - "totalRoll": "Gesamte Aufwertungen", - "randomizeStats": "Werte zufällig", - "randomizeRolls": "Aufwertungen zufällig", - "selectASubStat": "Wähle einen Nebenwert", - "selectASet": "Wähle ein Set", - "selectAMainStat": "Wähle einen Hauptwert", - "save": "Speichern", - "reset": "Zurücksetzen", - "roll": "Aufwertung", - "step": "Schritt", - "memoryOfChaos": "Vergessene Halle", - "pureFiction": "Reine Fiktion", - "apocalypticShadow": "Apokalyptischer Schatten", - "customEnemy": "Benutzerdefinierter Feind", - "simulatedUniverse": "Universum-Simulation", - "floor": "Ebene", - "side": "Seite", - "wave": "Welle", - "stage": "Phase", - "useCycleCount": "Zyklen-Zählung verwenden?", - "useTurbulenceBuff": "Turbulenz-Buff verwenden?", - "firstHalfEnemies": "Gegner erste Hälfte", - "secondHalfEnemies": "Gegner zweite Hälfte", - "firstNodeEnemies": "Gegner Knoten 1", - "secondNodeEnemies": "Gegner Knoten 2", - "thirdNodeEnemies": "Gegner Knoten 3", - "firstNode": "Knoten 1", - "secondNode": "Knoten 2", - "thirdNode": "Knoten 3", - "listEnemies": "Gegnerliste", - "turbulenceBuff": "Turbulenz-Buff", - "noEventSelected": "Kein Ereignis ausgewählt", - "noTurbulenceBuff": "Kein Turbulenz-Buff", - "upper": "Oben", - "lower": "Unten", - "upperToLower": "Oben -> Unten", - "lowerToUpper": "Unten -> Oben", - "selectMOCEvent": "MOC-Ereignis wählen", - "selectPFEvent": "PF-Ereignis wählen", - "selectASEvent": "AS-Ereignis wählen", - "selectCEEvent": "CE-Ereignis wählen", - "selectEvent": "Ereignis wählen", - "selectFloor": "Ebene wählen", - "selectSide": "Seite wählen", - "selectBuff": "Buff wählen", - "selectStage": "Phase wählen", - "previous": "Zurück", - "next": "Weiter", - "noMonstersFound": "Keine Monster gefunden", - "addNewWave": "Neue Welle hinzufügen", - "searchStage": "Phase suchen...", - "noStageFound": "Keine Phase gefunden", - "searchMonster": "Monster suchen...", - "changeRelic": "Relikt wechseln", - "deleteRelic": "Relikt löschen", - "deleteRelicConfirm": "Möchtest du das Relikt auf diesem Platz wirklich löschen?", - "setEffects": "Effekte einstellen", - "details": "Details", - "normal": "Standardangriff", - "bpskill": "Fähigkeit", - "maze": "Technik", - "ultra": "Ultimate", - "servantskill": "Memosprite-Fähigkeit", - "severaltalent": "Memosprite-Talent", - "singleattack": "Einzelangriff", - "enhance": "Verbessern", - "summon": "Beschwören", - "mazeattack": "Technik-Angriff", - "blast": "Explosion", - "restore": "Wiederherstellen", - "support": "Unterstützung", - "aoeattack": "Flächenangriff", - "impair": "Beeinträchtigen", - "bounce": "Abprallen", - "active": "Aktiv", - "defence": "Verteidigung", - "inactive": "Inaktiv", - "maxAll": "Alles maximieren", - "maxAllSuccess": "Fähigkeiten erfolgreich maximiert.", - "maxAllFailed": "Fehler beim Maximieren.", - "noRelicEquipped": "Kein Relikt ausgerüstet", - "anomalyArbitration": "Anomalie-Schiedsgericht", - "normalMode": "Normaler Modus", - "hardMode": "Schwerer Modus", - "selectPEAKEvent": "PEAK-Ereignis wählen", - "mode": "Modus", - "selectMode": "Modus wählen", - "rollBack": "Rückgängig", - "upRoll": "Aufwertung hoch", - "downRoll": "Aufwertung runter", - "actions": "Aktionen", - "avatars": "Avatare", - "quickView": "Schnellansicht", - "extraSetting": "Extra-Einstellungen", - "disableCensorship": "Zensur deaktivieren", - "hideUI": "UI verstecken", - "theoryCraftMode": "Theorycraft-Modus", - "cycleCount": "Zyklen-Anzahl", - "pleaseSelectAllSubStats": "Bitte alle Nebenwerte auswählen", - "subStatRollCountCannotBeZero": "Nebenwert-Aufwertungen dürfen nicht null sein", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Mehrpfad-Charakter", - "mainPath": "Hauptpfad", - "march7Path": "Pfad 7. März", - "challenge": "Herausforderung", - "skipNode": "Knoten überspringen", - "disableSkip": "Überspringen deaktivieren", - "skipNode1": "Knoten 1 überspringen", - "skipNode2": "Knoten 2 überspringen", - "extraFeatures": "Zusatzfunktionen", - "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.", - "detailChallengePeak": "Ändert die Peak-Saison in der aktuellen Anomalie.", - "detailHiddenUi": "Versteckt die Spiel-Benutzeroberfläche.", - "detailDisableCensorship": "Deaktiviert die Zensur im Spiel.", - "detailMultipathCharacter": "Ermöglicht das Ändern des Pfades bestimmter Charaktere.", - "trailblazer": "Trailblazer", - "listExtraEffect": "Liste Zusatzeffekte", - "extra": "Extra", - "customLineup": "Benutzerdefinierte Aufstellung" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly-Tools von Firefly Shelter" + }, + "DataPage": { + "skillType": "Fähigkeitstyp", + "skillName": "Fähigkeitsname", + "character": "Charakter", + "id": "ID", + "path": "Pfad", + "rarity": "Seltenheit", + "element": "Element", + "technique": "Technik", + "talent": "Talent", + "basic": "Standardangriff", + "skill": "Fähigkeit", + "ultimate": "Ultimate", + "servant": "Diener", + "damage": "Schaden", + "type": "Typ", + "warrior": "Die Zerstörung", + "knight": "Die Bewahrung", + "mage": "Die Gelehrsamkeit", + "priest": "Der Überfluss", + "rogue": "Die Jagd", + "shaman": "Die Harmonie", + "warlock": "Die Nichtigkeit", + "memory": "Die Erinnerung", + "elation": "Die Freude", + "fire": "Feuer", + "ice": "Eis", + "imaginary": "Imaginär", + "physical": "Physisch", + "quantum": "Quanten", + "thunder": "Blitz", + "wind": "Wind", + "hp": "LP", + "atk": "ANG", + "speed": "GES", + "critRate": "Krit. Rate", + "critDmg": "Krit. SCH", + "breakEffect": "Brucheffekt", + "effectRes": "Effekt-WDS", + "energyRegenerationRate": "Energie-Regenerationsrate", + "effectHitRate": "Effekttrefferquote", + "outgoingHealingBoost": "Ausgehender Heilungsbonus", + "fireDmgBoost": "Feuer-SCH-Bonus", + "iceDmgBoost": "Eis-SCH-Bonus", + "imaginaryDmgBoost": "Imaginär-SCH-Bonus", + "physicalDmgBoost": "Physisch-SCH-Bonus", + "quantumDmgBoost": "Quanten-SCH-Bonus", + "thunderDmgBoost": "Blitz-SCH-Bonus", + "windDmgBoost": "Wind-SCH-Bonus", + "pursued": "Zusätzlicher Schaden", + "true damage": "Wahrer Schaden", + "elationdamage": "Freude-Schaden", + "follow-up": "Folgeangriff-Schaden", + "elemental damage": "Bruch- und Superbruch-Schaden", + "dot": "Schaden über Zeit", + "qte": "QTE-Fähigkeit", + "level": "Stufe", + "relics": "Relikte", + "eidolons": "Eidolons", + "lightcones": "Lichtkegel", + "loadData": "Daten laden", + "exportData": "Daten exportieren", + "connectSetting": "Verbindungseinstellungen", + "connected": "Verbunden", + "unconnected": "Nicht verbunden", + "psConnection": "PS-Verbindung", + "connectionType": "Verbindungstyp", + "status": "Status", + "connectPs": "Mit PS verbinden", + "disconnect": "Trennen", + "other": "Andere", + "freeSr": "FreeSR", + "database": "Datenbank", + "enka": "Enka", + "monsterSetting": "Monstereinstellungen", + "serverUrl": "Server-URL", + "privateType": "Privater Typ", + "local": "Lokal", + "server": "Server", + "username": "Benutzername", + "password": "Passwort", + "placeholderServerUrl": "Server-URL eingeben", + "placeholderUsername": "Benutzernamen eingeben", + "placeholderPassword": "Passwort eingeben", + "connectedSuccess": "Erfolgreich mit PS verbunden", + "connectedFailed": "Verbindung zu PS fehlgeschlagen", + "syncSuccess": "Daten erfolgreich mit PS synchronisiert", + "syncFailed": "Fehler beim Synchronisieren", + "sync": "Synchronisieren", + "importSetting": "Import-Einstellungen", + "profile": "Profil", + "default": "Standard", + "copyProfiles": "Profile kopieren", + "addNewProfile": "Neues Profil hinzufügen", + "createNewProfile": "Neues Profil erstellen", + "editProfile": "Profil bearbeiten", + "placeholderProfileName": "Profilnamen eingeben", + "profileName": "Profilname", + "create": "Erstellen", + "update": "Aktualisieren", + "characterInformation": "Charakterinformationen", + "skills": "Fähigkeiten", + "showcaseCard": "Showcase-Karte", + "comingSoon": "Demnächst", + "characterName": "Charaktername", + "placeholderCharacter": "Charakternamen eingeben", + "characterSettings": "Charaktereinstellungen", + "levelConfiguration": "Stufenkonfiguration", + "characterLevel": "Charakterstufe", + "max": "MAX", + "ultimateEnergy": "Ultimate-Energie", + "currentEnergy": "Aktuelle Energie", + "setTo50": "Auf 50% setzen", + "battleConfiguration": "Kampfkonfiguration", + "useTechnique": "Technik verwenden", + "techniqueNote": "Technikeffekte vor dem Kampf aktivieren", + "enhancement": "Verbesserung", + "enhancementLevel": "Verbesserungsstufe", + "origin": "Ursprung", + "enhancedNote": "Höhere Verbesserungen schalten Fähigkeiten frei", + "lightconeEquipment": "Lichtkegel-Ausrüstung", + "lightconeSettings": "Lichtkegel-Einstellungen", + "placeholderLevel": "Stufe eingeben", + "superimpositionRank": "Überlagerungsrang", + "ranksNote": "Höhere Ränge bieten stärkere Effekte", + "changeLightcone": "Lichtkegel wechseln", + "removeLightcone": "Lichtkegel entfernen", + "equipLightcone": "Lichtkegel ausrüsten", + "noLightconeEquipped": "Kein Lichtkegel ausgerüstet", + "equipLightconeNote": "Rüste einen Lichtkegel aus, um deinen Charakter zu stärken", + "filter": "Filter", + "selectedCharacters": "Ausgewählte Charaktere", + "selectedProfiles": "Ausgewählte Profile", + "clearAll": "Alles löschen", + "selectAll": "Alles auswählen", + "copy": "Kopieren", + "copied": "Kopiert", + "noAvatarSelected": "Kein Charakter ausgewählt", + "noAvatarToCopySelected": "Kein Charakter zum Kopieren ausgewählt", + "pleaseSelectAtLeastOneProfile": "Bitte wähle mindestens ein Profil aus", + "pleaseEnterUid": "Bitte UID eingeben", + "failedToFetchEnkaData": "Fehler beim Abrufen der Enka-Daten", + "pleaseSelectAtLeastOneCharacter": "Bitte wähle mindestens einen Charakter aus", + "noDataToImport": "Keine Daten zum Importieren", + "pleaseSelectAFile": "Bitte wähle eine Datei", + "fileMustBeAValidJsonFile": "Die Datei muss eine gültige JSON-Datei sein", + "importEnkaDataSuccess": "Enka-Daten erfolgreich importiert", + "importFreeSRDataSuccess": "FreeSR-Daten erfolgreich importiert", + "importDatabaseSuccess": "Datenbank erfolgreich importiert", + "getData": "Daten abrufen", + "import": "Importieren", + "freeSRImport": "FreeSR-Import", + "onlySupportFreeSRJsonFile": "Unterstützt nur FreeSR-JSON-Dateien", + "pickAFile": "Datei auswählen", + "lightConeSetting": "Lichtkegel-Einstellung", + "relicMaker": "Relikt-Ersteller", + "pleaseSelectAllOptions": "Bitte wähle alle Optionen", + "relicSavedSuccessfully": "Relikt erfolgreich gespeichert", + "mainSettings": "Haupteinstellungen", + "mainStat": "Hauptwert", + "set": "Set", + "pleaseSelectASet": "Bitte wähle ein Set", + "effectBonus": "Effektbonus", + "totalRoll": "Gesamte Aufwertungen", + "randomizeStats": "Werte zufällig", + "randomizeRolls": "Aufwertungen zufällig", + "selectASubStat": "Wähle einen Nebenwert", + "selectASet": "Wähle ein Set", + "selectAMainStat": "Wähle einen Hauptwert", + "save": "Speichern", + "reset": "Zurücksetzen", + "roll": "Aufwertung", + "step": "Schritt", + "memoryOfChaos": "Vergessene Halle", + "pureFiction": "Reine Fiktion", + "apocalypticShadow": "Apokalyptischer Schatten", + "customEnemy": "Benutzerdefinierter Feind", + "simulatedUniverse": "Universum-Simulation", + "floor": "Ebene", + "side": "Seite", + "wave": "Welle", + "stage": "Phase", + "useCycleCount": "Zyklen-Zählung verwenden?", + "useTurbulenceBuff": "Turbulenz-Buff verwenden?", + "firstHalfEnemies": "Gegner erste Hälfte", + "secondHalfEnemies": "Gegner zweite Hälfte", + "firstNodeEnemies": "Gegner Knoten 1", + "secondNodeEnemies": "Gegner Knoten 2", + "thirdNodeEnemies": "Gegner Knoten 3", + "firstNode": "Knoten 1", + "secondNode": "Knoten 2", + "thirdNode": "Knoten 3", + "listEnemies": "Gegnerliste", + "turbulenceBuff": "Turbulenz-Buff", + "noEventSelected": "Kein Ereignis ausgewählt", + "noTurbulenceBuff": "Kein Turbulenz-Buff", + "upper": "Oben", + "lower": "Unten", + "upperToLower": "Oben -> Unten", + "lowerToUpper": "Unten -> Oben", + "selectMOCEvent": "MOC-Ereignis wählen", + "selectPFEvent": "PF-Ereignis wählen", + "selectASEvent": "AS-Ereignis wählen", + "selectCEEvent": "CE-Ereignis wählen", + "selectEvent": "Ereignis wählen", + "selectFloor": "Ebene wählen", + "selectSide": "Seite wählen", + "selectBuff": "Buff wählen", + "selectStage": "Phase wählen", + "previous": "Zurück", + "next": "Weiter", + "noMonstersFound": "Keine Monster gefunden", + "addNewWave": "Neue Welle hinzufügen", + "searchStage": "Phase suchen...", + "noStageFound": "Keine Phase gefunden", + "searchMonster": "Monster suchen...", + "changeRelic": "Relikt wechseln", + "deleteRelic": "Relikt löschen", + "deleteRelicConfirm": "Möchtest du das Relikt auf diesem Platz wirklich löschen?", + "setEffects": "Effekte einstellen", + "details": "Details", + "normal": "Standardangriff", + "bpskill": "Fähigkeit", + "maze": "Technik", + "ultra": "Ultimate", + "servantskill": "Memosprite-Fähigkeit", + "severaltalent": "Memosprite-Talent", + "singleattack": "Einzelangriff", + "enhance": "Verbessern", + "summon": "Beschwören", + "mazeattack": "Technik-Angriff", + "blast": "Explosion", + "restore": "Wiederherstellen", + "support": "Unterstützung", + "aoeattack": "Flächenangriff", + "impair": "Beeinträchtigen", + "bounce": "Abprallen", + "active": "Aktiv", + "defence": "Verteidigung", + "inactive": "Inaktiv", + "maxAll": "Alles maximieren", + "maxAllSuccess": "Fähigkeiten erfolgreich maximiert.", + "maxAllFailed": "Fehler beim Maximieren.", + "noRelicEquipped": "Kein Relikt ausgerüstet", + "anomalyArbitration": "Anomalie-Schiedsgericht", + "normalMode": "Normaler Modus", + "hardMode": "Schwerer Modus", + "selectPEAKEvent": "PEAK-Ereignis wählen", + "mode": "Modus", + "selectMode": "Modus wählen", + "rollBack": "Rückgängig", + "upRoll": "Aufwertung hoch", + "downRoll": "Aufwertung runter", + "actions": "Aktionen", + "avatars": "Avatare", + "quickView": "Schnellansicht", + "extraSetting": "Extra-Einstellungen", + "disableCensorship": "Zensur deaktivieren", + "hideUI": "UI verstecken", + "theoryCraftMode": "Theorycraft-Modus", + "cycleCount": "Zyklen-Anzahl", + "pleaseSelectAllSubStats": "Bitte alle Nebenwerte auswählen", + "subStatRollCountCannotBeZero": "Nebenwert-Aufwertungen dürfen nicht null sein", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Mehrpfad-Charakter", + "mainPath": "Hauptpfad", + "march7Path": "Pfad 7. März", + "challenge": "Herausforderung", + "skipNode": "Knoten überspringen", + "disableSkip": "Überspringen deaktivieren", + "skipNode1": "Knoten 1 überspringen", + "skipNode2": "Knoten 2 überspringen", + "extraFeatures": "Zusatzfunktionen", + "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.", + "detailChallengePeak": "Ändert die Peak-Saison in der aktuellen Anomalie.", + "detailHiddenUi": "Versteckt die Spiel-Benutzeroberfläche.", + "detailDisableCensorship": "Deaktiviert die Zensur im Spiel.", + "detailMultipathCharacter": "Ermöglicht das Ändern des Pfades bestimmter Charaktere.", + "trailblazer": "Trailblazer", + "listExtraEffect": "Liste Zusatzeffekte", + "extra": "Extra", + "customLineup": "Benutzerdefinierte Aufstellung" + } } \ No newline at end of file diff --git a/messages/en.json b/messages/en.json index 59cecb8..0a09f41 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Firefly tools by Firefly Shelter" - }, - "DataPage": { - "skillType": "Skill Type", - "skillName": "Skill Name", - "character": "Character", - "id": "Id", - "path": "Path", - "rarity": "Rarity", - "element": "Element", - "technique": "Technique", - "talent": "Talent", - "basic": "Basic Attack", - "skill": "Skill", - "ultimate": "Ultimate", - "servant": "Servant", - "damage": "Damage", - "type": "Type", - "warrior": "The Destruction", - "knight": "The Preservation", - "mage": "The Erudition", - "priest": "The Abundance", - "rogue": "The Hunt", - "shaman": "The Harmony", - "warlock": "The Nihility", - "memory": "The Remembrance", - "elation": "The Elation", - "fire": "Fire", - "ice": "Ice", - "imaginary": "Imaginary", - "physical": "Physical", - "quantum": "Quantum", - "thunder": "Thunder", - "wind": "Wind", - "hp": "Hp", - "atk": "Atk", - "speed": "Speed", - "critRate": "Crit Rate", - "critDmg": "Crit Dmg", - "breakEffect": "Break Effect", - "effectRes": "Effect Res", - "energyRegenerationRate": "Energy Regeneration Rate", - "effectHitRate": "Effect Hit Rate", - "outgoingHealingBoost": "Outgoing Healing Boost", - "fireDmgBoost": "Fire damage boost", - "iceDmgBoost": "Ice damage Boost", - "imaginaryDmgBoost": "Imaginary damage boost", - "physicalDmgBoost": "Physical damage boost", - "quantumDmgBoost": "Quantum damage boost", - "thunderDmgBoost": "Thunder damage boost", - "windDmgBoost": "Wind damage boost", - "pursued": "Additional damage", - "true damage": "True damage", - "elationdamage": "Elation damage", - "follow-up": "Follow-up Damage", - "elemental damage": "Break and Super break damage", - "dot": "Damage over time ", - "qte": "QTE Skill", - "level": "Level", - "relics": "Relics", - "eidolons": "Eidolons", - "lightcones": "Lightcones", - "loadData": "Load data", - "exportData": "Export data", - "connectSetting": "Connection Setting", - "connected": "Connected", - "unconnected": "Unconnected", - "psConnection": "PS Connection", - "connectionType": "Connection Type", - "status": "Status", - "connectPs": "Connect PS", - "disconnect": "Disconnect", - "other": "Other", - "freeSr": "FreeSR", - "database": "Database", - "enka": "Enka", - "monsterSetting": "Monster Setting", - "serverUrl": "Server URL", - "privateType": "Private Type", - "local": "Local", - "server": "Server", - "username": "Username", - "password": "Password", - "placeholderServerUrl": "Enter server URL", - "placeholderUsername": "Enter username", - "placeholderPassword": "Enter password", - "connectedSuccess": "Connected to PS successfully", - "connectedFailed": "Failed to connect to PS", - "syncSuccess": "Synced data to PS successfully", - "syncFailed": "Failed to sync data to PS", - "sync": "Sync", - "importSetting": "Import Setting", - "profile": "Profile", - "default": "Default", - "copyProfiles": "Copy profiles", - "addNewProfile": "Add new profile", - "createNewProfile": "Create new profile", - "editProfile": "Edit profile", - "placeholderProfileName": "Enter profile name", - "profileName": "Profile name", - "create": "Create", - "update": "Update", - "characterInformation": "Character Information", - "skills": "Skills", - "showcaseCard": "Showcase Card", - "comingSoon": "Coming soon", - "characterName": "Character name", - "placeholderCharacter": "Enter character name", - "characterSettings": "Character Settings", - "levelConfiguration": "Level Configuration", - "characterLevel": "Character Level", - "max": "MAX", - "ultimateEnergy": "Ultimate Energy", - "currentEnergy": "Current Energy", - "setTo50": "Set to 50%", - "battleConfiguration": "Battle Configuration", - "useTechnique": "Use Technique", - "techniqueNote": "Enable pre-battle technique effects", - "enhancement": "Enhancement", - "enhancementLevel": "Enhancement Level", - "origin": "Origin", - "enhancedNote": "Higher enhanced unlock additional abilities", - "lightconeEquipment": "Lightcone Equipment", - "lightconeSettings": "Lightcone Settings", - "placeholderLevel": "Enter level", - "superimpositionRank": "Superimposition Rank", - "ranksNote": "Higher ranks provide stronger effects", - "changeLightcone": "Change Lightcone", - "removeLightcone": "Remove Lightcone", - "equipLightcone": "Equip Lightcone", - "noLightconeEquipped": "No Lightcone Equipped", - "equipLightconeNote": "Equip a lightcone to enhance your character's abilities", - "filter": "Filter", - "selectedCharacters": "Selected Characters", - "selectedProfiles": "Selected Profiles", - "clearAll": "Clear All", - "selectAll": "Select All", - "copy": "Copy", - "copied": "Copied", - "noAvatarSelected": "No avatar selected", - "noAvatarToCopySelected": "No avatar to copy selected", - "pleaseSelectAtLeastOneProfile": "Please select at least one profile", - "pleaseEnterUid": "Please enter UID", - "failedToFetchEnkaData": "Failed to fetch enka data", - "pleaseSelectAtLeastOneCharacter": "Please select at least one character", - "noDataToImport": "No data to import", - "pleaseSelectAFile": "Please select a file", - "fileMustBeAValidJsonFile": "File must be a valid json file", - "importEnkaDataSuccess": "Import Enka data success", - "importFreeSRDataSuccess": "Import FreeSR data success", - "importDatabaseSuccess": "Import database success", - "getData": "Get Data", - "import": "Import", - "freeSRImport": "FreeSR Import", - "onlySupportFreeSRJsonFile": "Only support FreeSR json file", - "pickAFile": "Pick a file", - "lightConeSetting": "LightCone Setting", - "relicMaker": "Relic Maker", - "pleaseSelectAllOptions": "Please select all options", - "relicSavedSuccessfully": "Relic saved successfully", - "mainSettings": "Main Settings", - "mainStat": "Main Stat", - "set": "Set", - "pleaseSelectASet": "Please select a set", - "effectBonus": "Effect Bonus", - "totalRoll": "Total Roll", - "randomizeStats": "Randomize Stats", - "randomizeRolls": "Randomize Rolls", - "selectASubStat": "Select a sub stat", - "selectASet": "Select a set", - "selectAMainStat": "Select a main stat", - "save": "Save", - "reset": "Reset", - "roll": "Roll", - "step": "Step", - "memoryOfChaos": "Memory of Chaos", - "pureFiction": "Pure Fiction", - "apocalypticShadow": "Apocalyptic Shadow", - "customEnemy": "Custom Enemy", - "simulatedUniverse": "Simulated Universe", - "floor": "Floor", - "side": "Side", - "wave": "Wave", - "stage": "Stage", - "useCycleCount": "Use cycle count?", - "useTurbulenceBuff": "Use turbulence buff?", - "firstHalfEnemies": "First half enemies", - "secondHalfEnemies": "Second half enemies", - "firstNodeEnemies": "First node enemies", - "secondNodeEnemies": "Second node enemies", - "thirdNodeEnemies": "Third node enemies", - "firstNode": "First Node", - "secondNode": "Second Node", - "thirdNode": "Third Node", - "listEnemies": "List enemies", - "turbulenceBuff": "Turbulence Buff", - "noEventSelected": "No event selected", - "noTurbulenceBuff": "No Turbulence Buff", - "upper": "Upper", - "lower": "Lower", - "upperToLower": "Upper -> Lower", - "lowerToUpper": "Lower -> Upper", - "selectMOCEvent": "Select MOC Event", - "selectPFEvent": "Select PF Event", - "selectASEvent": "Select AS Event", - "selectCEEvent": "Select CE Event", - "selectEvent": "Select Event", - "selectFloor": "Select a Floor", - "selectSide": "Select a Side", - "selectBuff": "Select a Buff", - "selectStage": "Select a Stage", - "previous": "Previous", - "next": "Next", - "noMonstersFound": "No monsters found", - "addNewWave": "Add New Wave", - "searchStage": "Search stage...", - "noStageFound": "No stage found", - "searchMonster": "Search monster...", - "changeRelic": "Change relic", - "deleteRelic": "Delete relic", - "deleteRelicConfirm": "Are you sure you want to delete relic in slot", - "setEffects": "Set Effects", - "details": "Details", - "normal": "Basic ATK", - "bpskill": "Skill", - "maze": "Technique", - "ultra": "Ultimate", - "servantskill": "Memosprite Skill", - "severaltalent": "Memosprite Talent", - "singleattack": "Single Attack", - "enhance": "Enhance", - "summon": "Summon", - "mazeattack": "Technique Attack", - "blast": "Blast", - "restore": "Restore", - "support": "Support", - "aoeattack": "AoE Attack", - "impair": "Impair", - "bounce": "Bounce", - "active": "Active", - "defence": "Defence", - "inactive": "Inactive", - "maxAll": "Max All", - "maxAllSuccess": "Successfully set skill level to max.", - "maxAllFailed": "Failed to set skill level to max.", - "noRelicEquipped": "No relic equipped", - "anomalyArbitration": "Anomaly Arbitration", - "normalMode": "Normal Mode", - "hardMode": "Hard Mode", - "selectPEAKEvent": "Select PEAK Event", - "mode": "Mode", - "selectMode": "Select a mode", - "rollBack": "Roll Back", - "upRoll": "Up Roll", - "downRoll": "Down Roll", - "actions": "Actions", - "avatars": "Avatars", - "quickView": "Quick View", - "extraSetting": "Extra Settings", - "disableCensorship": "Disable Censorship", - "hideUI": "Hide UI", - "theoryCraftMode": "Theorycraft Mode", - "cycleCount": "Cycle Count", - "pleaseSelectAllSubStats": "Please select all sub stats", - "subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Multipath Character", - "mainPath": "Main Path", - "march7Path": "March 7 Path", - "challenge": "Challenge", - "skipNode": "Skip Node", - "disableSkip": "Disable skip", - "skipNode1": "Skip node 1", - "skipNode2": "Skip node 2", - "extraFeatures": "Extra Features", - "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.", - "detailChallengePeak": "Allows changing the Peak season in the current Anomaly.", - "detailHiddenUi": "Enabling this feature will hide the game UI.", - "detailDisableCensorship": "Enabling this feature will disable in-game censorship.", - "detailMultipathCharacter": "Allows changing the Path of certain characters.", - "trailblazer": "Trailblazer", - "listExtraEffect": "List Extra Effect", - "extra": "Extra", - "customLineup": "Custom Lineup" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "Skill Type", + "skillName": "Skill Name", + "character": "Character", + "id": "Id", + "path": "Path", + "rarity": "Rarity", + "element": "Element", + "technique": "Technique", + "talent": "Talent", + "basic": "Basic Attack", + "skill": "Skill", + "ultimate": "Ultimate", + "servant": "Servant", + "damage": "Damage", + "type": "Type", + "warrior": "The Destruction", + "knight": "The Preservation", + "mage": "The Erudition", + "priest": "The Abundance", + "rogue": "The Hunt", + "shaman": "The Harmony", + "warlock": "The Nihility", + "memory": "The Remembrance", + "elation": "The Elation", + "fire": "Fire", + "ice": "Ice", + "imaginary": "Imaginary", + "physical": "Physical", + "quantum": "Quantum", + "thunder": "Thunder", + "wind": "Wind", + "hp": "Hp", + "atk": "Atk", + "speed": "Speed", + "critRate": "Crit Rate", + "critDmg": "Crit Dmg", + "breakEffect": "Break Effect", + "effectRes": "Effect Res", + "energyRegenerationRate": "Energy Regeneration Rate", + "effectHitRate": "Effect Hit Rate", + "outgoingHealingBoost": "Outgoing Healing Boost", + "fireDmgBoost": "Fire damage boost", + "iceDmgBoost": "Ice damage Boost", + "imaginaryDmgBoost": "Imaginary damage boost", + "physicalDmgBoost": "Physical damage boost", + "quantumDmgBoost": "Quantum damage boost", + "thunderDmgBoost": "Thunder damage boost", + "windDmgBoost": "Wind damage boost", + "pursued": "Additional damage", + "true damage": "True damage", + "elationdamage": "Elation damage", + "follow-up": "Follow-up Damage", + "elemental damage": "Break and Super break damage", + "dot": "Damage over time ", + "qte": "QTE Skill", + "level": "Level", + "relics": "Relics", + "eidolons": "Eidolons", + "lightcones": "Lightcones", + "loadData": "Load data", + "exportData": "Export data", + "connectSetting": "Connection Setting", + "connected": "Connected", + "unconnected": "Unconnected", + "psConnection": "PS Connection", + "connectionType": "Connection Type", + "status": "Status", + "connectPs": "Connect PS", + "disconnect": "Disconnect", + "other": "Other", + "freeSr": "FreeSR", + "database": "Database", + "enka": "Enka", + "monsterSetting": "Monster Setting", + "serverUrl": "Server URL", + "privateType": "Private Type", + "local": "Local", + "server": "Server", + "username": "Username", + "password": "Password", + "placeholderServerUrl": "Enter server URL", + "placeholderUsername": "Enter username", + "placeholderPassword": "Enter password", + "connectedSuccess": "Connected to PS successfully", + "connectedFailed": "Failed to connect to PS", + "syncSuccess": "Synced data to PS successfully", + "syncFailed": "Failed to sync data to PS", + "sync": "Sync", + "importSetting": "Import Setting", + "profile": "Profile", + "default": "Default", + "copyProfiles": "Copy profiles", + "addNewProfile": "Add new profile", + "createNewProfile": "Create new profile", + "editProfile": "Edit profile", + "placeholderProfileName": "Enter profile name", + "profileName": "Profile name", + "create": "Create", + "update": "Update", + "characterInformation": "Character Information", + "skills": "Skills", + "showcaseCard": "Showcase Card", + "comingSoon": "Coming soon", + "characterName": "Character name", + "placeholderCharacter": "Enter character name", + "characterSettings": "Character Settings", + "levelConfiguration": "Level Configuration", + "characterLevel": "Character Level", + "max": "MAX", + "ultimateEnergy": "Ultimate Energy", + "currentEnergy": "Current Energy", + "setTo50": "Set to 50%", + "battleConfiguration": "Battle Configuration", + "useTechnique": "Use Technique", + "techniqueNote": "Enable pre-battle technique effects", + "enhancement": "Enhancement", + "enhancementLevel": "Enhancement Level", + "origin": "Origin", + "enhancedNote": "Higher enhanced unlock additional abilities", + "lightconeEquipment": "Lightcone Equipment", + "lightconeSettings": "Lightcone Settings", + "placeholderLevel": "Enter level", + "superimpositionRank": "Superimposition Rank", + "ranksNote": "Higher ranks provide stronger effects", + "changeLightcone": "Change Lightcone", + "removeLightcone": "Remove Lightcone", + "equipLightcone": "Equip Lightcone", + "noLightconeEquipped": "No Lightcone Equipped", + "equipLightconeNote": "Equip a lightcone to enhance your character's abilities", + "filter": "Filter", + "selectedCharacters": "Selected Characters", + "selectedProfiles": "Selected Profiles", + "clearAll": "Clear All", + "selectAll": "Select All", + "copy": "Copy", + "copied": "Copied", + "noAvatarSelected": "No avatar selected", + "noAvatarToCopySelected": "No avatar to copy selected", + "pleaseSelectAtLeastOneProfile": "Please select at least one profile", + "pleaseEnterUid": "Please enter UID", + "failedToFetchEnkaData": "Failed to fetch enka data", + "pleaseSelectAtLeastOneCharacter": "Please select at least one character", + "noDataToImport": "No data to import", + "pleaseSelectAFile": "Please select a file", + "fileMustBeAValidJsonFile": "File must be a valid json file", + "importEnkaDataSuccess": "Import Enka data success", + "importFreeSRDataSuccess": "Import FreeSR data success", + "importDatabaseSuccess": "Import database success", + "getData": "Get Data", + "import": "Import", + "freeSRImport": "FreeSR Import", + "onlySupportFreeSRJsonFile": "Only support FreeSR json file", + "pickAFile": "Pick a file", + "lightConeSetting": "LightCone Setting", + "relicMaker": "Relic Maker", + "pleaseSelectAllOptions": "Please select all options", + "relicSavedSuccessfully": "Relic saved successfully", + "mainSettings": "Main Settings", + "mainStat": "Main Stat", + "set": "Set", + "pleaseSelectASet": "Please select a set", + "effectBonus": "Effect Bonus", + "totalRoll": "Total Roll", + "randomizeStats": "Randomize Stats", + "randomizeRolls": "Randomize Rolls", + "selectASubStat": "Select a sub stat", + "selectASet": "Select a set", + "selectAMainStat": "Select a main stat", + "save": "Save", + "reset": "Reset", + "roll": "Roll", + "step": "Step", + "memoryOfChaos": "Memory of Chaos", + "pureFiction": "Pure Fiction", + "apocalypticShadow": "Apocalyptic Shadow", + "customEnemy": "Custom Enemy", + "simulatedUniverse": "Simulated Universe", + "floor": "Floor", + "side": "Side", + "wave": "Wave", + "stage": "Stage", + "useCycleCount": "Use cycle count?", + "useTurbulenceBuff": "Use turbulence buff?", + "firstHalfEnemies": "First half enemies", + "secondHalfEnemies": "Second half enemies", + "firstNodeEnemies": "First node enemies", + "secondNodeEnemies": "Second node enemies", + "thirdNodeEnemies": "Third node enemies", + "firstNode": "First Node", + "secondNode": "Second Node", + "thirdNode": "Third Node", + "listEnemies": "List enemies", + "turbulenceBuff": "Turbulence Buff", + "noEventSelected": "No event selected", + "noTurbulenceBuff": "No Turbulence Buff", + "upper": "Upper", + "lower": "Lower", + "upperToLower": "Upper -> Lower", + "lowerToUpper": "Lower -> Upper", + "selectMOCEvent": "Select MOC Event", + "selectPFEvent": "Select PF Event", + "selectASEvent": "Select AS Event", + "selectCEEvent": "Select CE Event", + "selectEvent": "Select Event", + "selectFloor": "Select a Floor", + "selectSide": "Select a Side", + "selectBuff": "Select a Buff", + "selectStage": "Select a Stage", + "previous": "Previous", + "next": "Next", + "noMonstersFound": "No monsters found", + "addNewWave": "Add New Wave", + "searchStage": "Search stage...", + "noStageFound": "No stage found", + "searchMonster": "Search monster...", + "changeRelic": "Change relic", + "deleteRelic": "Delete relic", + "deleteRelicConfirm": "Are you sure you want to delete relic in slot", + "setEffects": "Set Effects", + "details": "Details", + "normal": "Basic ATK", + "bpskill": "Skill", + "maze": "Technique", + "ultra": "Ultimate", + "servantskill": "Memosprite Skill", + "severaltalent": "Memosprite Talent", + "singleattack": "Single Attack", + "enhance": "Enhance", + "summon": "Summon", + "mazeattack": "Technique Attack", + "blast": "Blast", + "restore": "Restore", + "support": "Support", + "aoeattack": "AoE Attack", + "impair": "Impair", + "bounce": "Bounce", + "active": "Active", + "defence": "Defence", + "inactive": "Inactive", + "maxAll": "Max All", + "maxAllSuccess": "Successfully set skill level to max.", + "maxAllFailed": "Failed to set skill level to max.", + "noRelicEquipped": "No relic equipped", + "anomalyArbitration": "Anomaly Arbitration", + "normalMode": "Normal Mode", + "hardMode": "Hard Mode", + "selectPEAKEvent": "Select PEAK Event", + "mode": "Mode", + "selectMode": "Select a mode", + "rollBack": "Roll Back", + "upRoll": "Up Roll", + "downRoll": "Down Roll", + "actions": "Actions", + "avatars": "Avatars", + "quickView": "Quick View", + "extraSetting": "Extra Settings", + "disableCensorship": "Disable Censorship", + "hideUI": "Hide UI", + "theoryCraftMode": "Theorycraft Mode", + "cycleCount": "Cycle Count", + "pleaseSelectAllSubStats": "Please select all sub stats", + "subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Multipath Character", + "mainPath": "Main Path", + "march7Path": "March 7 Path", + "challenge": "Challenge", + "skipNode": "Skip Node", + "disableSkip": "Disable skip", + "skipNode1": "Skip node 1", + "skipNode2": "Skip node 2", + "extraFeatures": "Extra Features", + "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.", + "detailChallengePeak": "Allows changing the Peak season in the current Anomaly.", + "detailHiddenUi": "Enabling this feature will hide the game UI.", + "detailDisableCensorship": "Enabling this feature will disable in-game censorship.", + "detailMultipathCharacter": "Allows changing the Path of certain characters.", + "trailblazer": "Trailblazer", + "listExtraEffect": "List Extra Effect", + "extra": "Extra", + "customLineup": "Custom Lineup" + } } \ No newline at end of file diff --git a/messages/es.json b/messages/es.json index 4d1560e..9d7bf94 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Herramientas Firefly por Firefly Shelter" - }, - "DataPage": { - "skillType": "Tipo de habilidad", - "skillName": "Nombre de la habilidad", - "character": "Personaje", - "id": "Id", - "path": "Vía", - "rarity": "Rareza", - "element": "Elemento", - "technique": "Técnica", - "talent": "Talento", - "basic": "Ataque Básico", - "skill": "Habilidad", - "ultimate": "Habilidad Definitiva", - "servant": "Mnemoduende", - "damage": "Daño", - "type": "Tipo", - "warrior": "La Destrucción", - "knight": "La Conservación", - "mage": "La Erudición", - "priest": "La Abundancia", - "rogue": "La Cacería", - "shaman": "La Armonía", - "warlock": "La Nihilidad", - "memory": "La Reminiscencia", - "elation": "La Exultación", - "fire": "Fuego", - "ice": "Hielo", - "imaginary": "Imaginario", - "physical": "Físico", - "quantum": "Cuántico", - "thunder": "Rayo", - "wind": "Viento", - "hp": "PV", - "atk": "ATQ", - "speed": "VEL", - "critRate": "Prob. CRIT", - "critDmg": "Daño CRIT", - "breakEffect": "Efecto de Ruptura", - "effectRes": "RES a Efecto", - "energyRegenerationRate": "Recuperación de Energía", - "effectHitRate": "Acierto de Efecto", - "outgoingHealingBoost": "Bono de Curación", - "fireDmgBoost": "Aumento de Daño de Fuego", - "iceDmgBoost": "Aumento de Daño de Hielo", - "imaginaryDmgBoost": "Aumento de Daño Imaginario", - "physicalDmgBoost": "Aumento de Daño Físico", - "quantumDmgBoost": "Aumento de Daño Cuántico", - "thunderDmgBoost": "Aumento de Daño de Rayo", - "windDmgBoost": "Aumento de Daño de Viento", - "pursued": "Daño adicional", - "true damage": "Daño verdadero", - "elationdamage": "Daño de Exultación", - "follow-up": "Daño de Ataque Adicional", - "elemental damage": "Daño de Ruptura y Superruptura", - "dot": "Daño con el tiempo", - "qte": "Habilidad QTE", - "level": "Nivel", - "relics": "Artefactos", - "eidolons": "Eidolones", - "lightcones": "Conos de Luz", - "loadData": "Cargar datos", - "exportData": "Exportar datos", - "connectSetting": "Ajustes de conexión", - "connected": "Conectado", - "unconnected": "Desconectado", - "psConnection": "Conexión PS", - "connectionType": "Tipo de conexión", - "status": "Estado", - "connectPs": "Conectar PS", - "disconnect": "Desconectar", - "other": "Otro", - "freeSr": "FreeSR", - "database": "Base de datos", - "enka": "Enka", - "monsterSetting": "Ajuste de Monstruos", - "serverUrl": "URL del Servidor", - "privateType": "Tipo privado", - "local": "Local", - "server": "Servidor", - "username": "Usuario", - "password": "Contraseña", - "placeholderServerUrl": "Introducir URL del servidor", - "placeholderUsername": "Introducir usuario", - "placeholderPassword": "Introducir contraseña", - "connectedSuccess": "Conectado a PS exitosamente", - "connectedFailed": "Fallo al conectar a PS", - "syncSuccess": "Datos sincronizados a PS exitosamente", - "syncFailed": "Fallo al sincronizar datos a PS", - "sync": "Sincronizar", - "importSetting": "Ajustes de Importación", - "profile": "Perfil", - "default": "Por defecto", - "copyProfiles": "Copiar perfiles", - "addNewProfile": "Añadir nuevo perfil", - "createNewProfile": "Crear nuevo perfil", - "editProfile": "Editar perfil", - "placeholderProfileName": "Introducir nombre de perfil", - "profileName": "Nombre del perfil", - "create": "Crear", - "update": "Actualizar", - "characterInformation": "Información del personaje", - "skills": "Habilidades", - "showcaseCard": "Tarjeta de presentación del personaje", - "comingSoon": "Próximamente", - "characterName": "Nombre del personaje", - "placeholderCharacter": "Introducir nombre del personaje", - "characterSettings": "Ajustes del Personaje", - "levelConfiguration": "Configuración de Nivel", - "characterLevel": "Nivel de Personaje", - "max": "MÁX", - "ultimateEnergy": "Energía de Habilidad Definitiva", - "currentEnergy": "Energía al inicio del combate", - "setTo50": "Ajustar al 50%", - "battleConfiguration": "Configuración de Batalla", - "useTechnique": "Usar Técnica", - "techniqueNote": "Activar efectos de técnica pre-batalla", - "enhancement": "Mejora", - "enhancementLevel": "Nivel de Mejora", - "origin": "Original", - "enhancedNote": "Niveles superiores desbloquean más habilidades", - "lightconeEquipment": "Equipamiento de Cono de Luz", - "lightconeSettings": "Ajustes de Cono de Luz", - "placeholderLevel": "Introducir nivel", - "superimpositionRank": "Rango de Superposición", - "ranksNote": "Rangos mayores ofrecen efectos más fuertes", - "changeLightcone": "Cambiar Cono de Luz", - "removeLightcone": "Quitar Cono de Luz", - "equipLightcone": "Equipar Cono de Luz", - "noLightconeEquipped": "Sin Cono de Luz", - "equipLightconeNote": "Equipa un cono de luz para mejorar tu personaje", - "filter": "Filtro", - "selectedCharacters": "Personajes Seleccionados", - "selectedProfiles": "Perfiles Seleccionados", - "clearAll": "Limpiar Todo", - "selectAll": "Seleccionar Todo", - "copy": "Copiar", - "copied": "Copiado", - "noAvatarSelected": "Ningún personaje seleccionado", - "noAvatarToCopySelected": "Ningún personaje seleccionado para copiar", - "pleaseSelectAtLeastOneProfile": "Por favor, selecciona al menos un perfil", - "pleaseEnterUid": "Por favor, introduce UID", - "failedToFetchEnkaData": "Fallo al obtener datos de Enka", - "pleaseSelectAtLeastOneCharacter": "Por favor, selecciona al menos un personaje", - "noDataToImport": "Sin datos para importar", - "pleaseSelectAFile": "Por favor, selecciona un archivo", - "fileMustBeAValidJsonFile": "El archivo debe ser un JSON válido", - "importEnkaDataSuccess": "Datos de Enka importados con éxito", - "importFreeSRDataSuccess": "Datos de FreeSR importados con éxito", - "importDatabaseSuccess": "Base de datos importada con éxito", - "getData": "Obtener Datos", - "import": "Importar", - "freeSRImport": "Importar FreeSR", - "onlySupportFreeSRJsonFile": "Solo soporta archivos JSON de FreeSR", - "pickAFile": "Elegir un archivo", - "lightConeSetting": "Ajuste de Cono de Luz", - "relicMaker": "Creador de Artefactos", - "pleaseSelectAllOptions": "Por favor selecciona todas las opciones", - "relicSavedSuccessfully": "Artefacto guardado exitosamente", - "mainSettings": "Ajustes Principales", - "mainStat": "Estadística Principal", - "set": "Conjunto", - "pleaseSelectASet": "Por favor, selecciona un conjunto", - "effectBonus": "Bono de Efecto", - "totalRoll": "Total de Mejoras", - "randomizeStats": "Estadísticas Aleatorias", - "randomizeRolls": "Mejoras Aleatorias", - "selectASubStat": "Seleccionar estadística secundaria", - "selectASet": "Seleccionar conjunto", - "selectAMainStat": "Seleccionar estadística principal", - "save": "Guardar", - "reset": "Reiniciar", - "roll": "Mejora", - "step": "Paso", - "memoryOfChaos": "Memoria del Caos", - "pureFiction": "Pura Ficción", - "apocalypticShadow": "Sombra Apocalíptica", - "customEnemy": "Enemigo Personalizado", - "simulatedUniverse": "Universo Simulado", - "floor": "Piso", - "side": "Lado", - "wave": "Oleada", - "stage": "Etapa", - "useCycleCount": "¿Usar conteo de ciclos?", - "useTurbulenceBuff": "¿Usar buff de turbulencia?", - "firstHalfEnemies": "Enemigos primera mitad", - "secondHalfEnemies": "Enemigos segunda mitad", - "firstNodeEnemies": "Enemigos del nodo 1", - "secondNodeEnemies": "Enemigos del nodo 2", - "thirdNodeEnemies": "Enemigos del nodo 3", - "firstNode": "Nodo 1", - "secondNode": "Nodo 2", - "thirdNode": "Nodo 3", - "listEnemies": "Lista de enemigos", - "turbulenceBuff": "Buff de Turbulencia", - "noEventSelected": "Ningún evento seleccionado", - "noTurbulenceBuff": "Sin Buff de Turbulencia", - "upper": "Superior", - "lower": "Inferior", - "upperToLower": "Superior -> Inferior", - "lowerToUpper": "Inferior -> Superior", - "selectMOCEvent": "Seleccionar evento de MOC", - "selectPFEvent": "Seleccionar evento de PF", - "selectASEvent": "Seleccionar evento de AS", - "selectCEEvent": "Seleccionar evento de CE", - "selectEvent": "Seleccionar Evento", - "selectFloor": "Seleccionar Piso", - "selectSide": "Seleccionar Lado", - "selectBuff": "Seleccionar Buff", - "selectStage": "Seleccionar Etapa", - "previous": "Anterior", - "next": "Siguiente", - "noMonstersFound": "No se encontraron monstruos", - "addNewWave": "Añadir Nueva Oleada", - "searchStage": "Buscar etapa...", - "noStageFound": "No se encontró etapa", - "searchMonster": "Buscar monstruo...", - "changeRelic": "Cambiar artefacto", - "deleteRelic": "Borrar artefacto", - "deleteRelicConfirm": "¿Seguro que quieres borrar el artefacto en este espacio?", - "setEffects": "Configurar Efectos", - "details": "Detalles", - "normal": "Ataque Básico", - "bpskill": "Habilidad", - "maze": "Técnica", - "ultra": "Habilidad Definitiva", - "servantskill": "Habilidad de Mnemoduende", - "severaltalent": "Talento de Mnemoduende", - "singleattack": "Ataque Individual", - "enhance": "Mejorar", - "summon": "Invocar", - "mazeattack": "Ataque de Técnica", - "blast": "Ráfaga", - "restore": "Restaurar", - "support": "Soporte", - "aoeattack": "Ataque en Área", - "impair": "Debilitación", - "bounce": "Rebote", - "active": "Activo", - "defence": "Defensa", - "inactive": "Inactivo", - "maxAll": "Maximizar Todo", - "maxAllSuccess": "Habilidades maximizadas con éxito.", - "maxAllFailed": "Fallo al maximizar habilidades.", - "noRelicEquipped": "Sin artefacto equipado", - "anomalyArbitration": "Arbitraje Atípico", - "normalMode": "Modo Normal", - "hardMode": "Modo Difícil", - "selectPEAKEvent": "Seleccionar evento PEAK", - "mode": "Modo", - "selectMode": "Seleccionar un modo", - "rollBack": "Retroceder", - "upRoll": "Subir Mejora", - "downRoll": "Bajar Mejora", - "actions": "Acciones", - "avatars": "Avatares", - "quickView": "Vista Rápida", - "extraSetting": "Ajustes Extra", - "disableCensorship": "Desactivar Censura", - "hideUI": "Ocultar UI", - "theoryCraftMode": "Modo Theorycraft", - "cycleCount": "Conteo de Ciclos", - "pleaseSelectAllSubStats": "Por favor selecciona todas las sub-estadísticas", - "subStatRollCountCannotBeZero": "El conteo de mejoras no puede ser cero", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Personaje Multivía", - "mainPath": "Vía Principal", - "march7Path": "Vía de Siete de Marzo", - "challenge": "Desafío", - "skipNode": "Saltar Nodo", - "disableSkip": "Desactivar salto", - "skipNode1": "Saltar nodo 1", - "skipNode2": "Saltar nodo 2", - "extraFeatures": "Características Extra", - "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.", - "detailChallengePeak": "Permite cambiar la temporada actual de Arbitraje Atípico.", - "detailHiddenUi": "Oculta la interfaz del juego.", - "detailDisableCensorship": "Desactiva la censura dentro del juego.", - "detailMultipathCharacter": "Permite cambiar la Vía de algunos personajes.", - "trailblazer": "Trazacaminos", - "listExtraEffect": "Lista de Efectos Extra", - "extra": "Extra", - "customLineup": "Alineación de equipo personalizada" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Herramientas Firefly por Firefly Shelter" + }, + "DataPage": { + "skillType": "Tipo de habilidad", + "skillName": "Nombre de la habilidad", + "character": "Personaje", + "id": "Id", + "path": "Vía", + "rarity": "Rareza", + "element": "Elemento", + "technique": "Técnica", + "talent": "Talento", + "basic": "Ataque Básico", + "skill": "Habilidad", + "ultimate": "Habilidad Definitiva", + "servant": "Mnemoduende", + "damage": "Daño", + "type": "Tipo", + "warrior": "La Destrucción", + "knight": "La Conservación", + "mage": "La Erudición", + "priest": "La Abundancia", + "rogue": "La Cacería", + "shaman": "La Armonía", + "warlock": "La Nihilidad", + "memory": "La Reminiscencia", + "elation": "La Exultación", + "fire": "Fuego", + "ice": "Hielo", + "imaginary": "Imaginario", + "physical": "Físico", + "quantum": "Cuántico", + "thunder": "Rayo", + "wind": "Viento", + "hp": "PV", + "atk": "ATQ", + "speed": "VEL", + "critRate": "Prob. CRIT", + "critDmg": "Daño CRIT", + "breakEffect": "Efecto de Ruptura", + "effectRes": "RES a Efecto", + "energyRegenerationRate": "Recuperación de Energía", + "effectHitRate": "Acierto de Efecto", + "outgoingHealingBoost": "Bono de Curación", + "fireDmgBoost": "Aumento de Daño de Fuego", + "iceDmgBoost": "Aumento de Daño de Hielo", + "imaginaryDmgBoost": "Aumento de Daño Imaginario", + "physicalDmgBoost": "Aumento de Daño Físico", + "quantumDmgBoost": "Aumento de Daño Cuántico", + "thunderDmgBoost": "Aumento de Daño de Rayo", + "windDmgBoost": "Aumento de Daño de Viento", + "pursued": "Daño adicional", + "true damage": "Daño verdadero", + "elationdamage": "Daño de Exultación", + "follow-up": "Daño de Ataque Adicional", + "elemental damage": "Daño de Ruptura y Superruptura", + "dot": "Daño con el tiempo", + "qte": "Habilidad QTE", + "level": "Nivel", + "relics": "Artefactos", + "eidolons": "Eidolones", + "lightcones": "Conos de Luz", + "loadData": "Cargar datos", + "exportData": "Exportar datos", + "connectSetting": "Ajustes de conexión", + "connected": "Conectado", + "unconnected": "Desconectado", + "psConnection": "Conexión PS", + "connectionType": "Tipo de conexión", + "status": "Estado", + "connectPs": "Conectar PS", + "disconnect": "Desconectar", + "other": "Otro", + "freeSr": "FreeSR", + "database": "Base de datos", + "enka": "Enka", + "monsterSetting": "Ajuste de Monstruos", + "serverUrl": "URL del Servidor", + "privateType": "Tipo privado", + "local": "Local", + "server": "Servidor", + "username": "Usuario", + "password": "Contraseña", + "placeholderServerUrl": "Introducir URL del servidor", + "placeholderUsername": "Introducir usuario", + "placeholderPassword": "Introducir contraseña", + "connectedSuccess": "Conectado a PS exitosamente", + "connectedFailed": "Fallo al conectar a PS", + "syncSuccess": "Datos sincronizados a PS exitosamente", + "syncFailed": "Fallo al sincronizar datos a PS", + "sync": "Sincronizar", + "importSetting": "Ajustes de Importación", + "profile": "Perfil", + "default": "Por defecto", + "copyProfiles": "Copiar perfiles", + "addNewProfile": "Añadir nuevo perfil", + "createNewProfile": "Crear nuevo perfil", + "editProfile": "Editar perfil", + "placeholderProfileName": "Introducir nombre de perfil", + "profileName": "Nombre del perfil", + "create": "Crear", + "update": "Actualizar", + "characterInformation": "Información del personaje", + "skills": "Habilidades", + "showcaseCard": "Tarjeta de presentación del personaje", + "comingSoon": "Próximamente", + "characterName": "Nombre del personaje", + "placeholderCharacter": "Introducir nombre del personaje", + "characterSettings": "Ajustes del Personaje", + "levelConfiguration": "Configuración de Nivel", + "characterLevel": "Nivel de Personaje", + "max": "MÁX", + "ultimateEnergy": "Energía de Habilidad Definitiva", + "currentEnergy": "Energía al inicio del combate", + "setTo50": "Ajustar al 50%", + "battleConfiguration": "Configuración de Batalla", + "useTechnique": "Usar Técnica", + "techniqueNote": "Activar efectos de técnica pre-batalla", + "enhancement": "Mejora", + "enhancementLevel": "Nivel de Mejora", + "origin": "Original", + "enhancedNote": "Niveles superiores desbloquean más habilidades", + "lightconeEquipment": "Equipamiento de Cono de Luz", + "lightconeSettings": "Ajustes de Cono de Luz", + "placeholderLevel": "Introducir nivel", + "superimpositionRank": "Rango de Superposición", + "ranksNote": "Rangos mayores ofrecen efectos más fuertes", + "changeLightcone": "Cambiar Cono de Luz", + "removeLightcone": "Quitar Cono de Luz", + "equipLightcone": "Equipar Cono de Luz", + "noLightconeEquipped": "Sin Cono de Luz", + "equipLightconeNote": "Equipa un cono de luz para mejorar tu personaje", + "filter": "Filtro", + "selectedCharacters": "Personajes Seleccionados", + "selectedProfiles": "Perfiles Seleccionados", + "clearAll": "Limpiar Todo", + "selectAll": "Seleccionar Todo", + "copy": "Copiar", + "copied": "Copiado", + "noAvatarSelected": "Ningún personaje seleccionado", + "noAvatarToCopySelected": "Ningún personaje seleccionado para copiar", + "pleaseSelectAtLeastOneProfile": "Por favor, selecciona al menos un perfil", + "pleaseEnterUid": "Por favor, introduce UID", + "failedToFetchEnkaData": "Fallo al obtener datos de Enka", + "pleaseSelectAtLeastOneCharacter": "Por favor, selecciona al menos un personaje", + "noDataToImport": "Sin datos para importar", + "pleaseSelectAFile": "Por favor, selecciona un archivo", + "fileMustBeAValidJsonFile": "El archivo debe ser un JSON válido", + "importEnkaDataSuccess": "Datos de Enka importados con éxito", + "importFreeSRDataSuccess": "Datos de FreeSR importados con éxito", + "importDatabaseSuccess": "Base de datos importada con éxito", + "getData": "Obtener Datos", + "import": "Importar", + "freeSRImport": "Importar FreeSR", + "onlySupportFreeSRJsonFile": "Solo soporta archivos JSON de FreeSR", + "pickAFile": "Elegir un archivo", + "lightConeSetting": "Ajuste de Cono de Luz", + "relicMaker": "Creador de Artefactos", + "pleaseSelectAllOptions": "Por favor selecciona todas las opciones", + "relicSavedSuccessfully": "Artefacto guardado exitosamente", + "mainSettings": "Ajustes Principales", + "mainStat": "Estadística Principal", + "set": "Conjunto", + "pleaseSelectASet": "Por favor, selecciona un conjunto", + "effectBonus": "Bono de Efecto", + "totalRoll": "Total de Mejoras", + "randomizeStats": "Estadísticas Aleatorias", + "randomizeRolls": "Mejoras Aleatorias", + "selectASubStat": "Seleccionar estadística secundaria", + "selectASet": "Seleccionar conjunto", + "selectAMainStat": "Seleccionar estadística principal", + "save": "Guardar", + "reset": "Reiniciar", + "roll": "Mejora", + "step": "Paso", + "memoryOfChaos": "Memoria del Caos", + "pureFiction": "Pura Ficción", + "apocalypticShadow": "Sombra Apocalíptica", + "customEnemy": "Enemigo Personalizado", + "simulatedUniverse": "Universo Simulado", + "floor": "Piso", + "side": "Lado", + "wave": "Oleada", + "stage": "Etapa", + "useCycleCount": "¿Usar conteo de ciclos?", + "useTurbulenceBuff": "¿Usar buff de turbulencia?", + "firstHalfEnemies": "Enemigos primera mitad", + "secondHalfEnemies": "Enemigos segunda mitad", + "firstNodeEnemies": "Enemigos del nodo 1", + "secondNodeEnemies": "Enemigos del nodo 2", + "thirdNodeEnemies": "Enemigos del nodo 3", + "firstNode": "Nodo 1", + "secondNode": "Nodo 2", + "thirdNode": "Nodo 3", + "listEnemies": "Lista de enemigos", + "turbulenceBuff": "Buff de Turbulencia", + "noEventSelected": "Ningún evento seleccionado", + "noTurbulenceBuff": "Sin Buff de Turbulencia", + "upper": "Superior", + "lower": "Inferior", + "upperToLower": "Superior -> Inferior", + "lowerToUpper": "Inferior -> Superior", + "selectMOCEvent": "Seleccionar evento de MOC", + "selectPFEvent": "Seleccionar evento de PF", + "selectASEvent": "Seleccionar evento de AS", + "selectCEEvent": "Seleccionar evento de CE", + "selectEvent": "Seleccionar Evento", + "selectFloor": "Seleccionar Piso", + "selectSide": "Seleccionar Lado", + "selectBuff": "Seleccionar Buff", + "selectStage": "Seleccionar Etapa", + "previous": "Anterior", + "next": "Siguiente", + "noMonstersFound": "No se encontraron monstruos", + "addNewWave": "Añadir Nueva Oleada", + "searchStage": "Buscar etapa...", + "noStageFound": "No se encontró etapa", + "searchMonster": "Buscar monstruo...", + "changeRelic": "Cambiar artefacto", + "deleteRelic": "Borrar artefacto", + "deleteRelicConfirm": "¿Seguro que quieres borrar el artefacto en este espacio?", + "setEffects": "Configurar Efectos", + "details": "Detalles", + "normal": "Ataque Básico", + "bpskill": "Habilidad", + "maze": "Técnica", + "ultra": "Habilidad Definitiva", + "servantskill": "Habilidad de Mnemoduende", + "severaltalent": "Talento de Mnemoduende", + "singleattack": "Ataque Individual", + "enhance": "Mejorar", + "summon": "Invocar", + "mazeattack": "Ataque de Técnica", + "blast": "Ráfaga", + "restore": "Restaurar", + "support": "Soporte", + "aoeattack": "Ataque en Área", + "impair": "Debilitación", + "bounce": "Rebote", + "active": "Activo", + "defence": "Defensa", + "inactive": "Inactivo", + "maxAll": "Maximizar Todo", + "maxAllSuccess": "Habilidades maximizadas con éxito.", + "maxAllFailed": "Fallo al maximizar habilidades.", + "noRelicEquipped": "Sin artefacto equipado", + "anomalyArbitration": "Arbitraje Atípico", + "normalMode": "Modo Normal", + "hardMode": "Modo Difícil", + "selectPEAKEvent": "Seleccionar evento PEAK", + "mode": "Modo", + "selectMode": "Seleccionar un modo", + "rollBack": "Retroceder", + "upRoll": "Subir Mejora", + "downRoll": "Bajar Mejora", + "actions": "Acciones", + "avatars": "Avatares", + "quickView": "Vista Rápida", + "extraSetting": "Ajustes Extra", + "disableCensorship": "Desactivar Censura", + "hideUI": "Ocultar UI", + "theoryCraftMode": "Modo Theorycraft", + "cycleCount": "Conteo de Ciclos", + "pleaseSelectAllSubStats": "Por favor selecciona todas las sub-estadísticas", + "subStatRollCountCannotBeZero": "El conteo de mejoras no puede ser cero", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Personaje Multivía", + "mainPath": "Vía Principal", + "march7Path": "Vía de Siete de Marzo", + "challenge": "Desafío", + "skipNode": "Saltar Nodo", + "disableSkip": "Desactivar salto", + "skipNode1": "Saltar nodo 1", + "skipNode2": "Saltar nodo 2", + "extraFeatures": "Características Extra", + "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.", + "detailChallengePeak": "Permite cambiar la temporada actual de Arbitraje Atípico.", + "detailHiddenUi": "Oculta la interfaz del juego.", + "detailDisableCensorship": "Desactiva la censura dentro del juego.", + "detailMultipathCharacter": "Permite cambiar la Vía de algunos personajes.", + "trailblazer": "Trazacaminos", + "listExtraEffect": "Lista de Efectos Extra", + "extra": "Extra", + "customLineup": "Alineación de equipo personalizada" + } } \ No newline at end of file diff --git a/messages/fr.json b/messages/fr.json index cbb21ae..bb1ccc7 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Outils Firefly par Firefly Shelter" - }, - "DataPage": { - "skillType": "Type de Compétence", - "skillName": "Nom de Compétence", - "character": "Personnage", - "id": "Id", - "path": "Voie", - "rarity": "Rareté", - "element": "Élément", - "technique": "Technique", - "talent": "Talent", - "basic": "Attaque Normale", - "skill": "Compétence", - "ultimate": "Ultime", - "servant": "Serviteur", - "damage": "Dégâts", - "type": "Type", - "warrior": "La Destruction", - "knight": "La Préservation", - "mage": "L'Érudition", - "priest": "L'Abondance", - "rogue": "La Chasse", - "shaman": "L'Harmonie", - "warlock": "La Nihilité", - "memory": "Le Souvenir", - "elation": "L'Allégresse", - "fire": "Feu", - "ice": "Glace", - "imaginary": "Imaginaire", - "physical": "Physique", - "quantum": "Quantique", - "thunder": "Foudre", - "wind": "Vent", - "hp": "PV", - "atk": "ATQ", - "speed": "VIT", - "critRate": "Taux CRIT", - "critDmg": "DGT CRIT", - "breakEffect": "Effet de Rupture", - "effectRes": "RÉS aux effets", - "energyRegenerationRate": "Taux de régénération d'énergie", - "effectHitRate": "Chances d'application d'effets", - "outgoingHealingBoost": "Augmentation des soins", - "fireDmgBoost": "Bonus de dégâts de Feu", - "iceDmgBoost": "Bonus de dégâts de Glace", - "imaginaryDmgBoost": "Bonus de dégâts Imaginaire", - "physicalDmgBoost": "Bonus de dégâts Physique", - "quantumDmgBoost": "Bonus de dégâts Quantique", - "thunderDmgBoost": "Bonus de dégâts de Foudre", - "windDmgBoost": "Bonus de dégâts de Vent", - "pursued": "Dégâts additionnels", - "true damage": "Dégâts bruts", - "elationdamage": "Dégâts d'Allégresse", - "follow-up": "Dégâts d'attaque de suivi", - "elemental damage": "Dégâts de Rupture et Super Rupture", - "dot": "Dégâts sur la durée", - "qte": "Compétence QTE", - "level": "Niveau", - "relics": "Reliques", - "eidolons": "Eidolons", - "lightcones": "Cônes de Lumière", - "loadData": "Charger les données", - "exportData": "Exporter les données", - "connectSetting": "Paramètres de connexion", - "connected": "Connecté", - "unconnected": "Déconnecté", - "psConnection": "Connexion PS", - "connectionType": "Type de connexion", - "status": "Statut", - "connectPs": "Connecter PS", - "disconnect": "Déconnecter", - "other": "Autre", - "freeSr": "FreeSR", - "database": "Base de données", - "enka": "Enka", - "monsterSetting": "Paramètre de monstres", - "serverUrl": "URL du Serveur", - "privateType": "Type privé", - "local": "Local", - "server": "Serveur", - "username": "Nom d'utilisateur", - "password": "Mot de passe", - "placeholderServerUrl": "Entrer l'URL du serveur", - "placeholderUsername": "Entrer le nom d'utilisateur", - "placeholderPassword": "Entrer le mot de passe", - "connectedSuccess": "Connecté au PS avec succès", - "connectedFailed": "Échec de connexion au PS", - "syncSuccess": "Données synchronisées au PS avec succès", - "syncFailed": "Échec de synchronisation au PS", - "sync": "Synchroniser", - "importSetting": "Paramètres d'importation", - "profile": "Profil", - "default": "Défaut", - "copyProfiles": "Copier les profils", - "addNewProfile": "Ajouter un nouveau profil", - "createNewProfile": "Créer un nouveau profil", - "editProfile": "Éditer le profil", - "placeholderProfileName": "Entrer le nom du profil", - "profileName": "Nom du profil", - "create": "Créer", - "update": "Mettre à jour", - "characterInformation": "Informations du personnage", - "skills": "Compétences", - "showcaseCard": "Carte de présentation", - "comingSoon": "Bientôt disponible", - "characterName": "Nom du personnage", - "placeholderCharacter": "Entrer le nom du personnage", - "characterSettings": "Paramètres du personnage", - "levelConfiguration": "Configuration de niveau", - "characterLevel": "Niveau du personnage", - "max": "MAX", - "ultimateEnergy": "Énergie de l'Ultime", - "currentEnergy": "Énergie actuelle", - "setTo50": "Régler sur 50%", - "battleConfiguration": "Configuration de combat", - "useTechnique": "Utiliser Technique", - "techniqueNote": "Activer les effets de technique avant le combat", - "enhancement": "Amélioration", - "enhancementLevel": "Niveau d'amélioration", - "origin": "Origine", - "enhancedNote": "Les améliorations élevées débloquent des compétences", - "lightconeEquipment": "Équipement de Cône de Lumière", - "lightconeSettings": "Paramètres de Cône de Lumière", - "placeholderLevel": "Entrer le niveau", - "superimpositionRank": "Rang de Superposition", - "ranksNote": "Les rangs élevés offrent des effets plus forts", - "changeLightcone": "Changer de Cône de Lumière", - "removeLightcone": "Retirer le Cône de Lumière", - "equipLightcone": "Équiper un Cône de Lumière", - "noLightconeEquipped": "Aucun Cône de Lumière", - "equipLightconeNote": "Équipez un cône pour renforcer votre personnage", - "filter": "Filtre", - "selectedCharacters": "Personnages sélectionnés", - "selectedProfiles": "Profils sélectionnés", - "clearAll": "Tout effacer", - "selectAll": "Tout sélectionner", - "copy": "Copier", - "copied": "Copié", - "noAvatarSelected": "Aucun personnage sélectionné", - "noAvatarToCopySelected": "Aucun personnage à copier sélectionné", - "pleaseSelectAtLeastOneProfile": "Veuillez sélectionner au moins un profil", - "pleaseEnterUid": "Veuillez entrer un UID", - "failedToFetchEnkaData": "Échec de récupération des données Enka", - "pleaseSelectAtLeastOneCharacter": "Veuillez sélectionner au moins un personnage", - "noDataToImport": "Aucune donnée à importer", - "pleaseSelectAFile": "Veuillez sélectionner un fichier", - "fileMustBeAValidJsonFile": "Le fichier doit être un JSON valide", - "importEnkaDataSuccess": "Importation Enka réussie", - "importFreeSRDataSuccess": "Importation FreeSR réussie", - "importDatabaseSuccess": "Importation de base de données réussie", - "getData": "Obtenir des données", - "import": "Importer", - "freeSRImport": "Importation FreeSR", - "onlySupportFreeSRJsonFile": "Supporte uniquement les fichiers JSON FreeSR", - "pickAFile": "Choisir un fichier", - "lightConeSetting": "Paramètre de Cône de Lumière", - "relicMaker": "Créateur de Reliques", - "pleaseSelectAllOptions": "Veuillez sélectionner toutes les options", - "relicSavedSuccessfully": "Relique sauvegardée avec succès", - "mainSettings": "Paramètres principaux", - "mainStat": "Statistique principale", - "set": "Set", - "pleaseSelectASet": "Veuillez sélectionner un set", - "effectBonus": "Bonus d'effet", - "totalRoll": "Total d'améliorations", - "randomizeStats": "Statistiques aléatoires", - "randomizeRolls": "Améliorations aléatoires", - "selectASubStat": "Sélectionner une stat secondaire", - "selectASet": "Sélectionner un set", - "selectAMainStat": "Sélectionner une stat principale", - "save": "Sauvegarder", - "reset": "Réinitialiser", - "roll": "Amélioration", - "step": "Étape", - "memoryOfChaos": "Mémoire du Chaos", - "pureFiction": "Pure Fiction", - "apocalypticShadow": "Ombre Apocalyptique", - "customEnemy": "Ennemi personnalisé", - "simulatedUniverse": "Univers Simulé", - "floor": "Étage", - "side": "Côté", - "wave": "Vague", - "stage": "Niveau", - "useCycleCount": "Utiliser le compteur de cycles ?", - "useTurbulenceBuff": "Utiliser le buff de turbulence ?", - "firstHalfEnemies": "Ennemis première moitié", - "secondHalfEnemies": "Ennemis deuxième moitié", - "firstNodeEnemies": "Ennemis du nœud 1", - "secondNodeEnemies": "Ennemis du nœud 2", - "thirdNodeEnemies": "Ennemis du nœud 3", - "firstNode": "Nœud 1", - "secondNode": "Nœud 2", - "thirdNode": "Nœud 3", - "listEnemies": "Liste des ennemis", - "turbulenceBuff": "Buff de Turbulence", - "noEventSelected": "Aucun événement sélectionné", - "noTurbulenceBuff": "Aucun Buff de Turbulence", - "upper": "Supérieur", - "lower": "Inférieur", - "upperToLower": "Supérieur -> Inférieur", - "lowerToUpper": "Inférieur -> Supérieur", - "selectMOCEvent": "Sélectionner un événement MOC", - "selectPFEvent": "Sélectionner un événement PF", - "selectASEvent": "Sélectionner un événement AS", - "selectCEEvent": "Sélectionner un événement CE", - "selectEvent": "Sélectionner un événement", - "selectFloor": "Sélectionner un étage", - "selectSide": "Sélectionner un côté", - "selectBuff": "Sélectionner un buff", - "selectStage": "Sélectionner un niveau", - "previous": "Précédent", - "next": "Suivant", - "noMonstersFound": "Aucun monstre trouvé", - "addNewWave": "Ajouter une nouvelle vague", - "searchStage": "Chercher un niveau...", - "noStageFound": "Aucun niveau trouvé", - "searchMonster": "Chercher un monstre...", - "changeRelic": "Changer de relique", - "deleteRelic": "Supprimer la relique", - "deleteRelicConfirm": "Êtes-vous sûr de vouloir supprimer la relique de cet emplacement ?", - "setEffects": "Définir les effets", - "details": "Détails", - "normal": "Attaque Normale", - "bpskill": "Compétence", - "maze": "Technique", - "ultra": "Ultime", - "servantskill": "Compétence de Mémolutin", - "severaltalent": "Talent de Mémolutin", - "singleattack": "Attaque à cible unique", - "enhance": "Renforcement", - "summon": "Invocation", - "mazeattack": "Attaque de Technique", - "blast": "Diffusion", - "restore": "Restauration", - "support": "Soutien", - "aoeattack": "Attaque de zone", - "impair": "Malus", - "bounce": "Rebond", - "active": "Actif", - "defence": "Défense", - "inactive": "Inactif", - "maxAll": "Tout maximiser", - "maxAllSuccess": "Compétences maximisées avec succès.", - "maxAllFailed": "Échec de maximisation.", - "noRelicEquipped": "Aucune relique équipée", - "anomalyArbitration": "Arbitrage d'Anomalie", - "normalMode": "Mode Normal", - "hardMode": "Mode Difficile", - "selectPEAKEvent": "Sélectionner un événement PEAK", - "mode": "Mode", - "selectMode": "Sélectionner un mode", - "rollBack": "Annuler l'étape", - "upRoll": "Augmenter l'amélioration", - "downRoll": "Diminuer l'amélioration", - "actions": "Actions", - "avatars": "Avatars", - "quickView": "Aperçu rapide", - "extraSetting": "Paramètres supplémentaires", - "disableCensorship": "Désactiver la censure", - "hideUI": "Cacher l'UI", - "theoryCraftMode": "Mode Theorycraft", - "cycleCount": "Nombre de cycles", - "pleaseSelectAllSubStats": "Veuillez sélectionner toutes les stats secondaires", - "subStatRollCountCannotBeZero": "Les améliorations de stat secondaire ne peuvent être à zéro", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Personnage Multi-Voies", - "mainPath": "Voie Principale", - "march7Path": "Voie de March 7th", - "challenge": "Défi", - "skipNode": "Passer le nœud", - "disableSkip": "Désactiver le passage", - "skipNode1": "Passer le nœud 1", - "skipNode2": "Passer le nœud 2", - "extraFeatures": "Fonctionnalités supplémentaires", - "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.", - "detailChallengePeak": "Permet de changer la saison de Peak dans l'anomalie actuelle.", - "detailHiddenUi": "Cachera l'interface du jeu.", - "detailDisableCensorship": "Désactivera la censure du jeu.", - "detailMultipathCharacter": "Permet de changer la Voie de certains personnages.", - "trailblazer": "Pionnier", - "listExtraEffect": "Liste des effets supplémentaires", - "extra": "Extra", - "customLineup": "Composition personnalisée" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Outils Firefly par Firefly Shelter" + }, + "DataPage": { + "skillType": "Type de Compétence", + "skillName": "Nom de Compétence", + "character": "Personnage", + "id": "Id", + "path": "Voie", + "rarity": "Rareté", + "element": "Élément", + "technique": "Technique", + "talent": "Talent", + "basic": "Attaque Normale", + "skill": "Compétence", + "ultimate": "Ultime", + "servant": "Serviteur", + "damage": "Dégâts", + "type": "Type", + "warrior": "La Destruction", + "knight": "La Préservation", + "mage": "L'Érudition", + "priest": "L'Abondance", + "rogue": "La Chasse", + "shaman": "L'Harmonie", + "warlock": "La Nihilité", + "memory": "Le Souvenir", + "elation": "L'Allégresse", + "fire": "Feu", + "ice": "Glace", + "imaginary": "Imaginaire", + "physical": "Physique", + "quantum": "Quantique", + "thunder": "Foudre", + "wind": "Vent", + "hp": "PV", + "atk": "ATQ", + "speed": "VIT", + "critRate": "Taux CRIT", + "critDmg": "DGT CRIT", + "breakEffect": "Effet de Rupture", + "effectRes": "RÉS aux effets", + "energyRegenerationRate": "Taux de régénération d'énergie", + "effectHitRate": "Chances d'application d'effets", + "outgoingHealingBoost": "Augmentation des soins", + "fireDmgBoost": "Bonus de dégâts de Feu", + "iceDmgBoost": "Bonus de dégâts de Glace", + "imaginaryDmgBoost": "Bonus de dégâts Imaginaire", + "physicalDmgBoost": "Bonus de dégâts Physique", + "quantumDmgBoost": "Bonus de dégâts Quantique", + "thunderDmgBoost": "Bonus de dégâts de Foudre", + "windDmgBoost": "Bonus de dégâts de Vent", + "pursued": "Dégâts additionnels", + "true damage": "Dégâts bruts", + "elationdamage": "Dégâts d'Allégresse", + "follow-up": "Dégâts d'attaque de suivi", + "elemental damage": "Dégâts de Rupture et Super Rupture", + "dot": "Dégâts sur la durée", + "qte": "Compétence QTE", + "level": "Niveau", + "relics": "Reliques", + "eidolons": "Eidolons", + "lightcones": "Cônes de Lumière", + "loadData": "Charger les données", + "exportData": "Exporter les données", + "connectSetting": "Paramètres de connexion", + "connected": "Connecté", + "unconnected": "Déconnecté", + "psConnection": "Connexion PS", + "connectionType": "Type de connexion", + "status": "Statut", + "connectPs": "Connecter PS", + "disconnect": "Déconnecter", + "other": "Autre", + "freeSr": "FreeSR", + "database": "Base de données", + "enka": "Enka", + "monsterSetting": "Paramètre de monstres", + "serverUrl": "URL du Serveur", + "privateType": "Type privé", + "local": "Local", + "server": "Serveur", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "placeholderServerUrl": "Entrer l'URL du serveur", + "placeholderUsername": "Entrer le nom d'utilisateur", + "placeholderPassword": "Entrer le mot de passe", + "connectedSuccess": "Connecté au PS avec succès", + "connectedFailed": "Échec de connexion au PS", + "syncSuccess": "Données synchronisées au PS avec succès", + "syncFailed": "Échec de synchronisation au PS", + "sync": "Synchroniser", + "importSetting": "Paramètres d'importation", + "profile": "Profil", + "default": "Défaut", + "copyProfiles": "Copier les profils", + "addNewProfile": "Ajouter un nouveau profil", + "createNewProfile": "Créer un nouveau profil", + "editProfile": "Éditer le profil", + "placeholderProfileName": "Entrer le nom du profil", + "profileName": "Nom du profil", + "create": "Créer", + "update": "Mettre à jour", + "characterInformation": "Informations du personnage", + "skills": "Compétences", + "showcaseCard": "Carte de présentation", + "comingSoon": "Bientôt disponible", + "characterName": "Nom du personnage", + "placeholderCharacter": "Entrer le nom du personnage", + "characterSettings": "Paramètres du personnage", + "levelConfiguration": "Configuration de niveau", + "characterLevel": "Niveau du personnage", + "max": "MAX", + "ultimateEnergy": "Énergie de l'Ultime", + "currentEnergy": "Énergie actuelle", + "setTo50": "Régler sur 50%", + "battleConfiguration": "Configuration de combat", + "useTechnique": "Utiliser Technique", + "techniqueNote": "Activer les effets de technique avant le combat", + "enhancement": "Amélioration", + "enhancementLevel": "Niveau d'amélioration", + "origin": "Origine", + "enhancedNote": "Les améliorations élevées débloquent des compétences", + "lightconeEquipment": "Équipement de Cône de Lumière", + "lightconeSettings": "Paramètres de Cône de Lumière", + "placeholderLevel": "Entrer le niveau", + "superimpositionRank": "Rang de Superposition", + "ranksNote": "Les rangs élevés offrent des effets plus forts", + "changeLightcone": "Changer de Cône de Lumière", + "removeLightcone": "Retirer le Cône de Lumière", + "equipLightcone": "Équiper un Cône de Lumière", + "noLightconeEquipped": "Aucun Cône de Lumière", + "equipLightconeNote": "Équipez un cône pour renforcer votre personnage", + "filter": "Filtre", + "selectedCharacters": "Personnages sélectionnés", + "selectedProfiles": "Profils sélectionnés", + "clearAll": "Tout effacer", + "selectAll": "Tout sélectionner", + "copy": "Copier", + "copied": "Copié", + "noAvatarSelected": "Aucun personnage sélectionné", + "noAvatarToCopySelected": "Aucun personnage à copier sélectionné", + "pleaseSelectAtLeastOneProfile": "Veuillez sélectionner au moins un profil", + "pleaseEnterUid": "Veuillez entrer un UID", + "failedToFetchEnkaData": "Échec de récupération des données Enka", + "pleaseSelectAtLeastOneCharacter": "Veuillez sélectionner au moins un personnage", + "noDataToImport": "Aucune donnée à importer", + "pleaseSelectAFile": "Veuillez sélectionner un fichier", + "fileMustBeAValidJsonFile": "Le fichier doit être un JSON valide", + "importEnkaDataSuccess": "Importation Enka réussie", + "importFreeSRDataSuccess": "Importation FreeSR réussie", + "importDatabaseSuccess": "Importation de base de données réussie", + "getData": "Obtenir des données", + "import": "Importer", + "freeSRImport": "Importation FreeSR", + "onlySupportFreeSRJsonFile": "Supporte uniquement les fichiers JSON FreeSR", + "pickAFile": "Choisir un fichier", + "lightConeSetting": "Paramètre de Cône de Lumière", + "relicMaker": "Créateur de Reliques", + "pleaseSelectAllOptions": "Veuillez sélectionner toutes les options", + "relicSavedSuccessfully": "Relique sauvegardée avec succès", + "mainSettings": "Paramètres principaux", + "mainStat": "Statistique principale", + "set": "Set", + "pleaseSelectASet": "Veuillez sélectionner un set", + "effectBonus": "Bonus d'effet", + "totalRoll": "Total d'améliorations", + "randomizeStats": "Statistiques aléatoires", + "randomizeRolls": "Améliorations aléatoires", + "selectASubStat": "Sélectionner une stat secondaire", + "selectASet": "Sélectionner un set", + "selectAMainStat": "Sélectionner une stat principale", + "save": "Sauvegarder", + "reset": "Réinitialiser", + "roll": "Amélioration", + "step": "Étape", + "memoryOfChaos": "Mémoire du Chaos", + "pureFiction": "Pure Fiction", + "apocalypticShadow": "Ombre Apocalyptique", + "customEnemy": "Ennemi personnalisé", + "simulatedUniverse": "Univers Simulé", + "floor": "Étage", + "side": "Côté", + "wave": "Vague", + "stage": "Niveau", + "useCycleCount": "Utiliser le compteur de cycles ?", + "useTurbulenceBuff": "Utiliser le buff de turbulence ?", + "firstHalfEnemies": "Ennemis première moitié", + "secondHalfEnemies": "Ennemis deuxième moitié", + "firstNodeEnemies": "Ennemis du nœud 1", + "secondNodeEnemies": "Ennemis du nœud 2", + "thirdNodeEnemies": "Ennemis du nœud 3", + "firstNode": "Nœud 1", + "secondNode": "Nœud 2", + "thirdNode": "Nœud 3", + "listEnemies": "Liste des ennemis", + "turbulenceBuff": "Buff de Turbulence", + "noEventSelected": "Aucun événement sélectionné", + "noTurbulenceBuff": "Aucun Buff de Turbulence", + "upper": "Supérieur", + "lower": "Inférieur", + "upperToLower": "Supérieur -> Inférieur", + "lowerToUpper": "Inférieur -> Supérieur", + "selectMOCEvent": "Sélectionner un événement MOC", + "selectPFEvent": "Sélectionner un événement PF", + "selectASEvent": "Sélectionner un événement AS", + "selectCEEvent": "Sélectionner un événement CE", + "selectEvent": "Sélectionner un événement", + "selectFloor": "Sélectionner un étage", + "selectSide": "Sélectionner un côté", + "selectBuff": "Sélectionner un buff", + "selectStage": "Sélectionner un niveau", + "previous": "Précédent", + "next": "Suivant", + "noMonstersFound": "Aucun monstre trouvé", + "addNewWave": "Ajouter une nouvelle vague", + "searchStage": "Chercher un niveau...", + "noStageFound": "Aucun niveau trouvé", + "searchMonster": "Chercher un monstre...", + "changeRelic": "Changer de relique", + "deleteRelic": "Supprimer la relique", + "deleteRelicConfirm": "Êtes-vous sûr de vouloir supprimer la relique de cet emplacement ?", + "setEffects": "Définir les effets", + "details": "Détails", + "normal": "Attaque Normale", + "bpskill": "Compétence", + "maze": "Technique", + "ultra": "Ultime", + "servantskill": "Compétence de Mémolutin", + "severaltalent": "Talent de Mémolutin", + "singleattack": "Attaque à cible unique", + "enhance": "Renforcement", + "summon": "Invocation", + "mazeattack": "Attaque de Technique", + "blast": "Diffusion", + "restore": "Restauration", + "support": "Soutien", + "aoeattack": "Attaque de zone", + "impair": "Malus", + "bounce": "Rebond", + "active": "Actif", + "defence": "Défense", + "inactive": "Inactif", + "maxAll": "Tout maximiser", + "maxAllSuccess": "Compétences maximisées avec succès.", + "maxAllFailed": "Échec de maximisation.", + "noRelicEquipped": "Aucune relique équipée", + "anomalyArbitration": "Arbitrage d'Anomalie", + "normalMode": "Mode Normal", + "hardMode": "Mode Difficile", + "selectPEAKEvent": "Sélectionner un événement PEAK", + "mode": "Mode", + "selectMode": "Sélectionner un mode", + "rollBack": "Annuler l'étape", + "upRoll": "Augmenter l'amélioration", + "downRoll": "Diminuer l'amélioration", + "actions": "Actions", + "avatars": "Avatars", + "quickView": "Aperçu rapide", + "extraSetting": "Paramètres supplémentaires", + "disableCensorship": "Désactiver la censure", + "hideUI": "Cacher l'UI", + "theoryCraftMode": "Mode Theorycraft", + "cycleCount": "Nombre de cycles", + "pleaseSelectAllSubStats": "Veuillez sélectionner toutes les stats secondaires", + "subStatRollCountCannotBeZero": "Les améliorations de stat secondaire ne peuvent être à zéro", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Personnage Multi-Voies", + "mainPath": "Voie Principale", + "march7Path": "Voie de March 7th", + "challenge": "Défi", + "skipNode": "Passer le nœud", + "disableSkip": "Désactiver le passage", + "skipNode1": "Passer le nœud 1", + "skipNode2": "Passer le nœud 2", + "extraFeatures": "Fonctionnalités supplémentaires", + "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.", + "detailChallengePeak": "Permet de changer la saison de Peak dans l'anomalie actuelle.", + "detailHiddenUi": "Cachera l'interface du jeu.", + "detailDisableCensorship": "Désactivera la censure du jeu.", + "detailMultipathCharacter": "Permet de changer la Voie de certains personnages.", + "trailblazer": "Pionnier", + "listExtraEffect": "Liste des effets supplémentaires", + "extra": "Extra", + "customLineup": "Composition personnalisée" + } } \ No newline at end of file diff --git a/messages/id.json b/messages/id.json index a1d3949..7591acb 100644 --- a/messages/id.json +++ b/messages/id.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Alat Firefly oleh Firefly Shelter" - }, - "DataPage": { - "skillType": "Tipe Skill", - "skillName": "Nama Skill", - "character": "Karakter", - "id": "Id", - "path": "Path", - "rarity": "Rarity", - "element": "Elemen", - "technique": "Technique", - "talent": "Talent", - "basic": "Basic Attack", - "skill": "Skill", - "ultimate": "Ultimate", - "servant": "Servant", - "damage": "Damage", - "type": "Tipe", - "warrior": "The Destruction", - "knight": "The Preservation", - "mage": "The Erudition", - "priest": "The Abundance", - "rogue": "The Hunt", - "shaman": "The Harmony", - "warlock": "The Nihility", - "memory": "The Remembrance", - "elation": "The Elation", - "fire": "Fire", - "ice": "Ice", - "imaginary": "Imaginary", - "physical": "Physical", - "quantum": "Quantum", - "thunder": "Lightning", - "wind": "Wind", - "hp": "HP", - "atk": "ATK", - "speed": "SPD", - "critRate": "CRIT Rate", - "critDmg": "CRIT DMG", - "breakEffect": "Break Effect", - "effectRes": "Effect RES", - "energyRegenerationRate": "Energy Regeneration Rate", - "effectHitRate": "Effect Hit Rate", - "outgoingHealingBoost": "Outgoing Healing Boost", - "fireDmgBoost": "Fire DMG Boost", - "iceDmgBoost": "Ice DMG Boost", - "imaginaryDmgBoost": "Imaginary DMG Boost", - "physicalDmgBoost": "Physical DMG Boost", - "quantumDmgBoost": "Quantum DMG Boost", - "thunderDmgBoost": "Lightning DMG Boost", - "windDmgBoost": "Wind DMG Boost", - "pursued": "Additional Damage", - "true damage": "True Damage", - "elationdamage": "Elation Damage", - "follow-up": "Follow-up Attack Damage", - "elemental damage": "Break dan Super Break Damage", - "dot": "Damage Over Time", - "qte": "QTE Skill", - "level": "Level", - "relics": "Relic", - "eidolons": "Eidolon", - "lightcones": "Light Cone", - "loadData": "Muat data", - "exportData": "Ekspor data", - "connectSetting": "Pengaturan Koneksi", - "connected": "Terhubung", - "unconnected": "Tidak terhubung", - "psConnection": "Koneksi PS", - "connectionType": "Tipe Koneksi", - "status": "Status", - "connectPs": "Hubungkan PS", - "disconnect": "Putuskan sambungan", - "other": "Lainnya", - "freeSr": "FreeSR", - "database": "Database", - "enka": "Enka", - "monsterSetting": "Pengaturan Monster", - "serverUrl": "URL Server", - "privateType": "Tipe Private", - "local": "Lokal", - "server": "Server", - "username": "Username", - "password": "Password", - "placeholderServerUrl": "Masukkan URL server", - "placeholderUsername": "Masukkan username", - "placeholderPassword": "Masukkan password", - "connectedSuccess": "Berhasil terhubung ke PS", - "connectedFailed": "Gagal terhubung ke PS", - "syncSuccess": "Berhasil sinkronisasi data ke PS", - "syncFailed": "Gagal sinkronisasi data ke PS", - "sync": "Sinkronisasi", - "importSetting": "Pengaturan Impor", - "profile": "Profil", - "default": "Default", - "copyProfiles": "Salin profil", - "addNewProfile": "Tambah profil baru", - "createNewProfile": "Buat profil baru", - "editProfile": "Edit profil", - "placeholderProfileName": "Masukkan nama profil", - "profileName": "Nama profil", - "create": "Buat", - "update": "Perbarui", - "characterInformation": "Informasi Karakter", - "skills": "Skill", - "showcaseCard": "Showcase Card", - "comingSoon": "Segera hadir", - "characterName": "Nama Karakter", - "placeholderCharacter": "Masukkan nama karakter", - "characterSettings": "Pengaturan Karakter", - "levelConfiguration": "Konfigurasi Level", - "characterLevel": "Level Karakter", - "max": "MAX", - "ultimateEnergy": "Energy Ultimate", - "currentEnergy": "Energy Saat Ini", - "setTo50": "Atur ke 50%", - "battleConfiguration": "Konfigurasi Pertempuran", - "useTechnique": "Gunakan Technique", - "techniqueNote": "Aktifkan efek technique sebelum pertempuran", - "enhancement": "Enhancement", - "enhancementLevel": "Level Enhancement", - "origin": "Asli", - "enhancedNote": "Enhancement lebih tinggi membuka kemampuan tambahan", - "lightconeEquipment": "Peralatan Light Cone", - "lightconeSettings": "Pengaturan Light Cone", - "placeholderLevel": "Masukkan level", - "superimpositionRank": "Superimposition Rank", - "ranksNote": "Rank lebih tinggi memberikan efek lebih kuat", - "changeLightcone": "Ganti Light Cone", - "removeLightcone": "Lepas Light Cone", - "equipLightcone": "Pakai Light Cone", - "noLightconeEquipped": "Tidak Ada Light Cone", - "equipLightconeNote": "Pakai light cone untuk memperkuat karaktermu", - "filter": "Filter", - "selectedCharacters": "Karakter Dipilih", - "selectedProfiles": "Profil Dipilih", - "clearAll": "Bersihkan Semua", - "selectAll": "Pilih Semua", - "copy": "Salin", - "copied": "Tersalin", - "noAvatarSelected": "Tidak ada karakter dipilih", - "noAvatarToCopySelected": "Tidak ada karakter untuk disalin", - "pleaseSelectAtLeastOneProfile": "Pilih setidaknya satu profil", - "pleaseEnterUid": "Silakan masukkan UID", - "failedToFetchEnkaData": "Gagal mengambil data Enka", - "pleaseSelectAtLeastOneCharacter": "Pilih setidaknya satu karakter", - "noDataToImport": "Tidak ada data untuk diimpor", - "pleaseSelectAFile": "Silakan pilih file", - "fileMustBeAValidJsonFile": "File harus berupa file JSON yang valid", - "importEnkaDataSuccess": "Impor data Enka berhasil", - "importFreeSRDataSuccess": "Impor data FreeSR berhasil", - "importDatabaseSuccess": "Impor database berhasil", - "getData": "Dapatkan Data", - "import": "Impor", - "freeSRImport": "Impor FreeSR", - "onlySupportFreeSRJsonFile": "Hanya mendukung file JSON FreeSR", - "pickAFile": "Pilih sebuah file", - "lightConeSetting": "Pengaturan Light Cone", - "relicMaker": "Pembuat Relic", - "pleaseSelectAllOptions": "Silakan pilih semua opsi", - "relicSavedSuccessfully": "Relic berhasil disimpan", - "mainSettings": "Pengaturan Utama", - "mainStat": "Statistik Utama", - "set": "Set", - "pleaseSelectASet": "Pilih sebuah set", - "effectBonus": "Bonus Efek", - "totalRoll": "Total Roll", - "randomizeStats": "Acak Statistik", - "randomizeRolls": "Acak Roll", - "selectASubStat": "Pilih sub stat", - "selectASet": "Pilih sebuah set", - "selectAMainStat": "Pilih stat utama", - "save": "Simpan", - "reset": "Reset", - "roll": "Roll", - "step": "Langkah", - "memoryOfChaos": "Memory of Chaos", - "pureFiction": "Pure Fiction", - "apocalypticShadow": "Apocalyptic Shadow", - "customEnemy": "Musuh Kustom", - "simulatedUniverse": "Simulated Universe", - "floor": "Lantai", - "side": "Sisi", - "wave": "Gelombang", - "stage": "Tahap", - "useCycleCount": "Gunakan hitungan siklus?", - "useTurbulenceBuff": "Gunakan buff turbulence?", - "firstHalfEnemies": "Musuh paruh pertama", - "secondHalfEnemies": "Musuh paruh kedua", - "firstNodeEnemies": "Musuh Node 1", - "secondNodeEnemies": "Musuh Node 2", - "thirdNodeEnemies": "Musuh Node 3", - "firstNode": "Node 1", - "secondNode": "Node 2", - "thirdNode": "Node 3", - "listEnemies": "Daftar musuh", - "turbulenceBuff": "Turbulence Buff", - "noEventSelected": "Tidak ada event dipilih", - "noTurbulenceBuff": "Tanpa Turbulence Buff", - "upper": "Atas", - "lower": "Bawah", - "upperToLower": "Atas -> Bawah", - "lowerToUpper": "Bawah -> Atas", - "selectMOCEvent": "Pilih Event MOC", - "selectPFEvent": "Pilih Event PF", - "selectASEvent": "Pilih Event AS", - "selectCEEvent": "Pilih Event CE", - "selectEvent": "Pilih Event", - "selectFloor": "Pilih Lantai", - "selectSide": "Pilih Sisi", - "selectBuff": "Pilih Buff", - "selectStage": "Pilih Tahap", - "previous": "Sebelumnya", - "next": "Selanjutnya", - "noMonstersFound": "Tidak ada monster ditemukan", - "addNewWave": "Tambah Gelombang Baru", - "searchStage": "Cari tahap...", - "noStageFound": "Tahap tidak ditemukan", - "searchMonster": "Cari monster...", - "changeRelic": "Ganti relic", - "deleteRelic": "Hapus relic", - "deleteRelicConfirm": "Yakin ingin menghapus relic di slot ini?", - "setEffects": "Atur Efek", - "details": "Detail", - "normal": "Basic ATK", - "bpskill": "Skill", - "maze": "Technique", - "ultra": "Ultimate", - "servantskill": "Skill Memosprite", - "severaltalent": "Talent Memosprite", - "singleattack": "Single Attack", - "enhance": "Enhance", - "summon": "Summon", - "mazeattack": "Technique Attack", - "blast": "Blast", - "restore": "Restore", - "support": "Support", - "aoeattack": "AoE Attack", - "impair": "Impair", - "bounce": "Bounce", - "active": "Aktif", - "defence": "Defence", - "inactive": "Nonaktif", - "maxAll": "Maksimalkan Semua", - "maxAllSuccess": "Berhasil memaksimalkan level skill.", - "maxAllFailed": "Gagal memaksimalkan level skill.", - "noRelicEquipped": "Tidak ada relic dipakai", - "anomalyArbitration": "Anomaly Arbitration", - "normalMode": "Mode Normal", - "hardMode": "Mode Sulit", - "selectPEAKEvent": "Pilih Event PEAK", - "mode": "Mode", - "selectMode": "Pilih Mode", - "rollBack": "Roll Back", - "upRoll": "Up Roll", - "downRoll": "Down Roll", - "actions": "Aksi", - "avatars": "Avatar", - "quickView": "Tampilan Cepat", - "extraSetting": "Pengaturan Ekstra", - "disableCensorship": "Nonaktifkan Sensor", - "hideUI": "Sembunyikan UI", - "theoryCraftMode": "Mode Theorycraft", - "cycleCount": "Hitungan Siklus", - "pleaseSelectAllSubStats": "Pilih semua sub statistik", - "subStatRollCountCannotBeZero": "Hitungan roll sub stat tidak boleh nol", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Karakter Multipath", - "mainPath": "Path Utama", - "march7Path": "Path March 7th", - "challenge": "Tantangan", - "skipNode": "Lewati Node", - "disableSkip": "Nonaktifkan lewati", - "skipNode1": "Lewati node 1", - "skipNode2": "Lewati node 2", - "extraFeatures": "Fitur Ekstra", - "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.", - "detailChallengePeak": "Memungkinkan perubahan musim Peak dalam Anomaly saat ini.", - "detailHiddenUi": "Mengaktifkan fitur ini akan menyembunyikan UI game.", - "detailDisableCensorship": "Mengaktifkan fitur ini akan mematikan sensor dalam game.", - "detailMultipathCharacter": "Memungkinkan perubahan Path untuk karakter tertentu.", - "trailblazer": "Trailblazer", - "listExtraEffect": "Daftar Efek Ekstra", - "extra": "Ekstra", - "customLineup": "Lineup Kustom" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Alat Firefly oleh Firefly Shelter" + }, + "DataPage": { + "skillType": "Tipe Skill", + "skillName": "Nama Skill", + "character": "Karakter", + "id": "Id", + "path": "Path", + "rarity": "Rarity", + "element": "Elemen", + "technique": "Technique", + "talent": "Talent", + "basic": "Basic Attack", + "skill": "Skill", + "ultimate": "Ultimate", + "servant": "Servant", + "damage": "Damage", + "type": "Tipe", + "warrior": "The Destruction", + "knight": "The Preservation", + "mage": "The Erudition", + "priest": "The Abundance", + "rogue": "The Hunt", + "shaman": "The Harmony", + "warlock": "The Nihility", + "memory": "The Remembrance", + "elation": "The Elation", + "fire": "Fire", + "ice": "Ice", + "imaginary": "Imaginary", + "physical": "Physical", + "quantum": "Quantum", + "thunder": "Lightning", + "wind": "Wind", + "hp": "HP", + "atk": "ATK", + "speed": "SPD", + "critRate": "CRIT Rate", + "critDmg": "CRIT DMG", + "breakEffect": "Break Effect", + "effectRes": "Effect RES", + "energyRegenerationRate": "Energy Regeneration Rate", + "effectHitRate": "Effect Hit Rate", + "outgoingHealingBoost": "Outgoing Healing Boost", + "fireDmgBoost": "Fire DMG Boost", + "iceDmgBoost": "Ice DMG Boost", + "imaginaryDmgBoost": "Imaginary DMG Boost", + "physicalDmgBoost": "Physical DMG Boost", + "quantumDmgBoost": "Quantum DMG Boost", + "thunderDmgBoost": "Lightning DMG Boost", + "windDmgBoost": "Wind DMG Boost", + "pursued": "Additional Damage", + "true damage": "True Damage", + "elationdamage": "Elation Damage", + "follow-up": "Follow-up Attack Damage", + "elemental damage": "Break dan Super Break Damage", + "dot": "Damage Over Time", + "qte": "QTE Skill", + "level": "Level", + "relics": "Relic", + "eidolons": "Eidolon", + "lightcones": "Light Cone", + "loadData": "Muat data", + "exportData": "Ekspor data", + "connectSetting": "Pengaturan Koneksi", + "connected": "Terhubung", + "unconnected": "Tidak terhubung", + "psConnection": "Koneksi PS", + "connectionType": "Tipe Koneksi", + "status": "Status", + "connectPs": "Hubungkan PS", + "disconnect": "Putuskan sambungan", + "other": "Lainnya", + "freeSr": "FreeSR", + "database": "Database", + "enka": "Enka", + "monsterSetting": "Pengaturan Monster", + "serverUrl": "URL Server", + "privateType": "Tipe Private", + "local": "Lokal", + "server": "Server", + "username": "Username", + "password": "Password", + "placeholderServerUrl": "Masukkan URL server", + "placeholderUsername": "Masukkan username", + "placeholderPassword": "Masukkan password", + "connectedSuccess": "Berhasil terhubung ke PS", + "connectedFailed": "Gagal terhubung ke PS", + "syncSuccess": "Berhasil sinkronisasi data ke PS", + "syncFailed": "Gagal sinkronisasi data ke PS", + "sync": "Sinkronisasi", + "importSetting": "Pengaturan Impor", + "profile": "Profil", + "default": "Default", + "copyProfiles": "Salin profil", + "addNewProfile": "Tambah profil baru", + "createNewProfile": "Buat profil baru", + "editProfile": "Edit profil", + "placeholderProfileName": "Masukkan nama profil", + "profileName": "Nama profil", + "create": "Buat", + "update": "Perbarui", + "characterInformation": "Informasi Karakter", + "skills": "Skill", + "showcaseCard": "Showcase Card", + "comingSoon": "Segera hadir", + "characterName": "Nama Karakter", + "placeholderCharacter": "Masukkan nama karakter", + "characterSettings": "Pengaturan Karakter", + "levelConfiguration": "Konfigurasi Level", + "characterLevel": "Level Karakter", + "max": "MAX", + "ultimateEnergy": "Energy Ultimate", + "currentEnergy": "Energy Saat Ini", + "setTo50": "Atur ke 50%", + "battleConfiguration": "Konfigurasi Pertempuran", + "useTechnique": "Gunakan Technique", + "techniqueNote": "Aktifkan efek technique sebelum pertempuran", + "enhancement": "Enhancement", + "enhancementLevel": "Level Enhancement", + "origin": "Asli", + "enhancedNote": "Enhancement lebih tinggi membuka kemampuan tambahan", + "lightconeEquipment": "Peralatan Light Cone", + "lightconeSettings": "Pengaturan Light Cone", + "placeholderLevel": "Masukkan level", + "superimpositionRank": "Superimposition Rank", + "ranksNote": "Rank lebih tinggi memberikan efek lebih kuat", + "changeLightcone": "Ganti Light Cone", + "removeLightcone": "Lepas Light Cone", + "equipLightcone": "Pakai Light Cone", + "noLightconeEquipped": "Tidak Ada Light Cone", + "equipLightconeNote": "Pakai light cone untuk memperkuat karaktermu", + "filter": "Filter", + "selectedCharacters": "Karakter Dipilih", + "selectedProfiles": "Profil Dipilih", + "clearAll": "Bersihkan Semua", + "selectAll": "Pilih Semua", + "copy": "Salin", + "copied": "Tersalin", + "noAvatarSelected": "Tidak ada karakter dipilih", + "noAvatarToCopySelected": "Tidak ada karakter untuk disalin", + "pleaseSelectAtLeastOneProfile": "Pilih setidaknya satu profil", + "pleaseEnterUid": "Silakan masukkan UID", + "failedToFetchEnkaData": "Gagal mengambil data Enka", + "pleaseSelectAtLeastOneCharacter": "Pilih setidaknya satu karakter", + "noDataToImport": "Tidak ada data untuk diimpor", + "pleaseSelectAFile": "Silakan pilih file", + "fileMustBeAValidJsonFile": "File harus berupa file JSON yang valid", + "importEnkaDataSuccess": "Impor data Enka berhasil", + "importFreeSRDataSuccess": "Impor data FreeSR berhasil", + "importDatabaseSuccess": "Impor database berhasil", + "getData": "Dapatkan Data", + "import": "Impor", + "freeSRImport": "Impor FreeSR", + "onlySupportFreeSRJsonFile": "Hanya mendukung file JSON FreeSR", + "pickAFile": "Pilih sebuah file", + "lightConeSetting": "Pengaturan Light Cone", + "relicMaker": "Pembuat Relic", + "pleaseSelectAllOptions": "Silakan pilih semua opsi", + "relicSavedSuccessfully": "Relic berhasil disimpan", + "mainSettings": "Pengaturan Utama", + "mainStat": "Statistik Utama", + "set": "Set", + "pleaseSelectASet": "Pilih sebuah set", + "effectBonus": "Bonus Efek", + "totalRoll": "Total Roll", + "randomizeStats": "Acak Statistik", + "randomizeRolls": "Acak Roll", + "selectASubStat": "Pilih sub stat", + "selectASet": "Pilih sebuah set", + "selectAMainStat": "Pilih stat utama", + "save": "Simpan", + "reset": "Reset", + "roll": "Roll", + "step": "Langkah", + "memoryOfChaos": "Memory of Chaos", + "pureFiction": "Pure Fiction", + "apocalypticShadow": "Apocalyptic Shadow", + "customEnemy": "Musuh Kustom", + "simulatedUniverse": "Simulated Universe", + "floor": "Lantai", + "side": "Sisi", + "wave": "Gelombang", + "stage": "Tahap", + "useCycleCount": "Gunakan hitungan siklus?", + "useTurbulenceBuff": "Gunakan buff turbulence?", + "firstHalfEnemies": "Musuh paruh pertama", + "secondHalfEnemies": "Musuh paruh kedua", + "firstNodeEnemies": "Musuh Node 1", + "secondNodeEnemies": "Musuh Node 2", + "thirdNodeEnemies": "Musuh Node 3", + "firstNode": "Node 1", + "secondNode": "Node 2", + "thirdNode": "Node 3", + "listEnemies": "Daftar musuh", + "turbulenceBuff": "Turbulence Buff", + "noEventSelected": "Tidak ada event dipilih", + "noTurbulenceBuff": "Tanpa Turbulence Buff", + "upper": "Atas", + "lower": "Bawah", + "upperToLower": "Atas -> Bawah", + "lowerToUpper": "Bawah -> Atas", + "selectMOCEvent": "Pilih Event MOC", + "selectPFEvent": "Pilih Event PF", + "selectASEvent": "Pilih Event AS", + "selectCEEvent": "Pilih Event CE", + "selectEvent": "Pilih Event", + "selectFloor": "Pilih Lantai", + "selectSide": "Pilih Sisi", + "selectBuff": "Pilih Buff", + "selectStage": "Pilih Tahap", + "previous": "Sebelumnya", + "next": "Selanjutnya", + "noMonstersFound": "Tidak ada monster ditemukan", + "addNewWave": "Tambah Gelombang Baru", + "searchStage": "Cari tahap...", + "noStageFound": "Tahap tidak ditemukan", + "searchMonster": "Cari monster...", + "changeRelic": "Ganti relic", + "deleteRelic": "Hapus relic", + "deleteRelicConfirm": "Yakin ingin menghapus relic di slot ini?", + "setEffects": "Atur Efek", + "details": "Detail", + "normal": "Basic ATK", + "bpskill": "Skill", + "maze": "Technique", + "ultra": "Ultimate", + "servantskill": "Skill Memosprite", + "severaltalent": "Talent Memosprite", + "singleattack": "Single Attack", + "enhance": "Enhance", + "summon": "Summon", + "mazeattack": "Technique Attack", + "blast": "Blast", + "restore": "Restore", + "support": "Support", + "aoeattack": "AoE Attack", + "impair": "Impair", + "bounce": "Bounce", + "active": "Aktif", + "defence": "Defence", + "inactive": "Nonaktif", + "maxAll": "Maksimalkan Semua", + "maxAllSuccess": "Berhasil memaksimalkan level skill.", + "maxAllFailed": "Gagal memaksimalkan level skill.", + "noRelicEquipped": "Tidak ada relic dipakai", + "anomalyArbitration": "Anomaly Arbitration", + "normalMode": "Mode Normal", + "hardMode": "Mode Sulit", + "selectPEAKEvent": "Pilih Event PEAK", + "mode": "Mode", + "selectMode": "Pilih Mode", + "rollBack": "Roll Back", + "upRoll": "Up Roll", + "downRoll": "Down Roll", + "actions": "Aksi", + "avatars": "Avatar", + "quickView": "Tampilan Cepat", + "extraSetting": "Pengaturan Ekstra", + "disableCensorship": "Nonaktifkan Sensor", + "hideUI": "Sembunyikan UI", + "theoryCraftMode": "Mode Theorycraft", + "cycleCount": "Hitungan Siklus", + "pleaseSelectAllSubStats": "Pilih semua sub statistik", + "subStatRollCountCannotBeZero": "Hitungan roll sub stat tidak boleh nol", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Karakter Multipath", + "mainPath": "Path Utama", + "march7Path": "Path March 7th", + "challenge": "Tantangan", + "skipNode": "Lewati Node", + "disableSkip": "Nonaktifkan lewati", + "skipNode1": "Lewati node 1", + "skipNode2": "Lewati node 2", + "extraFeatures": "Fitur Ekstra", + "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.", + "detailChallengePeak": "Memungkinkan perubahan musim Peak dalam Anomaly saat ini.", + "detailHiddenUi": "Mengaktifkan fitur ini akan menyembunyikan UI game.", + "detailDisableCensorship": "Mengaktifkan fitur ini akan mematikan sensor dalam game.", + "detailMultipathCharacter": "Memungkinkan perubahan Path untuk karakter tertentu.", + "trailblazer": "Trailblazer", + "listExtraEffect": "Daftar Efek Ekstra", + "extra": "Ekstra", + "customLineup": "Lineup Kustom" + } } \ No newline at end of file diff --git a/messages/ja.json b/messages/ja.json index 8f0e6e0..daddf97 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -1,289 +1,289 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Firefly tools by Firefly Shelter" - }, - "DataPage": { - "skillType": "スキルタイプ", - "skillName": "スキル名", - "character": "キャラクター", - "id": "ID", - "path": "運命", - "rarity": "レアリティ", - "element": "属性", - "technique": "秘技", - "talent": "天賦", - "basic": "通常攻撃", - "skill": "スキル", - "ultimate": "アルティメット", - "servant": "サーバント", - "damage": "ダメージ", - "type": "タイプ", - "warrior": "壊滅", - "knight": "存護", - "mage": "知恵", - "priest": "豊穣", - "rogue": "巡狩", - "shaman": "調和", - "warlock": "虚無", - "memory": "記憶", - "elation": "愉悦", - "fire": "火", - "ice": "氷", - "imaginary": "虚数", - "physical": "物理", - "quantum": "量子", - "thunder": "雷", - "wind": "風", - "hp": "HP", - "atk": "攻撃", - "speed": "速度", - "critRate": "会心率", - "critDmg": "会心ダメージ", - "breakEffect": "撃破特効", - "effectRes": "効果抵抗", - "energyRegenerationRate": "EP回復効率", - "effectHitRate": "効果命中率", - "outgoingHealingBoost": "回復増加", - "fireDmgBoost": "火属性ダメージ強化", - "iceDmgBoost": "氷属性ダメージ強化", - "imaginaryDmgBoost": "幻影ダメージ強化", - "physicalDmgBoost": "物理ダメージ強化", - "quantumDmgBoost": "量子ダメージ強化", - "thunderDmgBoost": "雷属性ダメージ強化", - "windDmgBoost": "風属性ダメージ強化", - "pursued": "追加ダメージ", - "true damage": "確定ダメージ", - "elationdamage": "愉悦ダメージ", - "follow-up": "追撃ダメージ", - "elemental damage": "属性ダメージ", - "dot": "継続ダメージ", - "qte": "QTEスキル", - "level": "レベル", - "relics": "遺物", - "eidolons": "エイドロン", - "lightcones": "光円錐", - "loadData": "データ読み込み", - "exportData": "データ出力", - "connectSetting": "接続設定", - "connected": "接続済み", - "unconnected": "未接続", - "psConnection": "PS接続", - "connectionType": "接続タイプ", - "status": "ステータス", - "connectPs": "PS接続", - "disconnect": "切断", - "other": "その他", - "freeSr": "FreeSR", - "database": "データベース", - "enka": "Enka", - "monsterSetting": "モンスター設定", - "serverUrl": "サーバー URL", - "privateType": "プライベートタイプ", - "local": "ローカル", - "server": "サーバー", - "username": "ユーザー名", - "password": "パスワード", - "placeholderServerUrl": "サーバー URL を入力", - "placeholderUsername": "ユーザー名を入力", - "placeholderPassword": "パスワードを入力", - "connectedSuccess": "PS接続成功", - "connectedFailed": "PS接続失敗", - "syncSuccess": "PSへのデータ同期に成功", - "syncFailed": "PSへのデータ同期に失敗", - "sync": "同期", - "importSetting": "インポート設定", - "profile": "プロファイル", - "default": "デフォルト", - "copyProfiles": "プロファイルをコピー", - "addNewProfile": "プロファイルを追加", - "createNewProfile": "新しいプロファイルを作成", - "editProfile": "プロファイル編集", - "placeholderProfileName": "プロファイル名を入力", - "profileName": "プロファイル名", - "create": "作成", - "update": "更新", - "characterInformation": "キャラクター情報", - "skills": "スキル", - "showcaseCard": "ショーケースカード", - "comingSoon": "近日公開", - "characterName": "キャラクター名", - "placeholderCharacter": "キャラクター名を入力", - "characterSettings": "キャラクター設定", - "levelConfiguration": "レベル構成", - "characterLevel": "キャラクターレベル", - "max": "最大", - "ultimateEnergy": "アルティメットエネルギー", - "currentEnergy": "現在のエネルギー", - "setTo50": "50%に設定", - "battleConfiguration": "バトル構成", - "useTechnique": "秘技を使用", - "techniqueNote": "バトル前に秘技効果を有効化", - "enhancement": "強化", - "enhancementLevel": "強化レベル", - "origin": "由来", - "enhancedNote": "強化レベルに応じて追加スキル解放", - "lightconeEquipment": "光円錐装備", - "lightconeSettings": "光円錐の設定", - "placeholderLevel": "レベルを入力", - "superimpositionRank": "重ねランク", - "ranksNote": "ランクが高いほど効果強化", - "changeLightcone": "光円錐を変更", - "removeLightcone": "光円錐を外す", - "equipLightcone": "光円錐を装備", - "noLightconeEquipped": "光円錐が装備されていません", - "equipLightconeNote": "強化のため光円錐を装備してください", - "filter": "フィルター", - "selectedCharacters": "選択中のキャラクター", - "selectedProfiles": "選択中のプロファイル", - "clearAll": "全てクリア", - "selectAll": "全て選択", - "copy": "コピー", - "copied": "コピー済み", - "noAvatarSelected": "キャラクターが選択されていません", - "noAvatarToCopySelected": "コピーするキャラクター選択されていません", - "pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください", - "pleaseEnterUid": "UIDを入力してください", - "failedToFetchEnkaData": "Enka のデータ取得に失敗", - "pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください", - "noDataToImport": "インポートするデータがありません", - "pleaseSelectAFile": "ファイルを選択してください", - "fileMustBeAValidJsonFile": "有効な JSON ファイルである必要があります", - "importEnkaDataSuccess": "Enka データのインポート成功", - "importFreeSRDataSuccess": "FreeSR データのインポート成功", - "importDatabaseSuccess": "データベースのインポート成功", - "getData": "データ取得", - "import": "インポート", - "freeSRImport": "FreeSR インポート", - "onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応", - "pickAFile": "ファイル選択", - "lightConeSetting": "ライトコーン設定", - "relicMaker": "遺物製作", - "pleaseSelectAllOptions": "すべてのオプションを選択してください", - "relicSavedSuccessfully": "遺物の保存に成功しました", - "mainSettings": "メイン設定", - "mainStat": "メインステータス", - "set": "セット", - "pleaseSelectASet": "セットを選択してください", - "effectBonus": "効果ボーナス", - "totalRoll": "総ロール数", - "randomizeStats": "ステータスランダム化", - "randomizeRolls": "ロール回数ランダム化", - "selectASubStat": "サブステータスを選択", - "selectASet": "セットを選択", - "selectAMainStat": "メインステータスを選んでください", - "save": "保存", - "reset": "リセット", - "roll": "ロール", - "step": "ステップ", - "memoryOfChaos": "混沌の記憶", - "pureFiction": "虚構叙事", - "apocalypticShadow": "末日の幻影", - "customEnemy": "カスタム敵", - "simulatedUniverse": "模擬宇宙", - "floor": "階層", - "side": "前半/後半", - "wave": "ウェーブ", - "stage": "ステージ", - "useCycleCount": "サイクル数を使用しますか?", - "useTurbulenceBuff": "乱気流バフを使用しますか?", - "firstHalfEnemies": "前半の敵", - "secondHalfEnemies": "後半の敵", - "firstNodeEnemies": "ノード 1 の敵", - "secondNodeEnemies": "ノード 2 の敵", - "thirdNodeEnemies": "ノード 3 の敵", - "firstNode": "ノード 1", - "secondNode": "ノード 2", - "thirdNode": "ノード 3", - "turbulenceBuff": "乱気流バフ", - "noEventSelected": "イベントが選択されていません", - "noTurbulenceBuff": "乱気流バフがありません", - "upper": "上半", - "lower": "下半", - "upperToLower": "上半 -> 下半", - "lowerToUpper": "下半 -> 上半", - "selectMOCEvent": "MOC イベントを選択", - "selectPFEvent": "PF イベントを選択", - "selectASEvent": "AS イベントを選択", - "selectCEEvent": "CE イベントを選択", - "selectEvent": "イベントを選択", - "selectFloor": "階層を選択", - "selectSide": "前半/後半を選択", - "selectBuff": "バフを選択", - "selectStage": "ステージを選択", - "previous": "前へ", - "next": "次へ", - "noMonstersFound": "敵が見つかりません", - "addNewWave": "新しいウェーブを追加", - "searchStage": "ステージを検索...", - "noStageFound": "ステージが見つかりません", - "searchMonster": "敵を検索...", - "changeRelic": "遺物を変更", - "deleteRelic": "遺物を削除", - "deleteRelicConfirm": "この遺物を削除してもよろしいですか?", - "setEffects": "効果を設定", - "details": "詳細", - "normal": "通常攻撃", - "bpskill": "戦闘スキル", - "maze": "秘技", - "ultra": "必殺技", - "servantskill": "メモスプライトスキル", - "severaltalent": "メモスプライトの才能", - "singleattack": "単体攻撃", - "enhance": "強化", - "summon": "召喚", - "blast": "爆発", - "restore": "回復", - "support": "支援", - "aoeattack": "範囲攻撃", - "mazeattack": "迷宮秘技", - "impair": "弱体化", - "bounce": "弾跳", - "active": "アクティブ", - "inactive": "非アクティブ", - "defence": "防御", - "maxAll": "すべて最大化", - "maxAllSuccess": "スキルレベルを最大に設定しました。", - "maxAllFailed": "スキルレベルの最大設定に失敗しました。", - "noRelicEquipped": "遺物が装備されていません", - "anomalyArbitration": "異相の仲裁", - "normalMode": "通常モード", - "hardMode": "困難モード", - "selectPEAKEvent": "PEAK イベントを選択", - "mode": "モード", - "selectMode": "モードを選択", - "rollBack": "前の状態に戻る", - "upRoll": "サブステータスを増やす", - "downRoll": "サブステータスを減らす", - "actions": "アクション", - "avatars": "アバター", - "quickView": "クイックビュー", - "extraSetting": "追加設定", - "disableCensorship": "検閲を無効化", - "hideUI": "UIを非表示", - "theoryCraftMode": "シアリークラフトモード", - "cycleCount": "サイクル数", - "pleaseSelectAllSubStats": "すべてのサブステータスを選択してください", - "subStatRollCountCannotBeZero": "サブステータスの行数は0にできません", - "theoryCraft": "シアリークラフト", - "multipathCharacter": "複数運命キャラ", - "mainPath": "主人公の運命", - "march7Path": "三月なのかの運命", - "challenge": "挑戦", - "skipNode": "ノードをスキップ", - "disableSkip": "スキップ無効", - "skipNode1": "ノード1をスキップ", - "skipNode2": "ノード2をスキップ", - "extraFeatures": "追加機能", - "detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。", - "detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事の(ノード1/ノード2)をスキップできます。", - "detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。", - "detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。", - "detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。", - "detailMultipathCharacter": "一部キャラクターの運命を変更できます。", - "trailblazer": "開拓者", - "listExtraEffect": "追加効果一覧", - "extra": "追加", - "customLineup": "カスタム編成" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "スキルタイプ", + "skillName": "スキル名", + "character": "キャラクター", + "id": "ID", + "path": "運命", + "rarity": "レアリティ", + "element": "属性", + "technique": "秘技", + "talent": "天賦", + "basic": "通常攻撃", + "skill": "スキル", + "ultimate": "アルティメット", + "servant": "サーバント", + "damage": "ダメージ", + "type": "タイプ", + "warrior": "壊滅", + "knight": "存護", + "mage": "知恵", + "priest": "豊穣", + "rogue": "巡狩", + "shaman": "調和", + "warlock": "虚無", + "memory": "記憶", + "elation": "愉悦", + "fire": "火", + "ice": "氷", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "風", + "hp": "HP", + "atk": "攻撃", + "speed": "速度", + "critRate": "会心率", + "critDmg": "会心ダメージ", + "breakEffect": "撃破特効", + "effectRes": "効果抵抗", + "energyRegenerationRate": "EP回復効率", + "effectHitRate": "効果命中率", + "outgoingHealingBoost": "回復増加", + "fireDmgBoost": "火属性ダメージ強化", + "iceDmgBoost": "氷属性ダメージ強化", + "imaginaryDmgBoost": "幻影ダメージ強化", + "physicalDmgBoost": "物理ダメージ強化", + "quantumDmgBoost": "量子ダメージ強化", + "thunderDmgBoost": "雷属性ダメージ強化", + "windDmgBoost": "風属性ダメージ強化", + "pursued": "追加ダメージ", + "true damage": "確定ダメージ", + "elationdamage": "愉悦ダメージ", + "follow-up": "追撃ダメージ", + "elemental damage": "属性ダメージ", + "dot": "継続ダメージ", + "qte": "QTEスキル", + "level": "レベル", + "relics": "遺物", + "eidolons": "エイドロン", + "lightcones": "光円錐", + "loadData": "データ読み込み", + "exportData": "データ出力", + "connectSetting": "接続設定", + "connected": "接続済み", + "unconnected": "未接続", + "psConnection": "PS接続", + "connectionType": "接続タイプ", + "status": "ステータス", + "connectPs": "PS接続", + "disconnect": "切断", + "other": "その他", + "freeSr": "FreeSR", + "database": "データベース", + "enka": "Enka", + "monsterSetting": "モンスター設定", + "serverUrl": "サーバー URL", + "privateType": "プライベートタイプ", + "local": "ローカル", + "server": "サーバー", + "username": "ユーザー名", + "password": "パスワード", + "placeholderServerUrl": "サーバー URL を入力", + "placeholderUsername": "ユーザー名を入力", + "placeholderPassword": "パスワードを入力", + "connectedSuccess": "PS接続成功", + "connectedFailed": "PS接続失敗", + "syncSuccess": "PSへのデータ同期に成功", + "syncFailed": "PSへのデータ同期に失敗", + "sync": "同期", + "importSetting": "インポート設定", + "profile": "プロファイル", + "default": "デフォルト", + "copyProfiles": "プロファイルをコピー", + "addNewProfile": "プロファイルを追加", + "createNewProfile": "新しいプロファイルを作成", + "editProfile": "プロファイル編集", + "placeholderProfileName": "プロファイル名を入力", + "profileName": "プロファイル名", + "create": "作成", + "update": "更新", + "characterInformation": "キャラクター情報", + "skills": "スキル", + "showcaseCard": "ショーケースカード", + "comingSoon": "近日公開", + "characterName": "キャラクター名", + "placeholderCharacter": "キャラクター名を入力", + "characterSettings": "キャラクター設定", + "levelConfiguration": "レベル構成", + "characterLevel": "キャラクターレベル", + "max": "最大", + "ultimateEnergy": "アルティメットエネルギー", + "currentEnergy": "現在のエネルギー", + "setTo50": "50%に設定", + "battleConfiguration": "バトル構成", + "useTechnique": "秘技を使用", + "techniqueNote": "バトル前に秘技効果を有効化", + "enhancement": "強化", + "enhancementLevel": "強化レベル", + "origin": "由来", + "enhancedNote": "強化レベルに応じて追加スキル解放", + "lightconeEquipment": "光円錐装備", + "lightconeSettings": "光円錐の設定", + "placeholderLevel": "レベルを入力", + "superimpositionRank": "重ねランク", + "ranksNote": "ランクが高いほど効果強化", + "changeLightcone": "光円錐を変更", + "removeLightcone": "光円錐を外す", + "equipLightcone": "光円錐を装備", + "noLightconeEquipped": "光円錐が装備されていません", + "equipLightconeNote": "強化のため光円錐を装備してください", + "filter": "フィルター", + "selectedCharacters": "選択中のキャラクター", + "selectedProfiles": "選択中のプロファイル", + "clearAll": "全てクリア", + "selectAll": "全て選択", + "copy": "コピー", + "copied": "コピー済み", + "noAvatarSelected": "キャラクターが選択されていません", + "noAvatarToCopySelected": "コピーするキャラクター選択されていません", + "pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください", + "pleaseEnterUid": "UIDを入力してください", + "failedToFetchEnkaData": "Enka のデータ取得に失敗", + "pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください", + "noDataToImport": "インポートするデータがありません", + "pleaseSelectAFile": "ファイルを選択してください", + "fileMustBeAValidJsonFile": "有効な JSON ファイルである必要があります", + "importEnkaDataSuccess": "Enka データのインポート成功", + "importFreeSRDataSuccess": "FreeSR データのインポート成功", + "importDatabaseSuccess": "データベースのインポート成功", + "getData": "データ取得", + "import": "インポート", + "freeSRImport": "FreeSR インポート", + "onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応", + "pickAFile": "ファイル選択", + "lightConeSetting": "ライトコーン設定", + "relicMaker": "遺物製作", + "pleaseSelectAllOptions": "すべてのオプションを選択してください", + "relicSavedSuccessfully": "遺物の保存に成功しました", + "mainSettings": "メイン設定", + "mainStat": "メインステータス", + "set": "セット", + "pleaseSelectASet": "セットを選択してください", + "effectBonus": "効果ボーナス", + "totalRoll": "総ロール数", + "randomizeStats": "ステータスランダム化", + "randomizeRolls": "ロール回数ランダム化", + "selectASubStat": "サブステータスを選択", + "selectASet": "セットを選択", + "selectAMainStat": "メインステータスを選んでください", + "save": "保存", + "reset": "リセット", + "roll": "ロール", + "step": "ステップ", + "memoryOfChaos": "混沌の記憶", + "pureFiction": "虚構叙事", + "apocalypticShadow": "末日の幻影", + "customEnemy": "カスタム敵", + "simulatedUniverse": "模擬宇宙", + "floor": "階層", + "side": "前半/後半", + "wave": "ウェーブ", + "stage": "ステージ", + "useCycleCount": "サイクル数を使用しますか?", + "useTurbulenceBuff": "乱気流バフを使用しますか?", + "firstHalfEnemies": "前半の敵", + "secondHalfEnemies": "後半の敵", + "firstNodeEnemies": "ノード 1 の敵", + "secondNodeEnemies": "ノード 2 の敵", + "thirdNodeEnemies": "ノード 3 の敵", + "firstNode": "ノード 1", + "secondNode": "ノード 2", + "thirdNode": "ノード 3", + "turbulenceBuff": "乱気流バフ", + "noEventSelected": "イベントが選択されていません", + "noTurbulenceBuff": "乱気流バフがありません", + "upper": "上半", + "lower": "下半", + "upperToLower": "上半 -> 下半", + "lowerToUpper": "下半 -> 上半", + "selectMOCEvent": "MOC イベントを選択", + "selectPFEvent": "PF イベントを選択", + "selectASEvent": "AS イベントを選択", + "selectCEEvent": "CE イベントを選択", + "selectEvent": "イベントを選択", + "selectFloor": "階層を選択", + "selectSide": "前半/後半を選択", + "selectBuff": "バフを選択", + "selectStage": "ステージを選択", + "previous": "前へ", + "next": "次へ", + "noMonstersFound": "敵が見つかりません", + "addNewWave": "新しいウェーブを追加", + "searchStage": "ステージを検索...", + "noStageFound": "ステージが見つかりません", + "searchMonster": "敵を検索...", + "changeRelic": "遺物を変更", + "deleteRelic": "遺物を削除", + "deleteRelicConfirm": "この遺物を削除してもよろしいですか?", + "setEffects": "効果を設定", + "details": "詳細", + "normal": "通常攻撃", + "bpskill": "戦闘スキル", + "maze": "秘技", + "ultra": "必殺技", + "servantskill": "メモスプライトスキル", + "severaltalent": "メモスプライトの才能", + "singleattack": "単体攻撃", + "enhance": "強化", + "summon": "召喚", + "blast": "爆発", + "restore": "回復", + "support": "支援", + "aoeattack": "範囲攻撃", + "mazeattack": "迷宮秘技", + "impair": "弱体化", + "bounce": "弾跳", + "active": "アクティブ", + "inactive": "非アクティブ", + "defence": "防御", + "maxAll": "すべて最大化", + "maxAllSuccess": "スキルレベルを最大に設定しました。", + "maxAllFailed": "スキルレベルの最大設定に失敗しました。", + "noRelicEquipped": "遺物が装備されていません", + "anomalyArbitration": "異相の仲裁", + "normalMode": "通常モード", + "hardMode": "困難モード", + "selectPEAKEvent": "PEAK イベントを選択", + "mode": "モード", + "selectMode": "モードを選択", + "rollBack": "前の状態に戻る", + "upRoll": "サブステータスを増やす", + "downRoll": "サブステータスを減らす", + "actions": "アクション", + "avatars": "アバター", + "quickView": "クイックビュー", + "extraSetting": "追加設定", + "disableCensorship": "検閲を無効化", + "hideUI": "UIを非表示", + "theoryCraftMode": "シアリークラフトモード", + "cycleCount": "サイクル数", + "pleaseSelectAllSubStats": "すべてのサブステータスを選択してください", + "subStatRollCountCannotBeZero": "サブステータスの行数は0にできません", + "theoryCraft": "シアリークラフト", + "multipathCharacter": "複数運命キャラ", + "mainPath": "主人公の運命", + "march7Path": "三月なのかの運命", + "challenge": "挑戦", + "skipNode": "ノードをスキップ", + "disableSkip": "スキップ無効", + "skipNode1": "ノード1をスキップ", + "skipNode2": "ノード2をスキップ", + "extraFeatures": "追加機能", + "detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。", + "detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事の(ノード1/ノード2)をスキップできます。", + "detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。", + "detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。", + "detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。", + "detailMultipathCharacter": "一部キャラクターの運命を変更できます。", + "trailblazer": "開拓者", + "listExtraEffect": "追加効果一覧", + "extra": "追加", + "customLineup": "カスタム編成" + } } \ No newline at end of file diff --git a/messages/ko.json b/messages/ko.json index dad78ae..0ea9b51 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -1,289 +1,289 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Firefly tools by Firefly Shelter" - }, - "DataPage": { - "skillType": "스킬 유형", - "skillName": "스킬 이름", - "character": "캐릭터", - "id": "ID", - "path": "운명", - "rarity": "희귀도", - "element": "속성", - "technique": "비기", - "talent": "탈렌트", - "basic": "기본 공격", - "skill": "스킬", - "ultimate": "궁극기", - "servant": "서번트", - "damage": "데미지", - "type": "종류", - "warrior": "파괴", - "knight": "보호", - "mage": "박학", - "priest": "풍요", - "rogue": "사냥", - "shaman": "조화", - "warlock": "허무", - "memory": "기억", - "elation": "환락", - "fire": "화염", - "ice": "얼음", - "imaginary": "환상", - "physical": "물리", - "quantum": "양자", - "thunder": "번개", - "wind": "바람", - "hp": "HP", - "atk": "공격력", - "speed": "속도", - "critRate": "치명타 확률", - "critDmg": "치명타 피해", - "breakEffect": "파괴 피해", - "effectRes": "효과 저항", - "energyRegenerationRate": "에너지 회복속도", - "effectHitRate": "효과 적중률", - "outgoingHealingBoost": "치유 강화", - "fireDmgBoost": "화염 피해 증가", - "iceDmgBoost": "얼음 피해 증가", - "imaginaryDmgBoost": "환상 피해 증가", - "physicalDmgBoost": "물리 피해 증가", - "quantumDmgBoost": "양자 피해 증가", - "thunderDmgBoost": "번개 피해 증가", - "windDmgBoost": "바람 피해 증가", - "pursued": "추가 피해", - "true damage": "진실한 피해", - "elationdamage": "환락 피해", - "follow-up": "후속 피해", - "elemental damage": "파괴 및 초파괴 피해", - "dot": "시간 경과 피해", - "qte": "QTE 스킬", - "level": "레벨", - "relics": "유물", - "eidolons": "에이돌론", - "lightcones": "라이트콘", - "loadData": "데이터 불러오기", - "exportData": "데이터 내보내기", - "connectSetting": "연결 설정", - "connected": "연결됨", - "unconnected": "연결 안됨", - "psConnection": "PS 연결", - "connectionType": "연결 타입", - "status": "상태", - "connectPs": "PS 연결", - "disconnect": "연결 끊기", - "other": "기타", - "freeSr": "FreeSR", - "database": "데이터베이스", - "enka": "Enka", - "monsterSetting": "몬스터 설정", - "serverUrl": "서버 URL", - "privateType": "프라이빗 타입", - "local": "로컬", - "server": "서버", - "username": "사용자명", - "password": "비밀번호", - "placeholderServerUrl": "서버 URL 입력", - "placeholderUsername": "사용자명 입력", - "placeholderPassword": "비밀번호 입력", - "connectedSuccess": "PS에 성공적으로 연결됨", - "connectedFailed": "PS 연결 실패", - "syncSuccess": "PS에 데이터 동기화 성공", - "syncFailed": "PS에 데이터 동기화 실패", - "sync": "동기화", - "importSetting": "설정 가져오기", - "profile": "프로필", - "default": "기본", - "copyProfiles": "프로필 복사", - "addNewProfile": "새 프로필 추가", - "createNewProfile": "새 프로필 생성", - "editProfile": "프로필 편집", - "placeholderProfileName": "프로필 이름 입력", - "profileName": "프로필 이름", - "create": "생성", - "update": "업데이트", - "characterInformation": "캐릭터 정보", - "skills": "스킬", - "showcaseCard": "쇼케이스 카드", - "comingSoon": "곧 출시", - "characterName": "캐릭터 이름", - "placeholderCharacter": "캐릭터 이름 입력", - "characterSettings": "캐릭터 설정", - "levelConfiguration": "레벨 구성", - "characterLevel": "캐릭터 레벨", - "max": "최대", - "ultimateEnergy": "궁극 에너지", - "currentEnergy": "현재 에너지", - "setTo50": "50%로 설정", - "battleConfiguration": "전투 구성", - "useTechnique": "비기 사용", - "techniqueNote": "전투 전 비기 효과 활성화", - "enhancement": "강화", - "enhancementLevel": "강화 레벨", - "origin": "출처", - "enhancedNote": "강화 레벨이 높을수록 추가 효과 해제", - "lightconeEquipment": "라이트콘 장비", - "lightconeSettings": "라이트콘 설정", - "placeholderLevel": "레벨 입력", - "superimpositionRank": "중첩 등급", - "ranksNote": "등급이 높을수록 효과가 강함", - "changeLightcone": "라이트콘 변경", - "removeLightcone": "라이트콘 제거", - "equipLightcone": "라이트콘 장착", - "noLightconeEquipped": "라이트콘 미장착", - "equipLightconeNote": "캐릭터 능력 향상을 위해 장착하세요", - "filter": "필터", - "selectedCharacters": "선택된 캐릭터", - "selectedProfiles": "선택된 프로필", - "clearAll": "전체 지우기", - "selectAll": "전체 선택", - "copy": "복사", - "copied": "복사됨", - "noAvatarSelected": "캐릭터가 선택되지 않음", - "noAvatarToCopySelected": "복사할 캐릭터가 선택되지 않음", - "pleaseSelectAtLeastOneProfile": "프로필을 최소 하나 선택하세요", - "pleaseEnterUid": "UID를 입력해주세요", - "failedToFetchEnkaData": "Enka 데이터 가져오기 실패", - "pleaseSelectAtLeastOneCharacter": "캐릭터를 최소 하나 선택하세요", - "noDataToImport": "가져올 데이터가 없습니다", - "pleaseSelectAFile": "파일을 선택하세요", - "fileMustBeAValidJsonFile": "유효한 JSON 파일이어야 합니다", - "importEnkaDataSuccess": "Enka 데이터 가져오기 성공", - "importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공", - "importDatabaseSuccess": "데이터베이스 가져오기 성공", - "getData": "데이터 가져오기", - "import": "가져오기", - "freeSRImport": "FreeSR 가져오기", - "onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원", - "pickAFile": "파일 선택", - "lightConeSetting": "라이트콘 설정", - "relicMaker": "유물 제작", - "pleaseSelectAllOptions": "모든 옵션을 선택해주세요", - "relicSavedSuccessfully": "유물 저장 성공", - "mainSettings": "메인 설정", - "mainStat": "주 속성", - "set": "세트", - "pleaseSelectASet": "세트 하나를 선택해주세요", - "effectBonus": "효과 보너스", - "totalRoll": "총 횟수", - "randomizeStats": "속성 랜덤화", - "randomizeRolls": "횟수 랜덤화", - "selectASubStat": "부 속성 선택", - "selectASet": "세트 선택", - "selectAMainStat": "주 속성 선택", - "save": "저장", - "reset": "초기화", - "roll": "굴리기", - "step": "단계", - "memoryOfChaos": "망각의 정원", - "pureFiction": "허구 이야기", - "apocalypticShadow": "종말의 환영", - "customEnemy": "커스텀 적", - "simulatedUniverse": "시뮬레이티드 유니버스", - "floor": "층수", - "side": "전반/후반", - "wave": "웨이브", - "stage": "스테이지", - "useCycleCount": "사이클 수 사용?", - "useTurbulenceBuff": "난류 버프 사용?", - "firstHalfEnemies": "전반 적", - "secondHalfEnemies": "후반 적", - "firstNodeEnemies": "노드 1 적", - "secondNodeEnemies": "노드 2 적", - "thirdNodeEnemies": "노드 3 적", - "firstNode": "노드 1", - "secondNode": "노드 2", - "thirdNode": "노드 3", - "turbulenceBuff": "난류 버프", - "noEventSelected": "이벤트가 선택되지 않음", - "noTurbulenceBuff": "난류 버프가 없음", - "upper": "상반", - "lower": "하반", - "upperToLower": "상반 -> 하반", - "lowerToUpper": "하반 -> 상반", - "selectMOCEvent": "MOC 이벤트 선택", - "selectPFEvent": "PF 이벤트 선택", - "selectASEvent": "AS 이벤트 선택", - "selectCEEvent": "CE 이벤트 선택", - "selectEvent": "이벤트 선택", - "selectFloor": "층 선택", - "selectSide": "전반/후반 선택", - "selectBuff": "버프 선택", - "selectStage": "스테이지 선택", - "previous": "이전", - "next": "다음", - "noMonstersFound": "적을 찾을 수 없음", - "addNewWave": "새 웨이브 추가", - "searchStage": "스테이지 검색...", - "noStageFound": "스테이지를 찾을 수 없음", - "searchMonster": "적 검색...", - "changeRelic": "유물 변경", - "deleteRelic": "유물 삭제", - "deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?", - "setEffects": "효과 설정", - "details": "상세", - "normal": "기본 공격", - "bpskill": "스킬", - "maze": "전술", - "ultra": "필살기", - "servantskill": "메모스프라이트 스킬", - "severaltalent": "메모스프라이트 재능", - "singleattack": "단일 공격", - "enhance": "강화", - "summon": "소환", - "blast": "광역 공격", - "restore": "회복", - "support": "지원", - "aoeattack": "광역 공격", - "mazeattack": "미궁 비기", - "impair": "약화", - "bounce": "튕김", - "active": "활성", - "inactive": "비활성", - "defence": "방어", - "maxAll": "모두 최대치", - "maxAllSuccess": "스킬 레벨이 최대치로 설정되었습니다.", - "maxAllFailed": "스킬 레벨을 최대치로 설정하지 못했습니다.", - "noRelicEquipped": "성유물이 장착되지 않음", - "anomalyArbitration": "이상 중재", - "normalMode": "일반 모드", - "hardMode": "어려움 모드", - "selectPEAKEvent": "PEAK 이벤트 선택", - "mode": "모드", - "selectMode": "모드를 선택", - "rollBack": "이전 상태로 되돌리기", - "upRoll": "부옵션 추가", - "downRoll": "부옵션 감소", - "actions": "동작", - "avatars": "아바타", - "quickView": "빠른 조회", - "extraSetting": "추가 설정", - "disableCensorship": "검열 비활성화", - "hideUI": "UI 숨기기", - "theoryCraftMode": "Theory Craft 모드", - "cycleCount": "사이클 수", - "pleaseSelectAllSubStats": "모든 부옵션을 선택하세요", - "subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다", - "theoryCraft": "Theory Craft", - "multipathCharacter": "다중 운명 캐릭터", - "mainPath": "개척자 운명", - "march7Path": "삼월칠일 운명", - "challenge": "도전", - "skipNode": "노드 건너뛰기", - "disableSkip": "건너뛰기 비활성화", - "skipNode1": "노드 1 건너뛰기", - "skipNode2": "노드 2 건너뛰기", - "extraFeatures": "추가 기능", - "detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.", - "detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.", - "detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.", - "detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.", - "detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.", - "detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.", - "trailblazer": "개척자", - "listExtraEffect": "추가 효과 목록", - "extra": "추가", - "customLineup": "커스텀 편성" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "스킬 유형", + "skillName": "스킬 이름", + "character": "캐릭터", + "id": "ID", + "path": "운명", + "rarity": "희귀도", + "element": "속성", + "technique": "비기", + "talent": "탈렌트", + "basic": "기본 공격", + "skill": "스킬", + "ultimate": "궁극기", + "servant": "서번트", + "damage": "데미지", + "type": "종류", + "warrior": "파괴", + "knight": "보호", + "mage": "박학", + "priest": "풍요", + "rogue": "사냥", + "shaman": "조화", + "warlock": "허무", + "memory": "기억", + "elation": "환락", + "fire": "화염", + "ice": "얼음", + "imaginary": "환상", + "physical": "물리", + "quantum": "양자", + "thunder": "번개", + "wind": "바람", + "hp": "HP", + "atk": "공격력", + "speed": "속도", + "critRate": "치명타 확률", + "critDmg": "치명타 피해", + "breakEffect": "파괴 피해", + "effectRes": "효과 저항", + "energyRegenerationRate": "에너지 회복속도", + "effectHitRate": "효과 적중률", + "outgoingHealingBoost": "치유 강화", + "fireDmgBoost": "화염 피해 증가", + "iceDmgBoost": "얼음 피해 증가", + "imaginaryDmgBoost": "환상 피해 증가", + "physicalDmgBoost": "물리 피해 증가", + "quantumDmgBoost": "양자 피해 증가", + "thunderDmgBoost": "번개 피해 증가", + "windDmgBoost": "바람 피해 증가", + "pursued": "추가 피해", + "true damage": "진실한 피해", + "elationdamage": "환락 피해", + "follow-up": "후속 피해", + "elemental damage": "파괴 및 초파괴 피해", + "dot": "시간 경과 피해", + "qte": "QTE 스킬", + "level": "레벨", + "relics": "유물", + "eidolons": "에이돌론", + "lightcones": "라이트콘", + "loadData": "데이터 불러오기", + "exportData": "데이터 내보내기", + "connectSetting": "연결 설정", + "connected": "연결됨", + "unconnected": "연결 안됨", + "psConnection": "PS 연결", + "connectionType": "연결 타입", + "status": "상태", + "connectPs": "PS 연결", + "disconnect": "연결 끊기", + "other": "기타", + "freeSr": "FreeSR", + "database": "데이터베이스", + "enka": "Enka", + "monsterSetting": "몬스터 설정", + "serverUrl": "서버 URL", + "privateType": "프라이빗 타입", + "local": "로컬", + "server": "서버", + "username": "사용자명", + "password": "비밀번호", + "placeholderServerUrl": "서버 URL 입력", + "placeholderUsername": "사용자명 입력", + "placeholderPassword": "비밀번호 입력", + "connectedSuccess": "PS에 성공적으로 연결됨", + "connectedFailed": "PS 연결 실패", + "syncSuccess": "PS에 데이터 동기화 성공", + "syncFailed": "PS에 데이터 동기화 실패", + "sync": "동기화", + "importSetting": "설정 가져오기", + "profile": "프로필", + "default": "기본", + "copyProfiles": "프로필 복사", + "addNewProfile": "새 프로필 추가", + "createNewProfile": "새 프로필 생성", + "editProfile": "프로필 편집", + "placeholderProfileName": "프로필 이름 입력", + "profileName": "프로필 이름", + "create": "생성", + "update": "업데이트", + "characterInformation": "캐릭터 정보", + "skills": "스킬", + "showcaseCard": "쇼케이스 카드", + "comingSoon": "곧 출시", + "characterName": "캐릭터 이름", + "placeholderCharacter": "캐릭터 이름 입력", + "characterSettings": "캐릭터 설정", + "levelConfiguration": "레벨 구성", + "characterLevel": "캐릭터 레벨", + "max": "최대", + "ultimateEnergy": "궁극 에너지", + "currentEnergy": "현재 에너지", + "setTo50": "50%로 설정", + "battleConfiguration": "전투 구성", + "useTechnique": "비기 사용", + "techniqueNote": "전투 전 비기 효과 활성화", + "enhancement": "강화", + "enhancementLevel": "강화 레벨", + "origin": "출처", + "enhancedNote": "강화 레벨이 높을수록 추가 효과 해제", + "lightconeEquipment": "라이트콘 장비", + "lightconeSettings": "라이트콘 설정", + "placeholderLevel": "레벨 입력", + "superimpositionRank": "중첩 등급", + "ranksNote": "등급이 높을수록 효과가 강함", + "changeLightcone": "라이트콘 변경", + "removeLightcone": "라이트콘 제거", + "equipLightcone": "라이트콘 장착", + "noLightconeEquipped": "라이트콘 미장착", + "equipLightconeNote": "캐릭터 능력 향상을 위해 장착하세요", + "filter": "필터", + "selectedCharacters": "선택된 캐릭터", + "selectedProfiles": "선택된 프로필", + "clearAll": "전체 지우기", + "selectAll": "전체 선택", + "copy": "복사", + "copied": "복사됨", + "noAvatarSelected": "캐릭터가 선택되지 않음", + "noAvatarToCopySelected": "복사할 캐릭터가 선택되지 않음", + "pleaseSelectAtLeastOneProfile": "프로필을 최소 하나 선택하세요", + "pleaseEnterUid": "UID를 입력해주세요", + "failedToFetchEnkaData": "Enka 데이터 가져오기 실패", + "pleaseSelectAtLeastOneCharacter": "캐릭터를 최소 하나 선택하세요", + "noDataToImport": "가져올 데이터가 없습니다", + "pleaseSelectAFile": "파일을 선택하세요", + "fileMustBeAValidJsonFile": "유효한 JSON 파일이어야 합니다", + "importEnkaDataSuccess": "Enka 데이터 가져오기 성공", + "importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공", + "importDatabaseSuccess": "데이터베이스 가져오기 성공", + "getData": "데이터 가져오기", + "import": "가져오기", + "freeSRImport": "FreeSR 가져오기", + "onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원", + "pickAFile": "파일 선택", + "lightConeSetting": "라이트콘 설정", + "relicMaker": "유물 제작", + "pleaseSelectAllOptions": "모든 옵션을 선택해주세요", + "relicSavedSuccessfully": "유물 저장 성공", + "mainSettings": "메인 설정", + "mainStat": "주 속성", + "set": "세트", + "pleaseSelectASet": "세트 하나를 선택해주세요", + "effectBonus": "효과 보너스", + "totalRoll": "총 횟수", + "randomizeStats": "속성 랜덤화", + "randomizeRolls": "횟수 랜덤화", + "selectASubStat": "부 속성 선택", + "selectASet": "세트 선택", + "selectAMainStat": "주 속성 선택", + "save": "저장", + "reset": "초기화", + "roll": "굴리기", + "step": "단계", + "memoryOfChaos": "망각의 정원", + "pureFiction": "허구 이야기", + "apocalypticShadow": "종말의 환영", + "customEnemy": "커스텀 적", + "simulatedUniverse": "시뮬레이티드 유니버스", + "floor": "층수", + "side": "전반/후반", + "wave": "웨이브", + "stage": "스테이지", + "useCycleCount": "사이클 수 사용?", + "useTurbulenceBuff": "난류 버프 사용?", + "firstHalfEnemies": "전반 적", + "secondHalfEnemies": "후반 적", + "firstNodeEnemies": "노드 1 적", + "secondNodeEnemies": "노드 2 적", + "thirdNodeEnemies": "노드 3 적", + "firstNode": "노드 1", + "secondNode": "노드 2", + "thirdNode": "노드 3", + "turbulenceBuff": "난류 버프", + "noEventSelected": "이벤트가 선택되지 않음", + "noTurbulenceBuff": "난류 버프가 없음", + "upper": "상반", + "lower": "하반", + "upperToLower": "상반 -> 하반", + "lowerToUpper": "하반 -> 상반", + "selectMOCEvent": "MOC 이벤트 선택", + "selectPFEvent": "PF 이벤트 선택", + "selectASEvent": "AS 이벤트 선택", + "selectCEEvent": "CE 이벤트 선택", + "selectEvent": "이벤트 선택", + "selectFloor": "층 선택", + "selectSide": "전반/후반 선택", + "selectBuff": "버프 선택", + "selectStage": "스테이지 선택", + "previous": "이전", + "next": "다음", + "noMonstersFound": "적을 찾을 수 없음", + "addNewWave": "새 웨이브 추가", + "searchStage": "스테이지 검색...", + "noStageFound": "스테이지를 찾을 수 없음", + "searchMonster": "적 검색...", + "changeRelic": "유물 변경", + "deleteRelic": "유물 삭제", + "deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?", + "setEffects": "효과 설정", + "details": "상세", + "normal": "기본 공격", + "bpskill": "스킬", + "maze": "전술", + "ultra": "필살기", + "servantskill": "메모스프라이트 스킬", + "severaltalent": "메모스프라이트 재능", + "singleattack": "단일 공격", + "enhance": "강화", + "summon": "소환", + "blast": "광역 공격", + "restore": "회복", + "support": "지원", + "aoeattack": "광역 공격", + "mazeattack": "미궁 비기", + "impair": "약화", + "bounce": "튕김", + "active": "활성", + "inactive": "비활성", + "defence": "방어", + "maxAll": "모두 최대치", + "maxAllSuccess": "스킬 레벨이 최대치로 설정되었습니다.", + "maxAllFailed": "스킬 레벨을 최대치로 설정하지 못했습니다.", + "noRelicEquipped": "성유물이 장착되지 않음", + "anomalyArbitration": "이상 중재", + "normalMode": "일반 모드", + "hardMode": "어려움 모드", + "selectPEAKEvent": "PEAK 이벤트 선택", + "mode": "모드", + "selectMode": "모드를 선택", + "rollBack": "이전 상태로 되돌리기", + "upRoll": "부옵션 추가", + "downRoll": "부옵션 감소", + "actions": "동작", + "avatars": "아바타", + "quickView": "빠른 조회", + "extraSetting": "추가 설정", + "disableCensorship": "검열 비활성화", + "hideUI": "UI 숨기기", + "theoryCraftMode": "Theory Craft 모드", + "cycleCount": "사이클 수", + "pleaseSelectAllSubStats": "모든 부옵션을 선택하세요", + "subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다", + "theoryCraft": "Theory Craft", + "multipathCharacter": "다중 운명 캐릭터", + "mainPath": "개척자 운명", + "march7Path": "삼월칠일 운명", + "challenge": "도전", + "skipNode": "노드 건너뛰기", + "disableSkip": "건너뛰기 비활성화", + "skipNode1": "노드 1 건너뛰기", + "skipNode2": "노드 2 건너뛰기", + "extraFeatures": "추가 기능", + "detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.", + "detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.", + "detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.", + "detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.", + "detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.", + "detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.", + "trailblazer": "개척자", + "listExtraEffect": "추가 효과 목록", + "extra": "추가", + "customLineup": "커스텀 편성" + } } \ No newline at end of file diff --git a/messages/pt.json b/messages/pt.json index d925603..c42d655 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Ferramentas Firefly por Firefly Shelter" - }, - "DataPage": { - "skillType": "Tipo de Habilidade", - "skillName": "Nome da Habilidade", - "character": "Personagem", - "id": "Id", - "path": "Caminho", - "rarity": "Raridade", - "element": "Elemento", - "technique": "Técnica", - "talent": "Talento", - "basic": "Ataque Básico", - "skill": "Perícia", - "ultimate": "Perícia Suprema", - "servant": "Servo", - "damage": "Dano", - "type": "Tipo", - "warrior": "A Destruição", - "knight": "A Preservação", - "mage": "A Erudição", - "priest": "A Abundância", - "rogue": "A Caça", - "shaman": "A Harmonia", - "warlock": "A Inexistência", - "memory": "A Lembrança", - "elation": "A Alegria", - "fire": "Fogo", - "ice": "Gelo", - "imaginary": "Imaginário", - "physical": "Físico", - "quantum": "Quântico", - "thunder": "Raio", - "wind": "Vento", - "hp": "PV", - "atk": "ATQ", - "speed": "VEL", - "critRate": "Taxa CRIT", - "critDmg": "Dano CRIT", - "breakEffect": "Efeito de Quebra", - "effectRes": "RES a Efeito", - "energyRegenerationRate": "Taxa de Regeneração de Energia", - "effectHitRate": "Taxa de Acerto de Efeito", - "outgoingHealingBoost": "Bônus de Cura", - "fireDmgBoost": "Bônus de Dano de Fogo", - "iceDmgBoost": "Bônus de Dano de Gelo", - "imaginaryDmgBoost": "Bônus de Dano Imaginário", - "physicalDmgBoost": "Bônus de Dano Físico", - "quantumDmgBoost": "Bônus de Dano Quântico", - "thunderDmgBoost": "Bônus de Dano de Raio", - "windDmgBoost": "Bônus de Dano de Vento", - "pursued": "Dano Adicional", - "true damage": "Dano Verdadeiro", - "elationdamage": "Dano de Alegria", - "follow-up": "Dano de Ataque Extra", - "elemental damage": "Dano de Quebra e Superquebra", - "dot": "Dano Contínuo", - "qte": "Habilidade QTE", - "level": "Nível", - "relics": "Relíquias", - "eidolons": "Eidolons", - "lightcones": "Cones de Luz", - "loadData": "Carregar dados", - "exportData": "Exportar dados", - "connectSetting": "Configuração de Conexão", - "connected": "Conectado", - "unconnected": "Desconectado", - "psConnection": "Conexão PS", - "connectionType": "Tipo de Conexão", - "status": "Status", - "connectPs": "Conectar PS", - "disconnect": "Desconectar", - "other": "Outro", - "freeSr": "FreeSR", - "database": "Banco de dados", - "enka": "Enka", - "monsterSetting": "Configuração de Monstros", - "serverUrl": "URL do Servidor", - "privateType": "Tipo Privado", - "local": "Local", - "server": "Servidor", - "username": "Usuário", - "password": "Senha", - "placeholderServerUrl": "Digite o URL do servidor", - "placeholderUsername": "Digite o usuário", - "placeholderPassword": "Digite a senha", - "connectedSuccess": "Conectado ao PS com sucesso", - "connectedFailed": "Falha ao conectar ao PS", - "syncSuccess": "Dados sincronizados com o PS com sucesso", - "syncFailed": "Falha ao sincronizar dados com o PS", - "sync": "Sincronizar", - "importSetting": "Configuração de Importação", - "profile": "Perfil", - "default": "Padrão", - "copyProfiles": "Copiar perfis", - "addNewProfile": "Adicionar novo perfil", - "createNewProfile": "Criar novo perfil", - "editProfile": "Editar perfil", - "placeholderProfileName": "Digite o nome do perfil", - "profileName": "Nome do perfil", - "create": "Criar", - "update": "Atualizar", - "characterInformation": "Informação do Personagem", - "skills": "Habilidades", - "showcaseCard": "Cartão de Exibição", - "comingSoon": "Em breve", - "characterName": "Nome do personagem", - "placeholderCharacter": "Digite o nome do personagem", - "characterSettings": "Configurações de Personagem", - "levelConfiguration": "Configuração de Nível", - "characterLevel": "Nível do Personagem", - "max": "MÁX", - "ultimateEnergy": "Energia da Perícia Suprema", - "currentEnergy": "Energia Atual", - "setTo50": "Definir para 50%", - "battleConfiguration": "Configuração de Batalha", - "useTechnique": "Usar Técnica", - "techniqueNote": "Ativar efeitos de técnica pré-batalha", - "enhancement": "Aprimoramento", - "enhancementLevel": "Nível de Aprimoramento", - "origin": "Origem", - "enhancedNote": "Níveis altos desbloqueiam habilidades", - "lightconeEquipment": "Equipamento de Cone de Luz", - "lightconeSettings": "Configurações de Cone de Luz", - "placeholderLevel": "Digite o nível", - "superimpositionRank": "Rank de Sobreposição", - "ranksNote": "Ranks maiores dão efeitos mais fortes", - "changeLightcone": "Mudar Cone de Luz", - "removeLightcone": "Remover Cone de Luz", - "equipLightcone": "Equipar Cone de Luz", - "noLightconeEquipped": "Sem Cone de Luz", - "equipLightconeNote": "Equipe um cone de luz para melhorar o personagem", - "filter": "Filtro", - "selectedCharacters": "Personagens Selecionados", - "selectedProfiles": "Perfis Selecionados", - "clearAll": "Limpar Tudo", - "selectAll": "Selecionar Tudo", - "copy": "Copiar", - "copied": "Copiado", - "noAvatarSelected": "Nenhum personagem selecionado", - "noAvatarToCopySelected": "Nenhum personagem para copiar", - "pleaseSelectAtLeastOneProfile": "Por favor, selecione ao menos um perfil", - "pleaseEnterUid": "Por favor, insira o UID", - "failedToFetchEnkaData": "Falha ao obter dados Enka", - "pleaseSelectAtLeastOneCharacter": "Selecione ao menos um personagem", - "noDataToImport": "Sem dados para importar", - "pleaseSelectAFile": "Por favor, selecione um arquivo", - "fileMustBeAValidJsonFile": "O arquivo deve ser um JSON válido", - "importEnkaDataSuccess": "Dados Enka importados com sucesso", - "importFreeSRDataSuccess": "Dados FreeSR importados com sucesso", - "importDatabaseSuccess": "Banco de dados importado com sucesso", - "getData": "Obter Dados", - "import": "Importar", - "freeSRImport": "Importação FreeSR", - "onlySupportFreeSRJsonFile": "Suporta apenas arquivos JSON FreeSR", - "pickAFile": "Escolher um arquivo", - "lightConeSetting": "Configuração de Cone de Luz", - "relicMaker": "Criador de Relíquias", - "pleaseSelectAllOptions": "Por favor selecione todas as opções", - "relicSavedSuccessfully": "Relíquia salva com sucesso", - "mainSettings": "Configurações Principais", - "mainStat": "Atributo Principal", - "set": "Conjunto", - "pleaseSelectASet": "Selecione um conjunto", - "effectBonus": "Bônus de Efeito", - "totalRoll": "Total de Melhorias", - "randomizeStats": "Atributos Aleatórios", - "randomizeRolls": "Melhorias Aleatórias", - "selectASubStat": "Selecionar sub-atributo", - "selectASet": "Selecionar conjunto", - "selectAMainStat": "Selecionar atributo principal", - "save": "Salvar", - "reset": "Redefinir", - "roll": "Melhoria", - "step": "Passo", - "memoryOfChaos": "Memória do Caos", - "pureFiction": "Pura Ficção", - "apocalypticShadow": "Sombra Apocalíptica", - "customEnemy": "Inimigo Personalizado", - "simulatedUniverse": "Universo Simulado", - "floor": "Andar", - "side": "Lado", - "wave": "Onda", - "stage": "Fase", - "useCycleCount": "Usar contagem de ciclos?", - "useTurbulenceBuff": "Usar buff de turbulência?", - "firstHalfEnemies": "Inimigos da primeira metade", - "secondHalfEnemies": "Inimigos da segunda metade", - "firstNodeEnemies": "Inimigos do nodo 1", - "secondNodeEnemies": "Inimigos do nodo 2", - "thirdNodeEnemies": "Inimigos do nodo 3", - "firstNode": "Nodo 1", - "secondNode": "Nodo 2", - "thirdNode": "Nodo 3", - "listEnemies": "Lista de inimigos", - "turbulenceBuff": "Buff de Turbulência", - "noEventSelected": "Nenhum evento selecionado", - "noTurbulenceBuff": "Sem Buff de Turbulência", - "upper": "Superior", - "lower": "Inferior", - "upperToLower": "Superior -> Inferior", - "lowerToUpper": "Inferior -> Superior", - "selectMOCEvent": "Selecionar Evento MOC", - "selectPFEvent": "Selecionar Evento PF", - "selectASEvent": "Selecionar Evento AS", - "selectCEEvent": "Selecionar Evento CE", - "selectEvent": "Selecionar Evento", - "selectFloor": "Selecionar Andar", - "selectSide": "Selecionar Lado", - "selectBuff": "Selecionar Buff", - "selectStage": "Selecionar Fase", - "previous": "Anterior", - "next": "Próximo", - "noMonstersFound": "Nenhum monstro encontrado", - "addNewWave": "Adicionar Nova Onda", - "searchStage": "Buscar fase...", - "noStageFound": "Fase não encontrada", - "searchMonster": "Buscar monstro...", - "changeRelic": "Mudar relíquia", - "deleteRelic": "Deletar relíquia", - "deleteRelicConfirm": "Tem certeza que deseja deletar a relíquia neste slot?", - "setEffects": "Definir Efeitos", - "details": "Detalhes", - "normal": "Ataque Básico", - "bpskill": "Perícia", - "maze": "Técnica", - "ultra": "Perícia Suprema", - "servantskill": "Habilidade do Memoespirito", - "severaltalent": "Talento do Memoespirito", - "singleattack": "Ataque Único", - "enhance": "Aprimorar", - "summon": "Invocar", - "mazeattack": "Ataque de Técnica", - "blast": "Explosão", - "restore": "Restaurar", - "support": "Suporte", - "aoeattack": "Ataque em Área", - "impair": "Enfraquecer", - "bounce": "Rebote", - "active": "Ativo", - "defence": "Defesa", - "inactive": "Inativo", - "maxAll": "Maximizar Tudo", - "maxAllSuccess": "Habilidades maximizadas com sucesso.", - "maxAllFailed": "Falha ao maximizar habilidades.", - "noRelicEquipped": "Sem relíquia equipada", - "anomalyArbitration": "Arbitragem de Anomalia", - "normalMode": "Modo Normal", - "hardMode": "Modo Difícil", - "selectPEAKEvent": "Selecionar Evento PEAK", - "mode": "Modo", - "selectMode": "Selecionar um modo", - "rollBack": "Reverter", - "upRoll": "Aumentar Melhoria", - "downRoll": "Diminuir Melhoria", - "actions": "Ações", - "avatars": "Avatares", - "quickView": "Visualização Rápida", - "extraSetting": "Configurações Extras", - "disableCensorship": "Desativar Censura", - "hideUI": "Ocultar UI", - "theoryCraftMode": "Modo Theorycraft", - "cycleCount": "Contagem de Ciclos", - "pleaseSelectAllSubStats": "Selecione todos os sub-atributos", - "subStatRollCountCannotBeZero": "As melhorias do sub-atributo não podem ser zero", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Personagem Multicaminho", - "mainPath": "Caminho Principal", - "march7Path": "Caminho de 7 de Março", - "challenge": "Desafio", - "skipNode": "Pular Nó", - "disableSkip": "Desativar pulo", - "skipNode1": "Pular nó 1", - "skipNode2": "Pular nó 2", - "extraFeatures": "Recursos Extras", - "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.", - "detailChallengePeak": "Permite mudar a temporada de Peak na Anomalia atual.", - "detailHiddenUi": "Irá esconder a interface do jogo.", - "detailDisableCensorship": "Desativa a censura no jogo.", - "detailMultipathCharacter": "Permite mudar o Caminho de certos personagens.", - "trailblazer": "Desbravador", - "listExtraEffect": "Lista de Efeitos Extras", - "extra": "Extra", - "customLineup": "Formação Personalizada" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Ferramentas Firefly por Firefly Shelter" + }, + "DataPage": { + "skillType": "Tipo de Habilidade", + "skillName": "Nome da Habilidade", + "character": "Personagem", + "id": "Id", + "path": "Caminho", + "rarity": "Raridade", + "element": "Elemento", + "technique": "Técnica", + "talent": "Talento", + "basic": "Ataque Básico", + "skill": "Perícia", + "ultimate": "Perícia Suprema", + "servant": "Servo", + "damage": "Dano", + "type": "Tipo", + "warrior": "A Destruição", + "knight": "A Preservação", + "mage": "A Erudição", + "priest": "A Abundância", + "rogue": "A Caça", + "shaman": "A Harmonia", + "warlock": "A Inexistência", + "memory": "A Lembrança", + "elation": "A Alegria", + "fire": "Fogo", + "ice": "Gelo", + "imaginary": "Imaginário", + "physical": "Físico", + "quantum": "Quântico", + "thunder": "Raio", + "wind": "Vento", + "hp": "PV", + "atk": "ATQ", + "speed": "VEL", + "critRate": "Taxa CRIT", + "critDmg": "Dano CRIT", + "breakEffect": "Efeito de Quebra", + "effectRes": "RES a Efeito", + "energyRegenerationRate": "Taxa de Regeneração de Energia", + "effectHitRate": "Taxa de Acerto de Efeito", + "outgoingHealingBoost": "Bônus de Cura", + "fireDmgBoost": "Bônus de Dano de Fogo", + "iceDmgBoost": "Bônus de Dano de Gelo", + "imaginaryDmgBoost": "Bônus de Dano Imaginário", + "physicalDmgBoost": "Bônus de Dano Físico", + "quantumDmgBoost": "Bônus de Dano Quântico", + "thunderDmgBoost": "Bônus de Dano de Raio", + "windDmgBoost": "Bônus de Dano de Vento", + "pursued": "Dano Adicional", + "true damage": "Dano Verdadeiro", + "elationdamage": "Dano de Alegria", + "follow-up": "Dano de Ataque Extra", + "elemental damage": "Dano de Quebra e Superquebra", + "dot": "Dano Contínuo", + "qte": "Habilidade QTE", + "level": "Nível", + "relics": "Relíquias", + "eidolons": "Eidolons", + "lightcones": "Cones de Luz", + "loadData": "Carregar dados", + "exportData": "Exportar dados", + "connectSetting": "Configuração de Conexão", + "connected": "Conectado", + "unconnected": "Desconectado", + "psConnection": "Conexão PS", + "connectionType": "Tipo de Conexão", + "status": "Status", + "connectPs": "Conectar PS", + "disconnect": "Desconectar", + "other": "Outro", + "freeSr": "FreeSR", + "database": "Banco de dados", + "enka": "Enka", + "monsterSetting": "Configuração de Monstros", + "serverUrl": "URL do Servidor", + "privateType": "Tipo Privado", + "local": "Local", + "server": "Servidor", + "username": "Usuário", + "password": "Senha", + "placeholderServerUrl": "Digite o URL do servidor", + "placeholderUsername": "Digite o usuário", + "placeholderPassword": "Digite a senha", + "connectedSuccess": "Conectado ao PS com sucesso", + "connectedFailed": "Falha ao conectar ao PS", + "syncSuccess": "Dados sincronizados com o PS com sucesso", + "syncFailed": "Falha ao sincronizar dados com o PS", + "sync": "Sincronizar", + "importSetting": "Configuração de Importação", + "profile": "Perfil", + "default": "Padrão", + "copyProfiles": "Copiar perfis", + "addNewProfile": "Adicionar novo perfil", + "createNewProfile": "Criar novo perfil", + "editProfile": "Editar perfil", + "placeholderProfileName": "Digite o nome do perfil", + "profileName": "Nome do perfil", + "create": "Criar", + "update": "Atualizar", + "characterInformation": "Informação do Personagem", + "skills": "Habilidades", + "showcaseCard": "Cartão de Exibição", + "comingSoon": "Em breve", + "characterName": "Nome do personagem", + "placeholderCharacter": "Digite o nome do personagem", + "characterSettings": "Configurações de Personagem", + "levelConfiguration": "Configuração de Nível", + "characterLevel": "Nível do Personagem", + "max": "MÁX", + "ultimateEnergy": "Energia da Perícia Suprema", + "currentEnergy": "Energia Atual", + "setTo50": "Definir para 50%", + "battleConfiguration": "Configuração de Batalha", + "useTechnique": "Usar Técnica", + "techniqueNote": "Ativar efeitos de técnica pré-batalha", + "enhancement": "Aprimoramento", + "enhancementLevel": "Nível de Aprimoramento", + "origin": "Origem", + "enhancedNote": "Níveis altos desbloqueiam habilidades", + "lightconeEquipment": "Equipamento de Cone de Luz", + "lightconeSettings": "Configurações de Cone de Luz", + "placeholderLevel": "Digite o nível", + "superimpositionRank": "Rank de Sobreposição", + "ranksNote": "Ranks maiores dão efeitos mais fortes", + "changeLightcone": "Mudar Cone de Luz", + "removeLightcone": "Remover Cone de Luz", + "equipLightcone": "Equipar Cone de Luz", + "noLightconeEquipped": "Sem Cone de Luz", + "equipLightconeNote": "Equipe um cone de luz para melhorar o personagem", + "filter": "Filtro", + "selectedCharacters": "Personagens Selecionados", + "selectedProfiles": "Perfis Selecionados", + "clearAll": "Limpar Tudo", + "selectAll": "Selecionar Tudo", + "copy": "Copiar", + "copied": "Copiado", + "noAvatarSelected": "Nenhum personagem selecionado", + "noAvatarToCopySelected": "Nenhum personagem para copiar", + "pleaseSelectAtLeastOneProfile": "Por favor, selecione ao menos um perfil", + "pleaseEnterUid": "Por favor, insira o UID", + "failedToFetchEnkaData": "Falha ao obter dados Enka", + "pleaseSelectAtLeastOneCharacter": "Selecione ao menos um personagem", + "noDataToImport": "Sem dados para importar", + "pleaseSelectAFile": "Por favor, selecione um arquivo", + "fileMustBeAValidJsonFile": "O arquivo deve ser um JSON válido", + "importEnkaDataSuccess": "Dados Enka importados com sucesso", + "importFreeSRDataSuccess": "Dados FreeSR importados com sucesso", + "importDatabaseSuccess": "Banco de dados importado com sucesso", + "getData": "Obter Dados", + "import": "Importar", + "freeSRImport": "Importação FreeSR", + "onlySupportFreeSRJsonFile": "Suporta apenas arquivos JSON FreeSR", + "pickAFile": "Escolher um arquivo", + "lightConeSetting": "Configuração de Cone de Luz", + "relicMaker": "Criador de Relíquias", + "pleaseSelectAllOptions": "Por favor selecione todas as opções", + "relicSavedSuccessfully": "Relíquia salva com sucesso", + "mainSettings": "Configurações Principais", + "mainStat": "Atributo Principal", + "set": "Conjunto", + "pleaseSelectASet": "Selecione um conjunto", + "effectBonus": "Bônus de Efeito", + "totalRoll": "Total de Melhorias", + "randomizeStats": "Atributos Aleatórios", + "randomizeRolls": "Melhorias Aleatórias", + "selectASubStat": "Selecionar sub-atributo", + "selectASet": "Selecionar conjunto", + "selectAMainStat": "Selecionar atributo principal", + "save": "Salvar", + "reset": "Redefinir", + "roll": "Melhoria", + "step": "Passo", + "memoryOfChaos": "Memória do Caos", + "pureFiction": "Pura Ficção", + "apocalypticShadow": "Sombra Apocalíptica", + "customEnemy": "Inimigo Personalizado", + "simulatedUniverse": "Universo Simulado", + "floor": "Andar", + "side": "Lado", + "wave": "Onda", + "stage": "Fase", + "useCycleCount": "Usar contagem de ciclos?", + "useTurbulenceBuff": "Usar buff de turbulência?", + "firstHalfEnemies": "Inimigos da primeira metade", + "secondHalfEnemies": "Inimigos da segunda metade", + "firstNodeEnemies": "Inimigos do nodo 1", + "secondNodeEnemies": "Inimigos do nodo 2", + "thirdNodeEnemies": "Inimigos do nodo 3", + "firstNode": "Nodo 1", + "secondNode": "Nodo 2", + "thirdNode": "Nodo 3", + "listEnemies": "Lista de inimigos", + "turbulenceBuff": "Buff de Turbulência", + "noEventSelected": "Nenhum evento selecionado", + "noTurbulenceBuff": "Sem Buff de Turbulência", + "upper": "Superior", + "lower": "Inferior", + "upperToLower": "Superior -> Inferior", + "lowerToUpper": "Inferior -> Superior", + "selectMOCEvent": "Selecionar Evento MOC", + "selectPFEvent": "Selecionar Evento PF", + "selectASEvent": "Selecionar Evento AS", + "selectCEEvent": "Selecionar Evento CE", + "selectEvent": "Selecionar Evento", + "selectFloor": "Selecionar Andar", + "selectSide": "Selecionar Lado", + "selectBuff": "Selecionar Buff", + "selectStage": "Selecionar Fase", + "previous": "Anterior", + "next": "Próximo", + "noMonstersFound": "Nenhum monstro encontrado", + "addNewWave": "Adicionar Nova Onda", + "searchStage": "Buscar fase...", + "noStageFound": "Fase não encontrada", + "searchMonster": "Buscar monstro...", + "changeRelic": "Mudar relíquia", + "deleteRelic": "Deletar relíquia", + "deleteRelicConfirm": "Tem certeza que deseja deletar a relíquia neste slot?", + "setEffects": "Definir Efeitos", + "details": "Detalhes", + "normal": "Ataque Básico", + "bpskill": "Perícia", + "maze": "Técnica", + "ultra": "Perícia Suprema", + "servantskill": "Habilidade do Memoespirito", + "severaltalent": "Talento do Memoespirito", + "singleattack": "Ataque Único", + "enhance": "Aprimorar", + "summon": "Invocar", + "mazeattack": "Ataque de Técnica", + "blast": "Explosão", + "restore": "Restaurar", + "support": "Suporte", + "aoeattack": "Ataque em Área", + "impair": "Enfraquecer", + "bounce": "Rebote", + "active": "Ativo", + "defence": "Defesa", + "inactive": "Inativo", + "maxAll": "Maximizar Tudo", + "maxAllSuccess": "Habilidades maximizadas com sucesso.", + "maxAllFailed": "Falha ao maximizar habilidades.", + "noRelicEquipped": "Sem relíquia equipada", + "anomalyArbitration": "Arbitragem de Anomalia", + "normalMode": "Modo Normal", + "hardMode": "Modo Difícil", + "selectPEAKEvent": "Selecionar Evento PEAK", + "mode": "Modo", + "selectMode": "Selecionar um modo", + "rollBack": "Reverter", + "upRoll": "Aumentar Melhoria", + "downRoll": "Diminuir Melhoria", + "actions": "Ações", + "avatars": "Avatares", + "quickView": "Visualização Rápida", + "extraSetting": "Configurações Extras", + "disableCensorship": "Desativar Censura", + "hideUI": "Ocultar UI", + "theoryCraftMode": "Modo Theorycraft", + "cycleCount": "Contagem de Ciclos", + "pleaseSelectAllSubStats": "Selecione todos os sub-atributos", + "subStatRollCountCannotBeZero": "As melhorias do sub-atributo não podem ser zero", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Personagem Multicaminho", + "mainPath": "Caminho Principal", + "march7Path": "Caminho de 7 de Março", + "challenge": "Desafio", + "skipNode": "Pular Nó", + "disableSkip": "Desativar pulo", + "skipNode1": "Pular nó 1", + "skipNode2": "Pular nó 2", + "extraFeatures": "Recursos Extras", + "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.", + "detailChallengePeak": "Permite mudar a temporada de Peak na Anomalia atual.", + "detailHiddenUi": "Irá esconder a interface do jogo.", + "detailDisableCensorship": "Desativa a censura no jogo.", + "detailMultipathCharacter": "Permite mudar o Caminho de certos personagens.", + "trailblazer": "Desbravador", + "listExtraEffect": "Lista de Efeitos Extras", + "extra": "Extra", + "customLineup": "Formação Personalizada" + } } \ No newline at end of file diff --git a/messages/ru.json b/messages/ru.json index 5dd699b..c768566 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Инструменты Firefly от Firefly Shelter" - }, - "DataPage": { - "skillType": "Тип навыка", - "skillName": "Название навыка", - "character": "Персонаж", - "id": "ID", - "path": "Путь", - "rarity": "Редкость", - "element": "Элемент", - "technique": "Техника", - "talent": "Талант", - "basic": "Базовая атака", - "skill": "Навык", - "ultimate": "Сверхспособность", - "servant": "Слуга", - "damage": "Урон", - "type": "Тип", - "warrior": "Разрушение", - "knight": "Сохранение", - "mage": "Эрудиция", - "priest": "Изобилие", - "rogue": "Охота", - "shaman": "Гармония", - "warlock": "Небытие", - "memory": "Память", - "elation": "Радость", - "fire": "Огонь", - "ice": "Лед", - "imaginary": "Мнимый", - "physical": "Физический", - "quantum": "Квантовый", - "thunder": "Электрический", - "wind": "Ветряной", - "hp": "HP", - "atk": "Сила атаки", - "speed": "Скорость", - "critRate": "Крит. шанс", - "critDmg": "Крит. урон", - "breakEffect": "Эффект пробития", - "effectRes": "Сопротивление эффектам", - "energyRegenerationRate": "Скорость восст. энергии", - "effectHitRate": "Шанс попадания эффектов", - "outgoingHealingBoost": "Бонус исходящего исцеления", - "fireDmgBoost": "Бонус огненного урона", - "iceDmgBoost": "Бонус ледяного урона", - "imaginaryDmgBoost": "Бонус мнимого урона", - "physicalDmgBoost": "Бонус физ. урона", - "quantumDmgBoost": "Бонус квантового урона", - "thunderDmgBoost": "Бонус электр. урона", - "windDmgBoost": "Бонус ветряного урона", - "pursued": "Дополнительный урон", - "true damage": "Истинный урон", - "elationdamage": "Урон радости", - "follow-up": "Урон бонус-атаки", - "elemental damage": "Урон пробития и суперпробития", - "dot": "Периодический урон", - "qte": "QTE Навык", - "level": "Уровень", - "relics": "Реликвии", - "eidolons": "Эйдолоны", - "lightcones": "Световые конусы", - "loadData": "Загрузить данные", - "exportData": "Экспортировать данные", - "connectSetting": "Настройки подключения", - "connected": "Подключено", - "unconnected": "Не подключено", - "psConnection": "Подключение к PS", - "connectionType": "Тип подключения", - "status": "Статус", - "connectPs": "Подключить PS", - "disconnect": "Отключить", - "other": "Другое", - "freeSr": "FreeSR", - "database": "База данных", - "enka": "Enka", - "monsterSetting": "Настройки монстров", - "serverUrl": "URL сервера", - "privateType": "Приватный тип", - "local": "Локальный", - "server": "Сервер", - "username": "Имя пользователя", - "password": "Пароль", - "placeholderServerUrl": "Введите URL сервера", - "placeholderUsername": "Введите имя пользователя", - "placeholderPassword": "Введите пароль", - "connectedSuccess": "Успешное подключение к PS", - "connectedFailed": "Ошибка подключения к PS", - "syncSuccess": "Данные успешно синхронизированы с PS", - "syncFailed": "Ошибка синхронизации данных", - "sync": "Синхронизация", - "importSetting": "Настройки импорта", - "profile": "Профиль", - "default": "По умолчанию", - "copyProfiles": "Копировать профили", - "addNewProfile": "Добавить новый профиль", - "createNewProfile": "Создать новый профиль", - "editProfile": "Редактировать профиль", - "placeholderProfileName": "Введите имя профиля", - "profileName": "Имя профиля", - "create": "Создать", - "update": "Обновить", - "characterInformation": "Информация о персонаже", - "skills": "Навыки", - "showcaseCard": "Карточка персонажа", - "comingSoon": "Скоро", - "characterName": "Имя персонажа", - "placeholderCharacter": "Введите имя персонажа", - "characterSettings": "Настройки персонажа", - "levelConfiguration": "Конфигурация уровня", - "characterLevel": "Уровень персонажа", - "max": "МАКС", - "ultimateEnergy": "Энергия сверхспособности", - "currentEnergy": "Текущая энергия", - "setTo50": "Установить на 50%", - "battleConfiguration": "Конфигурация боя", - "useTechnique": "Использовать технику", - "techniqueNote": "Включить эффекты техники до боя", - "enhancement": "Усиление", - "enhancementLevel": "Уровень усиления", - "origin": "Оригинал", - "enhancedNote": "Высокие уровни открывают новые способности", - "lightconeEquipment": "Снаряжение конуса", - "lightconeSettings": "Настройки светового конуса", - "placeholderLevel": "Введите уровень", - "superimpositionRank": "Уровень наложения", - "ranksNote": "Высокие ранги дают более сильные эффекты", - "changeLightcone": "Изменить световой конус", - "removeLightcone": "Снять световой конус", - "equipLightcone": "Экипировать конус", - "noLightconeEquipped": "Световой конус не экипирован", - "equipLightconeNote": "Наденьте конус для усиления персонажа", - "filter": "Фильтр", - "selectedCharacters": "Выбранные персонажи", - "selectedProfiles": "Выбранные профили", - "clearAll": "Очистить всё", - "selectAll": "Выбрать всё", - "copy": "Копировать", - "copied": "Скопировано", - "noAvatarSelected": "Персонаж не выбран", - "noAvatarToCopySelected": "Нет персонажа для копирования", - "pleaseSelectAtLeastOneProfile": "Пожалуйста, выберите хотя бы один профиль", - "pleaseEnterUid": "Пожалуйста, введите UID", - "failedToFetchEnkaData": "Не удалось получить данные Enka", - "pleaseSelectAtLeastOneCharacter": "Пожалуйста, выберите хотя бы одного персонажа", - "noDataToImport": "Нет данных для импорта", - "pleaseSelectAFile": "Пожалуйста, выберите файл", - "fileMustBeAValidJsonFile": "Файл должен быть в формате JSON", - "importEnkaDataSuccess": "Успешный импорт из Enka", - "importFreeSRDataSuccess": "Успешный импорт из FreeSR", - "importDatabaseSuccess": "Успешный импорт базы данных", - "getData": "Получить данные", - "import": "Импорт", - "freeSRImport": "Импорт FreeSR", - "onlySupportFreeSRJsonFile": "Поддерживаются только JSON от FreeSR", - "pickAFile": "Выберите файл", - "lightConeSetting": "Настройки конуса", - "relicMaker": "Создатель реликвий", - "pleaseSelectAllOptions": "Пожалуйста, выберите все параметры", - "relicSavedSuccessfully": "Реликвия успешно сохранена", - "mainSettings": "Основные настройки", - "mainStat": "Основная характеристика", - "set": "Набор", - "pleaseSelectASet": "Выберите набор", - "effectBonus": "Бонус эффекта", - "totalRoll": "Всего улучшений", - "randomizeStats": "Случайные статы", - "randomizeRolls": "Случайные улучшения", - "selectASubStat": "Выберите доп. стат", - "selectASet": "Выберите набор", - "selectAMainStat": "Выберите основной стат", - "save": "Сохранить", - "reset": "Сброс", - "roll": "Улучшение", - "step": "Шаг", - "memoryOfChaos": "Зал забвения", - "pureFiction": "Чистый вымысел", - "apocalypticShadow": "Апокалиптическая тень", - "customEnemy": "Свой противник", - "simulatedUniverse": "Виртуальная вселенная", - "floor": "Этаж", - "side": "Половина", - "wave": "Волна", - "stage": "Стадия", - "useCycleCount": "Использовать счетчик циклов?", - "useTurbulenceBuff": "Использовать бафф турбулентности?", - "firstHalfEnemies": "Враги первой половины", - "secondHalfEnemies": "Враги второй половины", - "firstNodeEnemies": "Враги узла 1", - "secondNodeEnemies": "Враги узла 2", - "thirdNodeEnemies": "Враги узла 3", - "firstNode": "Узел 1", - "secondNode": "Узел 2", - "thirdNode": "Узел 3", - "listEnemies": "Список врагов", - "turbulenceBuff": "Бафф турбулентности", - "noEventSelected": "Событие не выбрано", - "noTurbulenceBuff": "Нет баффа турбулентности", - "upper": "Верх", - "lower": "Низ", - "upperToLower": "Верх -> Низ", - "lowerToUpper": "Низ -> Верх", - "selectMOCEvent": "Выбрать событие MOC", - "selectPFEvent": "Выбрать событие PF", - "selectASEvent": "Выбрать событие AS", - "selectCEEvent": "Выбрать событие CE", - "selectEvent": "Выбрать событие", - "selectFloor": "Выбрать этаж", - "selectSide": "Выбрать половину", - "selectBuff": "Выбрать бафф", - "selectStage": "Выбрать стадию", - "previous": "Назад", - "next": "Вперед", - "noMonstersFound": "Монстры не найдены", - "addNewWave": "Добавить новую волну", - "searchStage": "Поиск стадии...", - "noStageFound": "Стадия не найдена", - "searchMonster": "Поиск монстра...", - "changeRelic": "Изменить реликвию", - "deleteRelic": "Удалить реликвию", - "deleteRelicConfirm": "Удалить реликвию в этом слоте?", - "setEffects": "Настроить эффекты", - "details": "Детали", - "normal": "Базовая атака", - "bpskill": "Навык", - "maze": "Техника", - "ultra": "Сверхспособность", - "servantskill": "Навык мемоспрайта", - "severaltalent": "Талант мемоспрайта", - "singleattack": "Одиночная атака", - "enhance": "Усиление", - "summon": "Призыв", - "mazeattack": "Атака в технике", - "blast": "Взрыв", - "restore": "Восстановление", - "support": "Поддержка", - "aoeattack": "АоЕ атака", - "impair": "Ослабление", - "bounce": "Отскок", - "active": "Активен", - "defence": "Защита", - "inactive": "Неактивен", - "maxAll": "Улучшить всё", - "maxAllSuccess": "Уровни навыков улучшены до максимума.", - "maxAllFailed": "Не удалось улучшить навыки.", - "noRelicEquipped": "Нет экипированных реликвий", - "anomalyArbitration": "Аномальный арбитраж", - "normalMode": "Обычный режим", - "hardMode": "Сложный режим", - "selectPEAKEvent": "Выбрать событие PEAK", - "mode": "Режим", - "selectMode": "Выбрать режим", - "rollBack": "Отменить шаг", - "upRoll": "Повысить улучшение", - "downRoll": "Понизить улучшение", - "actions": "Действия", - "avatars": "Персонажи", - "quickView": "Быстрый просмотр", - "extraSetting": "Дополнительные настройки", - "disableCensorship": "Отключить цензуру", - "hideUI": "Скрыть интерфейс", - "theoryCraftMode": "Режим Theorycraft", - "cycleCount": "Счетчик циклов", - "pleaseSelectAllSubStats": "Пожалуйста, выберите все саб-статы", - "subStatRollCountCannotBeZero": "Количество улучшений саб-стата не может быть нулем", - "theoryCraft": "Theorycraft", - "multipathCharacter": "Многопутевой персонаж", - "mainPath": "Основной Путь", - "march7Path": "Путь Март 7", - "challenge": "Испытание", - "skipNode": "Пропустить узел", - "disableSkip": "Отключить пропуск", - "skipNode1": "Пропустить узел 1", - "skipNode2": "Пропустить узел 2", - "extraFeatures": "Дополнительные функции", - "detailTheoryCraft": "Включение этой функции позволяет настроить количество циклов и здоровье противников.", - "detailSkipNode": "Позволяет пропустить (Узел 1/Узел 2) в Зале забвения или Чистом вымысле.", - "detailChallengePeak": "Позволяет изменить сезон Peak в текущей Аномалии.", - "detailHiddenUi": "Скрывает игровой интерфейс.", - "detailDisableCensorship": "Отключает внутриигровую цензуру.", - "detailMultipathCharacter": "Позволяет изменить Путь некоторых персонажей.", - "trailblazer": "Первопроходец", - "listExtraEffect": "Список доп. эффектов", - "extra": "Экстра", - "customLineup": "Пользовательский отряд" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Инструменты Firefly от Firefly Shelter" + }, + "DataPage": { + "skillType": "Тип навыка", + "skillName": "Название навыка", + "character": "Персонаж", + "id": "ID", + "path": "Путь", + "rarity": "Редкость", + "element": "Элемент", + "technique": "Техника", + "talent": "Талант", + "basic": "Базовая атака", + "skill": "Навык", + "ultimate": "Сверхспособность", + "servant": "Слуга", + "damage": "Урон", + "type": "Тип", + "warrior": "Разрушение", + "knight": "Сохранение", + "mage": "Эрудиция", + "priest": "Изобилие", + "rogue": "Охота", + "shaman": "Гармония", + "warlock": "Небытие", + "memory": "Память", + "elation": "Радость", + "fire": "Огонь", + "ice": "Лед", + "imaginary": "Мнимый", + "physical": "Физический", + "quantum": "Квантовый", + "thunder": "Электрический", + "wind": "Ветряной", + "hp": "HP", + "atk": "Сила атаки", + "speed": "Скорость", + "critRate": "Крит. шанс", + "critDmg": "Крит. урон", + "breakEffect": "Эффект пробития", + "effectRes": "Сопротивление эффектам", + "energyRegenerationRate": "Скорость восст. энергии", + "effectHitRate": "Шанс попадания эффектов", + "outgoingHealingBoost": "Бонус исходящего исцеления", + "fireDmgBoost": "Бонус огненного урона", + "iceDmgBoost": "Бонус ледяного урона", + "imaginaryDmgBoost": "Бонус мнимого урона", + "physicalDmgBoost": "Бонус физ. урона", + "quantumDmgBoost": "Бонус квантового урона", + "thunderDmgBoost": "Бонус электр. урона", + "windDmgBoost": "Бонус ветряного урона", + "pursued": "Дополнительный урон", + "true damage": "Истинный урон", + "elationdamage": "Урон радости", + "follow-up": "Урон бонус-атаки", + "elemental damage": "Урон пробития и суперпробития", + "dot": "Периодический урон", + "qte": "QTE Навык", + "level": "Уровень", + "relics": "Реликвии", + "eidolons": "Эйдолоны", + "lightcones": "Световые конусы", + "loadData": "Загрузить данные", + "exportData": "Экспортировать данные", + "connectSetting": "Настройки подключения", + "connected": "Подключено", + "unconnected": "Не подключено", + "psConnection": "Подключение к PS", + "connectionType": "Тип подключения", + "status": "Статус", + "connectPs": "Подключить PS", + "disconnect": "Отключить", + "other": "Другое", + "freeSr": "FreeSR", + "database": "База данных", + "enka": "Enka", + "monsterSetting": "Настройки монстров", + "serverUrl": "URL сервера", + "privateType": "Приватный тип", + "local": "Локальный", + "server": "Сервер", + "username": "Имя пользователя", + "password": "Пароль", + "placeholderServerUrl": "Введите URL сервера", + "placeholderUsername": "Введите имя пользователя", + "placeholderPassword": "Введите пароль", + "connectedSuccess": "Успешное подключение к PS", + "connectedFailed": "Ошибка подключения к PS", + "syncSuccess": "Данные успешно синхронизированы с PS", + "syncFailed": "Ошибка синхронизации данных", + "sync": "Синхронизация", + "importSetting": "Настройки импорта", + "profile": "Профиль", + "default": "По умолчанию", + "copyProfiles": "Копировать профили", + "addNewProfile": "Добавить новый профиль", + "createNewProfile": "Создать новый профиль", + "editProfile": "Редактировать профиль", + "placeholderProfileName": "Введите имя профиля", + "profileName": "Имя профиля", + "create": "Создать", + "update": "Обновить", + "characterInformation": "Информация о персонаже", + "skills": "Навыки", + "showcaseCard": "Карточка персонажа", + "comingSoon": "Скоро", + "characterName": "Имя персонажа", + "placeholderCharacter": "Введите имя персонажа", + "characterSettings": "Настройки персонажа", + "levelConfiguration": "Конфигурация уровня", + "characterLevel": "Уровень персонажа", + "max": "МАКС", + "ultimateEnergy": "Энергия сверхспособности", + "currentEnergy": "Текущая энергия", + "setTo50": "Установить на 50%", + "battleConfiguration": "Конфигурация боя", + "useTechnique": "Использовать технику", + "techniqueNote": "Включить эффекты техники до боя", + "enhancement": "Усиление", + "enhancementLevel": "Уровень усиления", + "origin": "Оригинал", + "enhancedNote": "Высокие уровни открывают новые способности", + "lightconeEquipment": "Снаряжение конуса", + "lightconeSettings": "Настройки светового конуса", + "placeholderLevel": "Введите уровень", + "superimpositionRank": "Уровень наложения", + "ranksNote": "Высокие ранги дают более сильные эффекты", + "changeLightcone": "Изменить световой конус", + "removeLightcone": "Снять световой конус", + "equipLightcone": "Экипировать конус", + "noLightconeEquipped": "Световой конус не экипирован", + "equipLightconeNote": "Наденьте конус для усиления персонажа", + "filter": "Фильтр", + "selectedCharacters": "Выбранные персонажи", + "selectedProfiles": "Выбранные профили", + "clearAll": "Очистить всё", + "selectAll": "Выбрать всё", + "copy": "Копировать", + "copied": "Скопировано", + "noAvatarSelected": "Персонаж не выбран", + "noAvatarToCopySelected": "Нет персонажа для копирования", + "pleaseSelectAtLeastOneProfile": "Пожалуйста, выберите хотя бы один профиль", + "pleaseEnterUid": "Пожалуйста, введите UID", + "failedToFetchEnkaData": "Не удалось получить данные Enka", + "pleaseSelectAtLeastOneCharacter": "Пожалуйста, выберите хотя бы одного персонажа", + "noDataToImport": "Нет данных для импорта", + "pleaseSelectAFile": "Пожалуйста, выберите файл", + "fileMustBeAValidJsonFile": "Файл должен быть в формате JSON", + "importEnkaDataSuccess": "Успешный импорт из Enka", + "importFreeSRDataSuccess": "Успешный импорт из FreeSR", + "importDatabaseSuccess": "Успешный импорт базы данных", + "getData": "Получить данные", + "import": "Импорт", + "freeSRImport": "Импорт FreeSR", + "onlySupportFreeSRJsonFile": "Поддерживаются только JSON от FreeSR", + "pickAFile": "Выберите файл", + "lightConeSetting": "Настройки конуса", + "relicMaker": "Создатель реликвий", + "pleaseSelectAllOptions": "Пожалуйста, выберите все параметры", + "relicSavedSuccessfully": "Реликвия успешно сохранена", + "mainSettings": "Основные настройки", + "mainStat": "Основная характеристика", + "set": "Набор", + "pleaseSelectASet": "Выберите набор", + "effectBonus": "Бонус эффекта", + "totalRoll": "Всего улучшений", + "randomizeStats": "Случайные статы", + "randomizeRolls": "Случайные улучшения", + "selectASubStat": "Выберите доп. стат", + "selectASet": "Выберите набор", + "selectAMainStat": "Выберите основной стат", + "save": "Сохранить", + "reset": "Сброс", + "roll": "Улучшение", + "step": "Шаг", + "memoryOfChaos": "Зал забвения", + "pureFiction": "Чистый вымысел", + "apocalypticShadow": "Апокалиптическая тень", + "customEnemy": "Свой противник", + "simulatedUniverse": "Виртуальная вселенная", + "floor": "Этаж", + "side": "Половина", + "wave": "Волна", + "stage": "Стадия", + "useCycleCount": "Использовать счетчик циклов?", + "useTurbulenceBuff": "Использовать бафф турбулентности?", + "firstHalfEnemies": "Враги первой половины", + "secondHalfEnemies": "Враги второй половины", + "firstNodeEnemies": "Враги узла 1", + "secondNodeEnemies": "Враги узла 2", + "thirdNodeEnemies": "Враги узла 3", + "firstNode": "Узел 1", + "secondNode": "Узел 2", + "thirdNode": "Узел 3", + "listEnemies": "Список врагов", + "turbulenceBuff": "Бафф турбулентности", + "noEventSelected": "Событие не выбрано", + "noTurbulenceBuff": "Нет баффа турбулентности", + "upper": "Верх", + "lower": "Низ", + "upperToLower": "Верх -> Низ", + "lowerToUpper": "Низ -> Верх", + "selectMOCEvent": "Выбрать событие MOC", + "selectPFEvent": "Выбрать событие PF", + "selectASEvent": "Выбрать событие AS", + "selectCEEvent": "Выбрать событие CE", + "selectEvent": "Выбрать событие", + "selectFloor": "Выбрать этаж", + "selectSide": "Выбрать половину", + "selectBuff": "Выбрать бафф", + "selectStage": "Выбрать стадию", + "previous": "Назад", + "next": "Вперед", + "noMonstersFound": "Монстры не найдены", + "addNewWave": "Добавить новую волну", + "searchStage": "Поиск стадии...", + "noStageFound": "Стадия не найдена", + "searchMonster": "Поиск монстра...", + "changeRelic": "Изменить реликвию", + "deleteRelic": "Удалить реликвию", + "deleteRelicConfirm": "Удалить реликвию в этом слоте?", + "setEffects": "Настроить эффекты", + "details": "Детали", + "normal": "Базовая атака", + "bpskill": "Навык", + "maze": "Техника", + "ultra": "Сверхспособность", + "servantskill": "Навык мемоспрайта", + "severaltalent": "Талант мемоспрайта", + "singleattack": "Одиночная атака", + "enhance": "Усиление", + "summon": "Призыв", + "mazeattack": "Атака в технике", + "blast": "Взрыв", + "restore": "Восстановление", + "support": "Поддержка", + "aoeattack": "АоЕ атака", + "impair": "Ослабление", + "bounce": "Отскок", + "active": "Активен", + "defence": "Защита", + "inactive": "Неактивен", + "maxAll": "Улучшить всё", + "maxAllSuccess": "Уровни навыков улучшены до максимума.", + "maxAllFailed": "Не удалось улучшить навыки.", + "noRelicEquipped": "Нет экипированных реликвий", + "anomalyArbitration": "Аномальный арбитраж", + "normalMode": "Обычный режим", + "hardMode": "Сложный режим", + "selectPEAKEvent": "Выбрать событие PEAK", + "mode": "Режим", + "selectMode": "Выбрать режим", + "rollBack": "Отменить шаг", + "upRoll": "Повысить улучшение", + "downRoll": "Понизить улучшение", + "actions": "Действия", + "avatars": "Персонажи", + "quickView": "Быстрый просмотр", + "extraSetting": "Дополнительные настройки", + "disableCensorship": "Отключить цензуру", + "hideUI": "Скрыть интерфейс", + "theoryCraftMode": "Режим Theorycraft", + "cycleCount": "Счетчик циклов", + "pleaseSelectAllSubStats": "Пожалуйста, выберите все саб-статы", + "subStatRollCountCannotBeZero": "Количество улучшений саб-стата не может быть нулем", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Многопутевой персонаж", + "mainPath": "Основной Путь", + "march7Path": "Путь Март 7", + "challenge": "Испытание", + "skipNode": "Пропустить узел", + "disableSkip": "Отключить пропуск", + "skipNode1": "Пропустить узел 1", + "skipNode2": "Пропустить узел 2", + "extraFeatures": "Дополнительные функции", + "detailTheoryCraft": "Включение этой функции позволяет настроить количество циклов и здоровье противников.", + "detailSkipNode": "Позволяет пропустить (Узел 1/Узел 2) в Зале забвения или Чистом вымысле.", + "detailChallengePeak": "Позволяет изменить сезон Peak в текущей Аномалии.", + "detailHiddenUi": "Скрывает игровой интерфейс.", + "detailDisableCensorship": "Отключает внутриигровую цензуру.", + "detailMultipathCharacter": "Позволяет изменить Путь некоторых персонажей.", + "trailblazer": "Первопроходец", + "listExtraEffect": "Список доп. эффектов", + "extra": "Экстра", + "customLineup": "Пользовательский отряд" + } } \ No newline at end of file diff --git a/messages/th.json b/messages/th.json index b59f57b..988b734 100644 --- a/messages/th.json +++ b/messages/th.json @@ -1,290 +1,290 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "เครื่องมือ Firefly โดย Firefly Shelter" - }, - "DataPage": { - "skillType": "ประเภทสกิล", - "skillName": "ชื่อสกิล", - "character": "ตัวละคร", - "id": "ไอดี", - "path": "Path", - "rarity": "ระดับความหายาก", - "element": "ธาตุ", - "technique": "เทคนิค", - "talent": "พรสวรรค์", - "basic": "โจมตีปกติ", - "skill": "สกิลต่อสู้", - "ultimate": "ท่าไม้ตาย", - "servant": "ผู้ติดตาม", - "damage": "ความเสียหาย", - "type": "ประเภท", - "warrior": "ทำลายล้าง", - "knight": "อนุรักษ์", - "mage": "ปัญญา", - "priest": "เฟื่องฟู", - "rogue": "ล่าสังหาร", - "shaman": "ประสาน", - "warlock": "ลบล้าง", - "memory": "ลบล้าง", - "elation": "ปิติสุข", - "fire": "ไฟ", - "ice": "น้ำแข็ง", - "imaginary": "จินตภาพ", - "physical": "กายภาพ", - "quantum": "ควอนตัม", - "thunder": "สายฟ้า", - "wind": "ลม", - "hp": "HP", - "atk": "ATK", - "speed": "ความเร็ว", - "critRate": "อัตราคริติคอล", - "critDmg": "ความเสียหายคริติคอล", - "breakEffect": "เอฟเฟกต์ทำลายล้าง", - "effectRes": "ต้านทานสถานะ", - "energyRegenerationRate": "อัตราฟื้นฟูพลังงาน", - "effectHitRate": "อัตราสร้างสถานะ", - "outgoingHealingBoost": "โบนัสการรักษา", - "fireDmgBoost": "โบนัสความเสียหายไฟ", - "iceDmgBoost": "โบนัสความเสียหายน้ำแข็ง", - "imaginaryDmgBoost": "โบนัสความเสียหายจินตภาพ", - "physicalDmgBoost": "โบนัสความเสียหายกายภาพ", - "quantumDmgBoost": "โบนัสความเสียหายควอนตัม", - "thunderDmgBoost": "โบนัสความเสียหายสายฟ้า", - "windDmgBoost": "โบนัสความเสียหายลม", - "pursued": "ความเสียหายเพิ่มเติม", - "true damage": "ความเสียหายจริง", - "elationdamage": "ความเสียหายปิติสุข", - "follow-up": "ความเสียหายโจมตีต่อเนื่อง", - "elemental damage": "ความเสียหายทำลายล้างและซูเปอร์", - "dot": "ความเสียหายต่อเนื่อง", - "qte": "สกิล QTE", - "level": "เลเวล", - "relics": "รีลิกส์", - "eidolons": "Eidolon", - "lightcones": "Light Cone", - "loadData": "โหลดข้อมูล", - "exportData": "ส่งออกข้อมูล", - "connectSetting": "การตั้งค่าการเชื่อมต่อ", - "connected": "เชื่อมต่อแล้ว", - "unconnected": "ไม่ได้เชื่อมต่อ", - "psConnection": "การเชื่อมต่อ PS", - "connectionType": "ประเภทการเชื่อมต่อ", - "status": "สถานะ", - "connectPs": "เชื่อมต่อ PS", - "disconnect": "ยกเลิกการเชื่อมต่อ", - "other": "อื่นๆ", - "freeSr": "FreeSR", - "database": "ฐานข้อมูล", - "enka": "Enka", - "monsterSetting": "ตั้งค่ามอนสเตอร์", - "serverUrl": "URL เซิร์ฟเวอร์", - "privateType": "ประเภทเซิร์ฟเวอร์ส่วนตัว", - "local": "ในเครื่อง", - "server": "เซิร์ฟเวอร์", - "username": "ชื่อผู้ใช้", - "password": "รหัสผ่าน", - "placeholderServerUrl": "ป้อน URL เซิร์ฟเวอร์", - "placeholderUsername": "ป้อนชื่อผู้ใช้", - "placeholderPassword": "ป้อนรหัสผ่าน", - "connectedSuccess": "เชื่อมต่อกับ PS สำเร็จ", - "connectedFailed": "ล้มเหลวในการเชื่อมต่อกับ PS", - "syncSuccess": "ซิงค์ข้อมูลกับ PS สำเร็จ", - "syncFailed": "ล้มเหลวในการซิงค์ข้อมูลกับ PS", - "sync": "ซิงค์", - "importSetting": "การตั้งค่าการนำเข้า", - "profile": "โปรไฟล์", - "default": "ค่าเริ่มต้น", - "copyProfiles": "คัดลอกโปรไฟล์", - "addNewProfile": "เพิ่มโปรไฟล์ใหม่", - "createNewProfile": "สร้างโปรไฟล์ใหม่", - "editProfile": "แก้ไขโปรไฟล์", - "placeholderProfileName": "ป้อนชื่อโปรไฟล์", - "profileName": "ชื่อโปรไฟล์", - "create": "สร้าง", - "update": "อัปเดต", - "characterInformation": "ข้อมูลตัวละคร", - "skills": "สกิล", - "showcaseCard": "การ์ดแสดง", - "comingSoon": "เร็วๆ นี้", - "characterName": "ชื่อตัวละคร", - "placeholderCharacter": "ป้อนชื่อตัวละคร", - "characterSettings": "การตั้งค่าตัวละคร", - "levelConfiguration": "การกำหนดระดับเลเวล", - "characterLevel": "เลเวลตัวละคร", - "max": "สูงสุด", - "ultimateEnergy": "พลังงานท่าไม้ตาย", - "currentEnergy": "พลังงานปัจจุบัน", - "setTo50": "ตั้งค่าเป็น 50%", - "battleConfiguration": "การกำหนดค่าการต่อสู้", - "useTechnique": "ใช้เทคนิค", - "techniqueNote": "เปิดใช้เอฟเฟกต์เทคนิคก่อนการต่อสู้", - "enhancement": "การเสริมพลัง", - "enhancementLevel": "ระดับการเสริมพลัง", - "origin": "ดั้งเดิม", - "enhancedNote": "การเสริมพลังที่สูงขึ้นจะปลดล็อกความสามารถ", - "lightconeEquipment": "สวมใส่ Light Cone", - "lightconeSettings": "การตั้งค่า Light Cone", - "placeholderLevel": "ป้อนเลเวล", - "superimpositionRank": "ระดับการขัดเกลา", - "ranksNote": "ระดับที่สูงขึ้นให้เอฟเฟกต์ที่แข็งแกร่งกว่า", - "changeLightcone": "เปลี่ยน Light Cone", - "removeLightcone": "ถอด Light Cone", - "equipLightcone": "สวมใส่ Light Cone", - "noLightconeEquipped": "ไม่ได้สวมใส่ Light Cone", - "equipLightconeNote": "สวมใส่ Light Cone เพื่อเพิ่มพลัง", - "filter": "กรอง", - "selectedCharacters": "ตัวละครที่เลือก", - "selectedProfiles": "โปรไฟล์ที่เลือก", - "clearAll": "ล้างทั้งหมด", - "selectAll": "เลือกทั้งหมด", - "copy": "คัดลอก", - "copied": "คัดลอกแล้ว", - "noAvatarSelected": "ไม่ได้เลือกตัวละคร", - "noAvatarToCopySelected": "ไม่ได้เลือกตัวละครที่จะคัดลอก", - "pleaseSelectAtLeastOneProfile": "โปรดเลือกอย่างน้อยหนึ่งโปรไฟล์", - "pleaseEnterUid": "โปรดป้อน UID", - "failedToFetchEnkaData": "ไม่สามารถดึงข้อมูล Enka ได้", - "pleaseSelectAtLeastOneCharacter": "โปรดเลือกอย่างน้อยหนึ่งตัวละคร", - "noDataToImport": "ไม่มีข้อมูลที่จะนำเข้า", - "pleaseSelectAFile": "โปรดเลือกไฟล์", - "fileMustBeAValidJsonFile": "ไฟล์ต้องเป็นไฟล์ JSON ที่ถูกต้อง", - "importEnkaDataSuccess": "นำเข้าข้อมูล Enka สำเร็จ", - "importFreeSRDataSuccess": "นำเข้าข้อมูล FreeSR สำเร็จ", - "importDatabaseSuccess": "นำเข้าฐานข้อมูลสำเร็จ", - "getData": "รับข้อมูล", - "import": "นำเข้า", - "freeSRImport": "นำเข้า FreeSR", - "onlySupportFreeSRJsonFile": "รองรับเฉพาะไฟล์ JSON ของ FreeSR", - "pickAFile": "เลือกไฟล์", - "lightConeSetting": "การตั้งค่า Light Cone", - "relicMaker": "สร้างรีลิกส์", - "pleaseSelectAllOptions": "โปรดเลือกตัวเลือกทั้งหมด", - "relicSavedSuccessfully": "บันทึกรีลิกส์สำเร็จ", - "mainSettings": "การตั้งค่าหลัก", - "mainStat": "ค่าสเตตัสหลัก", - "set": "เซ็ต", - "pleaseSelectASet": "โปรดเลือกเซ็ต", - "effectBonus": "โบนัสเอฟเฟกต์", - "totalRoll": "จำนวนการอัปเกรด", - "randomizeStats": "สุ่มสเตตัส", - "randomizeRolls": "สุ่มอัปเกรด", - "selectASubStat": "เลือกสเตตัสรอง", - "selectASet": "เลือกเซ็ต", - "selectAMainStat": "เลือกสเตตัสหลัก", - "save": "บันทึก", - "reset": "รีเซ็ต", - "roll": "อัปเกรด", - "step": "ขั้น", - "memoryOfChaos": "Memory of Chaos", - "pureFiction": "Pure Fiction", - "apocalypticShadow": "Apocalyptic Shadow", - "customEnemy": "ศัตรูกำหนดเอง", - "simulatedUniverse": "Simulated Universe", - "floor": "ชั้น", - "side": "ฝั่ง", - "wave": "เวฟ", - "stage": "ด่าน", - "useCycleCount": "ใช้การนับรอบหรือไม่?", - "useTurbulenceBuff": "ใช้บัฟบรรยากาศหรือไม่?", - "firstHalfEnemies": "ศัตรูครึ่งแรก", - "secondHalfEnemies": "ศัตรูครึ่งหลัง", - "firstNodeEnemies": "ศัตรูโหนด 1", - "secondNodeEnemies": "ศัตรูโหนด 2", - "thirdNodeEnemies": "ศัตรูโหนด 3", - "firstNode": "โหนด 1", - "secondNode": "โหนด 2", - "thirdNode": "โหนด 3", - "listEnemies": "รายการศัตรู", - "turbulenceBuff": "บัฟบรรยากาศ", - "noEventSelected": "ไม่ได้เลือกอีเวนต์", - "noTurbulenceBuff": "ไม่มีบัฟบรรยากาศ", - "upper": "บน", - "lower": "ล่าง", - "upperToLower": "บน -> ล่าง", - "lowerToUpper": "ล่าง -> บน", - "selectMOCEvent": "เลือกอีเวนต์ MOC", - "selectPFEvent": "เลือกอีเวนต์ PF", - "selectASEvent": "เลือกอีเวนต์ AS", - "selectCEEvent": "เลือกอีเวนต์ CE", - "selectEvent": "เลือกอีเวนต์", - "selectFloor": "เลือกชั้น", - "selectSide": "เลือกฝั่ง", - "selectBuff": "เลือกบัฟ", - "selectStage": "เลือกด่าน", - "previous": "ก่อนหน้า", - "next": "ถัดไป", - "noMonstersFound": "ไม่พบมอนสเตอร์", - "addNewWave": "เพิ่มเวฟใหม่", - "searchStage": "ค้นหาด่าน...", - "noStageFound": "ไม่พบด่าน", - "searchMonster": "ค้นหามอนสเตอร์...", - "changeRelic": "เปลี่ยนรีลิกส์", - "deleteRelic": "ลบรีลิกส์", - "deleteRelicConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบรีลิกส์ในช่องนี้?", - "setEffects": "ตั้งค่าเอฟเฟกต์", - "details": "รายละเอียด", - "normal": "โจมตีปกติ", - "bpskill": "สกิลต่อสู้", - "maze": "เทคนิค", - "ultra": "ท่าไม้ตาย", - "servantskill": "สกิล Memosprite", - "severaltalent": "พรสวรรค์ Memosprite", - "singleattack": "โจมตีเป้าหมายเดียว", - "enhance": "เสริมพลัง", - "summon": "อัญเชิญ", - "mazeattack": "โจมตีแบบเทคนิค", - "blast": "กระจาย", - "restore": "ฟื้นฟู", - "support": "สนับสนุน", - "aoeattack": "โจมตีหมู่", - "impair": "ป่วน", - "bounce": "ชิ่ง", - "active": "เปิดใช้งาน", - "defence": "ป้องกัน", - "inactive": "ปิดใช้งาน", - "maxAll": "อัปเกรดทั้งหมด", - "maxAllSuccess": "อัปเกรดสกิลถึงระดับสูงสุดสำเร็จ", - "maxAllFailed": "อัปเกรดสกิลล้มเหลว", - "noRelicEquipped": "ไม่ได้สวมใส่รีลิกส์", - "anomalyArbitration": "Anomaly Arbitration", - "normalMode": "โหมดปกติ", - "hardMode": "โหมดยาก", - "selectPEAKEvent": "เลือกอีเวนต์ PEAK", - "mode": "โหมด", - "selectMode": "เลือกโหมด", - "rollBack": "ย้อนกลับ", - "upRoll": "เพิ่มอัปเกรด", - "downRoll": "ลดอัปเกรด", - "actions": "แอ็กชัน", - "avatars": "อวตาร", - "quickView": "ดูแบบด่วน", - "extraSetting": "การตั้งค่าเพิ่มเติม", - "disableCensorship": "ปิดการเซ็นเซอร์", - "hideUI": "ซ่อน UI", - "theoryCraftMode": "โหมด Theorycraft", - "cycleCount": "จำนวนรอบ", - "pleaseSelectAllSubStats": "โปรดเลือกสเตตัสรองทั้งหมด", - "subStatRollCountCannotBeZero": "จำนวนการอัปเกรดสเตตัสรองต้องไม่เป็นศูนย์", - "theoryCraft": "Theorycraft", - "multipathCharacter": "ตัวละครหลาย Path", - "mainPath": "Path หลัก", - "march7Path": "Path March 7th", - "challenge": "ท้าทาย", - "skipNode": "ข้ามโหนด", - "disableSkip": "ปิดใช้งานการข้าม", - "skipNode1": "ข้ามโหนด 1", - "skipNode2": "ข้ามโหนด 2", - "extraFeatures": "คุณสมบัติพิเศษ", - "detailTheoryCraft": "การเปิดคุณสมบัตินี้จะช่วยให้คุณปรับแต่งจำนวนรอบและปรับ HP ของศัตรูได้", - "detailSkipNode": "ช่วยให้ข้าม (โหนด 1/โหนด 2) ใน Memory of Chaos หรือ Pure Fiction", - "detailChallengePeak": "อนุญาตให้เปลี่ยนฤดูกาล Peak ใน Anomaly ปัจจุบัน", - "detailHiddenUi": "ซ่อนอินเทอร์เฟซของเกม", - "detailDisableCensorship": "ปิดการเซ็นเซอร์ในเกม", - "detailMultipathCharacter": "อนุญาตให้เปลี่ยน Path ของตัวละครบางตัว", - "trailblazer": "ผู้บุกเบิก", - "listExtraEffect": "รายการเอฟเฟกต์พิเศษ", - "extra": "พิเศษ", - "customLineup": "จัดทีมแบบกำหนดเอง" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "เครื่องมือ Firefly โดย Firefly Shelter" + }, + "DataPage": { + "skillType": "ประเภทสกิล", + "skillName": "ชื่อสกิล", + "character": "ตัวละคร", + "id": "ไอดี", + "path": "Path", + "rarity": "ระดับความหายาก", + "element": "ธาตุ", + "technique": "เทคนิค", + "talent": "พรสวรรค์", + "basic": "โจมตีปกติ", + "skill": "สกิลต่อสู้", + "ultimate": "ท่าไม้ตาย", + "servant": "ผู้ติดตาม", + "damage": "ความเสียหาย", + "type": "ประเภท", + "warrior": "ทำลายล้าง", + "knight": "อนุรักษ์", + "mage": "ปัญญา", + "priest": "เฟื่องฟู", + "rogue": "ล่าสังหาร", + "shaman": "ประสาน", + "warlock": "ลบล้าง", + "memory": "ลบล้าง", + "elation": "ปิติสุข", + "fire": "ไฟ", + "ice": "น้ำแข็ง", + "imaginary": "จินตภาพ", + "physical": "กายภาพ", + "quantum": "ควอนตัม", + "thunder": "สายฟ้า", + "wind": "ลม", + "hp": "HP", + "atk": "ATK", + "speed": "ความเร็ว", + "critRate": "อัตราคริติคอล", + "critDmg": "ความเสียหายคริติคอล", + "breakEffect": "เอฟเฟกต์ทำลายล้าง", + "effectRes": "ต้านทานสถานะ", + "energyRegenerationRate": "อัตราฟื้นฟูพลังงาน", + "effectHitRate": "อัตราสร้างสถานะ", + "outgoingHealingBoost": "โบนัสการรักษา", + "fireDmgBoost": "โบนัสความเสียหายไฟ", + "iceDmgBoost": "โบนัสความเสียหายน้ำแข็ง", + "imaginaryDmgBoost": "โบนัสความเสียหายจินตภาพ", + "physicalDmgBoost": "โบนัสความเสียหายกายภาพ", + "quantumDmgBoost": "โบนัสความเสียหายควอนตัม", + "thunderDmgBoost": "โบนัสความเสียหายสายฟ้า", + "windDmgBoost": "โบนัสความเสียหายลม", + "pursued": "ความเสียหายเพิ่มเติม", + "true damage": "ความเสียหายจริง", + "elationdamage": "ความเสียหายปิติสุข", + "follow-up": "ความเสียหายโจมตีต่อเนื่อง", + "elemental damage": "ความเสียหายทำลายล้างและซูเปอร์", + "dot": "ความเสียหายต่อเนื่อง", + "qte": "สกิล QTE", + "level": "เลเวล", + "relics": "รีลิกส์", + "eidolons": "Eidolon", + "lightcones": "Light Cone", + "loadData": "โหลดข้อมูล", + "exportData": "ส่งออกข้อมูล", + "connectSetting": "การตั้งค่าการเชื่อมต่อ", + "connected": "เชื่อมต่อแล้ว", + "unconnected": "ไม่ได้เชื่อมต่อ", + "psConnection": "การเชื่อมต่อ PS", + "connectionType": "ประเภทการเชื่อมต่อ", + "status": "สถานะ", + "connectPs": "เชื่อมต่อ PS", + "disconnect": "ยกเลิกการเชื่อมต่อ", + "other": "อื่นๆ", + "freeSr": "FreeSR", + "database": "ฐานข้อมูล", + "enka": "Enka", + "monsterSetting": "ตั้งค่ามอนสเตอร์", + "serverUrl": "URL เซิร์ฟเวอร์", + "privateType": "ประเภทเซิร์ฟเวอร์ส่วนตัว", + "local": "ในเครื่อง", + "server": "เซิร์ฟเวอร์", + "username": "ชื่อผู้ใช้", + "password": "รหัสผ่าน", + "placeholderServerUrl": "ป้อน URL เซิร์ฟเวอร์", + "placeholderUsername": "ป้อนชื่อผู้ใช้", + "placeholderPassword": "ป้อนรหัสผ่าน", + "connectedSuccess": "เชื่อมต่อกับ PS สำเร็จ", + "connectedFailed": "ล้มเหลวในการเชื่อมต่อกับ PS", + "syncSuccess": "ซิงค์ข้อมูลกับ PS สำเร็จ", + "syncFailed": "ล้มเหลวในการซิงค์ข้อมูลกับ PS", + "sync": "ซิงค์", + "importSetting": "การตั้งค่าการนำเข้า", + "profile": "โปรไฟล์", + "default": "ค่าเริ่มต้น", + "copyProfiles": "คัดลอกโปรไฟล์", + "addNewProfile": "เพิ่มโปรไฟล์ใหม่", + "createNewProfile": "สร้างโปรไฟล์ใหม่", + "editProfile": "แก้ไขโปรไฟล์", + "placeholderProfileName": "ป้อนชื่อโปรไฟล์", + "profileName": "ชื่อโปรไฟล์", + "create": "สร้าง", + "update": "อัปเดต", + "characterInformation": "ข้อมูลตัวละคร", + "skills": "สกิล", + "showcaseCard": "การ์ดแสดง", + "comingSoon": "เร็วๆ นี้", + "characterName": "ชื่อตัวละคร", + "placeholderCharacter": "ป้อนชื่อตัวละคร", + "characterSettings": "การตั้งค่าตัวละคร", + "levelConfiguration": "การกำหนดระดับเลเวล", + "characterLevel": "เลเวลตัวละคร", + "max": "สูงสุด", + "ultimateEnergy": "พลังงานท่าไม้ตาย", + "currentEnergy": "พลังงานปัจจุบัน", + "setTo50": "ตั้งค่าเป็น 50%", + "battleConfiguration": "การกำหนดค่าการต่อสู้", + "useTechnique": "ใช้เทคนิค", + "techniqueNote": "เปิดใช้เอฟเฟกต์เทคนิคก่อนการต่อสู้", + "enhancement": "การเสริมพลัง", + "enhancementLevel": "ระดับการเสริมพลัง", + "origin": "ดั้งเดิม", + "enhancedNote": "การเสริมพลังที่สูงขึ้นจะปลดล็อกความสามารถ", + "lightconeEquipment": "สวมใส่ Light Cone", + "lightconeSettings": "การตั้งค่า Light Cone", + "placeholderLevel": "ป้อนเลเวล", + "superimpositionRank": "ระดับการขัดเกลา", + "ranksNote": "ระดับที่สูงขึ้นให้เอฟเฟกต์ที่แข็งแกร่งกว่า", + "changeLightcone": "เปลี่ยน Light Cone", + "removeLightcone": "ถอด Light Cone", + "equipLightcone": "สวมใส่ Light Cone", + "noLightconeEquipped": "ไม่ได้สวมใส่ Light Cone", + "equipLightconeNote": "สวมใส่ Light Cone เพื่อเพิ่มพลัง", + "filter": "กรอง", + "selectedCharacters": "ตัวละครที่เลือก", + "selectedProfiles": "โปรไฟล์ที่เลือก", + "clearAll": "ล้างทั้งหมด", + "selectAll": "เลือกทั้งหมด", + "copy": "คัดลอก", + "copied": "คัดลอกแล้ว", + "noAvatarSelected": "ไม่ได้เลือกตัวละคร", + "noAvatarToCopySelected": "ไม่ได้เลือกตัวละครที่จะคัดลอก", + "pleaseSelectAtLeastOneProfile": "โปรดเลือกอย่างน้อยหนึ่งโปรไฟล์", + "pleaseEnterUid": "โปรดป้อน UID", + "failedToFetchEnkaData": "ไม่สามารถดึงข้อมูล Enka ได้", + "pleaseSelectAtLeastOneCharacter": "โปรดเลือกอย่างน้อยหนึ่งตัวละคร", + "noDataToImport": "ไม่มีข้อมูลที่จะนำเข้า", + "pleaseSelectAFile": "โปรดเลือกไฟล์", + "fileMustBeAValidJsonFile": "ไฟล์ต้องเป็นไฟล์ JSON ที่ถูกต้อง", + "importEnkaDataSuccess": "นำเข้าข้อมูล Enka สำเร็จ", + "importFreeSRDataSuccess": "นำเข้าข้อมูล FreeSR สำเร็จ", + "importDatabaseSuccess": "นำเข้าฐานข้อมูลสำเร็จ", + "getData": "รับข้อมูล", + "import": "นำเข้า", + "freeSRImport": "นำเข้า FreeSR", + "onlySupportFreeSRJsonFile": "รองรับเฉพาะไฟล์ JSON ของ FreeSR", + "pickAFile": "เลือกไฟล์", + "lightConeSetting": "การตั้งค่า Light Cone", + "relicMaker": "สร้างรีลิกส์", + "pleaseSelectAllOptions": "โปรดเลือกตัวเลือกทั้งหมด", + "relicSavedSuccessfully": "บันทึกรีลิกส์สำเร็จ", + "mainSettings": "การตั้งค่าหลัก", + "mainStat": "ค่าสเตตัสหลัก", + "set": "เซ็ต", + "pleaseSelectASet": "โปรดเลือกเซ็ต", + "effectBonus": "โบนัสเอฟเฟกต์", + "totalRoll": "จำนวนการอัปเกรด", + "randomizeStats": "สุ่มสเตตัส", + "randomizeRolls": "สุ่มอัปเกรด", + "selectASubStat": "เลือกสเตตัสรอง", + "selectASet": "เลือกเซ็ต", + "selectAMainStat": "เลือกสเตตัสหลัก", + "save": "บันทึก", + "reset": "รีเซ็ต", + "roll": "อัปเกรด", + "step": "ขั้น", + "memoryOfChaos": "Memory of Chaos", + "pureFiction": "Pure Fiction", + "apocalypticShadow": "Apocalyptic Shadow", + "customEnemy": "ศัตรูกำหนดเอง", + "simulatedUniverse": "Simulated Universe", + "floor": "ชั้น", + "side": "ฝั่ง", + "wave": "เวฟ", + "stage": "ด่าน", + "useCycleCount": "ใช้การนับรอบหรือไม่?", + "useTurbulenceBuff": "ใช้บัฟบรรยากาศหรือไม่?", + "firstHalfEnemies": "ศัตรูครึ่งแรก", + "secondHalfEnemies": "ศัตรูครึ่งหลัง", + "firstNodeEnemies": "ศัตรูโหนด 1", + "secondNodeEnemies": "ศัตรูโหนด 2", + "thirdNodeEnemies": "ศัตรูโหนด 3", + "firstNode": "โหนด 1", + "secondNode": "โหนด 2", + "thirdNode": "โหนด 3", + "listEnemies": "รายการศัตรู", + "turbulenceBuff": "บัฟบรรยากาศ", + "noEventSelected": "ไม่ได้เลือกอีเวนต์", + "noTurbulenceBuff": "ไม่มีบัฟบรรยากาศ", + "upper": "บน", + "lower": "ล่าง", + "upperToLower": "บน -> ล่าง", + "lowerToUpper": "ล่าง -> บน", + "selectMOCEvent": "เลือกอีเวนต์ MOC", + "selectPFEvent": "เลือกอีเวนต์ PF", + "selectASEvent": "เลือกอีเวนต์ AS", + "selectCEEvent": "เลือกอีเวนต์ CE", + "selectEvent": "เลือกอีเวนต์", + "selectFloor": "เลือกชั้น", + "selectSide": "เลือกฝั่ง", + "selectBuff": "เลือกบัฟ", + "selectStage": "เลือกด่าน", + "previous": "ก่อนหน้า", + "next": "ถัดไป", + "noMonstersFound": "ไม่พบมอนสเตอร์", + "addNewWave": "เพิ่มเวฟใหม่", + "searchStage": "ค้นหาด่าน...", + "noStageFound": "ไม่พบด่าน", + "searchMonster": "ค้นหามอนสเตอร์...", + "changeRelic": "เปลี่ยนรีลิกส์", + "deleteRelic": "ลบรีลิกส์", + "deleteRelicConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบรีลิกส์ในช่องนี้?", + "setEffects": "ตั้งค่าเอฟเฟกต์", + "details": "รายละเอียด", + "normal": "โจมตีปกติ", + "bpskill": "สกิลต่อสู้", + "maze": "เทคนิค", + "ultra": "ท่าไม้ตาย", + "servantskill": "สกิล Memosprite", + "severaltalent": "พรสวรรค์ Memosprite", + "singleattack": "โจมตีเป้าหมายเดียว", + "enhance": "เสริมพลัง", + "summon": "อัญเชิญ", + "mazeattack": "โจมตีแบบเทคนิค", + "blast": "กระจาย", + "restore": "ฟื้นฟู", + "support": "สนับสนุน", + "aoeattack": "โจมตีหมู่", + "impair": "ป่วน", + "bounce": "ชิ่ง", + "active": "เปิดใช้งาน", + "defence": "ป้องกัน", + "inactive": "ปิดใช้งาน", + "maxAll": "อัปเกรดทั้งหมด", + "maxAllSuccess": "อัปเกรดสกิลถึงระดับสูงสุดสำเร็จ", + "maxAllFailed": "อัปเกรดสกิลล้มเหลว", + "noRelicEquipped": "ไม่ได้สวมใส่รีลิกส์", + "anomalyArbitration": "Anomaly Arbitration", + "normalMode": "โหมดปกติ", + "hardMode": "โหมดยาก", + "selectPEAKEvent": "เลือกอีเวนต์ PEAK", + "mode": "โหมด", + "selectMode": "เลือกโหมด", + "rollBack": "ย้อนกลับ", + "upRoll": "เพิ่มอัปเกรด", + "downRoll": "ลดอัปเกรด", + "actions": "แอ็กชัน", + "avatars": "อวตาร", + "quickView": "ดูแบบด่วน", + "extraSetting": "การตั้งค่าเพิ่มเติม", + "disableCensorship": "ปิดการเซ็นเซอร์", + "hideUI": "ซ่อน UI", + "theoryCraftMode": "โหมด Theorycraft", + "cycleCount": "จำนวนรอบ", + "pleaseSelectAllSubStats": "โปรดเลือกสเตตัสรองทั้งหมด", + "subStatRollCountCannotBeZero": "จำนวนการอัปเกรดสเตตัสรองต้องไม่เป็นศูนย์", + "theoryCraft": "Theorycraft", + "multipathCharacter": "ตัวละครหลาย Path", + "mainPath": "Path หลัก", + "march7Path": "Path March 7th", + "challenge": "ท้าทาย", + "skipNode": "ข้ามโหนด", + "disableSkip": "ปิดใช้งานการข้าม", + "skipNode1": "ข้ามโหนด 1", + "skipNode2": "ข้ามโหนด 2", + "extraFeatures": "คุณสมบัติพิเศษ", + "detailTheoryCraft": "การเปิดคุณสมบัตินี้จะช่วยให้คุณปรับแต่งจำนวนรอบและปรับ HP ของศัตรูได้", + "detailSkipNode": "ช่วยให้ข้าม (โหนด 1/โหนด 2) ใน Memory of Chaos หรือ Pure Fiction", + "detailChallengePeak": "อนุญาตให้เปลี่ยนฤดูกาล Peak ใน Anomaly ปัจจุบัน", + "detailHiddenUi": "ซ่อนอินเทอร์เฟซของเกม", + "detailDisableCensorship": "ปิดการเซ็นเซอร์ในเกม", + "detailMultipathCharacter": "อนุญาตให้เปลี่ยน Path ของตัวละครบางตัว", + "trailblazer": "ผู้บุกเบิก", + "listExtraEffect": "รายการเอฟเฟกต์พิเศษ", + "extra": "พิเศษ", + "customLineup": "จัดทีมแบบกำหนดเอง" + } } \ No newline at end of file diff --git a/messages/vi.json b/messages/vi.json index 073e6d2..ea1a99d 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -1,289 +1,289 @@ -{ - "TabTitle": { - "title": "Firefly Tools", - "description": "Firefly tools by Firefly Shelter" - }, - "DataPage": { - "skillType": "Loại kỹ năng", - "skillName": "Tên kỹ năng", - "character": "Nhân vật", - "id": "ID", - "path": "Vận mệnh", - "rarity": "Độ hiếm", - "element": "Nguyên tố", - "technique": "Bí kỹ", - "talent": "Thiên phú", - "basic": "Đánh thường", - "skill": "Kỹ năng", - "ultimate": "Tuyệt kỹ", - "servant": "Phụ trợ", - "damage": "Sát thương", - "type": "Loại", - "warrior": "Hủy Diệt", - "knight": "Bảo Hộ", - "mage": "Tri Thức", - "priest": "Trù phú", - "rogue": "Săn Bắn", - "shaman": "Hòa Hợp", - "warlock": "Hư Vô", - "memory": "Ký Ức", - "elation": "Vui vẻ", - "fire": "Hỏa", - "ice": "Băng", - "imaginary": "Ảo Ảnh", - "physical": "Vật Lý", - "quantum": "Lượng Tử", - "thunder": "Lôi", - "wind": "Phong", - "hp": "HP", - "atk": "Tấn công", - "speed": "Tốc độ", - "critRate": "Tỷ lệ bạo", - "critDmg": "ST bạo", - "breakEffect": "sát thương kích phá", - "effectRes": "Kháng hiệu ứng", - "energyRegenerationRate": "Tốc độ hồi năng lượng", - "effectHitRate": "Tỷ lệ trúng hiệu ứng", - "outgoingHealingBoost": "Tăng hồi phục", - "fireDmgBoost": "Tăng sát thương Hỏa", - "iceDmgBoost": "Tăng sát thương Băng", - "imaginaryDmgBoost": "Tăng sát thương Ảo Ảnh", - "physicalDmgBoost": "Tăng sát thương Vật Lý", - "quantumDmgBoost": "Tăng sát thương Lượng Tử", - "thunderDmgBoost": "Tăng sát thương Lôi", - "windDmgBoost": "Tăng sát thương Phong", - "pursued": "Sát thương thêm", - "true damage": "Sát thương chuẩn", - "elationdamage": "Sát thương vui vẻ", - "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á", - "dot": "Sát thương theo thời gian", - "qte": "Kỹ năng QTE", - "level": "Cấp độ", - "relics": "Thánh di vật", - "eidolons": "Tinh hồn", - "lightcones": "Nón ánh sáng", - "loadData": "Tải dữ liệu", - "exportData": "Xuất dữ liệu", - "connectSetting": "Cài đặt kết nối", - "connected": "Đã kết nối", - "unconnected": "Chưa kết nối", - "psConnection": "Kết nối PS", - "connectionType": "Loại kết nối", - "status": "Trạng thái", - "connectPs": "Kết nối PS", - "disconnect": "Ngắt kết nối", - "other": "Khác", - "freeSr": "FreeSR", - "database": "Database", - "enka": "Enka", - "monsterSetting": "Cài đặt quái", - "serverUrl": "Địa chỉ server", - "privateType": "Loại riêng tư", - "local": "Cục bộ", - "server": "Máy chủ", - "username": "Tên người dùng", - "password": "Mật khẩu", - "placeholderServerUrl": "Nhập địa chỉ server", - "placeholderUsername": "Nhập tên người dùng", - "placeholderPassword": "Nhập mật khẩu", - "connectedSuccess": "Kết nối PS thành công", - "connectedFailed": "Kết nối PS thất bại", - "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", - "sync": "Đồng bộ", - "importSetting": "Cài đặt nhập", - "profile": "Hồ sơ", - "default": "Mặc định", - "copyProfiles": "Sao chép hồ sơ", - "addNewProfile": "Thêm hồ sơ mới", - "createNewProfile": "Tạo hồ sơ mới", - "editProfile": "Chỉnh sửa hồ sơ", - "placeholderProfileName": "Nhập tên hồ sơ", - "profileName": "Tên hồ sơ", - "create": "Tạo", - "update": "Cập nhật", - "characterInformation": "Thông tin nhân vật", - "skills": "Kỹ năng", - "showcaseCard": "Thẻ trưng bày", - "comingSoon": "Sắp ra mắt", - "characterName": "Tên nhân vật", - "placeholderCharacter": "Nhập tên nhân vật", - "characterSettings": "Cài đặt nhân vật", - "levelConfiguration": "Cấu hình cấp độ", - "characterLevel": "Cấp độ nhân vật", - "max": "Tối đa", - "ultimateEnergy": "Năng lượng tuyệt kỹ", - "currentEnergy": "Năng lượng hiện tại", - "setTo50": "Đặt thành 50%", - "battleConfiguration": "Cấu hình trận đấu", - "useTechnique": "Dùng bí kỹ", - "techniqueNote": "Bật hiệu ứng bí kỹ trước trận", - "enhancement": "Cường hóa", - "enhancementLevel": "Cấp độ cường hóa", - "origin": "Nguyên gốc", - "enhancedNote": "Cường hóa cao mở thêm kỹ năng", - "lightconeEquipment": "Trang bị nón ánh sáng", - "lightconeSettings": "Cài đặt nón ánh sáng", - "placeholderLevel": "Nhập cấp độ", - "superimpositionRank": "Bậc chồng kỹ năng", - "ranksNote": "Bậc càng cao hiệu ứng càng mạnh", - "changeLightcone": "Thay đổi nón ánh sáng", - "removeLightcone": "Gỡ nón ánh sáng", - "equipLightcone": "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", - "filter": "Lọc", - "selectedCharacters": "Nhân vật đã chọn", - "selectedProfiles": "Hồ sơ đã chọn", - "clearAll": "Xóa tất cả", - "selectAll": "Chọn tất cả", - "copy": "Sao chép", - "copied": "Đã sao chép", - "noAvatarSelected": "Chưa chọn nhân vật", - "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ơ", - "pleaseEnterUid": "Vui lòng nhập UID", - "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", - "noDataToImport": "Không có dữ liệu để nhập", - "pleaseSelectAFile": "Vui lòng chọn một tệp", - "fileMustBeAValidJsonFile": "Tệp phải là tệp JSON hợp lệ", - "importEnkaDataSuccess": "Nhập dữ liệu Enka 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", - "getData": "Lấy dữ liệu", - "import": "Nhập", - "freeSRImport": "Nhập FreeSR", - "onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR", - "pickAFile": "Chọn tệp", - "lightConeSetting": "Cài đặt Nón Ánh Sáng", - "relicMaker": "Trình tạo Thánh Di Vật", - "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", - "mainSettings": "Cài đặt chính", - "mainStat": "Chỉ số chính", - "set": "Bộ", - "pleaseSelectASet": "Vui lòng chọn một bộ", - "effectBonus": "Hiệu ứng cộng thêm", - "totalRoll": "Tổng số dòng", - "randomizeStats": "Ngẫu nhiên chỉ số", - "randomizeRolls": "Ngẫu nhiên số dòng", - "selectASubStat": "Chọn chỉ số phụ", - "selectASet": "Chọn một bộ", - "selectAMainStat": "Chọn chỉ số chính", - "save": "Lưu", - "reset": "Đặt lại toàn bộ", - "roll": "Số dòng", - "step": "Bước nhảy", - "memoryOfChaos": "Hồi ức hỗn độn", - "pureFiction": "Kể chuyện hư cấu", - "apocalypticShadow": "Ảo ảnh tận thế", - "customEnemy": "Kẻ địch tùy chỉnh", - "simulatedUniverse": "Vũ trụ mô phỏng", - "floor": "Tầng", - "side": "Nửa trận", - "wave": "Đợt", - "stage": "Màn", - "useCycleCount": "Dùng dếm chu kỳ?", - "useTurbulenceBuff": "Dùng buff hỗn loạn?", - "firstHalfEnemies": "Địch nửa đầu", - "secondHalfEnemies": "Địch nửa sau", - "firstNodeEnemies": "Địch Node 1", - "secondNodeEnemies": "Địch Node 2", - "thirdNodeEnemies": "Địch Node 3", - "firstNode": "Node 1", - "secondNode": "Node 2", - "thirdNode": "Node 3", - "turbulenceBuff": "Buff hỗn loạn", - "noEventSelected": "Không có sự kiện", - "noTurbulenceBuff": "Không có buff hỗn loạn", - "upper": "Nửa trên", - "lower": "Nửa dưới", - "upperToLower": "Nửa trên -> Nửa dưới", - "lowerToUpper": "Nửa dưới -> Nửa trên", - "selectMOCEvent": "Chọn sự kiện MOC", - "selectPFEvent": "Chọn sự kiện PF", - "selectASEvent": "Chọn sự kiện AS", - "selectCEEvent": "Chọn sự kiện CE", - "selectPEAKEvent": "Chọn sự kiện PEAK", - "selectEvent": "Chọn sự kiện", - "selectFloor": "Chọn tầng", - "selectSide": "Chọn nửa trận", - "selectBuff": "Chọn buff", - "selectStage": "Chọn màn", - "previous": "Trước", - "next": "Tiếp", - "noMonstersFound": "Không tìm thấy quái", - "addNewWave": "Thêm đợt mới", - "searchStage": "Tìm màn...", - "noStageFound": "Không tìm thấy màn", - "searchMonster": "Tìm quái...", - "changeRelic": "Thay đổi 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?", - "setEffects": "Thiết lập hiệu ứng", - "details": "Chi tiết", - "normal": "Đánh thường", - "bpskill": "Chiến kỹ", - "maze": "Bí kỹ", - "ultra": "Tuyệt kỹ", - "servantskill": "Kỹ năng vật triệu hồi", - "severaltalent": "Thiên phú vật triệu hồi", - "singleattack": "Tấn công đơn", - "enhance": "Cường hóa", - "summon": "Triệu hồi", - "blast": "Tấn công 3 mục tiêu", - "restore": "Hồi phục", - "support": "Hỗ trợ", - "aoeattack": "Tấn công đa mục tiêu", - "mazeattack": "Bí kỹ tấn công", - "impair": "Suy yếu", - "bounce": "Nảy bật", - "active": "Kích hoạt", - "inactive": "Không kích hoạt", - "defence": "Phòng thủ", - "maxAll": "Tối đa tất cả", - "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.", - "noRelicEquipped": "Không có di vật", - "anomalyArbitration": "Trọng tài dị tướng", - "normalMode": "Chế độ thường", - "hardMode": "Chế độ khó", - "mode": "Chế độ", - "selectMode": "Chọn chế độ", - "rollBack": "Quay lại bước trước", - "upRoll": "Tăng dòng", - "downRoll": "Giảm dòng", - "actions": "Hành động", - "avatars": "Nhân vật", - "quickView": "Xem nhanh", - "extraSetting": "Cài đặt bổ sung", - "disableCensorship": "Tắt kiểm duyệt", - "hideUI": "Ẩn giao diện", - "theoryCraftMode": "Chế độ Theory Craft", - "cycleCount": "Số vòng", - "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", - "theoryCraft": "Theory Craft", - "multipathCharacter": "Nhân vật đa Vận Mệnh", - "mainPath": "Vận Mệnh Nhân Vật Chính", - "march7Path": "Vận Mệnh March 7", - "challenge": "Thử thách", - "skipNode": "Bỏ qua node", - "disableSkip": "Tắt bỏ qua", - "skipNode1": "Bỏ qua node 1", - "skipNode2": "Bỏ qua node 2", - "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.", - "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.", - "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.", - "detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.", - "trailblazer": "Nhà khai phá", - "listExtraEffect": "Danh sách hiệu ứng bổ sung", - "extra": "Bổ sung", - "customLineup": "Đội hình tùy chỉnh" - } +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "Loại kỹ năng", + "skillName": "Tên kỹ năng", + "character": "Nhân vật", + "id": "ID", + "path": "Vận mệnh", + "rarity": "Độ hiếm", + "element": "Nguyên tố", + "technique": "Bí kỹ", + "talent": "Thiên phú", + "basic": "Đánh thường", + "skill": "Kỹ năng", + "ultimate": "Tuyệt kỹ", + "servant": "Phụ trợ", + "damage": "Sát thương", + "type": "Loại", + "warrior": "Hủy Diệt", + "knight": "Bảo Hộ", + "mage": "Tri Thức", + "priest": "Trù phú", + "rogue": "Săn Bắn", + "shaman": "Hòa Hợp", + "warlock": "Hư Vô", + "memory": "Ký Ức", + "elation": "Vui vẻ", + "fire": "Hỏa", + "ice": "Băng", + "imaginary": "Ảo Ảnh", + "physical": "Vật Lý", + "quantum": "Lượng Tử", + "thunder": "Lôi", + "wind": "Phong", + "hp": "HP", + "atk": "Tấn công", + "speed": "Tốc độ", + "critRate": "Tỷ lệ bạo", + "critDmg": "ST bạo", + "breakEffect": "sát thương kích phá", + "effectRes": "Kháng hiệu ứng", + "energyRegenerationRate": "Tốc độ hồi năng lượng", + "effectHitRate": "Tỷ lệ trúng hiệu ứng", + "outgoingHealingBoost": "Tăng hồi phục", + "fireDmgBoost": "Tăng sát thương Hỏa", + "iceDmgBoost": "Tăng sát thương Băng", + "imaginaryDmgBoost": "Tăng sát thương Ảo Ảnh", + "physicalDmgBoost": "Tăng sát thương Vật Lý", + "quantumDmgBoost": "Tăng sát thương Lượng Tử", + "thunderDmgBoost": "Tăng sát thương Lôi", + "windDmgBoost": "Tăng sát thương Phong", + "pursued": "Sát thương thêm", + "true damage": "Sát thương chuẩn", + "elationdamage": "Sát thương vui vẻ", + "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á", + "dot": "Sát thương theo thời gian", + "qte": "Kỹ năng QTE", + "level": "Cấp độ", + "relics": "Thánh di vật", + "eidolons": "Tinh hồn", + "lightcones": "Nón ánh sáng", + "loadData": "Tải dữ liệu", + "exportData": "Xuất dữ liệu", + "connectSetting": "Cài đặt kết nối", + "connected": "Đã kết nối", + "unconnected": "Chưa kết nối", + "psConnection": "Kết nối PS", + "connectionType": "Loại kết nối", + "status": "Trạng thái", + "connectPs": "Kết nối PS", + "disconnect": "Ngắt kết nối", + "other": "Khác", + "freeSr": "FreeSR", + "database": "Database", + "enka": "Enka", + "monsterSetting": "Cài đặt quái", + "serverUrl": "Địa chỉ server", + "privateType": "Loại riêng tư", + "local": "Cục bộ", + "server": "Máy chủ", + "username": "Tên người dùng", + "password": "Mật khẩu", + "placeholderServerUrl": "Nhập địa chỉ server", + "placeholderUsername": "Nhập tên người dùng", + "placeholderPassword": "Nhập mật khẩu", + "connectedSuccess": "Kết nối PS thành công", + "connectedFailed": "Kết nối PS thất bại", + "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", + "sync": "Đồng bộ", + "importSetting": "Cài đặt nhập", + "profile": "Hồ sơ", + "default": "Mặc định", + "copyProfiles": "Sao chép hồ sơ", + "addNewProfile": "Thêm hồ sơ mới", + "createNewProfile": "Tạo hồ sơ mới", + "editProfile": "Chỉnh sửa hồ sơ", + "placeholderProfileName": "Nhập tên hồ sơ", + "profileName": "Tên hồ sơ", + "create": "Tạo", + "update": "Cập nhật", + "characterInformation": "Thông tin nhân vật", + "skills": "Kỹ năng", + "showcaseCard": "Thẻ trưng bày", + "comingSoon": "Sắp ra mắt", + "characterName": "Tên nhân vật", + "placeholderCharacter": "Nhập tên nhân vật", + "characterSettings": "Cài đặt nhân vật", + "levelConfiguration": "Cấu hình cấp độ", + "characterLevel": "Cấp độ nhân vật", + "max": "Tối đa", + "ultimateEnergy": "Năng lượng tuyệt kỹ", + "currentEnergy": "Năng lượng hiện tại", + "setTo50": "Đặt thành 50%", + "battleConfiguration": "Cấu hình trận đấu", + "useTechnique": "Dùng bí kỹ", + "techniqueNote": "Bật hiệu ứng bí kỹ trước trận", + "enhancement": "Cường hóa", + "enhancementLevel": "Cấp độ cường hóa", + "origin": "Nguyên gốc", + "enhancedNote": "Cường hóa cao mở thêm kỹ năng", + "lightconeEquipment": "Trang bị nón ánh sáng", + "lightconeSettings": "Cài đặt nón ánh sáng", + "placeholderLevel": "Nhập cấp độ", + "superimpositionRank": "Bậc chồng kỹ năng", + "ranksNote": "Bậc càng cao hiệu ứng càng mạnh", + "changeLightcone": "Thay đổi nón ánh sáng", + "removeLightcone": "Gỡ nón ánh sáng", + "equipLightcone": "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", + "filter": "Lọc", + "selectedCharacters": "Nhân vật đã chọn", + "selectedProfiles": "Hồ sơ đã chọn", + "clearAll": "Xóa tất cả", + "selectAll": "Chọn tất cả", + "copy": "Sao chép", + "copied": "Đã sao chép", + "noAvatarSelected": "Chưa chọn nhân vật", + "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ơ", + "pleaseEnterUid": "Vui lòng nhập UID", + "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", + "noDataToImport": "Không có dữ liệu để nhập", + "pleaseSelectAFile": "Vui lòng chọn một tệp", + "fileMustBeAValidJsonFile": "Tệp phải là tệp JSON hợp lệ", + "importEnkaDataSuccess": "Nhập dữ liệu Enka 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", + "getData": "Lấy dữ liệu", + "import": "Nhập", + "freeSRImport": "Nhập FreeSR", + "onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR", + "pickAFile": "Chọn tệp", + "lightConeSetting": "Cài đặt Nón Ánh Sáng", + "relicMaker": "Trình tạo Thánh Di Vật", + "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", + "mainSettings": "Cài đặt chính", + "mainStat": "Chỉ số chính", + "set": "Bộ", + "pleaseSelectASet": "Vui lòng chọn một bộ", + "effectBonus": "Hiệu ứng cộng thêm", + "totalRoll": "Tổng số dòng", + "randomizeStats": "Ngẫu nhiên chỉ số", + "randomizeRolls": "Ngẫu nhiên số dòng", + "selectASubStat": "Chọn chỉ số phụ", + "selectASet": "Chọn một bộ", + "selectAMainStat": "Chọn chỉ số chính", + "save": "Lưu", + "reset": "Đặt lại toàn bộ", + "roll": "Số dòng", + "step": "Bước nhảy", + "memoryOfChaos": "Hồi ức hỗn độn", + "pureFiction": "Kể chuyện hư cấu", + "apocalypticShadow": "Ảo ảnh tận thế", + "customEnemy": "Kẻ địch tùy chỉnh", + "simulatedUniverse": "Vũ trụ mô phỏng", + "floor": "Tầng", + "side": "Nửa trận", + "wave": "Đợt", + "stage": "Màn", + "useCycleCount": "Dùng dếm chu kỳ?", + "useTurbulenceBuff": "Dùng buff hỗn loạn?", + "firstHalfEnemies": "Địch nửa đầu", + "secondHalfEnemies": "Địch nửa sau", + "firstNodeEnemies": "Địch Node 1", + "secondNodeEnemies": "Địch Node 2", + "thirdNodeEnemies": "Địch Node 3", + "firstNode": "Node 1", + "secondNode": "Node 2", + "thirdNode": "Node 3", + "turbulenceBuff": "Buff hỗn loạn", + "noEventSelected": "Không có sự kiện", + "noTurbulenceBuff": "Không có buff hỗn loạn", + "upper": "Nửa trên", + "lower": "Nửa dưới", + "upperToLower": "Nửa trên -> Nửa dưới", + "lowerToUpper": "Nửa dưới -> Nửa trên", + "selectMOCEvent": "Chọn sự kiện MOC", + "selectPFEvent": "Chọn sự kiện PF", + "selectASEvent": "Chọn sự kiện AS", + "selectCEEvent": "Chọn sự kiện CE", + "selectPEAKEvent": "Chọn sự kiện PEAK", + "selectEvent": "Chọn sự kiện", + "selectFloor": "Chọn tầng", + "selectSide": "Chọn nửa trận", + "selectBuff": "Chọn buff", + "selectStage": "Chọn màn", + "previous": "Trước", + "next": "Tiếp", + "noMonstersFound": "Không tìm thấy quái", + "addNewWave": "Thêm đợt mới", + "searchStage": "Tìm màn...", + "noStageFound": "Không tìm thấy màn", + "searchMonster": "Tìm quái...", + "changeRelic": "Thay đổi 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?", + "setEffects": "Thiết lập hiệu ứng", + "details": "Chi tiết", + "normal": "Đánh thường", + "bpskill": "Chiến kỹ", + "maze": "Bí kỹ", + "ultra": "Tuyệt kỹ", + "servantskill": "Kỹ năng vật triệu hồi", + "severaltalent": "Thiên phú vật triệu hồi", + "singleattack": "Tấn công đơn", + "enhance": "Cường hóa", + "summon": "Triệu hồi", + "blast": "Tấn công 3 mục tiêu", + "restore": "Hồi phục", + "support": "Hỗ trợ", + "aoeattack": "Tấn công đa mục tiêu", + "mazeattack": "Bí kỹ tấn công", + "impair": "Suy yếu", + "bounce": "Nảy bật", + "active": "Kích hoạt", + "inactive": "Không kích hoạt", + "defence": "Phòng thủ", + "maxAll": "Tối đa tất cả", + "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.", + "noRelicEquipped": "Không có di vật", + "anomalyArbitration": "Trọng tài dị tướng", + "normalMode": "Chế độ thường", + "hardMode": "Chế độ khó", + "mode": "Chế độ", + "selectMode": "Chọn chế độ", + "rollBack": "Quay lại bước trước", + "upRoll": "Tăng dòng", + "downRoll": "Giảm dòng", + "actions": "Hành động", + "avatars": "Nhân vật", + "quickView": "Xem nhanh", + "extraSetting": "Cài đặt bổ sung", + "disableCensorship": "Tắt kiểm duyệt", + "hideUI": "Ẩn giao diện", + "theoryCraftMode": "Chế độ Theory Craft", + "cycleCount": "Số vòng", + "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", + "theoryCraft": "Theory Craft", + "multipathCharacter": "Nhân vật đa Vận Mệnh", + "mainPath": "Vận Mệnh Nhân Vật Chính", + "march7Path": "Vận Mệnh March 7", + "challenge": "Thử thách", + "skipNode": "Bỏ qua node", + "disableSkip": "Tắt bỏ qua", + "skipNode1": "Bỏ qua node 1", + "skipNode2": "Bỏ qua node 2", + "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.", + "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.", + "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.", + "detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.", + "trailblazer": "Nhà khai phá", + "listExtraEffect": "Danh sách hiệu ứng bổ sung", + "extra": "Bổ sung", + "customLineup": "Đội hình tùy chỉnh" + } } \ No newline at end of file diff --git a/script/auto-gen.bat b/script/auto-gen.bat index 4816957..93959ae 100644 --- a/script/auto-gen.bat +++ b/script/auto-gen.bat @@ -1,13 +1,13 @@ -@echo off -echo [INFO] Create folder src\zod if not exist... -if not exist src\zod ( - mkdir src\zod -) - -echo [INFO] Start convert file .ts from src\types to Zod schemas... -for %%f in (src\types\*.ts) do ( - echo [ZOD] Chuyển %%f -> src\zod\%%~nf.zod.ts - npx ts-to-zod src\types\%%~nxf src\zod\%%~nf.zod.ts -) - -echo [DONE] ✅ Done +@echo off +echo [INFO] Create folder src\zod if not exist... +if not exist src\zod ( + mkdir src\zod +) + +echo [INFO] Start convert file .ts from src\types to Zod schemas... +for %%f in (src\types\*.ts) do ( + echo [ZOD] Chuyển %%f -> src\zod\%%~nf.zod.ts + npx ts-to-zod src\types\%%~nxf src\zod\%%~nf.zod.ts +) + +echo [DONE] ✅ Done diff --git a/src/app/api/data/[name]/route.ts b/src/app/api/data/[name]/route.ts index c8b7583..25ee82a 100644 --- a/src/app/api/data/[name]/route.ts +++ b/src/app/api/data/[name]/route.ts @@ -1,25 +1,25 @@ -import { getDataCache } from "@/lib/cache/cache" - -export async function GET( - req: Request, - { params }: { params: Promise<{ name: string }> } -) { - const { name } = await params - - const item = getDataCache(name) - - if (!item) { - return new Response("Not found", { status: 404 }) - } - - const headers: Record = { - "Content-Type": "application/json", - "Cache-Control": "public, max-age=3600" - } - - if (item.type === "br") { - headers["Content-Encoding"] = "br" - } - - return new Response(new Uint8Array(item.buf), { headers }) +import { getDataCache } from "@/lib/cache/cache" + +export async function GET( + req: Request, + { params }: { params: Promise<{ name: string }> } +) { + const { name } = await params + + const item = getDataCache(name) + + if (!item) { + return new Response("Not found", { status: 404 }) + } + + const headers: Record = { + "Content-Type": "application/json", + "Cache-Control": "public, max-age=3600" + } + + if (item.type === "br") { + headers["Content-Encoding"] = "br" + } + + return new Response(new Uint8Array(item.buf), { headers }) } \ No newline at end of file diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts index 585fe7d..82c8939 100644 --- a/src/app/api/proxy/route.ts +++ b/src/app/api/proxy/route.ts @@ -1,79 +1,79 @@ import { NextRequest, NextResponse } from 'next/server' import axios from 'axios' import net from 'net' - -function isPrivateHost(hostname: string): boolean { - if ( - hostname === 'localhost' || - hostname.startsWith('127.') || - hostname.startsWith('10.') || - hostname.startsWith('192.168.') || - /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname) - ) { - return true - } - - if (net.isIP(hostname)) { - return true - } - - return false -} - -export async function GET(req: NextRequest) { - try { - const { searchParams } = new URL(req.url) - const targetUrl = searchParams.get("url") - if (!targetUrl) { - return NextResponse.json({ error: "Missing url" }, { status: 400 }) - } - - const response = await fetch(targetUrl) - - if (!response.ok) { - return NextResponse.json({ error: "Failed to fetch image" }, { status: 500 }) - } - - const buffer = await response.arrayBuffer() - - return new NextResponse(buffer, { - headers: { - "Content-Type": response.headers.get("content-type") || "image/png", - "Cache-Control": "public, max-age=3600", - "Access-Control-Allow-Origin": "*", - }, - }) - } catch (e: unknown) { - return NextResponse.json({ error: (e as Error)?.message }, { status: 500 }) - } -} - - + +function isPrivateHost(hostname: string): boolean { + if ( + hostname === 'localhost' || + hostname.startsWith('127.') || + hostname.startsWith('10.') || + hostname.startsWith('192.168.') || + /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname) + ) { + return true + } + + if (net.isIP(hostname)) { + return true + } + + return false +} + +export async function GET(req: NextRequest) { + try { + const { searchParams } = new URL(req.url) + const targetUrl = searchParams.get("url") + if (!targetUrl) { + return NextResponse.json({ error: "Missing url" }, { status: 400 }) + } + + const response = await fetch(targetUrl) + + if (!response.ok) { + return NextResponse.json({ error: "Failed to fetch image" }, { status: 500 }) + } + + const buffer = await response.arrayBuffer() + + return new NextResponse(buffer, { + headers: { + "Content-Type": response.headers.get("content-type") || "image/png", + "Cache-Control": "public, max-age=3600", + "Access-Control-Allow-Origin": "*", + }, + }) + } catch (e: unknown) { + return NextResponse.json({ error: (e as Error)?.message }, { status: 500 }) + } +} + + export async function POST(request: NextRequest) { try { const body = await request.json() - const { serverUrl, method, ...payload } = body - - if (!serverUrl) { - return NextResponse.json({ error: 'Missing serverUrl' }, { status: 400 }) - } - if (!method) { - return NextResponse.json({ error: 'Missing method' }, { status: 400 }) - } - - let url = serverUrl.trim() - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = `http://${url}` - } - - const parsed = new URL(url) - if (isPrivateHost(parsed.hostname)) { - return NextResponse.json( - { error: `Connection to private/internal address (${parsed.hostname}) is not allowed` }, - { status: 403 } - ) - } - + const { serverUrl, method, ...payload } = body + + if (!serverUrl) { + return NextResponse.json({ error: 'Missing serverUrl' }, { status: 400 }) + } + if (!method) { + return NextResponse.json({ error: 'Missing method' }, { status: 400 }) + } + + let url = serverUrl.trim() + if (!url.startsWith('http://') && !url.startsWith('https://')) { + url = `http://${url}` + } + + const parsed = new URL(url) + if (isPrivateHost(parsed.hostname)) { + return NextResponse.json( + { error: `Connection to private/internal address (${parsed.hostname}) is not allowed` }, + { status: 403 } + ) + } + let response switch (method.toUpperCase()) { diff --git a/src/app/eidolons-info/page.tsx b/src/app/eidolons-info/page.tsx index 0b6e428..21a4853 100644 --- a/src/app/eidolons-info/page.tsx +++ b/src/app/eidolons-info/page.tsx @@ -1,9 +1,9 @@ -"use client" -import EidolonsInfo from "@/components/eidolonsInfo"; -export default function EidolonsInfoPage() { - return ( -
- -
- ); -} +"use client" +import EidolonsInfo from "@/components/eidolonsInfo"; +export default function EidolonsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/relics-info/page.tsx b/src/app/relics-info/page.tsx index 29b63e5..fb72df6 100644 --- a/src/app/relics-info/page.tsx +++ b/src/app/relics-info/page.tsx @@ -1,11 +1,11 @@ -"use client"; - -import RelicsInfo from "@/components/relicsInfo"; - -export default function RelicsInfoPage() { - return ( -
- -
- ); -} +"use client"; + +import RelicsInfo from "@/components/relicsInfo"; + +export default function RelicsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/showcase-card/page.tsx b/src/app/showcase-card/page.tsx index 06cb75f..1a26b14 100644 --- a/src/app/showcase-card/page.tsx +++ b/src/app/showcase-card/page.tsx @@ -1,11 +1,11 @@ -"use client" - -import ShowCaseInfo from "@/components/showcaseCard" - -export default function ShowcaseCard() { - return ( -
- -
- ) -} +"use client" + +import ShowCaseInfo from "@/components/showcaseCard" + +export default function ShowcaseCard() { + return ( +
+ +
+ ) +} diff --git a/src/app/skills-info/page.tsx b/src/app/skills-info/page.tsx index fbcb679..9d1d415 100644 --- a/src/app/skills-info/page.tsx +++ b/src/app/skills-info/page.tsx @@ -1,11 +1,11 @@ -"use client"; - -import SkillsInfo from "@/components/skillsInfo"; - -export default function SkillsInfoPage() { - return ( -
- -
- ); -} +"use client"; + +import SkillsInfo from "@/components/skillsInfo"; + +export default function SkillsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/components/actionBar/index.tsx b/src/components/actionBar/index.tsx index d28c9a3..48df368 100644 --- a/src/components/actionBar/index.tsx +++ b/src/components/actionBar/index.tsx @@ -1,445 +1,445 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client"; - -import { useTranslations } from "next-intl"; -import Image from "next/image"; -import { useRouter } from 'next/navigation' -import ParseText from "../parseText"; -import useLocaleStore from "@/stores/localeStore"; -import { getNameChar } from "@/helper/getName"; -import { useEffect, useMemo, useState } from "react"; -import { motion } from "framer-motion"; -import useModelStore from "@/stores/modelStore"; -import useUserDataStore from "@/stores/userDataStore"; -import { ModalConfig, RelicStore } from "@/types"; -import { toast } from "react-toastify"; -import useGlobalStore from "@/stores/globalStore"; -import { connectToPS, syncDataToPS } from "@/helper"; -import CopyImport from "../importBar/copy"; -import useCopyProfileStore from "@/stores/copyProfile"; -import AvatarBar from "../avatarBar"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function ActionBar() { - const router = useRouter() - const { avatarSelected } = useCurrentDataStore() - const { damageType, baseType } = useDetailDataStore() - const { setResetData } = useCopyProfileStore() - const transI18n = useTranslations("DataPage") - const { locale } = useLocaleStore() - const { - isOpenCreateProfile, - setIsOpenCreateProfile, - isOpenCopy, - setIsOpenCopy, - isOpenAvatars, - setIsOpenAvatars - } = useModelStore() - const { avatars, setAvatar } = useUserDataStore() - const [profileName, setProfileName] = useState(""); - const [formState, setFormState] = useState("EDIT"); - const [profileEdit, setProfileEdit] = useState(-1); - const { isConnectPS } = useGlobalStore() - - const profileCurrent = useMemo(() => { - if (!avatarSelected) return null; - const avatar = avatars[avatarSelected.ID]; - return avatar?.profileList[avatar.profileSelect] || null; - }, [avatarSelected, avatars]); - - const listProfile = useMemo(() => { - if (!avatarSelected) return []; - const avatar = avatars[avatarSelected.ID]; - return avatar?.profileList || []; - }, [avatarSelected, avatars]); - - - const handleUpdateProfile = () => { - if (!profileName.trim()) return; - if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) { - const newListProfile = [...listProfile] - const newProfile = { - profile_name: profileName, - lightcone: null, - relics: {} as Record - } - newListProfile.push(newProfile) - setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 }) - toast.success("Profile created successfully") - } else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) { - const newListProfile = [...listProfile] - newListProfile[profileEdit].profile_name = profileName; - setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile }) - toast.success("Profile updated successfully") - } - handleCloseModal("update_profile_modal"); - }; - - - const handleShow = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close(); - } - }; - - - const actionMove = (path: string) => { - router.push(`/${path}`) - } - - const handleProfileSelect = (profileId: number) => { - if (!avatarSelected) return; - if (avatars[avatarSelected.ID].profileSelect === profileId) return; - setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId }) - toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`) - } - - const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => { - e.stopPropagation() - if (!avatarSelected || profileId == 0) return; - if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) { - const newListProfile = [...listProfile] - newListProfile.splice(profileId, 1) - setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 }) - toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`) - } - - } - - const handleConnectOrSyncPS = async () => { - if (isConnectPS) { - const res = await syncDataToPS() - if (res.success) { - toast.success(transI18n("syncSuccess")) - } else { - toast.error(`${transI18n("syncFailed")}: ${res.message}`) - } - } else { - const res = await connectToPS() - if (res.success) { - toast.success(transI18n("connectedSuccess")) - } else { - toast.error(`${transI18n("connectedFailed")}: ${res.message}`) - } - } - } - - const modalConfigs: ModalConfig[] = [ - { - id: "update_profile_modal", - title: formState === "CREATE" ? transI18n("createNewProfile") : transI18n("editProfile"), - isOpen: isOpenCreateProfile, - onClose: () => { - setIsOpenCreateProfile(false) - handleCloseModal("update_profile_modal") - }, - content: ( -
-
- - setProfileName(e.target.value)} - /> -
-
- -
-
- ) - }, - { - id: "copy_profile_modal", - title: transI18n("copyProfiles").toUpperCase(), - isOpen: isOpenCopy, - onClose: () => { - setIsOpenCopy(false) - handleCloseModal("copy_profile_modal") - }, - content: - }, - { - id: "avatars_modal", - title: transI18n("avatars").toUpperCase(), - isOpen: isOpenAvatars, - onClose: () => { - setIsOpenAvatars(false) - handleCloseModal("avatars_modal") - }, - content: { setIsOpenAvatars(false); handleCloseModal("avatars_modal") }} /> - } - ] - - // Handle ESC key to close modal - useEffect(() => { - for (const item of modalConfigs) { - if (!item?.isOpen) { - handleCloseModal(item?.id || "") - } - } - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - for (const item of modalConfigs) { - handleCloseModal(item?.id || "") - } - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]); - - - - return ( -
-
- -
-
-
- {avatarSelected && ( -
- {'damage -

- {transI18n(avatarSelected.BaseType.toLowerCase())} -

-
{" / "}
- -
{`(${avatarSelected.ID})`}
-
- )} -
- -
- -
- {transI18n("profile")}: -
-
- - {profileCurrent?.profile_name} - - - - -
- -
    - {listProfile.map((profile, index) => ( -
  • - - {index !== 0 && ( - <> - - - - )} -
  • - ))} - -
  • - - -
  • -
-
-
- -
- -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
- -
-
- -
- - - - - - -
- - {modalConfigs.map(({ id, title, onClose, content }) => ( - -
-
- - ✕ - -
-
-

- {title} -

-
- {content} -
-
- ))} - - -
-
- ); +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; + +import { useTranslations } from "next-intl"; +import Image from "next/image"; +import { useRouter } from 'next/navigation' +import ParseText from "../parseText"; +import useLocaleStore from "@/stores/localeStore"; +import { getNameChar } from "@/helper/getName"; +import { useEffect, useMemo, useState } from "react"; +import { motion } from "framer-motion"; +import useModelStore from "@/stores/modelStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { ModalConfig, RelicStore } from "@/types"; +import { toast } from "react-toastify"; +import useGlobalStore from "@/stores/globalStore"; +import { connectToPS, syncDataToPS } from "@/helper"; +import CopyImport from "../importBar/copy"; +import useCopyProfileStore from "@/stores/copyProfile"; +import AvatarBar from "../avatarBar"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function ActionBar() { + const router = useRouter() + const { avatarSelected } = useCurrentDataStore() + const { damageType, baseType } = useDetailDataStore() + const { setResetData } = useCopyProfileStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore() + const { + isOpenCreateProfile, + setIsOpenCreateProfile, + isOpenCopy, + setIsOpenCopy, + isOpenAvatars, + setIsOpenAvatars + } = useModelStore() + const { avatars, setAvatar } = useUserDataStore() + const [profileName, setProfileName] = useState(""); + const [formState, setFormState] = useState("EDIT"); + const [profileEdit, setProfileEdit] = useState(-1); + const { isConnectPS } = useGlobalStore() + + const profileCurrent = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList[avatar.profileSelect] || null; + }, [avatarSelected, avatars]); + + const listProfile = useMemo(() => { + if (!avatarSelected) return []; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList || []; + }, [avatarSelected, avatars]); + + + const handleUpdateProfile = () => { + if (!profileName.trim()) return; + if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) { + const newListProfile = [...listProfile] + const newProfile = { + profile_name: profileName, + lightcone: null, + relics: {} as Record + } + newListProfile.push(newProfile) + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 }) + toast.success("Profile created successfully") + } else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) { + const newListProfile = [...listProfile] + newListProfile[profileEdit].profile_name = profileName; + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile }) + toast.success("Profile updated successfully") + } + handleCloseModal("update_profile_modal"); + }; + + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + + const actionMove = (path: string) => { + router.push(`/${path}`) + } + + const handleProfileSelect = (profileId: number) => { + if (!avatarSelected) return; + if (avatars[avatarSelected.ID].profileSelect === profileId) return; + setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId }) + toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`) + } + + const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => { + e.stopPropagation() + if (!avatarSelected || profileId == 0) return; + if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) { + const newListProfile = [...listProfile] + newListProfile.splice(profileId, 1) + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 }) + toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`) + } + + } + + const handleConnectOrSyncPS = async () => { + if (isConnectPS) { + const res = await syncDataToPS() + if (res.success) { + toast.success(transI18n("syncSuccess")) + } else { + toast.error(`${transI18n("syncFailed")}: ${res.message}`) + } + } else { + const res = await connectToPS() + if (res.success) { + toast.success(transI18n("connectedSuccess")) + } else { + toast.error(`${transI18n("connectedFailed")}: ${res.message}`) + } + } + } + + const modalConfigs: ModalConfig[] = [ + { + id: "update_profile_modal", + title: formState === "CREATE" ? transI18n("createNewProfile") : transI18n("editProfile"), + isOpen: isOpenCreateProfile, + onClose: () => { + setIsOpenCreateProfile(false) + handleCloseModal("update_profile_modal") + }, + content: ( +
+
+ + setProfileName(e.target.value)} + /> +
+
+ +
+
+ ) + }, + { + id: "copy_profile_modal", + title: transI18n("copyProfiles").toUpperCase(), + isOpen: isOpenCopy, + onClose: () => { + setIsOpenCopy(false) + handleCloseModal("copy_profile_modal") + }, + content: + }, + { + id: "avatars_modal", + title: transI18n("avatars").toUpperCase(), + isOpen: isOpenAvatars, + onClose: () => { + setIsOpenAvatars(false) + handleCloseModal("avatars_modal") + }, + content: { setIsOpenAvatars(false); handleCloseModal("avatars_modal") }} /> + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]); + + + + return ( +
+
+ +
+
+
+ {avatarSelected && ( +
+ {'damage +

+ {transI18n(avatarSelected.BaseType.toLowerCase())} +

+
{" / "}
+ +
{`(${avatarSelected.ID})`}
+
+ )} +
+ +
+ +
+ {transI18n("profile")}: +
+
+ + {profileCurrent?.profile_name} + + + + +
+ +
    + {listProfile.map((profile, index) => ( +
  • + + {index !== 0 && ( + <> + + + + )} +
  • + ))} + +
  • + + +
  • +
+
+
+ +
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ +
+
+ +
+ + + + + + +
+ + {modalConfigs.map(({ id, title, onClose, content }) => ( + +
+
+ + ✕ + +
+
+

+ {title} +

+
+ {content} +
+
+ ))} + + +
+
+ ); } \ No newline at end of file diff --git a/src/components/avatarBar/index.tsx b/src/components/avatarBar/index.tsx index 550c6c9..6cfb4b3 100644 --- a/src/components/avatarBar/index.tsx +++ b/src/components/avatarBar/index.tsx @@ -1,140 +1,140 @@ -"use client" -import Image from "next/image" -import { useMemo } from "react" -import CharacterCard from "../card/characterCard" -import useLocaleStore from "@/stores/localeStore" -import { useTranslations } from "next-intl" -import useDetailDataStore from "@/stores/detailDataStore" -import useCurrentDataStore from '@/stores/currentDataStore'; -import { calcRarity, getNameChar } from "@/helper" - -export default function AvatarBar({ onClose }: { onClose?: () => void }) { - const { - avatarSearch, - mapAvatarElementActive, - mapAvatarPathActive, - setAvatarSearch, - setAvatarSelected, - setMapAvatarElementActive, - setMapAvatarPathActive, - setSkillIDSelected, - } = useCurrentDataStore() - const { mapAvatar, baseType, damageType } = useDetailDataStore() - const transI18n = useTranslations("DataPage") - const {locale} = useLocaleStore() - - const listAvatar = useMemo(() => { - if (!mapAvatar || !locale || !transI18n) return [] - - let list = Object.values(mapAvatar) - - if (avatarSearch) { - list = list.filter(item => - getNameChar(locale, transI18n, item) - .toLowerCase() - .includes(avatarSearch.toLowerCase()) - ) - } - - const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v) - const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v) - - list = list.filter(item => - (allElementFalse || mapAvatarElementActive[item.DamageType]) && - (allPathFalse || mapAvatarPathActive[item.BaseType]) - ) - - list.sort((a, b) => { - const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) - if (r !== 0) return r - return b.ID - a.ID - }) - - return list - }, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n]) - - - return ( -
-
-
-
-
-
- setAvatarSearch(e.target.value)} - /> -
-
- {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( -
{ - setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] }) - }} - className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg" - style={{ - backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280" - }}> - {key} -
- ))} -
- -
- {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( -
{ - setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] }) - }} - className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer" - style={{ - backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280" - }} - > - - {key} -
- ))} - -
-
- -
-
    - {listAvatar.map((item, index) => ( -
    { - setAvatarSelected(item); - setSkillIDSelected(null) - if (onClose) onClose() - }}> - -
    - ))} -
-
-
- -
-
-
- ) -} +"use client" +import Image from "next/image" +import { useMemo } from "react" +import CharacterCard from "../card/characterCard" +import useLocaleStore from "@/stores/localeStore" +import { useTranslations } from "next-intl" +import useDetailDataStore from "@/stores/detailDataStore" +import useCurrentDataStore from '@/stores/currentDataStore'; +import { calcRarity, getNameChar } from "@/helper" + +export default function AvatarBar({ onClose }: { onClose?: () => void }) { + const { + avatarSearch, + mapAvatarElementActive, + mapAvatarPathActive, + setAvatarSearch, + setAvatarSelected, + setMapAvatarElementActive, + setMapAvatarPathActive, + setSkillIDSelected, + } = useCurrentDataStore() + const { mapAvatar, baseType, damageType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + const {locale} = useLocaleStore() + + const listAvatar = useMemo(() => { + if (!mapAvatar || !locale || !transI18n) return [] + + let list = Object.values(mapAvatar) + + if (avatarSearch) { + list = list.filter(item => + getNameChar(locale, transI18n, item) + .toLowerCase() + .includes(avatarSearch.toLowerCase()) + ) + } + + const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v) + const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v) + + list = list.filter(item => + (allElementFalse || mapAvatarElementActive[item.DamageType]) && + (allPathFalse || mapAvatarPathActive[item.BaseType]) + ) + + list.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return b.ID - a.ID + }) + + return list + }, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n]) + + + return ( +
+
+
+
+
+
+ setAvatarSearch(e.target.value)} + /> +
+
+ {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( +
{ + setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] }) + }} + className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg" + style={{ + backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280" + }}> + {key} +
+ ))} +
+ +
+ {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
{ + setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] }) + }} + className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer" + style={{ + backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280" + }} + > + + {key} +
+ ))} + +
+
+ +
+
    + {listAvatar.map((item, index) => ( +
    { + setAvatarSelected(item); + setSkillIDSelected(null) + if (onClose) onClose() + }}> + +
    + ))} +
+
+
+ +
+
+
+ ) +} diff --git a/src/components/avatarInfo/index.tsx b/src/components/avatarInfo/index.tsx index c6b3571..68e2ddd 100644 --- a/src/components/avatarInfo/index.tsx +++ b/src/components/avatarInfo/index.tsx @@ -1,529 +1,529 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client" - -import useUserDataStore from "@/stores/userDataStore"; -import { useEffect, useMemo } from "react"; -import { motion } from "framer-motion"; -import LightconeBar from '../lightconeBar' -import { calcPromotion, calcRarity, replaceByParam } from '@/helper'; -import { getSkillTree } from '@/helper/getSkillTree'; -import { useTranslations } from 'next-intl'; -import ParseText from '../parseText'; -import useLocaleStore from '@/stores/localeStore'; -import useModelStore from '@/stores/modelStore'; -import Image from 'next/image'; -import useCurrentDataStore from "@/stores/currentDataStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import { getLocaleName } from "@/helper/getName"; -export default function AvatarInfo() { - const { avatarSelected, setResetDataLightcone } = useCurrentDataStore() - const { avatars, setAvatars, setAvatar } = useUserDataStore() - const { isOpenLightcone, setIsOpenLightcone } = useModelStore() - const { mapLightCone, baseType } = useDetailDataStore() - const transI18n = useTranslations("DataPage") - const { locale } = useLocaleStore(); - - const lightcone = useMemo(() => { - if (!avatarSelected) return null; - const avatar = avatars[avatarSelected.ID]; - return avatar?.profileList[avatar.profileSelect]?.lightcone || null; - }, [avatarSelected, avatars]); - - const lightconeDetail = useMemo(() => { - if (!mapLightCone || !lightcone?.item_id) return null; - return mapLightCone?.[lightcone.item_id.toString()] || null; - }, [lightcone, mapLightCone]); - - const handleShow = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - setIsOpenLightcone(true); - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - setIsOpenLightcone(false); - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close(); - } - }; - - // Handle ESC key to close modal - useEffect(() => { - if (!isOpenLightcone) { - handleCloseModal("action_detail_modal"); - return; - } - - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape' && isOpenLightcone) { - handleCloseModal("action_detail_modal"); - } - }; - - window.addEventListener('keydown', handleEscKey); - - return () => window.removeEventListener('keydown', handleEscKey); - }, [isOpenLightcone]); - - return ( -
- {avatarSelected && avatars[avatarSelected?.ID.toString() || ""] && ( -
-
-
-
-
- {/* Header */} -
-

-
- {transI18n("characterSettings")} -

-
- -
- {/* Level Control */} -
-

-
- {transI18n("levelConfiguration")} -

- -
- -
- { - const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); - - setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); - }} - className="input input-bordered w-full pr-16 font-mono" - placeholder={transI18n("placeholderLevel")} - /> -
{ - setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: 80, promotion: calcPromotion(80) } }); - }} - className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> - {transI18n("max")} -
-
-
- { - const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); - setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); - }} - className="range range-info range-sm w-full" - /> -
-
-
- - {/* Energy Control */} -
-

-
- {transI18n("ultimateEnergy")} -

- -
- - { - if (!avatars[avatarSelected?.ID.toString() || ""]?.can_change_sp) return - const newSpValue = Math.min(avatars[avatarSelected?.ID.toString() || ""]?.sp_max, Math.max(0, parseInt(e.target.value) || 0)); - setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], sp_value: newSpValue } }); - }} - className="range range-warning range-sm w-full" - /> -
- 0% - {((avatars[avatarSelected?.ID.toString() || ""]?.sp_value / avatars[avatarSelected?.ID.toString() || ""]?.sp_max) * 100).toFixed(1)}% - 100% -
-
- -
-
-
- - {/* Technique Toggle */} -
-

-
- {transI18n("battleConfiguration")} -

- -
- -
- {transI18n("techniqueNote")} -
-
-
- - {/* Enhancement Selection */} - {avatarSelected?.Enhanced && ( -
-

- - - - {transI18n("enhancement")} -

- -
- - -
- {transI18n("enhancedNote")} -
-
-
- )} -
-
-
- -
-
-
-
-
-
- {/* Header */} -
-

-
- {transI18n("lightconeEquipment")} -

-
- - {lightcone && lightconeDetail ? ( -
-
- {/* Level & Rank Controls */} -
-

-
- {transI18n("lightconeSettings")} -

- -
- {/* Level Input */} -
- -
- { - const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) - const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } - const newAvatar = { ...avatars[avatarSelected.ID] } - newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone - setAvatar(newAvatar) - }} - className="input input-bordered w-full pr-16 font-mono" - placeholder={transI18n("placeholderLevel")} - /> -
{ - const newLightcone = { ...lightcone, level: 80, promotion: calcPromotion(80) } - const newAvatar = { ...avatars[avatarSelected.ID] } - newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone - setAvatar(newAvatar) - }} - className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> - {transI18n("max")} -
-
-
- { - const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) - const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } - const newAvatar = { ...avatars[avatarSelected.ID] } - newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone - setAvatar(newAvatar) - }} - className="range range-primary range-sm" - /> -
-
- - {/* Rank Selection */} -
- {/* Header */} -
- {transI18n("superimpositionRank")} - S{lightcone.rank} -
- - {/* Rank Buttons */} -
-
- {[1, 2, 3, 4, 5].map((r) => ( - - ))} -
-
- - {/* Help Text */} -
- {transI18n("ranksNote")} -
-
-
-
- - {/* Action Buttons */} -
- - -
-
- - {/* Lightcone Image */} -
-
- Lightcone -
-
- - {/* Lightcone Info & Controls */} -
- {/* Basic Info */} - {lightconeDetail && ( -
-
-

- -

-
- {transI18n(lightconeDetail.BaseType.toLowerCase())} -
-
- {calcRarity(lightconeDetail.Rarity) + "⭐"} -
-
- {"id: " + lightcone.item_id} -
-
- -
-
-
-
- )} - - -
-
- ) : ( - /* No Lightcone Equipped State */ -
-
handleShow("action_detail_modal")} - className="w-24 h-24 mx-auto mb-6 bg-base-300 rounded-full flex items-center justify-center cursor-pointer" - > - - - -
-

{transI18n("noLightconeEquipped")}

-

{transI18n("equipLightconeNote")}

- -
- )} -
-
-
-
-
- )} - - -
-
- handleCloseModal("action_detail_modal")} - > - ✕ - -
- -
- -
-
- - ); -} +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" + +import useUserDataStore from "@/stores/userDataStore"; +import { useEffect, useMemo } from "react"; +import { motion } from "framer-motion"; +import LightconeBar from '../lightconeBar' +import { calcPromotion, calcRarity, replaceByParam } from '@/helper'; +import { getSkillTree } from '@/helper/getSkillTree'; +import { useTranslations } from 'next-intl'; +import ParseText from '../parseText'; +import useLocaleStore from '@/stores/localeStore'; +import useModelStore from '@/stores/modelStore'; +import Image from 'next/image'; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { getLocaleName } from "@/helper/getName"; +export default function AvatarInfo() { + const { avatarSelected, setResetDataLightcone } = useCurrentDataStore() + const { avatars, setAvatars, setAvatar } = useUserDataStore() + const { isOpenLightcone, setIsOpenLightcone } = useModelStore() + const { mapLightCone, baseType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore(); + + const lightcone = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList[avatar.profileSelect]?.lightcone || null; + }, [avatarSelected, avatars]); + + const lightconeDetail = useMemo(() => { + if (!mapLightCone || !lightcone?.item_id) return null; + return mapLightCone?.[lightcone.item_id.toString()] || null; + }, [lightcone, mapLightCone]); + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + setIsOpenLightcone(true); + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + setIsOpenLightcone(false); + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + // Handle ESC key to close modal + useEffect(() => { + if (!isOpenLightcone) { + handleCloseModal("action_detail_modal"); + return; + } + + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isOpenLightcone) { + handleCloseModal("action_detail_modal"); + } + }; + + window.addEventListener('keydown', handleEscKey); + + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenLightcone]); + + return ( +
+ {avatarSelected && avatars[avatarSelected?.ID.toString() || ""] && ( +
+
+
+
+
+ {/* Header */} +
+

+
+ {transI18n("characterSettings")} +

+
+ +
+ {/* Level Control */} +
+

+
+ {transI18n("levelConfiguration")} +

+ +
+ +
+ { + const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); + + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); + }} + className="input input-bordered w-full pr-16 font-mono" + placeholder={transI18n("placeholderLevel")} + /> +
{ + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: 80, promotion: calcPromotion(80) } }); + }} + className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> + {transI18n("max")} +
+
+
+ { + const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); + }} + className="range range-info range-sm w-full" + /> +
+
+
+ + {/* Energy Control */} +
+

+
+ {transI18n("ultimateEnergy")} +

+ +
+ + { + if (!avatars[avatarSelected?.ID.toString() || ""]?.can_change_sp) return + const newSpValue = Math.min(avatars[avatarSelected?.ID.toString() || ""]?.sp_max, Math.max(0, parseInt(e.target.value) || 0)); + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], sp_value: newSpValue } }); + }} + className="range range-warning range-sm w-full" + /> +
+ 0% + {((avatars[avatarSelected?.ID.toString() || ""]?.sp_value / avatars[avatarSelected?.ID.toString() || ""]?.sp_max) * 100).toFixed(1)}% + 100% +
+
+ +
+
+
+ + {/* Technique Toggle */} +
+

+
+ {transI18n("battleConfiguration")} +

+ +
+ +
+ {transI18n("techniqueNote")} +
+
+
+ + {/* Enhancement Selection */} + {avatarSelected?.Enhanced && ( +
+

+ + + + {transI18n("enhancement")} +

+ +
+ + +
+ {transI18n("enhancedNote")} +
+
+
+ )} +
+
+
+ +
+
+
+
+
+
+ {/* Header */} +
+

+
+ {transI18n("lightconeEquipment")} +

+
+ + {lightcone && lightconeDetail ? ( +
+
+ {/* Level & Rank Controls */} +
+

+
+ {transI18n("lightconeSettings")} +

+ +
+ {/* Level Input */} +
+ +
+ { + const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) + const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="input input-bordered w-full pr-16 font-mono" + placeholder={transI18n("placeholderLevel")} + /> +
{ + const newLightcone = { ...lightcone, level: 80, promotion: calcPromotion(80) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> + {transI18n("max")} +
+
+
+ { + const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) + const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="range range-primary range-sm" + /> +
+
+ + {/* Rank Selection */} +
+ {/* Header */} +
+ {transI18n("superimpositionRank")} + S{lightcone.rank} +
+ + {/* Rank Buttons */} +
+
+ {[1, 2, 3, 4, 5].map((r) => ( + + ))} +
+
+ + {/* Help Text */} +
+ {transI18n("ranksNote")} +
+
+
+
+ + {/* Action Buttons */} +
+ + +
+
+ + {/* Lightcone Image */} +
+
+ Lightcone +
+
+ + {/* Lightcone Info & Controls */} +
+ {/* Basic Info */} + {lightconeDetail && ( +
+
+

+ +

+
+ {transI18n(lightconeDetail.BaseType.toLowerCase())} +
+
+ {calcRarity(lightconeDetail.Rarity) + "⭐"} +
+
+ {"id: " + lightcone.item_id} +
+
+ +
+
+
+
+ )} + + +
+
+ ) : ( + /* No Lightcone Equipped State */ +
+
handleShow("action_detail_modal")} + className="w-24 h-24 mx-auto mb-6 bg-base-300 rounded-full flex items-center justify-center cursor-pointer" + > + + + +
+

{transI18n("noLightconeEquipped")}

+

{transI18n("equipLightconeNote")}

+ +
+ )} +
+
+
+
+
+ )} + + +
+
+ handleCloseModal("action_detail_modal")} + > + ✕ + +
+ +
+ +
+
+ + ); +} diff --git a/src/components/card/characterCard.tsx b/src/components/card/characterCard.tsx index bfb816c..5b3b6ed 100644 --- a/src/components/card/characterCard.tsx +++ b/src/components/card/characterCard.tsx @@ -1,83 +1,83 @@ -"use client"; -import { getNameChar } from '@/helper'; -import useLocaleStore from '@/stores/localeStore'; -import { AvatarDetail } from '@/types'; -import ParseText from '../parseText'; -import Image from 'next/image'; -import { useTranslations } from 'next-intl'; -import useDetailDataStore from '@/stores/detailDataStore'; - -interface CharacterCardProps { - data: AvatarDetail -} - -export default function CharacterCard({ data }: CharacterCardProps) { - const { locale } = useLocaleStore(); - const transI18n = useTranslations("DataPage"); - const { baseType, damageType } = useDetailDataStore() - - return ( -
  • -
    - -
    - ALT - {data.DamageType.toLowerCase()} - {data.BaseType.toLowerCase()} -
    -
    - - -
  • - - ); -} +"use client"; +import { getNameChar } from '@/helper'; +import useLocaleStore from '@/stores/localeStore'; +import { AvatarDetail } from '@/types'; +import ParseText from '../parseText'; +import Image from 'next/image'; +import { useTranslations } from 'next-intl'; +import useDetailDataStore from '@/stores/detailDataStore'; + +interface CharacterCardProps { + data: AvatarDetail +} + +export default function CharacterCard({ data }: CharacterCardProps) { + const { locale } = useLocaleStore(); + const transI18n = useTranslations("DataPage"); + const { baseType, damageType } = useDetailDataStore() + + return ( +
  • +
    + +
    + ALT + {data.DamageType.toLowerCase()} + {data.BaseType.toLowerCase()} +
    +
    + + +
  • + + ); +} diff --git a/src/components/card/characterInfoCard.tsx b/src/components/card/characterInfoCard.tsx index 24f0d5b..fd20b9d 100644 --- a/src/components/card/characterInfoCard.tsx +++ b/src/components/card/characterInfoCard.tsx @@ -1,125 +1,125 @@ -"use client"; - -import React from 'react'; -import { CharacterInfoCardType } from '@/types'; -import useLocaleStore from '@/stores/localeStore'; -import Image from 'next/image'; -import ParseText from '../parseText'; -import useDetailDataStore from '@/stores/detailDataStore'; -import { getLocaleName } from '@/helper/getName'; - -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 { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore(); - const { locale } = useLocaleStore(); - - return ( -
    onCharacterToggle(character)} - > - - {/* Character Portrait */} -
    -
    - {getLocaleName(locale, - {mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()} - {mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()} -
    -
    - - {/* Character Name and Level */} -
    -
    - -
    Lv.{character.level} E{character.rank}
    -
    -
    - {character.relics.length > 0 && ( -
    - {character.relics.map((relic, index) => ( -
    -
    - Relic -
    -
    - +{relic.level} -
    -
    - ))} -
    - )} - - {/* Light Cone */} - {character.lightcone.item_id && ( -
    -
    - {getLocaleName(locale, - -
    -
    -
    -
    - -
    -
    Lv.{character.lightcone.level} S{character.lightcone.rank}
    -
    -
    -
    - )} -
    - ); +"use client"; + +import React from 'react'; +import { CharacterInfoCardType } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import Image from 'next/image'; +import ParseText from '../parseText'; +import useDetailDataStore from '@/stores/detailDataStore'; +import { getLocaleName } from '@/helper/getName'; + +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 { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore(); + const { locale } = useLocaleStore(); + + return ( +
    onCharacterToggle(character)} + > + + {/* Character Portrait */} +
    +
    + {getLocaleName(locale, + {mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()} + {mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()} +
    +
    + + {/* Character Name and Level */} +
    +
    + +
    Lv.{character.level} E{character.rank}
    +
    +
    + {character.relics.length > 0 && ( +
    + {character.relics.map((relic, index) => ( +
    +
    + Relic +
    +
    + +{relic.level} +
    +
    + ))} +
    + )} + + {/* Light Cone */} + {character.lightcone.item_id && ( +
    +
    + {getLocaleName(locale, + +
    +
    +
    +
    + +
    +
    Lv.{character.lightcone.level} S{character.lightcone.rank}
    +
    +
    +
    + )} +
    + ); }; \ No newline at end of file diff --git a/src/components/card/lightconeCard.tsx b/src/components/card/lightconeCard.tsx index e313a1f..a0d153c 100644 --- a/src/components/card/lightconeCard.tsx +++ b/src/components/card/lightconeCard.tsx @@ -1,53 +1,53 @@ -"use client"; - -import { getLocaleName } from '@/helper'; -import useLocaleStore from '@/stores/localeStore'; -import ParseText from '../parseText'; -import Image from 'next/image'; -import { LightConeDetail } from '@/types'; - -interface LightconeCardProps { - data: LightConeDetail -} - -export default function LightconeCard({ data }: LightconeCardProps) { - - const { locale } = useLocaleStore(); - const text = getLocaleName(locale, data.Name) - return ( -
  • -
    - -
    - ALT -
    -
    - - -
  • - - ); - -} +"use client"; + +import { getLocaleName } from '@/helper'; +import useLocaleStore from '@/stores/localeStore'; +import ParseText from '../parseText'; +import Image from 'next/image'; +import { LightConeDetail } from '@/types'; + +interface LightconeCardProps { + data: LightConeDetail +} + +export default function LightconeCard({ data }: LightconeCardProps) { + + const { locale } = useLocaleStore(); + const text = getLocaleName(locale, data.Name) + return ( +
  • +
    + +
    + ALT +
    +
    + + +
  • + + ); + +} diff --git a/src/components/card/profileCard.tsx b/src/components/card/profileCard.tsx index 64302ac..1a1065b 100644 --- a/src/components/card/profileCard.tsx +++ b/src/components/card/profileCard.tsx @@ -1,79 +1,79 @@ -"use client"; - -import React from 'react'; -import { AvatarProfileCardType } from '@/types'; -import useLocaleStore from '@/stores/localeStore'; -import Image from 'next/image'; -import ParseText from '../parseText'; -import useDetailDataStore from '@/stores/detailDataStore'; -import { getLocaleName } from '@/helper/getName'; - - -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 { mapLightCone } = useDetailDataStore(); - const { locale } = useLocaleStore(); - - return ( -
    onProfileToggle(profile)} - > - {/* Light Cone */} - {profile.lightcone && ( -
    -
    - {getLocaleName(locale, - -
    -
    -
    -
    - -
    -
    Lv.{profile.lightcone.level} S{profile.lightcone.rank}
    -
    -
    -
    - )} - - {Object.keys(profile.relics).length > 0 && ( -
    - {Object.values(profile.relics).map((relic, index) => ( -
    -
    - Relic -
    -
    - +{relic.level} -
    -
    - ))} -
    - )} -
    - ); +"use client"; + +import React from 'react'; +import { AvatarProfileCardType } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import Image from 'next/image'; +import ParseText from '../parseText'; +import useDetailDataStore from '@/stores/detailDataStore'; +import { getLocaleName } from '@/helper/getName'; + + +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 { mapLightCone } = useDetailDataStore(); + const { locale } = useLocaleStore(); + + return ( +
    onProfileToggle(profile)} + > + {/* Light Cone */} + {profile.lightcone && ( +
    +
    + {getLocaleName(locale, + +
    +
    +
    +
    + +
    +
    Lv.{profile.lightcone.level} S{profile.lightcone.rank}
    +
    +
    +
    + )} + + {Object.keys(profile.relics).length > 0 && ( +
    + {Object.values(profile.relics).map((relic, index) => ( +
    +
    + Relic +
    +
    + +{relic.level} +
    +
    + ))} +
    + )} +
    + ); }; \ No newline at end of file diff --git a/src/components/card/relicCard.tsx b/src/components/card/relicCard.tsx index 466a31d..674dd5b 100644 --- a/src/components/card/relicCard.tsx +++ b/src/components/card/relicCard.tsx @@ -1,196 +1,196 @@ -"use client"; - -import useRelicMakerStore from "@/stores/relicMakerStore"; -import useUserDataStore from "@/stores/userDataStore"; -import Image from "next/image"; -import { useMemo } from "react"; - -interface RelicCardProps { - slot: string - avatarId: string -} -const getRarityColor = (rarity: string) => { - 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 '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 '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'; - } -}; - -const getRarityName = (slot: string) => { - switch (slot) { - case '1': return ( -
    - Head -

    Head

    -
    - ); - case '2': return ( -
    - Hand -

    Hands

    -
    - ); - case '3': return ( -
    - Body -

    Body

    -
    - ); - case '4': return ( -
    - Foot -

    Feet

    -
    - ); - case '5': return ( -
    - Neck -

    Planar sphere

    -
    - ); - case '6': return ( -
    - Object -

    Link rope

    -
    - ); - default: return ''; - } -}; -export default function RelicCard({ slot, avatarId }: RelicCardProps) { - const { avatars } = useUserDataStore() - const { selectedRelicSlot } = useRelicMakerStore() - - const relicDetail = useMemo(() => { - const avatar = avatars[avatarId]; - if (avatar) { - if (avatar.profileList[avatar.profileSelect].relics[slot]) { - return avatar.profileList[avatar.profileSelect].relics[slot]; - } - return null; - } - return null; - }, [avatars, avatarId, slot]); - - return ( -
    - {relicDetail ? ( -
    -
    - - Relic - - - {/* Level Badge */} -
    - +{relicDetail.level} -
    -
    - -
    -
    {getRarityName(slot)}
    -
    -
    - ) : ( -
    -
    - - - - - - - {/* Level Badge */} -
    - +{0} -
    -
    - -
    -
    {getRarityName(slot)}
    -
    -
    - )} - -
    - ) +"use client"; + +import useRelicMakerStore from "@/stores/relicMakerStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { useMemo } from "react"; + +interface RelicCardProps { + slot: string + avatarId: string +} +const getRarityColor = (rarity: string) => { + 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 '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 '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'; + } +}; + +const getRarityName = (slot: string) => { + switch (slot) { + case '1': return ( +
    + Head +

    Head

    +
    + ); + case '2': return ( +
    + Hand +

    Hands

    +
    + ); + case '3': return ( +
    + Body +

    Body

    +
    + ); + case '4': return ( +
    + Foot +

    Feet

    +
    + ); + case '5': return ( +
    + Neck +

    Planar sphere

    +
    + ); + case '6': return ( +
    + Object +

    Link rope

    +
    + ); + default: return ''; + } +}; +export default function RelicCard({ slot, avatarId }: RelicCardProps) { + const { avatars } = useUserDataStore() + const { selectedRelicSlot } = useRelicMakerStore() + + const relicDetail = useMemo(() => { + const avatar = avatars[avatarId]; + if (avatar) { + if (avatar.profileList[avatar.profileSelect].relics[slot]) { + return avatar.profileList[avatar.profileSelect].relics[slot]; + } + return null; + } + return null; + }, [avatars, avatarId, slot]); + + return ( +
    + {relicDetail ? ( +
    +
    + + Relic + + + {/* Level Badge */} +
    + +{relicDetail.level} +
    +
    + +
    +
    {getRarityName(slot)}
    +
    +
    + ) : ( +
    +
    + + + + + + + {/* Level Badge */} +
    + +{0} +
    +
    + +
    +
    {getRarityName(slot)}
    +
    +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/card/simpleCharacterCard.tsx b/src/components/card/simpleCharacterCard.tsx index 8b61921..963697c 100644 --- a/src/components/card/simpleCharacterCard.tsx +++ b/src/components/card/simpleCharacterCard.tsx @@ -1,69 +1,69 @@ -"use client"; -import Image from 'next/image'; -import { AvatarDetail } from '@/types'; -import useDetailDataStore from '@/stores/detailDataStore'; - - -interface SimpleAvatarCardProps { - data: AvatarDetail; - isSelected?: boolean; - onClick?: () => void; - showRemoveHover?: boolean; -} - -export const SimpleAvatarCard = ({ data, isSelected, onClick, showRemoveHover }: SimpleAvatarCardProps) => { -const { baseType, damageType } = useDetailDataStore() - - return ( -
    - Avatar -
    - Element -
    -
    - Path -
    - - {showRemoveHover && ( -
    - - - -
    - )} -
    - ); +"use client"; +import Image from 'next/image'; +import { AvatarDetail } from '@/types'; +import useDetailDataStore from '@/stores/detailDataStore'; + + +interface SimpleAvatarCardProps { + data: AvatarDetail; + isSelected?: boolean; + onClick?: () => void; + showRemoveHover?: boolean; +} + +export const SimpleAvatarCard = ({ data, isSelected, onClick, showRemoveHover }: SimpleAvatarCardProps) => { +const { baseType, damageType } = useDetailDataStore() + + return ( +
    + Avatar +
    + Element +
    +
    + Path +
    + + {showRemoveHover && ( +
    + + + +
    + )} +
    + ); }; \ No newline at end of file diff --git a/src/components/changelog/index.tsx b/src/components/changelog/index.tsx index 3d75c08..f6173e9 100644 --- a/src/components/changelog/index.tsx +++ b/src/components/changelog/index.tsx @@ -1,91 +1,91 @@ - -import useLocaleStore from '@/stores/localeStore'; -import { Check, Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react'; - -export default function ChangelogBar() { - const { changelog } = useLocaleStore() - - const getIcon = (type: string) => { - switch (type) { - case 'feature': - return ; - case 'fix': - return ; - case 'improvement': - return ; - default: - return ; - } - }; - - const getBadgeClass = (type: string) => { - switch (type) { - case 'feature': - return 'badge-success'; - case 'fix': - return 'badge-error'; - case 'improvement': - return 'badge-warning'; - default: - return 'badge-info'; - } - }; - - const getTypeLabel = (type: string) => { - switch (type) { - case 'feature': - return 'Feature'; - case 'fix': - return 'Fix'; - case 'improvement': - return 'Improvement'; - default: - return 'Update'; - } - }; - - return ( -
    - {/* Alert */} - {/*
    - -
    */} - - {/* Timeline */} -
    - {changelog.map((change, index) => ( -
    -
    - {/* Version Header */} -
    -
    - {getIcon(change.type)} - {getTypeLabel(change.type)} -
    -

    - Version {change.version} -

    -
    - - {change.date} -
    -
    - - {/* Changes List */} -
      - {change.items.map((item, itemIndex) => ( -
    • -
      - -
      - {item} -
    • - ))} -
    -
    -
    - ))} -
    -
    - ); + +import useLocaleStore from '@/stores/localeStore'; +import { Check, Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react'; + +export default function ChangelogBar() { + const { changelog } = useLocaleStore() + + const getIcon = (type: string) => { + switch (type) { + case 'feature': + return ; + case 'fix': + return ; + case 'improvement': + return ; + default: + return ; + } + }; + + const getBadgeClass = (type: string) => { + switch (type) { + case 'feature': + return 'badge-success'; + case 'fix': + return 'badge-error'; + case 'improvement': + return 'badge-warning'; + default: + return 'badge-info'; + } + }; + + const getTypeLabel = (type: string) => { + switch (type) { + case 'feature': + return 'Feature'; + case 'fix': + return 'Fix'; + case 'improvement': + return 'Improvement'; + default: + return 'Update'; + } + }; + + return ( +
    + {/* Alert */} + {/*
    + +
    */} + + {/* Timeline */} +
    + {changelog.map((change, index) => ( +
    +
    + {/* Version Header */} +
    +
    + {getIcon(change.type)} + {getTypeLabel(change.type)} +
    +

    + Version {change.version} +

    +
    + + {change.date} +
    +
    + + {/* Changes List */} +
      + {change.items.map((item, itemIndex) => ( +
    • +
      + +
      + {item} +
    • + ))} +
    +
    +
    + ))} +
    +
    + ); } \ No newline at end of file diff --git a/src/components/clientDataFetcher/index.tsx b/src/components/clientDataFetcher/index.tsx index c1fa8c6..eb02e5b 100644 --- a/src/components/clientDataFetcher/index.tsx +++ b/src/components/clientDataFetcher/index.tsx @@ -1,56 +1,56 @@ -"use client" - -import { - useFetchASGroupData, - useFetchAvatarData, - useFetchChangelog, - useFetchConfigData, - useFetchLightconeData, - useFetchMOCGroupData, - useFetchMonsterData, - useFetchPeakGroupData, - useFetchPFGroupData, - useFetchRelicSetData -} from "@/lib/hooks" - -export default function ClientDataFetcher({ - children -}: { - children: React.ReactNode -}) { - const q1 = useFetchConfigData() - const q2 = useFetchAvatarData() - const q3 = useFetchLightconeData() - const q4 = useFetchRelicSetData() - const q5 = useFetchMonsterData() - const q6 = useFetchPFGroupData() - const q7 = useFetchMOCGroupData() - const q8 = useFetchASGroupData() - const q9 = useFetchPeakGroupData() - const q10 = useFetchChangelog() - - const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10] - - const loading = queries.some(q => q.isLoading) - - const progress = - (queries.filter(q => q.isSuccess).length / queries.length) * 100 - - if (loading) { - return ( -
    -
    Loading data...
    - - - -
    {Math.floor(progress)}%
    -
    - ) - } - - return <>{children} +"use client" + +import { + useFetchASGroupData, + useFetchAvatarData, + useFetchChangelog, + useFetchConfigData, + useFetchLightconeData, + useFetchMOCGroupData, + useFetchMonsterData, + useFetchPeakGroupData, + useFetchPFGroupData, + useFetchRelicSetData +} from "@/lib/hooks" + +export default function ClientDataFetcher({ + children +}: { + children: React.ReactNode +}) { + const q1 = useFetchConfigData() + const q2 = useFetchAvatarData() + const q3 = useFetchLightconeData() + const q4 = useFetchRelicSetData() + const q5 = useFetchMonsterData() + const q6 = useFetchPFGroupData() + const q7 = useFetchMOCGroupData() + const q8 = useFetchASGroupData() + const q9 = useFetchPeakGroupData() + const q10 = useFetchChangelog() + + const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10] + + const loading = queries.some(q => q.isLoading) + + const progress = + (queries.filter(q => q.isSuccess).length / queries.length) * 100 + + if (loading) { + return ( +
    +
    Loading data...
    + + + +
    {Math.floor(progress)}%
    +
    + ) + } + + return <>{children} } \ No newline at end of file diff --git a/src/components/connectBar/index.tsx b/src/components/connectBar/index.tsx index f55ca86..9e4a1fa 100644 --- a/src/components/connectBar/index.tsx +++ b/src/components/connectBar/index.tsx @@ -1,169 +1,169 @@ -"use client" - -import { connectToPS, syncDataToPS } from "@/helper" -import useConnectStore from "@/stores/connectStore" -import useGlobalStore from "@/stores/globalStore" -import { PSConnectType } from "@/types" -import { useTranslations } from "next-intl" -import { useState } from "react" - -export default function ConnectBar() { - const transI18n = useTranslations("DataPage") - const [message, setMessage] = useState({ text: '', type: '' }); - const { - connectionType, - privateType, - serverUrl, - username, - password, - setConnectionType, - setPrivateType, - setServerUrl, - setUsername, - setPassword - } = useConnectStore() - const { isConnectPS, setIsConnectPS } = useGlobalStore() - - return ( -
    -
    - - -
    - - {connectionType === PSConnectType.Other && ( -
    -
    - - setServerUrl(e.target.value)} - /> -
    -
    - - -
    - -
    - - setUsername(e.target.value)} - /> -
    -
    - - setPassword(e.target.value)} - /> -
    -
    - )} - - {message.text && ( -
    - {message.text} -
    - )} - -
    -
    - {transI18n("status")}: - - {isConnectPS ? transI18n("connected") : transI18n("unconnected")} - -
    - - {/* Buttons */} -
    - - - {isConnectPS && ( - - )} -
    -
    - -
    - ) +"use client" + +import { connectToPS, syncDataToPS } from "@/helper" +import useConnectStore from "@/stores/connectStore" +import useGlobalStore from "@/stores/globalStore" +import { PSConnectType } from "@/types" +import { useTranslations } from "next-intl" +import { useState } from "react" + +export default function ConnectBar() { + const transI18n = useTranslations("DataPage") + const [message, setMessage] = useState({ text: '', type: '' }); + const { + connectionType, + privateType, + serverUrl, + username, + password, + setConnectionType, + setPrivateType, + setServerUrl, + setUsername, + setPassword + } = useConnectStore() + const { isConnectPS, setIsConnectPS } = useGlobalStore() + + return ( +
    +
    + + +
    + + {connectionType === PSConnectType.Other && ( +
    +
    + + setServerUrl(e.target.value)} + /> +
    +
    + + +
    + +
    + + setUsername(e.target.value)} + /> +
    +
    + + setPassword(e.target.value)} + /> +
    +
    + )} + + {message.text && ( +
    + {message.text} +
    + )} + +
    +
    + {transI18n("status")}: + + {isConnectPS ? transI18n("connected") : transI18n("unconnected")} + +
    + + {/* Buttons */} +
    + + + {isConnectPS && ( + + )} +
    +
    + +
    + ) } \ No newline at end of file diff --git a/src/components/eidolonsInfo/index.tsx b/src/components/eidolonsInfo/index.tsx index 08f383b..1fa2a0e 100644 --- a/src/components/eidolonsInfo/index.tsx +++ b/src/components/eidolonsInfo/index.tsx @@ -1,85 +1,85 @@ -"use client" -import { replaceByParam, getLocaleName } from '@/helper'; -import Image from "next/image"; -import ParseText from "../parseText"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore"; -import { useMemo } from "react"; -import { useTranslations } from "next-intl"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import ExtraEffectList from '../extraInfo'; - - -export default function EidolonsInfo() { - const { avatarSelected } = useCurrentDataStore() - const { locale } = useLocaleStore() - const transI18n = useTranslations("DataPage") - const { setAvatars, avatars } = useUserDataStore() - - const charRank = useMemo(() => { - if (!avatarSelected) return null; - const avatar = avatars[avatarSelected.ID]; - if (avatar?.enhanced != "") { - return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks - } - return avatarSelected?.Ranks - }, [avatarSelected, avatars]); - - return ( -
    -

    -
    - {transI18n("eidolons")} -

    -
    - {charRank && avatars[avatarSelected?.ID || ""] && ( -
    - {Object.entries(charRank || {}).map(([key, rank]) => ( -
    -
    { - let newRank = Number(rank.Rank) - if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(rank.Rank)) { - newRank = Number(rank.Rank) - 1 - } - setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } }) - }} - > - {`Rank - -
    - {rank.Rank}. - -
    -
    - -
    -
    - -
    -
    - ))} - -
    - )} -
    -
    - ); -} +"use client" +import { replaceByParam, getLocaleName } from '@/helper'; +import Image from "next/image"; +import ParseText from "../parseText"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { useMemo } from "react"; +import { useTranslations } from "next-intl"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import ExtraEffectList from '../extraInfo'; + + +export default function EidolonsInfo() { + const { avatarSelected } = useCurrentDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const { setAvatars, avatars } = useUserDataStore() + + const charRank = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + if (avatar?.enhanced != "") { + return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks + } + return avatarSelected?.Ranks + }, [avatarSelected, avatars]); + + return ( +
    +

    +
    + {transI18n("eidolons")} +

    +
    + {charRank && avatars[avatarSelected?.ID || ""] && ( +
    + {Object.entries(charRank || {}).map(([key, rank]) => ( +
    +
    { + let newRank = Number(rank.Rank) + if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(rank.Rank)) { + newRank = Number(rank.Rank) - 1 + } + setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } }) + }} + > + {`Rank + +
    + {rank.Rank}. + +
    +
    + +
    +
    + +
    +
    + ))} + +
    + )} +
    +
    + ); +} diff --git a/src/components/extraInfo/index.tsx b/src/components/extraInfo/index.tsx index d1f16a9..85d98a3 100644 --- a/src/components/extraInfo/index.tsx +++ b/src/components/extraInfo/index.tsx @@ -1,99 +1,99 @@ -"use client" - -import { useState } from "react" -import { replaceByParam, getLocaleName } from "@/helper" -import { ExtraEffect } from "@/types" -import { useTranslations } from "next-intl" - -type Props = { - extras: Record | undefined - locale: string -} - -export default function ExtraEffectList({ extras, locale }: Props) { - const [openList, setOpenList] = useState(false) - const [openId, setOpenId] = useState(null) - const transI18n = useTranslations("DataPage") - if (!extras || Object.keys(extras).length === 0) return null - - return ( -
    -
    setOpenList(!openList)} - > - - {transI18n("listExtraEffect")} ({Object.keys(extras).length}) - - - - ▶ - -
    - -
    -
    - {Object.values(extras).map((extra) => { - const isOpen = openId === extra.ID - - return ( -
    -
    - setOpenId(isOpen ? null : extra.ID) - } - > -
    - - {transI18n("extra")} - - - - {getLocaleName(locale, extra.Name)} - -
    - - - ▶ - -
    - -
    -
    -
    -
    - ) - })} -
    -
    -
    - ) +"use client" + +import { useState } from "react" +import { replaceByParam, getLocaleName } from "@/helper" +import { ExtraEffect } from "@/types" +import { useTranslations } from "next-intl" + +type Props = { + extras: Record | undefined + locale: string +} + +export default function ExtraEffectList({ extras, locale }: Props) { + const [openList, setOpenList] = useState(false) + const [openId, setOpenId] = useState(null) + const transI18n = useTranslations("DataPage") + if (!extras || Object.keys(extras).length === 0) return null + + return ( +
    +
    setOpenList(!openList)} + > + + {transI18n("listExtraEffect")} ({Object.keys(extras).length}) + + + + ▶ + +
    + +
    +
    + {Object.values(extras).map((extra) => { + const isOpen = openId === extra.ID + + return ( +
    +
    + setOpenId(isOpen ? null : extra.ID) + } + > +
    + + {transI18n("extra")} + + + + {getLocaleName(locale, extra.Name)} + +
    + + + ▶ + +
    + +
    +
    +
    +
    + ) + })} +
    +
    +
    + ) } \ No newline at end of file diff --git a/src/components/extraSettingBar/index.tsx b/src/components/extraSettingBar/index.tsx index d15b40c..a24a642 100644 --- a/src/components/extraSettingBar/index.tsx +++ b/src/components/extraSettingBar/index.tsx @@ -1,584 +1,584 @@ -'use client' -import { motion } from "framer-motion" -import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info, RouteIcon, Search, CupSoda, UsersIcon } from "lucide-react" -import useGlobalStore from '@/stores/globalStore' -import { useTranslations } from "next-intl" -import { getLocaleName, getNameChar } from "@/helper" -import useLocaleStore from "@/stores/localeStore" -import SelectCustomImage from "../select/customSelectImage" -import { useMemo, useState } from "react" -import useDetailDataStore from "@/stores/detailDataStore" -import Editor from "react-simple-code-editor" -import Prism from "prismjs" -import "prismjs/components/prism-lua" -import "prismjs/themes/prism-tomorrow.css" -import { SimpleAvatarCard } from "../card/simpleCharacterCard" - -export default function ExtraSettingBar() { - const { extraData, setExtraData, isEnableChangePath, setIsEnableChangePath, isEnableLua, setIsEnableLua } = useGlobalStore() - const transI18n = useTranslations("DataPage") - const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore() - const { locale } = useLocaleStore() - const [showSearchStage, setShowSearchStage] = useState(false) - const [showSearchLineup, setShowSearchLineup] = useState(false) - const [isChildClick, setIsChildClick] = useState(false) - const [stageSearchTerm, setStageSearchTerm] = useState("") - const [stagePage, setStagePage] = useState(1) - - const pageSize = 30 - const stageList = useMemo(() => Object.values(stage), [stage]) - - const term = stageSearchTerm.toLowerCase() - - const filteredStages = useMemo(() => stageList.filter((s) => - getLocaleName(locale, s.Name).toLowerCase().includes(term) || - String(s.ID).toLowerCase().includes(term) - ), [stageList, term, locale]) - - const paginatedStages = useMemo(() => filteredStages.slice( - (stagePage - 1) * pageSize, - stagePage * pageSize - ), [filteredStages, stagePage, pageSize]) - - const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) - - const onChangeSearch = (v: string) => { - setStageSearchTerm(v) - setStagePage(1) - } - - return ( -
    - {extraData?.theory_craft && ( -
    -

    - {transI18n("theoryCraft")} -
    - -
    -

    - - - - - - {extraData?.theory_craft?.mode && ( - <> - - - - - - - - -
    - -
    - {(extraData?.theory_craft?.custom_lineup || []).length > 0 && ( - (extraData?.theory_craft?.custom_lineup || []).map((avatarId) => { - const avatarData = mapAvatar[avatarId]; - if (!avatarData) return null; - return ( - { - const newLineup = (extraData?.theory_craft?.custom_lineup || []).filter(id => id !== avatarId); - setExtraData({ - ...extraData, - theory_craft: { - stage_id: extraData?.theory_craft?.stage_id ?? 30118121, - cycle_count: extraData?.theory_craft?.cycle_count ?? 1, - mode: extraData?.theory_craft?.mode ?? false, - hp: extraData?.theory_craft?.hp ?? {}, - custom_lineup: newLineup - } - }); - }} - /> - ); - }) - )} - - -
    - - {showSearchLineup && ( -
    e.stopPropagation()} - className="mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-2xl" - > -
    - {transI18n("selectedCharacters")} - -
    - -
    - {Object.values(mapAvatar).map((avatar) => { - const currentLineup = extraData?.theory_craft?.custom_lineup || []; - const isSelected = currentLineup.includes(avatar.ID.toString()); - - return ( - { - const idStr = avatar.ID.toString(); - let newLineup = [...currentLineup]; - - if (isSelected) { - newLineup = newLineup.filter(id => id !== idStr); - } else { - newLineup.push(idStr); - } - - setExtraData({ - ...extraData, - theory_craft: { - stage_id: extraData?.theory_craft?.stage_id ?? 30118121, - cycle_count: extraData?.theory_craft?.cycle_count ?? 1, - mode: extraData?.theory_craft?.mode ?? false, - hp: extraData?.theory_craft?.hp ?? {}, - custom_lineup: newLineup - } - }); - }} - /> - ); - })} -
    -
    - )} -
    -
    - - )} -
    - )} - - {/*MULTIPATH CHAR */} -
    -

    - {transI18n("multipathCharacter")} -
    - -
    - setIsEnableChangePath(e.target.checked)} - /> -

    - {isEnableChangePath && ( - <> - - - - - - - - - )} -
    - - {/* CHALLENGE */} -
    -

    {transI18n("challenge")}

    - - - - - - - - -
    - - {/*EXTRA FEATURES */} -
    -

    {transI18n("extraFeatures")}

    - - {extraData?.setting?.hide_ui !== undefined && ( - - - - )} - - {extraData?.setting?.censorship !== undefined && ( - - - - )} -
    - -
    -

    - {"Lua"} -
    - -
    - setIsEnableLua(e.target.checked)} - /> -

    - {isEnableLua && ( - <> -
    - - -
    - - setExtraData( - { - ...extraData, - lua: code - } - ) - } - highlight={(code) => Prism.highlight(code, Prism.languages.lua, "lua")} - padding={16} - textareaClassName="outline-none" - className="text-sm font-mono min-h-75" - style={{ - background: "#1e1e1e", - color: "#fff", - }} - /> -
    -
    - - )} -
    - -
    - ) -} +'use client' +import { motion } from "framer-motion" +import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info, RouteIcon, Search, CupSoda, UsersIcon } from "lucide-react" +import useGlobalStore from '@/stores/globalStore' +import { useTranslations } from "next-intl" +import { getLocaleName, getNameChar } from "@/helper" +import useLocaleStore from "@/stores/localeStore" +import SelectCustomImage from "../select/customSelectImage" +import { useMemo, useState } from "react" +import useDetailDataStore from "@/stores/detailDataStore" +import Editor from "react-simple-code-editor" +import Prism from "prismjs" +import "prismjs/components/prism-lua" +import "prismjs/themes/prism-tomorrow.css" +import { SimpleAvatarCard } from "../card/simpleCharacterCard" + +export default function ExtraSettingBar() { + const { extraData, setExtraData, isEnableChangePath, setIsEnableChangePath, isEnableLua, setIsEnableLua } = useGlobalStore() + const transI18n = useTranslations("DataPage") + const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore() + const { locale } = useLocaleStore() + const [showSearchStage, setShowSearchStage] = useState(false) + const [showSearchLineup, setShowSearchLineup] = useState(false) + const [isChildClick, setIsChildClick] = useState(false) + const [stageSearchTerm, setStageSearchTerm] = useState("") + const [stagePage, setStagePage] = useState(1) + + const pageSize = 30 + const stageList = useMemo(() => Object.values(stage), [stage]) + + const term = stageSearchTerm.toLowerCase() + + const filteredStages = useMemo(() => stageList.filter((s) => + getLocaleName(locale, s.Name).toLowerCase().includes(term) || + String(s.ID).toLowerCase().includes(term) + ), [stageList, term, locale]) + + const paginatedStages = useMemo(() => filteredStages.slice( + (stagePage - 1) * pageSize, + stagePage * pageSize + ), [filteredStages, stagePage, pageSize]) + + const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) + + const onChangeSearch = (v: string) => { + setStageSearchTerm(v) + setStagePage(1) + } + + return ( +
    + {extraData?.theory_craft && ( +
    +

    + {transI18n("theoryCraft")} +
    + +
    +

    + + + + + + {extraData?.theory_craft?.mode && ( + <> + + + + + + + + +
    + +
    + {(extraData?.theory_craft?.custom_lineup || []).length > 0 && ( + (extraData?.theory_craft?.custom_lineup || []).map((avatarId) => { + const avatarData = mapAvatar[avatarId]; + if (!avatarData) return null; + return ( + { + const newLineup = (extraData?.theory_craft?.custom_lineup || []).filter(id => id !== avatarId); + setExtraData({ + ...extraData, + theory_craft: { + stage_id: extraData?.theory_craft?.stage_id ?? 30118121, + cycle_count: extraData?.theory_craft?.cycle_count ?? 1, + mode: extraData?.theory_craft?.mode ?? false, + hp: extraData?.theory_craft?.hp ?? {}, + custom_lineup: newLineup + } + }); + }} + /> + ); + }) + )} + + +
    + + {showSearchLineup && ( +
    e.stopPropagation()} + className="mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-2xl" + > +
    + {transI18n("selectedCharacters")} + +
    + +
    + {Object.values(mapAvatar).map((avatar) => { + const currentLineup = extraData?.theory_craft?.custom_lineup || []; + const isSelected = currentLineup.includes(avatar.ID.toString()); + + return ( + { + const idStr = avatar.ID.toString(); + let newLineup = [...currentLineup]; + + if (isSelected) { + newLineup = newLineup.filter(id => id !== idStr); + } else { + newLineup.push(idStr); + } + + setExtraData({ + ...extraData, + theory_craft: { + stage_id: extraData?.theory_craft?.stage_id ?? 30118121, + cycle_count: extraData?.theory_craft?.cycle_count ?? 1, + mode: extraData?.theory_craft?.mode ?? false, + hp: extraData?.theory_craft?.hp ?? {}, + custom_lineup: newLineup + } + }); + }} + /> + ); + })} +
    +
    + )} +
    +
    + + )} +
    + )} + + {/*MULTIPATH CHAR */} +
    +

    + {transI18n("multipathCharacter")} +
    + +
    + setIsEnableChangePath(e.target.checked)} + /> +

    + {isEnableChangePath && ( + <> + + + + + + + + + )} +
    + + {/* CHALLENGE */} +
    +

    {transI18n("challenge")}

    + + + + + + + + +
    + + {/*EXTRA FEATURES */} +
    +

    {transI18n("extraFeatures")}

    + + {extraData?.setting?.hide_ui !== undefined && ( + + + + )} + + {extraData?.setting?.censorship !== undefined && ( + + + + )} +
    + +
    +

    + {"Lua"} +
    + +
    + setIsEnableLua(e.target.checked)} + /> +

    + {isEnableLua && ( + <> +
    + + +
    + + setExtraData( + { + ...extraData, + lua: code + } + ) + } + highlight={(code) => Prism.highlight(code, Prism.languages.lua, "lua")} + padding={16} + textareaClassName="outline-none" + className="text-sm font-mono min-h-75" + style={{ + background: "#1e1e1e", + color: "#fff", + }} + /> +
    +
    + + )} +
    + +
    + ) +} diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx index 4992794..e9fb058 100644 --- a/src/components/footer/index.tsx +++ b/src/components/footer/index.tsx @@ -1,9 +1,9 @@ -export default function Footer() { - return ( -
    - -
    - ) -} +export default function Footer() { + return ( +
    + +
    + ) +} diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index 1e69938..8e31aa4 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -1,81 +1,81 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client" -import { downloadJson } from "@/helper"; -import { converterToFreeSRJson } from "@/helper/converterToFreeSRJson"; -import { useChangeTheme } from "@/hooks/useChangeTheme"; -import { listCurrentLanguage, listCurrentLanguageApi } from "@/constant/constant"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore"; -import { motion } from "framer-motion"; -import { useTranslations } from "next-intl"; -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import EnkaImport from "../importBar/enka"; -import useModelStore from "@/stores/modelStore"; -import FreeSRImport from "../importBar/freesr"; -import { toast } from "react-toastify"; -import { micsSchema } from "@/zod"; -import useGlobalStore from "@/stores/globalStore"; -import MonsterBar from "../monsterBar"; -import Image from "next/image"; -import ConnectBar from "../connectBar"; -import ExtraSettingBar from "../extraSettingBar"; -import ChangelogBar from "../changelog"; -import { ModalConfig } from "@/types"; -import { ScrollText } from "lucide-react"; - -const themes = [ - { label: "Winter" }, - { label: "Night" }, - { label: "Cupcake" }, - { label: "Coffee" }, -]; - -export default function Header() { - const { changeTheme } = useChangeTheme() - const { locale, setLocale } = useLocaleStore() - const { - avatars, - battle_type, - setAvatars, - setBattleType, - moc_config, - pf_config, - as_config, - ce_config, - peak_config, - setMocConfig, - setPfConfig, - setAsConfig, - setCeConfig, - setPeakConfig, - } = useUserDataStore() - - const router = useRouter() - const transI18n = useTranslations("DataPage") - const { - setIsOpenImport, - isOpenImport, - setIsOpenMonster, - isOpenMonster, - setIsOpenConnect, - isOpenConnect, - setIsOpenExtra, - isOpenExtra, - setIsChangelog, - isChangelog - } = useModelStore() - - const [importModal, setImportModal] = useState("enka"); - - const { isConnectPS, extraData } = useGlobalStore() - - useEffect(() => { - - const cookieLocale = document.cookie.split("; ") - .find((row) => row.startsWith("MYNEXTAPP_LOCALE")) - ?.split("=")[1]; - +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" +import { downloadJson } from "@/helper"; +import { converterToFreeSRJson } from "@/helper/converterToFreeSRJson"; +import { useChangeTheme } from "@/hooks/useChangeTheme"; +import { listCurrentLanguage, listCurrentLanguageApi } from "@/constant/constant"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { motion } from "framer-motion"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import EnkaImport from "../importBar/enka"; +import useModelStore from "@/stores/modelStore"; +import FreeSRImport from "../importBar/freesr"; +import { toast } from "react-toastify"; +import { micsSchema } from "@/zod"; +import useGlobalStore from "@/stores/globalStore"; +import MonsterBar from "../monsterBar"; +import Image from "next/image"; +import ConnectBar from "../connectBar"; +import ExtraSettingBar from "../extraSettingBar"; +import ChangelogBar from "../changelog"; +import { ModalConfig } from "@/types"; +import { ScrollText } from "lucide-react"; + +const themes = [ + { label: "Winter" }, + { label: "Night" }, + { label: "Cupcake" }, + { label: "Coffee" }, +]; + +export default function Header() { + const { changeTheme } = useChangeTheme() + const { locale, setLocale } = useLocaleStore() + const { + avatars, + battle_type, + setAvatars, + setBattleType, + moc_config, + pf_config, + as_config, + ce_config, + peak_config, + setMocConfig, + setPfConfig, + setAsConfig, + setCeConfig, + setPeakConfig, + } = useUserDataStore() + + const router = useRouter() + const transI18n = useTranslations("DataPage") + const { + setIsOpenImport, + isOpenImport, + setIsOpenMonster, + isOpenMonster, + setIsOpenConnect, + isOpenConnect, + setIsOpenExtra, + isOpenExtra, + setIsChangelog, + isChangelog + } = useModelStore() + + const [importModal, setImportModal] = useState("enka"); + + const { isConnectPS, extraData } = useGlobalStore() + + useEffect(() => { + + const cookieLocale = document.cookie.split("; ") + .find((row) => row.startsWith("MYNEXTAPP_LOCALE")) + ?.split("=")[1]; + if (cookieLocale) { if (!listCurrentLanguageApi.hasOwnProperty(cookieLocale)) { setLocale("en") @@ -85,535 +85,535 @@ export default function Header() { setLocale(cookieLocale) } - } else { - let browserLocale = navigator.language.slice(0, 2); - - if (!listCurrentLanguageApi.hasOwnProperty(browserLocale)) { - browserLocale = "en" - } - setLocale(browserLocale); - document.cookie = `MYNEXTAPP_LOCALE=${browserLocale};` - router.refresh() - } - }, [router, setLocale]) - - const changeLocale = (newLocale: string) => { - setLocale(newLocale) - document.cookie = `MYNEXTAPP_LOCALE=${newLocale};` - router.refresh() - } - - const handleShow = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close() - } - }; - - const modalConfigs: ModalConfig[] = [ - { - id: "connect_modal", - title: transI18n("psConnection"), - isOpen: isOpenConnect, - onClose: () => { - setIsOpenConnect(false) - handleCloseModal("connect_modal") - }, - content: - }, - { - id: "import_modal", - title: transI18n("importSetting"), - isOpen: isOpenImport, - onClose: () => { - setIsOpenImport(false) - handleCloseModal("import_modal") - }, - content: ( - <> - {importModal === "enka" && } - {importModal === "freesr" && } - - ) - }, - { - id: "monster_modal", - title: transI18n("monsterSetting"), - isOpen: isOpenMonster, - onClose: () => { - setIsOpenMonster(false) - handleCloseModal("monster_modal") - }, - content: - }, - { - id: "extra_modal", - title: transI18n("extraSetting"), - isOpen: isOpenExtra, - onClose: () => { - setIsOpenExtra(false) - handleCloseModal("extra_modal") - }, - content: - }, - { - id: "changelog_modal", - title: "Changelog", - isOpen: isChangelog, - onClose: () => { - setIsChangelog(false) - handleCloseModal("changelog_modal") - }, - content: - } - ] - - // Handle ESC key to close modal - useEffect(() => { - for (const item of modalConfigs) { - if (!item?.isOpen) { - handleCloseModal(item?.id || "") - } else { - handleShow(item?.id || "") - } - } - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - for (const item of modalConfigs) { - handleCloseModal(item?.id || "") - } - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isOpenImport, isOpenMonster, isOpenConnect, isOpenExtra, isChangelog]); - - const handleImportDatabase = (event: React.ChangeEvent) => { - - const file = event.target.files?.[0]; - if (!file) { - toast.error(transI18n("pleaseSelectAFile")) - return - } - if (!file.name.endsWith(".json") || file.type !== "application/json") { - toast.error(transI18n("fileMustBeAValidJsonFile")) - return - } - - if (file) { - - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target?.result as string); - const parsed = micsSchema.parse(data) - setAvatars(parsed.avatars) - setBattleType(parsed.battle_type) - setMocConfig(parsed.moc_config) - setPfConfig(parsed.pf_config) - setAsConfig(parsed.as_config) - setCeConfig(parsed.ce_config) - setPeakConfig(parsed.peak_config) - toast.success(transI18n("importDatabaseSuccess")) - } catch { - toast.error(transI18n("fileMustBeAValidJsonFile")) - } - }; - reader.readAsText(file); - } - - }; - - return ( -
    -
    - {/* Mobile menu dropdown */} -
    -
    - - - -
    - -
    - - {/* Logo */} - - -
    - Logo -
    -

    - Firefly Sr - - Tools - -

    -

    Firefly Shelter

    -
    -
    -
    - -
    - - {/* Desktop navigation */} -
    - -
    - - {/* Right side items */} -
    -
    -
    -
    - {isConnectPS ? transI18n("connected") : transI18n("unconnected")} -
    -
    -
    -
    - -
    { - setIsChangelog(true) - }} - > - -
    - - {/* Language selector - REFINED */} -
    - {/* Nút bấm hiển thị */} -
    - - - - - {listCurrentLanguage[locale as keyof typeof listCurrentLanguage]?.flag || "EN"} - - - - -
    - -
      - {Object.entries(listCurrentLanguage).map(([key, value]) => ( -
    • - -
    • - ))} -
    -
    -
    -
    - - - - - - -
    - -
    -
      - {themes.map((theme) => ( -
    • { - if (changeTheme) changeTheme(theme.label.toLowerCase()); - }} - > - -
    • - ))} -
    -
    -
    -
    - - {modalConfigs?.map(({ id, title, onClose, content }) => ( - -
    -
    - - ✕ - -
    - -
    -

    - {title} -

    -
    - - {content} -
    -
    - ))} -
    - ) + } else { + let browserLocale = navigator.language.slice(0, 2); + + if (!listCurrentLanguageApi.hasOwnProperty(browserLocale)) { + browserLocale = "en" + } + setLocale(browserLocale); + document.cookie = `MYNEXTAPP_LOCALE=${browserLocale};` + router.refresh() + } + }, [router, setLocale]) + + const changeLocale = (newLocale: string) => { + setLocale(newLocale) + document.cookie = `MYNEXTAPP_LOCALE=${newLocale};` + router.refresh() + } + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close() + } + }; + + const modalConfigs: ModalConfig[] = [ + { + id: "connect_modal", + title: transI18n("psConnection"), + isOpen: isOpenConnect, + onClose: () => { + setIsOpenConnect(false) + handleCloseModal("connect_modal") + }, + content: + }, + { + id: "import_modal", + title: transI18n("importSetting"), + isOpen: isOpenImport, + onClose: () => { + setIsOpenImport(false) + handleCloseModal("import_modal") + }, + content: ( + <> + {importModal === "enka" && } + {importModal === "freesr" && } + + ) + }, + { + id: "monster_modal", + title: transI18n("monsterSetting"), + isOpen: isOpenMonster, + onClose: () => { + setIsOpenMonster(false) + handleCloseModal("monster_modal") + }, + content: + }, + { + id: "extra_modal", + title: transI18n("extraSetting"), + isOpen: isOpenExtra, + onClose: () => { + setIsOpenExtra(false) + handleCloseModal("extra_modal") + }, + content: + }, + { + id: "changelog_modal", + title: "Changelog", + isOpen: isChangelog, + onClose: () => { + setIsChangelog(false) + handleCloseModal("changelog_modal") + }, + content: + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } else { + handleShow(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenImport, isOpenMonster, isOpenConnect, isOpenExtra, isChangelog]); + + const handleImportDatabase = (event: React.ChangeEvent) => { + + const file = event.target.files?.[0]; + if (!file) { + toast.error(transI18n("pleaseSelectAFile")) + return + } + if (!file.name.endsWith(".json") || file.type !== "application/json") { + toast.error(transI18n("fileMustBeAValidJsonFile")) + return + } + + if (file) { + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = JSON.parse(e.target?.result as string); + const parsed = micsSchema.parse(data) + setAvatars(parsed.avatars) + setBattleType(parsed.battle_type) + setMocConfig(parsed.moc_config) + setPfConfig(parsed.pf_config) + setAsConfig(parsed.as_config) + setCeConfig(parsed.ce_config) + setPeakConfig(parsed.peak_config) + toast.success(transI18n("importDatabaseSuccess")) + } catch { + toast.error(transI18n("fileMustBeAValidJsonFile")) + } + }; + reader.readAsText(file); + } + + }; + + return ( +
    +
    + {/* Mobile menu dropdown */} +
    +
    + + + +
    + +
    + + {/* Logo */} + + +
    + Logo +
    +

    + Firefly Sr + + Tools + +

    +

    Firefly Shelter

    +
    +
    +
    + +
    + + {/* Desktop navigation */} +
    + +
    + + {/* Right side items */} +
    +
    +
    +
    + {isConnectPS ? transI18n("connected") : transI18n("unconnected")} +
    +
    +
    +
    + +
    { + setIsChangelog(true) + }} + > + +
    + + {/* Language selector - REFINED */} +
    + {/* Nút bấm hiển thị */} +
    + + + + + {listCurrentLanguage[locale as keyof typeof listCurrentLanguage]?.flag || "EN"} + + + + +
    + +
      + {Object.entries(listCurrentLanguage).map(([key, value]) => ( +
    • + +
    • + ))} +
    +
    +
    +
    + + + + + + +
    + +
    +
      + {themes.map((theme) => ( +
    • { + if (changeTheme) changeTheme(theme.label.toLowerCase()); + }} + > + +
    • + ))} +
    +
    +
    +
    + + {modalConfigs?.map(({ id, title, onClose, content }) => ( + +
    +
    + + ✕ + +
    + +
    +

    + {title} +

    +
    + + {content} +
    +
    + ))} +
    + ) } diff --git a/src/components/importBar/copy.tsx b/src/components/importBar/copy.tsx index 8b4455a..3f542e6 100644 --- a/src/components/importBar/copy.tsx +++ b/src/components/importBar/copy.tsx @@ -1,74 +1,74 @@ -"use client" -import useUserDataStore from "@/stores/userDataStore"; -import { useState, useMemo } from "react"; -import useCopyProfileStore from "@/stores/copyProfile"; -import ProfileCard from "../card/profileCard"; -import { AvatarProfileCardType, AvatarProfileStore } from "@/types"; -import Image from "next/image"; -import { getNameChar, calcRarity } from "@/helper"; -import useLocaleStore from "@/stores/localeStore"; -import { useTranslations } from "next-intl"; -import SelectCustomImage from "../select/customSelectImage"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function CopyImport() { - const { avatars, setAvatar } = useUserDataStore(); - const { avatarSelected } = useCurrentDataStore() - const { mapAvatar, baseType, damageType } = useDetailDataStore() - const { locale } = useLocaleStore() - const { - selectedProfiles, - avatarCopySelected, - setSelectedProfiles, - setAvatarCopySelected, - listElement, - listPath, - listRank, - setListElement, - setListPath, - setListRank - } = useCopyProfileStore() - const transI18n = useTranslations("DataPage") - - const [message, setMessage] = useState({ - type: "", - text: "" - }) - - const listAvatar = useMemo(() => { - if (!mapAvatar || !locale || !transI18n) return [] - let list = Object.values(mapAvatar); - const allElementFalse = !Object.values(listElement).some(v => v) - const allPathFalse = !Object.values(listPath).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.sort((a, b) => { - const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) - if (r !== 0) return r - return a.ID - b.ID - }) - return list - }, [mapAvatar, listElement, listPath, listRank, locale, transI18n]) - - - const handleProfileToggle = (profile: AvatarProfileCardType) => { - if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) { - setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key)); - return; - } - setSelectedProfiles([...selectedProfiles, profile]); - }; - - - const clearSelection = () => { - setSelectedProfiles([]); - setMessage({ - type: "success", - text: transI18n("clearAll") - }) - }; - +"use client" +import useUserDataStore from "@/stores/userDataStore"; +import { useState, useMemo } from "react"; +import useCopyProfileStore from "@/stores/copyProfile"; +import ProfileCard from "../card/profileCard"; +import { AvatarProfileCardType, AvatarProfileStore } from "@/types"; +import Image from "next/image"; +import { getNameChar, calcRarity } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import { useTranslations } from "next-intl"; +import SelectCustomImage from "../select/customSelectImage"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function CopyImport() { + const { avatars, setAvatar } = useUserDataStore(); + const { avatarSelected } = useCurrentDataStore() + const { mapAvatar, baseType, damageType } = useDetailDataStore() + const { locale } = useLocaleStore() + const { + selectedProfiles, + avatarCopySelected, + setSelectedProfiles, + setAvatarCopySelected, + listElement, + listPath, + listRank, + setListElement, + setListPath, + setListRank + } = useCopyProfileStore() + const transI18n = useTranslations("DataPage") + + const [message, setMessage] = useState({ + type: "", + text: "" + }) + + const listAvatar = useMemo(() => { + if (!mapAvatar || !locale || !transI18n) return [] + let list = Object.values(mapAvatar); + const allElementFalse = !Object.values(listElement).some(v => v) + const allPathFalse = !Object.values(listPath).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.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return a.ID - b.ID + }) + return list + }, [mapAvatar, listElement, listPath, listRank, locale, transI18n]) + + + const handleProfileToggle = (profile: AvatarProfileCardType) => { + if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) { + setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key)); + return; + } + setSelectedProfiles([...selectedProfiles, profile]); + }; + + + const clearSelection = () => { + setSelectedProfiles([]); + setMessage({ + type: "success", + text: transI18n("clearAll") + }) + }; + const selectAll = () => { if (avatarCopySelected) { const sourceAvatar = avatars[avatarCopySelected.ID.toString()] @@ -79,32 +79,32 @@ export default function CopyImport() { return null; } return { - key: index, - ...profile, - } as AvatarProfileCardType - }).filter((profile) => profile !== null)); - } - setMessage({ - type: "success", - text: transI18n("selectAll") - }) - }; - - const handleCopy = () => { - if (selectedProfiles.length === 0) { - setMessage({ - type: "error", - text: transI18n("pleaseSelectAtLeastOneProfile") - }); - return; - } - if (!avatarCopySelected) { - setMessage({ - type: "error", - text: transI18n("noAvatarToCopySelected") - }); - return; - } + key: index, + ...profile, + } as AvatarProfileCardType + }).filter((profile) => profile !== null)); + } + setMessage({ + type: "success", + text: transI18n("selectAll") + }) + }; + + const handleCopy = () => { + if (selectedProfiles.length === 0) { + setMessage({ + type: "error", + text: transI18n("pleaseSelectAtLeastOneProfile") + }); + return; + } + if (!avatarCopySelected) { + setMessage({ + type: "error", + text: transI18n("noAvatarToCopySelected") + }); + return; + } if (!avatarSelected) { setMessage({ type: "error", @@ -130,180 +130,180 @@ export default function CopyImport() { return null; } return { - ...profile, - profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`, - } as AvatarProfileStore - }).filter((profile) => profile !== null); + ...profile, + profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`, + } as AvatarProfileStore + }).filter((profile) => profile !== null); const newAvatar = { ...targetAvatar, profileList: targetAvatar.profileList.concat(newListProfile), profileSelect: targetAvatar.profileList.length + newListProfile.length - 1, } - setAvatar(newAvatar); - setSelectedProfiles([]); - setMessage({ - type: "success", - text: transI18n("copied") - }); - }; - - return ( -
    -
    - {/* Header */} -
    - -
    - -
    -
    {transI18n("filter")}
    -
    - {/* Path */} -
    -
    - {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( -
    { - 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" - style={{ - backgroundColor: listPath[key] ? "#374151" : "#6B7280" - }}> - {key} -
    - ))} -
    -
    - - {/* Element */} -
    -
    - {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( -
    { - 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" - style={{ - backgroundColor: listElement[key] ? "#374151" : "#6B7280" - }}> - {key} -
    - ))} -
    -
    - - {/* Rank */} -
    -
    - {Object.entries(listRank).map(([key], index) => ( -
    { - 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" - style={{ - backgroundColor: listRank[key] ? "#374151" : "#6B7280" - }}> -
    {key}*
    -
    - ))} -
    -
    -
    - -
    -
    - - {listAvatar.length > 0 && ( -
    -
    {transI18n("characterName")}
    - ({ - value: avatar.ID.toString(), - label: getNameChar(locale, transI18n, avatar), - imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}` - }))} - excludeSet={[]} - selectedCustomSet={avatarCopySelected?.ID.toString() || ""} - placeholder="Character Select" - setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)} - /> -
    - )} - -
    -
    - - - - {/* Selection Info */} -
    - - {transI18n("selectedProfiles")}: {selectedProfiles.length} - - - - {selectedProfiles.length > 0 && ( - - )} -
    - {message.type && message.text && ( -
    {message.type == "error" ? "😭" : "🎉"} {message.text}
    - )} -
    - - {/* Character Grid */} -
    + setAvatar(newAvatar); + setSelectedProfiles([]); + setMessage({ + type: "success", + text: transI18n("copied") + }); + }; + + return ( +
    +
    + {/* Header */} +
    + +
    + +
    +
    {transI18n("filter")}
    +
    + {/* Path */} +
    +
    + {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + 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" + style={{ + backgroundColor: listPath[key] ? "#374151" : "#6B7280" + }}> + {key} +
    + ))} +
    +
    + + {/* Element */} +
    +
    + {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + 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" + style={{ + backgroundColor: listElement[key] ? "#374151" : "#6B7280" + }}> + {key} +
    + ))} +
    +
    + + {/* Rank */} +
    +
    + {Object.entries(listRank).map(([key], index) => ( +
    { + 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" + style={{ + backgroundColor: listRank[key] ? "#374151" : "#6B7280" + }}> +
    {key}*
    +
    + ))} +
    +
    +
    + +
    +
    + + {listAvatar.length > 0 && ( +
    +
    {transI18n("characterName")}
    + ({ + value: avatar.ID.toString(), + label: getNameChar(locale, transI18n, avatar), + imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}` + }))} + excludeSet={[]} + selectedCustomSet={avatarCopySelected?.ID.toString() || ""} + placeholder="Character Select" + setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)} + /> +
    + )} + +
    +
    + + + + {/* Selection Info */} +
    + + {transI18n("selectedProfiles")}: {selectedProfiles.length} + + + + {selectedProfiles.length > 0 && ( + + )} +
    + {message.type && message.text && ( +
    {message.type == "error" ? "😭" : "🎉"} {message.text}
    + )} +
    + + {/* Character Grid */} +
    {avatarCopySelected && avatars[avatarCopySelected?.ID.toString()]?.profileList.map((profile, index) => { if (!profile.lightcone?.item_id && Object.keys(profile.relics ?? {}).length == 0) { - return null; - } - return ( - - ) - })} -
    -
    -
    - ) + return null; + } + return ( + + ) + })} +
    +
    +
    + ) } diff --git a/src/components/importBar/enka.tsx b/src/components/importBar/enka.tsx index b567f61..fe03804 100644 --- a/src/components/importBar/enka.tsx +++ b/src/components/importBar/enka.tsx @@ -1,10 +1,10 @@ -"use client" -import { useState } from "react"; -import CharacterInfoCard from "../card/characterInfoCard"; -import useEnkaStore from "@/stores/enkaStore"; -import { SendDataThroughProxy } from "@/lib/api/api"; -import { CharacterInfoCardType, EnkaResponse } from "@/types"; -import useUserDataStore from "@/stores/userDataStore"; +"use client" +import { useState } from "react"; +import CharacterInfoCard from "../card/characterInfoCard"; +import useEnkaStore from "@/stores/enkaStore"; +import { SendDataThroughProxy } from "@/lib/api/api"; +import { CharacterInfoCardType, EnkaResponse } from "@/types"; +import useUserDataStore from "@/stores/userDataStore"; import { converterOneEnkaDataToAvatarStore } from "@/helper"; import useModelStore from "@/stores/modelStore"; 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), })), }); - -export default function EnkaImport() { - const { - uidInput, - setUidInput, - enkaData, - selectedCharacters, - setSelectedCharacters, - setEnkaData, - } = useEnkaStore(); - const transI18n = useTranslations("DataPage") - const { avatars, setAvatar } = useUserDataStore(); + +export default function EnkaImport() { + const { + uidInput, + setUidInput, + enkaData, + selectedCharacters, + setSelectedCharacters, + setEnkaData, + } = useEnkaStore(); + const transI18n = useTranslations("DataPage") + const { avatars, setAvatar } = useUserDataStore(); const { setIsOpenImport } = useModelStore() const { mapAvatar } = useDetailDataStore() const [isLoading, setIsLoading] = useState(false) const [Error, setError] = useState("") const validCharacters = enkaData?.detailInfo.avatarDetailList.filter((character) => mapAvatar?.[character.avatarId]) ?? [] - + const handlerFetchData = async () => { if (!uidInput) { setError(transI18n("pleaseEnterUid")) @@ -77,36 +77,36 @@ export default function EnkaImport() { setIsLoading(false) } } - - const handleCharacterToggle = (character: CharacterInfoCardType) => { - if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { - setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); - return; - } - setSelectedCharacters([...selectedCharacters, character]); - }; - - const clearSelection = () => { - setSelectedCharacters([]); - }; - + + const handleCharacterToggle = (character: CharacterInfoCardType) => { + if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { + setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); + return; + } + setSelectedCharacters([...selectedCharacters, character]); + }; + + const clearSelection = () => { + setSelectedCharacters([]); + }; + const selectAll = () => { if (enkaData) { setSelectedCharacters(validCharacters.map(toCharacterInfoCard)); } }; - - const handleImport = () => { - if (selectedCharacters.length === 0) { - setError(transI18n("pleaseSelectAtLeastOneCharacter")); - return; - } - if (!enkaData) { - setError(transI18n("noDataToImport")); - return; - } - setError(""); - const listAvatars = { ...avatars } + + const handleImport = () => { + if (selectedCharacters.length === 0) { + setError(transI18n("pleaseSelectAtLeastOneCharacter")); + return; + } + if (!enkaData) { + setError(transI18n("noDataToImport")); + return; + } + setError(""); + const listAvatars = { ...avatars } const parsed = enkaResponseSchema.safeParse(enkaData) if (!parsed.success) { setError(transI18n("failedToFetchEnkaData")); @@ -125,80 +125,80 @@ export default function EnkaImport() { acc[skill.pointId] = skill.level; return acc; }, {} as Record), - } - const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length) - if (newProfile) { - newAvatar.profileList.push(newProfile) - newAvatar.profileSelect = newAvatar.profileList.length - 1 - } - setAvatar(newAvatar) - } - - }) - setIsOpenImport(false) - toast.success(transI18n("importEnkaDataSuccess")) - }; - - return ( -
    -
    - {/* Header */} -
    -

    HSR UID

    - -
    - 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" - placeholder="Enter UID" - /> - - {selectedCharacters.length > 0 && ( - - )} -
    - {Error && ( -
    😭 {Error}
    - )} - - {/* Player Info */} - {enkaData && ( -
    -
    Name: {enkaData.detailInfo.nickname}
    -
    UID: {enkaData.detailInfo.uid}
    -
    Level: {enkaData.detailInfo.level}
    -
    - )} - - {/* Selection Info */} -
    - - {transI18n("selectedCharacters")}: {selectedCharacters.length} - - - -
    -
    - {isLoading && ( -
    -
    -
    - )} - {/* Character Grid */} -
    + } + const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length) + if (newProfile) { + newAvatar.profileList.push(newProfile) + newAvatar.profileSelect = newAvatar.profileList.length - 1 + } + setAvatar(newAvatar) + } + + }) + setIsOpenImport(false) + toast.success(transI18n("importEnkaDataSuccess")) + }; + + return ( +
    +
    + {/* Header */} +
    +

    HSR UID

    + +
    + 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" + placeholder="Enter UID" + /> + + {selectedCharacters.length > 0 && ( + + )} +
    + {Error && ( +
    😭 {Error}
    + )} + + {/* Player Info */} + {enkaData && ( +
    +
    Name: {enkaData.detailInfo.nickname}
    +
    UID: {enkaData.detailInfo.uid}
    +
    Level: {enkaData.detailInfo.level}
    +
    + )} + + {/* Selection Info */} +
    + + {transI18n("selectedCharacters")}: {selectedCharacters.length} + + + +
    +
    + {isLoading && ( +
    +
    +
    + )} + {/* Character Grid */} +
    {validCharacters.map((character) => ( - ))} -
    -
    -
    - ); + ))} +
    +
    +
    + ); } diff --git a/src/components/importBar/freesr.tsx b/src/components/importBar/freesr.tsx index e51314f..598dd01 100644 --- a/src/components/importBar/freesr.tsx +++ b/src/components/importBar/freesr.tsx @@ -1,15 +1,15 @@ -"use client" - -import useFreeSRStore from "@/stores/freesrStore"; -import useModelStore from "@/stores/modelStore"; -import useUserDataStore from "@/stores/userDataStore"; -import { CharacterInfoCardType } from "@/types"; -import { useState } from "react"; -import CharacterInfoCard from "../card/characterInfoCard"; -import { freeSrJsonSchema } from "@/zod"; -import { toast } from "react-toastify"; -import { converterOneFreeSRDataToAvatarStore } from "@/helper"; -import { useTranslations } from "next-intl"; +"use client" + +import useFreeSRStore from "@/stores/freesrStore"; +import useModelStore from "@/stores/modelStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { CharacterInfoCardType } from "@/types"; +import { useState } from "react"; +import CharacterInfoCard from "../card/characterInfoCard"; +import { freeSrJsonSchema } from "@/zod"; +import { toast } from "react-toastify"; +import { converterOneFreeSRDataToAvatarStore } from "@/helper"; +import { useTranslations } from "next-intl"; import useDetailDataStore from "@/stores/detailDataStore"; import { z } from "zod"; @@ -36,55 +36,55 @@ const toCharacterInfoCard = (data: FreeSRJsonInput, character: FreeSRJsonInput[" })), } } - -export default function FreeSRImport() { - const { avatars, setAvatar } = useUserDataStore(); - const { mapAvatar } = useDetailDataStore(); - const { setIsOpenImport } = useModelStore() - const [isLoading, setIsLoading] = useState(false) + +export default function FreeSRImport() { + const { avatars, setAvatar } = useUserDataStore(); + const { mapAvatar } = useDetailDataStore(); + const { setIsOpenImport } = useModelStore() + const [isLoading, setIsLoading] = useState(false) const [Error, setError] = useState("") const { freeSRData, setFreeSRData, selectedCharacters, setSelectedCharacters } = useFreeSRStore() const transI18n = useTranslations("DataPage") const parsedFreeSRData = freeSrJsonSchema.safeParse(freeSRData) const validFreeSRData = parsedFreeSRData.success ? parsedFreeSRData.data : null - - const handleCharacterToggle = (character: CharacterInfoCardType) => { - if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { - setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); - return; - } - setSelectedCharacters([...selectedCharacters, character]); - }; - - const clearSelection = () => { - setSelectedCharacters([]); - }; - + + const handleCharacterToggle = (character: CharacterInfoCardType) => { + if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { + setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); + return; + } + setSelectedCharacters([...selectedCharacters, character]); + }; + + const clearSelection = () => { + setSelectedCharacters([]); + }; + const selectAll = () => { const parsed = freeSrJsonSchema.safeParse(freeSRData) if (parsed.success) { setSelectedCharacters(Object.values(parsed.data.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => toCharacterInfoCard(parsed.data, character))); } }; - - const handlerReadFile = (event: React.ChangeEvent) => { - setIsLoading(true) - const file = event.target.files?.[0]; - if (!file) { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("pleaseSelectAFile")) - setIsLoading(false) - return - } - if (!file.name.endsWith(".json") || file.type !== "application/json") { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("fileMustBeAValidJsonFile")) - setIsLoading(false) - return - } - + + const handlerReadFile = (event: React.ChangeEvent) => { + setIsLoading(true) + const file = event.target.files?.[0]; + if (!file) { + setSelectedCharacters([]) + setFreeSRData(null) + setError(transI18n("pleaseSelectAFile")) + setIsLoading(false) + return + } + if (!file.name.endsWith(".json") || file.type !== "application/json") { + setSelectedCharacters([]) + setFreeSRData(null) + setError(transI18n("fileMustBeAValidJsonFile")) + setIsLoading(false) + return + } + if (file) { const reader = new FileReader(); @@ -121,18 +121,18 @@ export default function FreeSRImport() { reader.readAsText(file); } }; - - const handleImport = () => { - if (selectedCharacters.length === 0) { - setError(transI18n("pleaseSelectAtLeastOneCharacter")); - return; - } - if (!freeSRData) { - setError(transI18n("noDataToImport")); - return; - } - setError(""); - + + const handleImport = () => { + if (selectedCharacters.length === 0) { + setError(transI18n("pleaseSelectAtLeastOneCharacter")); + return; + } + if (!freeSRData) { + setError(transI18n("noDataToImport")); + return; + } + setError(""); + const parsed = freeSrJsonSchema.safeParse(freeSRData) if (!parsed.success) { setError(transI18n("fileMustBeAValidJsonFile")); @@ -144,73 +144,73 @@ export default function FreeSRImport() { filterData.forEach((character) => { const newAvatar = { ...listAvatars[character.avatar_id] } if (Object.keys(newAvatar).length !== 0) { - newAvatar.level = character.level - newAvatar.promotion = character.promotion - newAvatar.data = { - rank: character.data.rank ?? 0, - skills: character.data.skills - } + newAvatar.level = character.level + newAvatar.promotion = character.promotion + newAvatar.data = { + rank: character.data.rank ?? 0, + skills: character.data.skills + } const newProfile = converterOneFreeSRDataToAvatarStore(parsed.data, newAvatar.profileList.length, character.avatar_id) - if (newProfile) { - newAvatar.profileList.push(newProfile) - newAvatar.profileSelect = newAvatar.profileList.length - 1 - } - setAvatar(newAvatar) - } - - }) - setIsOpenImport(false) - toast.success(transI18n("importFreeSRDataSuccess")) - } - - return ( -
    -
    - {/* Header */} -
    -

    {transI18n("freeSRImport")}

    - -
    -
    - {transI18n("pickAFile")} - - -
    - - {selectedCharacters.length > 0 && ( - - )} -
    - {Error && ( -
    😭 {Error}
    - )} - - - {/* Selection Info */} -
    - - {transI18n("selectedCharacters")}: {selectedCharacters.length} - - - -
    -
    - {isLoading && ( -
    -
    -
    - )} - {/* Character Grid */} -
    + if (newProfile) { + newAvatar.profileList.push(newProfile) + newAvatar.profileSelect = newAvatar.profileList.length - 1 + } + setAvatar(newAvatar) + } + + }) + setIsOpenImport(false) + toast.success(transI18n("importFreeSRDataSuccess")) + } + + return ( +
    +
    + {/* Header */} +
    +

    {transI18n("freeSRImport")}

    + +
    +
    + {transI18n("pickAFile")} + + +
    + + {selectedCharacters.length > 0 && ( + + )} +
    + {Error && ( +
    😭 {Error}
    + )} + + + {/* Selection Info */} +
    + + {transI18n("selectedCharacters")}: {selectedCharacters.length} + + + +
    +
    + {isLoading && ( +
    +
    +
    + )} + {/* Character Grid */} +
    {validFreeSRData && Object.values(validFreeSRData.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => { return ( - ) - })} -
    -
    -
    - ) + ) + })} +
    +
    +
    + ) } diff --git a/src/components/lightconeBar/index.tsx b/src/components/lightconeBar/index.tsx index bda9d2c..25bcd79 100644 --- a/src/components/lightconeBar/index.tsx +++ b/src/components/lightconeBar/index.tsx @@ -1,145 +1,145 @@ -"use client" - -import { useMemo } from "react" -import Image from "next/image"; -import useLocaleStore from "@/stores/localeStore" -import LightconeCard from "../card/lightconeCard"; -import useUserDataStore from "@/stores/userDataStore"; -import useModelStore from "@/stores/modelStore"; -import { useTranslations } from "next-intl"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import { calcRarity, getLocaleName } from "@/helper"; - -export default function LightconeBar() { - const { locale } = useLocaleStore() - const { - avatarSelected, - mapLightconePathActive, - mapLightconeRankActive, - setMapLightconePathActive, - setMapLightconeRankActive, - lightconeSearch, - setLightconeSearch - } = useCurrentDataStore() - const { setAvatar, avatars } = useUserDataStore() - const { setIsOpenLightcone } = useModelStore() - const { mapLightCone, baseType } = useDetailDataStore() - const transI18n = useTranslations("DataPage") - - const listLightcone = useMemo(() => { - if (!mapLightCone || !locale) return [] - - let list = Object.values(mapLightCone) - - if (lightconeSearch) { - list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase())) - } - - const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v) - const allPathFalse = !Object.values(mapLightconePathActive).some(v => v) - - list = list.filter(item => - (allRankFalse || mapLightconeRankActive[item.Rarity]) && - (allPathFalse || mapLightconePathActive[item.BaseType]) - ) - - list.sort((a, b) => { - const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) - if (r !== 0) return r - return b.ID - a.ID - }) - - return list - }, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale]) - - - - return ( -
    -
    -

    - {transI18n("lightConeSetting")} -

    -
    -
    -
    -
    Search
    - setLightconeSearch(e.target.value)} - type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full" - /> -
    -
    -
    Filter
    -
    -
    - {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( -
    { - 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" - style={{ - backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280" - }} - > - {key} -
    - ))} -
    - -
    - {Object.keys(mapLightconeRankActive).map((key, index) => ( -
    { - 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" - style={{ - backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280" - }} - > -
    - {key}* -
    -
    - ))} -
    -
    - -
    -
    -
    - {listLightcone.map((item, index) => ( -
    { - if (avatarSelected) { - const avatar = avatars[avatarSelected?.ID?.toString()] - avatar.profileList[avatar.profileSelect].lightcone = { - level: 80, - item_id: item.ID, - rank: 1, - promotion: 6 - } - setAvatar({ ...avatar }) - setIsOpenLightcone(false) - } - }}> - -
    - ))} -
    -
    - ) +"use client" + +import { useMemo } from "react" +import Image from "next/image"; +import useLocaleStore from "@/stores/localeStore" +import LightconeCard from "../card/lightconeCard"; +import useUserDataStore from "@/stores/userDataStore"; +import useModelStore from "@/stores/modelStore"; +import { useTranslations } from "next-intl"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { calcRarity, getLocaleName } from "@/helper"; + +export default function LightconeBar() { + const { locale } = useLocaleStore() + const { + avatarSelected, + mapLightconePathActive, + mapLightconeRankActive, + setMapLightconePathActive, + setMapLightconeRankActive, + lightconeSearch, + setLightconeSearch + } = useCurrentDataStore() + const { setAvatar, avatars } = useUserDataStore() + const { setIsOpenLightcone } = useModelStore() + const { mapLightCone, baseType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + + const listLightcone = useMemo(() => { + if (!mapLightCone || !locale) return [] + + let list = Object.values(mapLightCone) + + if (lightconeSearch) { + list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase())) + } + + const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v) + const allPathFalse = !Object.values(mapLightconePathActive).some(v => v) + + list = list.filter(item => + (allRankFalse || mapLightconeRankActive[item.Rarity]) && + (allPathFalse || mapLightconePathActive[item.BaseType]) + ) + + list.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return b.ID - a.ID + }) + + return list + }, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale]) + + + + return ( +
    +
    +

    + {transI18n("lightConeSetting")} +

    +
    +
    +
    +
    Search
    + setLightconeSearch(e.target.value)} + type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full" + /> +
    +
    +
    Filter
    +
    +
    + {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + 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" + style={{ + backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280" + }} + > + {key} +
    + ))} +
    + +
    + {Object.keys(mapLightconeRankActive).map((key, index) => ( +
    { + 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" + style={{ + backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280" + }} + > +
    + {key}* +
    +
    + ))} +
    +
    + +
    +
    +
    + {listLightcone.map((item, index) => ( +
    { + if (avatarSelected) { + const avatar = avatars[avatarSelected?.ID?.toString()] + avatar.profileList[avatar.profileSelect].lightcone = { + level: 80, + item_id: item.ID, + rank: 1, + promotion: 6 + } + setAvatar({ ...avatar }) + setIsOpenLightcone(false) + } + }}> + +
    + ))} +
    +
    + ) } \ No newline at end of file diff --git a/src/components/monsterBar/as.tsx b/src/components/monsterBar/as.tsx index 6d799c0..e64e98e 100644 --- a/src/components/monsterBar/as.tsx +++ b/src/components/monsterBar/as.tsx @@ -1,341 +1,341 @@ -"use client" -import { useEffect, useMemo } from "react"; -import SelectCustomText from "../select/customSelectText"; -import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore"; -import Image from "next/image"; -import { ASEvent, MonsterStore } from "@/types"; -import { useTranslations } from "next-intl"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function AsBar() { - const { locale } = useLocaleStore() - const { - as_config, - setAsConfig - } = useUserDataStore() - const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() - const transI18n = useTranslations("DataPage") - - const challengeSelected = useMemo(() => { - return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id) - }, [as_config, mapAS]) - - const eventSelected = useMemo(() => { - return mapAS[as_config.event_id.toString()] - }, [as_config, mapAS]) - - - const floorSideList = useMemo(() => { - if (!eventSelected) return []; - - const floorList = [ - { - id: "firstNode", - name: transI18n("firstNode"), - wave: transI18n("firstNodeEnemies") - }, - { - id: "secondNode", - name: transI18n("secondNode"), - wave: transI18n("secondNodeEnemies") - }, - - ] - - if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === as_config.challenge_id) { - floorList.push({ - id: "thirdNode", - name: transI18n("thirdNode"), - wave: transI18n("thirdNodeEnemies") - }) - } - return floorList - }, [as_config.challenge_id, eventSelected, transI18n]) - - const buffList = useMemo(() => { - if (!eventSelected) return []; - - if (as_config.floor_side === "firstNode") { - return eventSelected?.BuffList1 ?? []; - } - - if (as_config.floor_side === "secondNode") { - return eventSelected?.BuffList2 ?? []; - } - - if (as_config.floor_side === "thirdNode" && eventSelected?.BuffList3) { - return eventSelected?.BuffList3 ?? []; - } - - return []; - }, [as_config.floor_side, eventSelected]); - - useEffect(() => { - if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return - const newBattleConfig = structuredClone(as_config) - newBattleConfig.cycle_count = challengeSelected.TurnLimit - - newBattleConfig.blessings = [] - if (as_config.buff_id !== 0) { - newBattleConfig.blessings.push({ - id: as_config.buff_id, - level: 1 - }) - } - - if (challengeSelected) { - challengeSelected.MazeBuff.map((item) => { - newBattleConfig.blessings.push({ - id: item.ID, - level: 1 - }) - }) - } - - newBattleConfig.monsters = [] - newBattleConfig.stage_id = 0 - - let targetEventList: ASEvent[] = [] - if (as_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { - targetEventList = challengeSelected.EventList1 - } else if (as_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { - targetEventList = challengeSelected.EventList2 - } else if (as_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { - targetEventList = eventSelected.Tierce.EventList - } - - if (targetEventList.length > 0) { - newBattleConfig.stage_id = targetEventList[0].ID - for (const wave of targetEventList[0].MonsterList) { - const newWave: MonsterStore[] = [] - for (const value of Object.values(wave)) { - newWave.push({ - monster_id: value, - level: targetEventList[0].Level, - amount: 1, - }) - } - newBattleConfig.monsters.push(newWave) - } - } - - setAsConfig(newBattleConfig) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - challengeSelected, - eventSelected, - mapAS, - as_config.event_id, - as_config.challenge_id, - as_config.floor_side, - as_config.buff_id, - ]) - - if (!mapAS) return null - return ( -
    - - {/* Title Card */} -
    -
    - b.ID - a.ID).map((as) => ({ - id: as.ID.toString(), - name: getLocaleName(locale, as.Name), - time: `${as.BeginTime} - ${as.EndTime}`, - }))} - excludeSet={[]} - selectedCustomSet={as_config.event_id.toString()} - placeholder={transI18n("selectASEvent")} - setSelectedCustomSet={(id) => setAsConfig({ - ...as_config, - event_id: Number(id), - challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0, - buff_id: 0 - })} - /> -
    - {/* Settings */} -
    - -
    - - -
    - -
    - - -
    -
    -
    StageId: {as_config?.stage_id}
    - {eventSelected && ( -
    - ({ - id: buff.ID?.toString() || "", - name: getLocaleName(locale, buff?.Name) || "", - description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), - }))} - excludeSet={[]} - selectedCustomSet={as_config?.buff_id?.toString()} - placeholder={transI18n("selectBuff")} - setSelectedCustomSet={(id) => setAsConfig({ ...as_config, buff_id: Number(id) })} - /> -
    - )} - {/* Turbulence Buff */} -
    -

    {transI18n("turbulenceBuff")}

    - {challengeSelected ? ( - challengeSelected.MazeBuff.map((buff, i) => ( -
    - )) - ) : ( -
    {transI18n("noTurbulenceBuff")}
    - )} -
    -
    - - {/* Enemy Waves */} - {(as_config?.challenge_id ?? 0) !== 0 && ( -
    - {floorSideList.map((side, i) => { - const eventList = side.id === "firstNode" - ? challengeSelected?.EventList1 - : side.id === "secondNode" - ? challengeSelected?.EventList2 - : side.id === "thirdNode" - ? eventSelected?.Tierce?.EventList - : []; - - if (!eventList || eventList.length === 0) return null; - const targetEvent = eventList[0]; - - return ( -
    -

    {side.wave}

    - - {targetEvent?.MonsterList?.map((wave, waveIndex) => ( -
    -

    {transI18n("wave")} {waveIndex + 1}

    -
    - {Object.values(wave).map((waveValue, enemyIndex) => { - const monsterStats = calcMonsterStats( - mapMonster?.[waveValue.toString()], - targetEvent?.EliteGroup, - targetEvent?.HardLevelGroup, - targetEvent?.Level, - hardLevelConfig, - eliteConfig - ); - return ( -
    -
    - Lv. {targetEvent.Level} -
    - -
    - {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( -
    - Enemy Icon -
    - )} -
    - -
    -
    -
    - HP - {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Speed - {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Toughness - {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    - -
    - - Weakness - -
    - {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( - {icon} - ))} -
    -
    -
    -
    - ) - })} -
    -
    - ))} -
    - )})} -
    - )} - -
    - ) +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { ASEvent, MonsterStore } from "@/types"; +import { useTranslations } from "next-intl"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function AsBar() { + const { locale } = useLocaleStore() + const { + as_config, + setAsConfig + } = useUserDataStore() + const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + + const challengeSelected = useMemo(() => { + return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id) + }, [as_config, mapAS]) + + const eventSelected = useMemo(() => { + return mapAS[as_config.event_id.toString()] + }, [as_config, mapAS]) + + + const floorSideList = useMemo(() => { + if (!eventSelected) return []; + + const floorList = [ + { + id: "firstNode", + name: transI18n("firstNode"), + wave: transI18n("firstNodeEnemies") + }, + { + id: "secondNode", + name: transI18n("secondNode"), + wave: transI18n("secondNodeEnemies") + }, + + ] + + if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === as_config.challenge_id) { + floorList.push({ + id: "thirdNode", + name: transI18n("thirdNode"), + wave: transI18n("thirdNodeEnemies") + }) + } + return floorList + }, [as_config.challenge_id, eventSelected, transI18n]) + + const buffList = useMemo(() => { + if (!eventSelected) return []; + + if (as_config.floor_side === "firstNode") { + return eventSelected?.BuffList1 ?? []; + } + + if (as_config.floor_side === "secondNode") { + return eventSelected?.BuffList2 ?? []; + } + + if (as_config.floor_side === "thirdNode" && eventSelected?.BuffList3) { + return eventSelected?.BuffList3 ?? []; + } + + return []; + }, [as_config.floor_side, eventSelected]); + + useEffect(() => { + if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return + const newBattleConfig = structuredClone(as_config) + newBattleConfig.cycle_count = challengeSelected.TurnLimit + + newBattleConfig.blessings = [] + if (as_config.buff_id !== 0) { + newBattleConfig.blessings.push({ + id: as_config.buff_id, + level: 1 + }) + } + + if (challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + + let targetEventList: ASEvent[] = [] + if (as_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { + targetEventList = challengeSelected.EventList1 + } else if (as_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { + targetEventList = challengeSelected.EventList2 + } else if (as_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { + targetEventList = eventSelected.Tierce.EventList + } + + if (targetEventList.length > 0) { + newBattleConfig.stage_id = targetEventList[0].ID + for (const wave of targetEventList[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: targetEventList[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + + setAsConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + challengeSelected, + eventSelected, + mapAS, + as_config.event_id, + as_config.challenge_id, + as_config.floor_side, + as_config.buff_id, + ]) + + if (!mapAS) return null + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((as) => ({ + id: as.ID.toString(), + name: getLocaleName(locale, as.Name), + time: `${as.BeginTime} - ${as.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={as_config.event_id.toString()} + placeholder={transI18n("selectASEvent")} + setSelectedCustomSet={(id) => setAsConfig({ + ...as_config, + event_id: Number(id), + challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0, + buff_id: 0 + })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + +
    + + +
    +
    +
    StageId: {as_config?.stage_id}
    + {eventSelected && ( +
    + ({ + id: buff.ID?.toString() || "", + name: getLocaleName(locale, buff?.Name) || "", + description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), + }))} + excludeSet={[]} + selectedCustomSet={as_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setAsConfig({ ...as_config, buff_id: Number(id) })} + /> +
    + )} + {/* Turbulence Buff */} +
    +

    {transI18n("turbulenceBuff")}

    + {challengeSelected ? ( + challengeSelected.MazeBuff.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + {(as_config?.challenge_id ?? 0) !== 0 && ( +
    + {floorSideList.map((side, i) => { + const eventList = side.id === "firstNode" + ? challengeSelected?.EventList1 + : side.id === "secondNode" + ? challengeSelected?.EventList2 + : side.id === "thirdNode" + ? eventSelected?.Tierce?.EventList + : []; + + if (!eventList || eventList.length === 0) return null; + const targetEvent = eventList[0]; + + return ( +
    +

    {side.wave}

    + + {targetEvent?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + targetEvent?.EliteGroup, + targetEvent?.HardLevelGroup, + targetEvent?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {targetEvent.Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + )})} +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/monsterBar/ce.tsx b/src/components/monsterBar/ce.tsx index e900891..9bc26b4 100644 --- a/src/components/monsterBar/ce.tsx +++ b/src/components/monsterBar/ce.tsx @@ -1,533 +1,533 @@ -"use client"; -import { useEffect, useMemo, useState } from "react"; -import { - Plus, - Trash2, - ChevronUp, - ChevronDown, - Search, - CopyPlus, -} from "lucide-react"; - -import useUserDataStore from "@/stores/userDataStore"; -import useLocaleStore from "@/stores/localeStore"; -import { getLocaleName } from "@/helper"; -import Image from "next/image"; -import { useTranslations } from "next-intl"; -import useGlobalStore from "@/stores/globalStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import { MonsterDetail } from "@/types"; - - -export default function CeBar() { - const [searchTerm, setSearchTerm] = useState(""); - const [showSearchWaveId, setShowSearchWaveId] = useState(null); - const { ce_config, setCeConfig } = useUserDataStore() - const { mapMonster, stage, damageType } = useDetailDataStore() - const { locale } = useLocaleStore() - const transI18n = useTranslations("DataPage") - const [showSearchStage, setShowSearchStage] = useState(false) - const [stageSearchTerm, setStageSearchTerm] = useState("") - const [stagePage, setStagePage] = useState(1) - const { extraData, setExtraData } = useGlobalStore() - - const pageSize = 30 - - const pageSizeMonsters = 30 - const [monsterPage, setMonsterPage] = useState(1) - - const filteredMonsters = useMemo(() => { - const newlistMonster = new Set() - for (const monster of Object.values(mapMonster)) { - if (getLocaleName(locale, monster.Name).toLowerCase().includes(searchTerm.toLowerCase())) { - newlistMonster.add(monster) - } - if (monster.ID.toString().includes(searchTerm.toLowerCase())) { - newlistMonster.add(monster) - } - } - return Array.from(newlistMonster) - }, [locale, searchTerm, mapMonster]); - - const paginatedMonsters = useMemo(() => - filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters), - [filteredMonsters, monsterPage] - ) - - const hasMoreMonsterPages = useMemo( - () => monsterPage * pageSizeMonsters < filteredMonsters.length, - [monsterPage, filteredMonsters] - ) - - useEffect(() => { - setMonsterPage(1) - }, [searchTerm]) - - const stageList = useMemo(() => Object.values(stage).map((item) => ({ - id: item.ID.toString(), - name: `${getLocaleName(locale, item.Name)} (${item.ID})`, - })), [stage, locale]) - - const filteredStages = useMemo(() => stageList.filter((s) => - s.name.toLowerCase().includes(stageSearchTerm.toLowerCase()) - ), [stageList, stageSearchTerm]) - - const paginatedStages = useMemo(() => filteredStages.slice( - (stagePage - 1) * pageSize, - stagePage * pageSize - ), [filteredStages, stagePage, pageSize]) - - const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) - - useEffect(() => { - setStagePage(1) - }, [stageSearchTerm]) - - - useEffect(() => { - if (!ce_config) return - if (!extraData || !extraData.theory_craft?.mode) return - - const newExtraData = structuredClone(extraData) - if (!newExtraData?.theory_craft?.hp) { - newExtraData.theory_craft!.hp = {} - } - - for (let i = 0; i < ce_config.monsters.length; i++) { - const waveKey = (i + 1).toString() - if (!newExtraData.theory_craft!.hp[waveKey]) { - newExtraData.theory_craft!.hp[waveKey] = [] - } - for (let j = 0; j < ce_config.monsters[i].length; j++) { - if (newExtraData.theory_craft!.hp[waveKey][j] === undefined) { - newExtraData.theory_craft!.hp[waveKey][j] = 0 - } - } - } - setExtraData(newExtraData) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ce_config]) - - - return ( -
    { - - setShowSearchWaveId(null) - setShowSearchStage(false) - }}> - -
    -
    - -
    - {showSearchStage && ( -
    e.stopPropagation()} className="absolute top-full mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-lg"> -
    - - -
    - -
    - {paginatedStages.length > 0 ? ( - <> - {paginatedStages.map((stage) => ( -
    { - if (ce_config.stage_id !== Number(stage.id)) { - setCeConfig({ ...ce_config, stage_id: Number(stage.id), cycle_count: 30 }) - } - setShowSearchStage(false) - setStageSearchTerm("") - }} - > - {stage.name} -
    - ))} - - - - ) : ( -
    {transI18n("noStageFound")}
    - )} -
    - {paginatedStages.length > 0 && ( -
    - - - -
    - )} -
    - )} -
    - -
    - {ce_config.monsters.map((wave, waveIndex) => ( -
    -
    -
    -

    {transI18n("wave")} {waveIndex + 1}

    -
    - - - - - - - -
    -
    - -
    - {wave.map((member, memberIndex) => ( -
    -
    -
    - - - -
    - {mapMonster?.[member.monster_id.toString()]?.Image?.IconPath && Enemy Icon} -
    - -
    - {mapMonster?.[member.monster_id.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( - {icon} - ))} -
    -
    -
    - {getLocaleName(locale, mapMonster?.[member.monster_id.toString()]?.Name)} {`(${member.monster_id})`} -
    -
    - LV. - { - const val = Number(e.target.value) - if (isNaN(val) || val < 1 || val > 95) return - if (ce_config.monsters[waveIndex][memberIndex].level === val) return - - const newCeConfig = structuredClone(ce_config) - newCeConfig.monsters[waveIndex][memberIndex].level = val - setCeConfig(newCeConfig) - }} - /> -
    - {(extraData?.theory_craft?.mode === true && ( -
    - HP - { - const val = Number(e.target.value) - if (isNaN(val) || val < 0) return - - const newData = structuredClone(extraData) - - if (!newData?.theory_craft?.hp?.[(waveIndex + 1).toString()]) { - newData.theory_craft!.hp![(waveIndex + 1).toString()] = [] - } - - newData.theory_craft!.hp![(waveIndex + 1).toString()][memberIndex] = val - - setExtraData(newData) - }} - /> -
    - ))} -
    - -
    -
    -
    - ))} - - {/* Add Member Button + Search */} -
    e.stopPropagation()} - > - - - {showSearchWaveId === waveIndex && ( -
    -
    - - - -
    - -
    - {paginatedMonsters.length > 0 ? ( - paginatedMonsters.map((monster) => ( -
    { - const newCeConfig = structuredClone(ce_config) - newCeConfig.monsters[waveIndex].push({ - monster_id: monster.ID, - level: 95, - amount: 1, - }) - setCeConfig(newCeConfig) - setShowSearchWaveId(null) - }} - > -
    - {mapMonster?.[monster.ID.toString()]?.Image?.IconPath && ( - Enemy Icon - )} -
    - {getLocaleName(locale, monster.Name)} {`(${monster.ID})`} -
    - )) - ) : ( -
    - {transI18n("noMonstersFound")} -
    - )} -
    - - {filteredMonsters.length > 0 && ( -
    - - - -
    - )} - -
    - )} -
    -
    -
    -
    - ))} - - {/* Add New Wave Button */} -
    -
    - -
    -
    -
    -
    - ); -} +"use client"; +import { useEffect, useMemo, useState } from "react"; +import { + Plus, + Trash2, + ChevronUp, + ChevronDown, + Search, + CopyPlus, +} from "lucide-react"; + +import useUserDataStore from "@/stores/userDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import { getLocaleName } from "@/helper"; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import useGlobalStore from "@/stores/globalStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { MonsterDetail } from "@/types"; + + +export default function CeBar() { + const [searchTerm, setSearchTerm] = useState(""); + const [showSearchWaveId, setShowSearchWaveId] = useState(null); + const { ce_config, setCeConfig } = useUserDataStore() + const { mapMonster, stage, damageType } = useDetailDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const [showSearchStage, setShowSearchStage] = useState(false) + const [stageSearchTerm, setStageSearchTerm] = useState("") + const [stagePage, setStagePage] = useState(1) + const { extraData, setExtraData } = useGlobalStore() + + const pageSize = 30 + + const pageSizeMonsters = 30 + const [monsterPage, setMonsterPage] = useState(1) + + const filteredMonsters = useMemo(() => { + const newlistMonster = new Set() + for (const monster of Object.values(mapMonster)) { + if (getLocaleName(locale, monster.Name).toLowerCase().includes(searchTerm.toLowerCase())) { + newlistMonster.add(monster) + } + if (monster.ID.toString().includes(searchTerm.toLowerCase())) { + newlistMonster.add(monster) + } + } + return Array.from(newlistMonster) + }, [locale, searchTerm, mapMonster]); + + const paginatedMonsters = useMemo(() => + filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters), + [filteredMonsters, monsterPage] + ) + + const hasMoreMonsterPages = useMemo( + () => monsterPage * pageSizeMonsters < filteredMonsters.length, + [monsterPage, filteredMonsters] + ) + + useEffect(() => { + setMonsterPage(1) + }, [searchTerm]) + + const stageList = useMemo(() => Object.values(stage).map((item) => ({ + id: item.ID.toString(), + name: `${getLocaleName(locale, item.Name)} (${item.ID})`, + })), [stage, locale]) + + const filteredStages = useMemo(() => stageList.filter((s) => + s.name.toLowerCase().includes(stageSearchTerm.toLowerCase()) + ), [stageList, stageSearchTerm]) + + const paginatedStages = useMemo(() => filteredStages.slice( + (stagePage - 1) * pageSize, + stagePage * pageSize + ), [filteredStages, stagePage, pageSize]) + + const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) + + useEffect(() => { + setStagePage(1) + }, [stageSearchTerm]) + + + useEffect(() => { + if (!ce_config) return + if (!extraData || !extraData.theory_craft?.mode) return + + const newExtraData = structuredClone(extraData) + if (!newExtraData?.theory_craft?.hp) { + newExtraData.theory_craft!.hp = {} + } + + for (let i = 0; i < ce_config.monsters.length; i++) { + const waveKey = (i + 1).toString() + if (!newExtraData.theory_craft!.hp[waveKey]) { + newExtraData.theory_craft!.hp[waveKey] = [] + } + for (let j = 0; j < ce_config.monsters[i].length; j++) { + if (newExtraData.theory_craft!.hp[waveKey][j] === undefined) { + newExtraData.theory_craft!.hp[waveKey][j] = 0 + } + } + } + setExtraData(newExtraData) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ce_config]) + + + return ( +
    { + + setShowSearchWaveId(null) + setShowSearchStage(false) + }}> + +
    +
    + +
    + {showSearchStage && ( +
    e.stopPropagation()} className="absolute top-full mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-lg"> +
    + + +
    + +
    + {paginatedStages.length > 0 ? ( + <> + {paginatedStages.map((stage) => ( +
    { + if (ce_config.stage_id !== Number(stage.id)) { + setCeConfig({ ...ce_config, stage_id: Number(stage.id), cycle_count: 30 }) + } + setShowSearchStage(false) + setStageSearchTerm("") + }} + > + {stage.name} +
    + ))} + + + + ) : ( +
    {transI18n("noStageFound")}
    + )} +
    + {paginatedStages.length > 0 && ( +
    + + + +
    + )} +
    + )} +
    + +
    + {ce_config.monsters.map((wave, waveIndex) => ( +
    +
    +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + + + + + + + +
    +
    + +
    + {wave.map((member, memberIndex) => ( +
    +
    +
    + + + +
    + {mapMonster?.[member.monster_id.toString()]?.Image?.IconPath && Enemy Icon} +
    + +
    + {mapMonster?.[member.monster_id.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    + {getLocaleName(locale, mapMonster?.[member.monster_id.toString()]?.Name)} {`(${member.monster_id})`} +
    +
    + LV. + { + const val = Number(e.target.value) + if (isNaN(val) || val < 1 || val > 95) return + if (ce_config.monsters[waveIndex][memberIndex].level === val) return + + const newCeConfig = structuredClone(ce_config) + newCeConfig.monsters[waveIndex][memberIndex].level = val + setCeConfig(newCeConfig) + }} + /> +
    + {(extraData?.theory_craft?.mode === true && ( +
    + HP + { + const val = Number(e.target.value) + if (isNaN(val) || val < 0) return + + const newData = structuredClone(extraData) + + if (!newData?.theory_craft?.hp?.[(waveIndex + 1).toString()]) { + newData.theory_craft!.hp![(waveIndex + 1).toString()] = [] + } + + newData.theory_craft!.hp![(waveIndex + 1).toString()][memberIndex] = val + + setExtraData(newData) + }} + /> +
    + ))} +
    + +
    +
    +
    + ))} + + {/* Add Member Button + Search */} +
    e.stopPropagation()} + > + + + {showSearchWaveId === waveIndex && ( +
    +
    + + + +
    + +
    + {paginatedMonsters.length > 0 ? ( + paginatedMonsters.map((monster) => ( +
    { + const newCeConfig = structuredClone(ce_config) + newCeConfig.monsters[waveIndex].push({ + monster_id: monster.ID, + level: 95, + amount: 1, + }) + setCeConfig(newCeConfig) + setShowSearchWaveId(null) + }} + > +
    + {mapMonster?.[monster.ID.toString()]?.Image?.IconPath && ( + Enemy Icon + )} +
    + {getLocaleName(locale, monster.Name)} {`(${monster.ID})`} +
    + )) + ) : ( +
    + {transI18n("noMonstersFound")} +
    + )} +
    + + {filteredMonsters.length > 0 && ( +
    + + + +
    + )} + +
    + )} +
    +
    +
    +
    + ))} + + {/* Add New Wave Button */} +
    +
    + +
    +
    +
    +
    + ); +} diff --git a/src/components/monsterBar/index.tsx b/src/components/monsterBar/index.tsx index 2b2d343..b8ea4d2 100644 --- a/src/components/monsterBar/index.tsx +++ b/src/components/monsterBar/index.tsx @@ -1,100 +1,100 @@ -import MocBar from "./moc"; -import useUserDataStore from "@/stores/userDataStore"; -import PfBar from "./pf"; -import Image from "next/image"; -import AsBar from "./as"; -import { useTranslations } from "next-intl"; -import CeBar from "./ce"; -import PeakBar from "./peak"; - -export default function MonsterBar() { - const { battle_type, setBattleType } = useUserDataStore() - const transI18n = useTranslations("DataPage") - - const navItems = [ - { name: transI18n("memoryOfChaos"), icon: 'AbyssIcon01', value: 'MOC' }, - { name: transI18n("pureFiction"), icon: 'ChallengeStory', value: 'PF' }, - { name: transI18n("apocalypticShadow"), icon: 'ChallengeBoss', value: 'AS' }, - { name: transI18n("anomalyArbitration"), icon: 'ChallengePeakIcon', value: 'PEAK' }, - { name: transI18n("customEnemy"), icon: 'MonsterIcon', value: 'CE' }, - { name: transI18n("simulatedUniverse"), icon: 'SimulatedUniverse', value: 'SU' }, - - ]; - - - return ( -
    - {/* Header Navigation */} - - - {(battle_type.toUpperCase() === "DEFAULT" || battle_type.toUpperCase() === "") && ( -
    - {transI18n("noEventSelected")} -
    - )} - {battle_type.toUpperCase() === 'MOC' && } - {battle_type.toUpperCase() === 'PF' && } - {battle_type.toUpperCase() === 'AS' && } - {battle_type.toUpperCase() === 'CE' && } - {battle_type.toUpperCase() === 'PEAK' && } - {battle_type.toUpperCase() === 'SU' && ( -
    - {transI18n("comingSoon")} -
    - )} - -
    - ) +import MocBar from "./moc"; +import useUserDataStore from "@/stores/userDataStore"; +import PfBar from "./pf"; +import Image from "next/image"; +import AsBar from "./as"; +import { useTranslations } from "next-intl"; +import CeBar from "./ce"; +import PeakBar from "./peak"; + +export default function MonsterBar() { + const { battle_type, setBattleType } = useUserDataStore() + const transI18n = useTranslations("DataPage") + + const navItems = [ + { name: transI18n("memoryOfChaos"), icon: 'AbyssIcon01', value: 'MOC' }, + { name: transI18n("pureFiction"), icon: 'ChallengeStory', value: 'PF' }, + { name: transI18n("apocalypticShadow"), icon: 'ChallengeBoss', value: 'AS' }, + { name: transI18n("anomalyArbitration"), icon: 'ChallengePeakIcon', value: 'PEAK' }, + { name: transI18n("customEnemy"), icon: 'MonsterIcon', value: 'CE' }, + { name: transI18n("simulatedUniverse"), icon: 'SimulatedUniverse', value: 'SU' }, + + ]; + + + return ( +
    + {/* Header Navigation */} + + + {(battle_type.toUpperCase() === "DEFAULT" || battle_type.toUpperCase() === "") && ( +
    + {transI18n("noEventSelected")} +
    + )} + {battle_type.toUpperCase() === 'MOC' && } + {battle_type.toUpperCase() === 'PF' && } + {battle_type.toUpperCase() === 'AS' && } + {battle_type.toUpperCase() === 'CE' && } + {battle_type.toUpperCase() === 'PEAK' && } + {battle_type.toUpperCase() === 'SU' && ( +
    + {transI18n("comingSoon")} +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/monsterBar/moc.tsx b/src/components/monsterBar/moc.tsx index 6cfac7f..6c8442b 100644 --- a/src/components/monsterBar/moc.tsx +++ b/src/components/monsterBar/moc.tsx @@ -1,342 +1,342 @@ -"use client" - -import { useEffect, useMemo } from "react"; -import SelectCustomText from "../select/customSelectText"; -import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore"; -import Image from "next/image"; -import { useTranslations } from "next-intl"; -import { MoCEvent, MonsterStore } from "@/types"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function MocBar() { - const { locale } = useLocaleStore() - const { - moc_config, - setMocConfig - } = useUserDataStore() - const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() - - const transI18n = useTranslations("DataPage") - - const challengeSelected = useMemo(() => { - return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id) - }, [moc_config, mapMoc]) - - const eventSelected = useMemo(() => { - return mapMoc[moc_config.event_id.toString()] - }, [moc_config, mapMoc]) - - const floorSideList = useMemo(() => { - if (!eventSelected) return []; - - const floorList = [ - { - id: "firstNode", - name: transI18n("firstNode"), - wave: transI18n("firstNodeEnemies") - }, - { - id: "secondNode", - name: transI18n("secondNode"), - wave: transI18n("secondNodeEnemies") - }, - - ] - - if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === moc_config.challenge_id) { - floorList.push({ - id: "thirdNode", - name: transI18n("thirdNode"), - wave: transI18n("thirdNodeEnemies") - }) - } - return floorList - }, [moc_config.challenge_id, eventSelected, transI18n]) - - useEffect(() => { - if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return - - const newBattleConfig = structuredClone(moc_config) - newBattleConfig.cycle_count = 0 - if (moc_config.use_cycle_count) { - newBattleConfig.cycle_count = challengeSelected.TurnLimit - } - newBattleConfig.blessings = [] - if (moc_config.use_turbulence_buff && challengeSelected) { - challengeSelected.MazeBuff.map((item) => { - newBattleConfig.blessings.push({ - id: item.ID, - level: 1 - }) - }) - } - newBattleConfig.monsters = [] - newBattleConfig.stage_id = 0 - - let targetEventList: MoCEvent[] = [] - if (moc_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { - targetEventList = challengeSelected.EventList1 - } else if (moc_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { - targetEventList = challengeSelected.EventList2 - } else if (moc_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { - targetEventList = eventSelected.Tierce.EventList - } - - if (targetEventList.length > 0) { - newBattleConfig.stage_id = targetEventList[0].ID - for (const wave of targetEventList[0].MonsterList) { - const newWave: MonsterStore[] = [] - for (const value of Object.values(wave)) { - newWave.push({ - monster_id: value, - level: targetEventList[0].Level, - amount: 1, - }) - } - newBattleConfig.monsters.push(newWave) - } - } - - setMocConfig(newBattleConfig) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - challengeSelected, - eventSelected, - moc_config.event_id, - moc_config.challenge_id, - moc_config.floor_side, - moc_config.use_cycle_count, - moc_config.use_turbulence_buff, - mapMoc, - ]) - - if (!mapMoc) return null - - return ( -
    - - {/* Title Card */} -
    -
    - b.ID - a.ID).map((moc) => ({ - id: moc.ID.toString(), - name: getLocaleName(locale, moc.Name), - time: `${moc.BeginTime} - ${moc.EndTime}`, - }))} - excludeSet={[]} - selectedCustomSet={moc_config.event_id.toString()} - placeholder={transI18n("selectMOCEvent")} - setSelectedCustomSet={(id) => setMocConfig({ - ...moc_config, - event_id: Number(id), - challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0, - })} - /> -
    - {/* Settings */} - -
    - -
    - - -
    - -
    - - -
    - - - -
    - -
    -
    -
    StageId: {moc_config?.stage_id}
    - - {/* Turbulence Buff */} -
    -
    - setMocConfig({ ...moc_config, use_turbulence_buff: e.target.checked })} - className="checkbox checkbox-primary" - /> - setMocConfig({ ...moc_config, use_turbulence_buff: !moc_config.use_turbulence_buff })} - className="font-bold text-success cursor-pointer"> - {transI18n("useTurbulenceBuff")} - -
    - {challengeSelected ? ( - challengeSelected.MazeBuff.map((buff, i) => ( -
    - )) - ) : ( -
    {transI18n("noTurbulenceBuff")}
    - )} -
    -
    - - {/* Enemy Waves */} - {(moc_config?.challenge_id ?? 0) !== 0 && ( -
    - {floorSideList.map((side, i) => { - const eventList = side.id === "firstNode" - ? challengeSelected?.EventList1 - : side.id === "secondNode" - ? challengeSelected?.EventList2 - : side.id === "thirdNode" - ? eventSelected?.Tierce?.EventList - : []; - - if (!eventList || eventList.length === 0) return null; - const targetEvent = eventList[0]; - - return ( -
    -

    {side.wave}

    - - {targetEvent?.MonsterList?.map((wave, waveIndex) => ( -
    -

    {transI18n("wave")} {waveIndex + 1}

    -
    - {Object.values(wave).map((waveValue, enemyIndex) => { - const monsterStats = calcMonsterStats( - mapMonster?.[waveValue.toString()], - targetEvent?.EliteGroup, - targetEvent?.HardLevelGroup, - targetEvent?.Level, - hardLevelConfig, - eliteConfig - ); - return ( -
    -
    - Lv. {targetEvent.Level} -
    - -
    - {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( -
    - Enemy Icon -
    - )} -
    - -
    -
    -
    - HP - {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Speed - {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Toughness - {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    - -
    - - Weakness - -
    - {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( - {icon} - ))} -
    -
    -
    -
    - ) - })} -
    -
    - ))} -
    - ) - })} -
    - )} - -
    - ) +"use client" + +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import { MoCEvent, MonsterStore } from "@/types"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function MocBar() { + const { locale } = useLocaleStore() + const { + moc_config, + setMocConfig + } = useUserDataStore() + const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + + const transI18n = useTranslations("DataPage") + + const challengeSelected = useMemo(() => { + return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id) + }, [moc_config, mapMoc]) + + const eventSelected = useMemo(() => { + return mapMoc[moc_config.event_id.toString()] + }, [moc_config, mapMoc]) + + const floorSideList = useMemo(() => { + if (!eventSelected) return []; + + const floorList = [ + { + id: "firstNode", + name: transI18n("firstNode"), + wave: transI18n("firstNodeEnemies") + }, + { + id: "secondNode", + name: transI18n("secondNode"), + wave: transI18n("secondNodeEnemies") + }, + + ] + + if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === moc_config.challenge_id) { + floorList.push({ + id: "thirdNode", + name: transI18n("thirdNode"), + wave: transI18n("thirdNodeEnemies") + }) + } + return floorList + }, [moc_config.challenge_id, eventSelected, transI18n]) + + useEffect(() => { + if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return + + const newBattleConfig = structuredClone(moc_config) + newBattleConfig.cycle_count = 0 + if (moc_config.use_cycle_count) { + newBattleConfig.cycle_count = challengeSelected.TurnLimit + } + newBattleConfig.blessings = [] + if (moc_config.use_turbulence_buff && challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + + let targetEventList: MoCEvent[] = [] + if (moc_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { + targetEventList = challengeSelected.EventList1 + } else if (moc_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { + targetEventList = challengeSelected.EventList2 + } else if (moc_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { + targetEventList = eventSelected.Tierce.EventList + } + + if (targetEventList.length > 0) { + newBattleConfig.stage_id = targetEventList[0].ID + for (const wave of targetEventList[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: targetEventList[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + + setMocConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + challengeSelected, + eventSelected, + moc_config.event_id, + moc_config.challenge_id, + moc_config.floor_side, + moc_config.use_cycle_count, + moc_config.use_turbulence_buff, + mapMoc, + ]) + + if (!mapMoc) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((moc) => ({ + id: moc.ID.toString(), + name: getLocaleName(locale, moc.Name), + time: `${moc.BeginTime} - ${moc.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={moc_config.event_id.toString()} + placeholder={transI18n("selectMOCEvent")} + setSelectedCustomSet={(id) => setMocConfig({ + ...moc_config, + event_id: Number(id), + challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0, + })} + /> +
    + {/* Settings */} + +
    + +
    + + +
    + +
    + + +
    + + + +
    + +
    +
    +
    StageId: {moc_config?.stage_id}
    + + {/* Turbulence Buff */} +
    +
    + setMocConfig({ ...moc_config, use_turbulence_buff: e.target.checked })} + className="checkbox checkbox-primary" + /> + setMocConfig({ ...moc_config, use_turbulence_buff: !moc_config.use_turbulence_buff })} + className="font-bold text-success cursor-pointer"> + {transI18n("useTurbulenceBuff")} + +
    + {challengeSelected ? ( + challengeSelected.MazeBuff.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + {(moc_config?.challenge_id ?? 0) !== 0 && ( +
    + {floorSideList.map((side, i) => { + const eventList = side.id === "firstNode" + ? challengeSelected?.EventList1 + : side.id === "secondNode" + ? challengeSelected?.EventList2 + : side.id === "thirdNode" + ? eventSelected?.Tierce?.EventList + : []; + + if (!eventList || eventList.length === 0) return null; + const targetEvent = eventList[0]; + + return ( +
    +

    {side.wave}

    + + {targetEvent?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + targetEvent?.EliteGroup, + targetEvent?.HardLevelGroup, + targetEvent?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {targetEvent.Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + ) + })} +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/monsterBar/peak.tsx b/src/components/monsterBar/peak.tsx index 090d620..bf75f39 100644 --- a/src/components/monsterBar/peak.tsx +++ b/src/components/monsterBar/peak.tsx @@ -1,308 +1,308 @@ -"use client" -import { useEffect, useMemo } from "react"; -import SelectCustomText from "../select/customSelectText"; -import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore";; -import Image from "next/image"; -import { useTranslations } from "next-intl"; -import { MonsterStore } from "@/types"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function PeakBar() { - const { locale } = useLocaleStore() - const { - peak_config, - setPeakConfig - } = useUserDataStore() - const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore() - const transI18n = useTranslations("DataPage") - - const listFloor = useMemo(() => { - const peak = mapPeak?.[peak_config?.event_id?.toString()] - if (!peak) return [] - - return [...peak.PreLevel, peak.BossLevel].filter(it => it != null) - }, [peak_config, mapPeak]) - const eventSelected = useMemo(() => { - return mapPeak?.[peak_config?.event_id?.toString()] - }, [peak_config, mapPeak]) - - const bossConfig = useMemo(() => { - return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig; - }, [peak_config, mapPeak]) - - const challengeSelected = useMemo(() => { - const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id)) - if ( - challenge - && challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID - && bossConfig - && peak_config?.boss_mode === "Hard" - ) { - return { challenge: bossConfig, isBoss: true } - } - return { - challenge: challenge, - isBoss: challenge?.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID, - } - }, [peak_config, listFloor, mapPeak, bossConfig]) - - useEffect(() => { - if (!challengeSelected.challenge) return - if (peak_config.event_id !== 0 && peak_config.challenge_id !== 0 && challengeSelected) { - const newBattleConfig = structuredClone(peak_config) - newBattleConfig.cycle_count = 6 - newBattleConfig.blessings = [] - for (const value of challengeSelected?.challenge?.MazeBuff) { - newBattleConfig.blessings.push({ - id: value.ID, - level: 1 - }) - } - if (peak_config.buff_id !== 0 && challengeSelected.isBoss) { - newBattleConfig.blessings.push({ - id: peak_config.buff_id, - level: 1 - }) - } - newBattleConfig.monsters = [] - newBattleConfig.stage_id = challengeSelected.challenge.EventList[0].ID - for (const wave of challengeSelected.challenge.EventList[0].MonsterList) { - if (!wave) continue - const newWave: MonsterStore[] = [] - for (const value of Object.values(wave)) { - if (!value) continue - newWave.push({ - monster_id: value, - level: challengeSelected.challenge.EventList[0].Level, - amount: 1, - }) - } - newBattleConfig.monsters.push(newWave) - } - - setPeakConfig(newBattleConfig) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - peak_config.event_id, - peak_config.challenge_id, - peak_config.buff_id, - peak_config.boss_mode, - mapPeak, - ]) - - if (!mapPeak) return null - - return ( -
    - - {/* Title Card */} -
    -
    - b.ID - a.ID).map((peak) => ({ - id: peak.ID.toString(), - name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`, - }))} - excludeSet={[]} - selectedCustomSet={peak_config.event_id.toString()} - placeholder={transI18n("selectPEAKEvent")} - setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })} - /> -
    - {/* Settings */} -
    - -
    - - -
    - {eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && ( -
    - - -
    - )} - -
    -
    StageId: {peak_config?.stage_id}
    - { - eventSelected - && eventSelected.BossLevel?.ID === peak_config.challenge_id - && bossConfig - && ( -
    - ({ - id: buff.ID.toString(), - name: getLocaleName(locale, buff?.Name || ""), - description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []), - })) - } - excludeSet={[]} - selectedCustomSet={peak_config?.buff_id?.toString()} - placeholder={transI18n("selectBuff")} - setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, buff_id: Number(id) })} - /> -
    - ) - } - - {/* Turbulence Buff */} - -
    -

    - {transI18n("turbulenceBuff")} -

    - - {challengeSelected?.challenge && challengeSelected?.challenge?.MazeBuff?.length > 0 ? ( - challengeSelected.challenge.MazeBuff.map((subOption, index) => ( -
    - -
    -
    - )) - ) : ( -
    {transI18n("noTurbulenceBuff")}
    - )} -
    -
    - - {/* Enemy Waves */} - - {(peak_config?.challenge_id ?? 0) !== 0 && ( -
    - -
    -

    {getLocaleName(locale, challengeSelected?.challenge?.Name)}

    - - - {challengeSelected?.challenge && Object.values(challengeSelected?.challenge?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => ( -
    -

    {transI18n("wave")} {waveIndex + 1}

    -
    - {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { - const monsterStats = calcMonsterStats( - mapMonster?.[monsterId.toString()], - waveValue.EliteGroup, - challengeSelected?.challenge?.EventList?.[0]?.HardLevelGroup || 0, - challengeSelected?.challenge?.EventList?.[0]?.Level || 0, - hardLevelConfig, - eliteConfig - ); - return ( -
    -
    - Lv. {challengeSelected?.challenge?.EventList?.[0]?.Level} -
    - -
    - {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( -
    - Enemy Icon -
    - )} -
    - -
    -
    -
    - HP - {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Speed - {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Toughness - {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    - -
    - - Weakness - -
    - {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( - {icon} - ))} -
    -
    -
    -
    - ) - })} -
    -
    - ))} -
    -
    - )} - -
    - ) +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore";; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import { MonsterStore } from "@/types"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function PeakBar() { + const { locale } = useLocaleStore() + const { + peak_config, + setPeakConfig + } = useUserDataStore() + const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + + const listFloor = useMemo(() => { + const peak = mapPeak?.[peak_config?.event_id?.toString()] + if (!peak) return [] + + return [...peak.PreLevel, peak.BossLevel].filter(it => it != null) + }, [peak_config, mapPeak]) + const eventSelected = useMemo(() => { + return mapPeak?.[peak_config?.event_id?.toString()] + }, [peak_config, mapPeak]) + + const bossConfig = useMemo(() => { + return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig; + }, [peak_config, mapPeak]) + + const challengeSelected = useMemo(() => { + const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id)) + if ( + challenge + && challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID + && bossConfig + && peak_config?.boss_mode === "Hard" + ) { + return { challenge: bossConfig, isBoss: true } + } + return { + challenge: challenge, + isBoss: challenge?.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID, + } + }, [peak_config, listFloor, mapPeak, bossConfig]) + + useEffect(() => { + if (!challengeSelected.challenge) return + if (peak_config.event_id !== 0 && peak_config.challenge_id !== 0 && challengeSelected) { + const newBattleConfig = structuredClone(peak_config) + newBattleConfig.cycle_count = 6 + newBattleConfig.blessings = [] + for (const value of challengeSelected?.challenge?.MazeBuff) { + newBattleConfig.blessings.push({ + id: value.ID, + level: 1 + }) + } + if (peak_config.buff_id !== 0 && challengeSelected.isBoss) { + newBattleConfig.blessings.push({ + id: peak_config.buff_id, + level: 1 + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = challengeSelected.challenge.EventList[0].ID + for (const wave of challengeSelected.challenge.EventList[0].MonsterList) { + if (!wave) continue + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + if (!value) continue + newWave.push({ + monster_id: value, + level: challengeSelected.challenge.EventList[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + + setPeakConfig(newBattleConfig) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + peak_config.event_id, + peak_config.challenge_id, + peak_config.buff_id, + peak_config.boss_mode, + mapPeak, + ]) + + if (!mapPeak) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((peak) => ({ + id: peak.ID.toString(), + name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`, + }))} + excludeSet={[]} + selectedCustomSet={peak_config.event_id.toString()} + placeholder={transI18n("selectPEAKEvent")} + setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + {eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && ( +
    + + +
    + )} + +
    +
    StageId: {peak_config?.stage_id}
    + { + eventSelected + && eventSelected.BossLevel?.ID === peak_config.challenge_id + && bossConfig + && ( +
    + ({ + id: buff.ID.toString(), + name: getLocaleName(locale, buff?.Name || ""), + description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []), + })) + } + excludeSet={[]} + selectedCustomSet={peak_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, buff_id: Number(id) })} + /> +
    + ) + } + + {/* Turbulence Buff */} + +
    +

    + {transI18n("turbulenceBuff")} +

    + + {challengeSelected?.challenge && challengeSelected?.challenge?.MazeBuff?.length > 0 ? ( + challengeSelected.challenge.MazeBuff.map((subOption, index) => ( +
    + +
    +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + + {(peak_config?.challenge_id ?? 0) !== 0 && ( +
    + +
    +

    {getLocaleName(locale, challengeSelected?.challenge?.Name)}

    + + + {challengeSelected?.challenge && Object.values(challengeSelected?.challenge?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[monsterId.toString()], + waveValue.EliteGroup, + challengeSelected?.challenge?.EventList?.[0]?.HardLevelGroup || 0, + challengeSelected?.challenge?.EventList?.[0]?.Level || 0, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.challenge?.EventList?.[0]?.Level} +
    + +
    + {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/monsterBar/pf.tsx b/src/components/monsterBar/pf.tsx index d0bd1ef..3db761c 100644 --- a/src/components/monsterBar/pf.tsx +++ b/src/components/monsterBar/pf.tsx @@ -1,341 +1,341 @@ -"use client" -import { useEffect, useMemo } from "react"; -import SelectCustomText from "../select/customSelectText"; -import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; -import useLocaleStore from "@/stores/localeStore"; -import useUserDataStore from "@/stores/userDataStore"; -import Image from "next/image"; -import { MonsterStore, PFEvent } from "@/types"; -import { useTranslations } from "next-intl"; -import useDetailDataStore from "@/stores/detailDataStore"; - -export default function PfBar() { - const { locale } = useLocaleStore() - const { - pf_config, - setPfConfig - } = useUserDataStore() - const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() - - const transI18n = useTranslations("DataPage") - const challengeSelected = useMemo(() => { - return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id) - }, [pf_config, mapPF]) - - const eventSelected = useMemo(() => { - return mapPF[pf_config.event_id.toString()] - }, [pf_config, mapPF]) - - const floorSideList = useMemo(() => { - if (!eventSelected) return []; - - const floorList = [ - { - id: "firstNode", - name: transI18n("firstNode"), - wave: transI18n("firstNodeEnemies") - }, - { - id: "secondNode", - name: transI18n("secondNode"), - wave: transI18n("secondNodeEnemies") - }, - - ] - - if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === pf_config.challenge_id) { - floorList.push({ - id: "thirdNode", - name: transI18n("thirdNode"), - wave: transI18n("thirdNodeEnemies") - }) - } - return floorList - }, [pf_config.challenge_id, eventSelected, transI18n]) - - - useEffect(() => { - if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) { - return - } - const newBattleConfig = structuredClone(pf_config) - newBattleConfig.cycle_count = challengeSelected.TurnLimit - newBattleConfig.blessings = [] - if (pf_config.buff_id !== 0) { - newBattleConfig.blessings.push({ - id: pf_config.buff_id, - level: 1 - }) - } - if (challengeSelected) { - challengeSelected.MazeBuff.map((item) => { - newBattleConfig.blessings.push({ - id: item.ID, - level: 1 - }) - }) - } - newBattleConfig.monsters = [] - newBattleConfig.stage_id = 0 - - let targetEventList: PFEvent[] = [] - if (pf_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { - targetEventList = challengeSelected.EventList1 - } else if (pf_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { - targetEventList = challengeSelected.EventList2 - } else if (pf_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { - targetEventList = eventSelected.Tierce.EventList - } - - if (targetEventList.length > 0) { - newBattleConfig.stage_id = targetEventList[0].ID - for (const wave of targetEventList[0].MonsterList) { - const newWave: MonsterStore[] = [] - for (const value of Object.values(wave)) { - newWave.push({ - monster_id: value as number, - level: targetEventList[0].Level, - amount: 1, - }) - } - newBattleConfig.monsters.push(newWave) - } - } - - setPfConfig(newBattleConfig) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - challengeSelected, - eventSelected, - pf_config.event_id, - pf_config.challenge_id, - pf_config.floor_side, - pf_config.buff_id, - mapPF, - ]) - if (!mapPF) return null - - return ( -
    - - {/* Title Card */} -
    -
    - b.ID - a.ID).map((pf) => ({ - id: pf.ID.toString(), - name: getLocaleName(locale, pf.Name), - time: `${pf.BeginTime} - ${pf.EndTime}`, - }))} - excludeSet={[]} - selectedCustomSet={pf_config.event_id.toString()} - placeholder={transI18n("selectPFEvent")} - setSelectedCustomSet={(id) => setPfConfig({ - ...pf_config, - event_id: Number(id), - challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0, - buff_id: 0 - })} - /> -
    - {/* Settings */} -
    - -
    - - -
    - -
    - - -
    -
    -
    StageId: {pf_config?.stage_id}
    - {eventSelected && ( -
    - ({ - id: buff.ID?.toString() || "", - name: getLocaleName(locale, buff?.Name) || "", - description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), - }))} - excludeSet={[]} - selectedCustomSet={pf_config?.buff_id?.toString()} - placeholder={transI18n("selectBuff")} - setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, buff_id: Number(id) })} - /> -
    - )} - {/* Turbulence Buff */} -
    -

    {transI18n("turbulenceBuff")}

    - {eventSelected && eventSelected.SubOption.length > 0 ? ( - eventSelected.SubOption.map((subOption, index) => ( -
    - -
    -
    - )) - ) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? ( - challengeSelected?.MazeBuff?.map((buff, i) => ( -
    - )) - ) : ( -
    {transI18n("noTurbulenceBuff")}
    - )} -
    -
    - - {/* Enemy Waves */} - - {(pf_config?.challenge_id ?? 0) !== 0 && ( -
    - {floorSideList.map((side, i) => { - const eventList = side.id === "firstNode" - ? challengeSelected?.EventList1 - : side.id === "secondNode" - ? challengeSelected?.EventList2 - : side.id === "thirdNode" - ? eventSelected?.Tierce?.EventList - : []; - - if (!eventList || eventList.length === 0) return null; - const targetEvent = eventList[0]; - - return ( -
    -

    {side.wave}

    - - {targetEvent && Object.values(targetEvent.Infinite || []).map((waveValue, waveIndex) => ( -
    -

    {transI18n("wave")} {waveIndex + 1}

    -
    - {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { - const monsterStats = calcMonsterStats( - mapMonster?.[monsterId.toString()], - waveValue.EliteGroup, - targetEvent?.HardLevelGroup, - targetEvent?.Level, - hardLevelConfig, - eliteConfig - ); - return ( -
    -
    - Lv. {targetEvent.Level} -
    - -
    - {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( -
    - Enemy Icon -
    - )} -
    - -
    -
    -
    - HP - {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Speed - {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    - -
    - Toughness - {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} -
    -
    - -
    - - Weakness - -
    - {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( - {icon} - ))} -
    -
    -
    -
    - ) - })} -
    -
    - ))} -
    - ) - })} -
    - )} - -
    - ) +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { MonsterStore, PFEvent } from "@/types"; +import { useTranslations } from "next-intl"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function PfBar() { + const { locale } = useLocaleStore() + const { + pf_config, + setPfConfig + } = useUserDataStore() + const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + + const transI18n = useTranslations("DataPage") + const challengeSelected = useMemo(() => { + return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id) + }, [pf_config, mapPF]) + + const eventSelected = useMemo(() => { + return mapPF[pf_config.event_id.toString()] + }, [pf_config, mapPF]) + + const floorSideList = useMemo(() => { + if (!eventSelected) return []; + + const floorList = [ + { + id: "firstNode", + name: transI18n("firstNode"), + wave: transI18n("firstNodeEnemies") + }, + { + id: "secondNode", + name: transI18n("secondNode"), + wave: transI18n("secondNodeEnemies") + }, + + ] + + if (eventSelected?.Tierce && eventSelected.Tierce.PreChallenge === pf_config.challenge_id) { + floorList.push({ + id: "thirdNode", + name: transI18n("thirdNode"), + wave: transI18n("thirdNodeEnemies") + }) + } + return floorList + }, [pf_config.challenge_id, eventSelected, transI18n]) + + + useEffect(() => { + if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) { + return + } + const newBattleConfig = structuredClone(pf_config) + newBattleConfig.cycle_count = challengeSelected.TurnLimit + newBattleConfig.blessings = [] + if (pf_config.buff_id !== 0) { + newBattleConfig.blessings.push({ + id: pf_config.buff_id, + level: 1 + }) + } + if (challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + + let targetEventList: PFEvent[] = [] + if (pf_config.floor_side === "firstNode" && challengeSelected.EventList1.length > 0) { + targetEventList = challengeSelected.EventList1 + } else if (pf_config.floor_side === "secondNode" && challengeSelected.EventList2.length > 0) { + targetEventList = challengeSelected.EventList2 + } else if (pf_config.floor_side === "thirdNode" && eventSelected?.Tierce && eventSelected.Tierce.EventList.length > 0) { + targetEventList = eventSelected.Tierce.EventList + } + + if (targetEventList.length > 0) { + newBattleConfig.stage_id = targetEventList[0].ID + for (const wave of targetEventList[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value as number, + level: targetEventList[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + + setPfConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + challengeSelected, + eventSelected, + pf_config.event_id, + pf_config.challenge_id, + pf_config.floor_side, + pf_config.buff_id, + mapPF, + ]) + if (!mapPF) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((pf) => ({ + id: pf.ID.toString(), + name: getLocaleName(locale, pf.Name), + time: `${pf.BeginTime} - ${pf.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={pf_config.event_id.toString()} + placeholder={transI18n("selectPFEvent")} + setSelectedCustomSet={(id) => setPfConfig({ + ...pf_config, + event_id: Number(id), + challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0, + buff_id: 0 + })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + +
    + + +
    +
    +
    StageId: {pf_config?.stage_id}
    + {eventSelected && ( +
    + ({ + id: buff.ID?.toString() || "", + name: getLocaleName(locale, buff?.Name) || "", + description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), + }))} + excludeSet={[]} + selectedCustomSet={pf_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, buff_id: Number(id) })} + /> +
    + )} + {/* Turbulence Buff */} +
    +

    {transI18n("turbulenceBuff")}

    + {eventSelected && eventSelected.SubOption.length > 0 ? ( + eventSelected.SubOption.map((subOption, index) => ( +
    + +
    +
    + )) + ) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? ( + challengeSelected?.MazeBuff?.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + + {(pf_config?.challenge_id ?? 0) !== 0 && ( +
    + {floorSideList.map((side, i) => { + const eventList = side.id === "firstNode" + ? challengeSelected?.EventList1 + : side.id === "secondNode" + ? challengeSelected?.EventList2 + : side.id === "thirdNode" + ? eventSelected?.Tierce?.EventList + : []; + + if (!eventList || eventList.length === 0) return null; + const targetEvent = eventList[0]; + + return ( +
    +

    {side.wave}

    + + {targetEvent && Object.values(targetEvent.Infinite || []).map((waveValue, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[monsterId.toString()], + waveValue.EliteGroup, + targetEvent?.HardLevelGroup, + targetEvent?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {targetEvent.Level} +
    + +
    + {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + ) + })} +
    + )} + +
    + ) } \ No newline at end of file diff --git a/src/components/parseText/index.tsx b/src/components/parseText/index.tsx index 5f06404..82f0bbf 100644 --- a/src/components/parseText/index.tsx +++ b/src/components/parseText/index.tsx @@ -1,16 +1,16 @@ - -'use client' -import { parseRuby } from "@/helper"; - -interface TextProps { - text: string; - locale: string; - className?: string; -} - -export default function ParseText({ text, locale, className }: TextProps) { - if (locale === "ja") { - return
    ; - } - return
    {text}
    ; + +'use client' +import { parseRuby } from "@/helper"; + +interface TextProps { + text: string; + locale: string; + className?: string; +} + +export default function ParseText({ text, locale, className }: TextProps) { + if (locale === "ja") { + return
    ; + } + return
    {text}
    ; } \ No newline at end of file diff --git a/src/components/queryProvider/index.tsx b/src/components/queryProvider/index.tsx index d46af40..2685a7f 100644 --- a/src/components/queryProvider/index.tsx +++ b/src/components/queryProvider/index.tsx @@ -1,12 +1,12 @@ -"use client"; - -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import React from 'react'; - -export default function QueryProviderWrapper({ children }: { children: React.ReactNode }) { - const [queryClient] = React.useState(() => new QueryClient()); - - return ( - {children} - ); +"use client"; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import React from 'react'; + +export default function QueryProviderWrapper({ children }: { children: React.ReactNode }) { + const [queryClient] = React.useState(() => new QueryClient()); + + return ( + {children} + ); } \ No newline at end of file diff --git a/src/components/quickView/index.tsx b/src/components/quickView/index.tsx index 186f88c..dc18633 100644 --- a/src/components/quickView/index.tsx +++ b/src/components/quickView/index.tsx @@ -1,473 +1,473 @@ -"use client" -import NextImage from "next/image" -import useUserDataStore from "@/stores/userDataStore"; -import { useTranslations } from "next-intl"; -import { useMemo } from "react"; -import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper"; -import { mappingStats } from "@/constant/constant"; -import RelicShowcase from "../showcaseCard/relicShowcase"; -import useLocaleStore from "@/stores/localeStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import useCurrentDataStore from "@/stores/currentDataStore"; - -export default function QuickView() { - const { avatars } = useUserDataStore() - const transI18n = useTranslations("DataPage") - const { locale } = useLocaleStore() - const { avatarSelected, } = useCurrentDataStore() - const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore() - - - const avatarSkillTree = useMemo(() => { - if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} - if (avatars[avatarSelected?.ID?.toString()].enhanced) { - return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} - } - return avatarSelected?.SkillTrees || {} - }, [avatarSelected, avatars]) - - const avatarData = useMemo(() => { - if (!avatarSelected) return - return avatars[avatarSelected?.ID?.toString()] - }, [avatarSelected, avatars]) - - const avatarProfile = useMemo(() => { - if (!avatarSelected || !avatarData) return - return avatarData?.profileList?.[avatarData?.profileSelect] - }, [avatarSelected, avatarData]) - - const relicEffects = useMemo(() => { - const avatar = avatars[avatarSelected?.ID?.toString() || ""]; - const relicCount: { [key: string]: number } = {}; - if (avatar) { - for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { - if (relicCount[relic.relic_set_id]) { - relicCount[relic.relic_set_id]++; - } else { - relicCount[relic.relic_set_id] = 1; - } - } - } - const listEffects: { key: string, count: number }[] = []; - Object.entries(relicCount).forEach(([key, value]) => { - if (value >= 2) { - listEffects.push({ key: key, count: value }); - } - }); - return listEffects; - }, [avatars, avatarSelected]); - - const relicStats = useMemo(() => { - if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return - - return Object.entries(avatarProfile?.relics).map(([key, value]) => { - const mainAffixMap = mainAffix["5" + key] - const subAffixMap = subAffix["5"] - if (!mainAffixMap || !subAffixMap) return - return { - img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, - mainAffix: { - property: mainAffixMap?.[value?.main_affix_id]?.Property, - level: value?.level, - valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), - detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] - }, - subAffix: value?.sub_affixes?.map((subValue) => { - return { - property: subAffixMap?.[subValue?.sub_affix_id]?.Property, - valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), - detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], - step: subValue?.step, - count: subValue?.count - } - }) - } - }) - }, [avatarSelected, avatarProfile, mainAffix, subAffix]) - - const characterStats = useMemo(() => { - if (!avatarSelected || !avatarData) return - const charPromotion = calcPromotion(avatarData.level) - - const statsData: Record = { - HP: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, - avatarData.level - ), - name: "HP", - icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", - unit: "", - round: 0 - }, - ATK: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, - avatarData.level - ), - name: "ATK", - icon: "spriteoutput/ui/avatar/icon/IconAttack.png", - unit: "", - round: 0 - }, - DEF: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, - avatarData.level - ), - name: "DEF", - icon: "spriteoutput/ui/avatar/icon/IconDefence.png", - unit: "", - round: 0 - }, - SPD: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, - name: "SPD", - icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", - unit: "", - round: 1 - }, - CRITRate: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, - name: "CRIT Rate", - icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", - unit: "%", - round: 1 - }, - CRITDmg: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, - name: "CRIT DMG", - icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", - unit: "%", - round: 1 - }, - BreakEffect: { - value: 0, - base: 0, - name: "Break Effect", - icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", - unit: "%", - round: 1 - }, - EffectRES: { - value: 0, - base: 0, - name: "Effect RES", - icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", - unit: "%", - round: 1 - }, - EnergyRate: { - value: 0, - base: 0, - name: "Energy Rate", - icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", - unit: "%", - round: 1 - }, - EffectHitRate: { - value: 0, - base: 0, - name: "Effect Hit Rate", - icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", - unit: "%", - round: 1 - }, - HealBoost: { - value: 0, - base: 0, - name: "Healing Boost", - icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", - unit: "%", - round: 1 - }, - PhysicalAdd: { - value: 0, - base: 0, - name: "Physical Boost", - icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", - unit: "%", - round: 1 - }, - FireAdd: { - value: 0, - base: 0, - name: "Fire Boost", - icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", - unit: "%", - round: 1 - }, - IceAdd: { - value: 0, - base: 0, - name: "Ice Boost", - icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", - unit: "%", - round: 1 - }, - ThunderAdd: { - value: 0, - base: 0, - name: "Thunder Boost", - icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", - unit: "%", - round: 1 - }, - WindAdd: { - value: 0, - base: 0, - name: "Wind Boost", - icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", - unit: "%", - round: 1 - }, - QuantumAdd: { - value: 0, - base: 0, - name: "Quantum Boost", - icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", - unit: "%", - round: 1 - }, - ImaginaryAdd: { - value: 0, - base: 0, - name: "Imaginary Boost", - icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", - unit: "%", - round: 1 - }, - ElationAdd: { - value: 0, - base: 0, - name: "Elation Boost", - icon: "spriteoutput/ui/avatar/icon/IconJoy.png", - unit: "%", - round: 1 - } - } - - if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { - const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) - statsData.HP.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, - avatarProfile?.lightcone?.level - ) - statsData.HP.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHPAdd, - avatarProfile?.lightcone?.level - ) - statsData.ATK.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd, - avatarProfile?.lightcone?.level - ) - statsData.ATK.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd, - avatarProfile?.lightcone?.level - ) - statsData.DEF.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd, - avatarProfile?.lightcone?.level - ) - statsData.DEF.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd, - avatarProfile?.lightcone?.level - ) - - const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus - if (bonusData && bonusData.length > 0) { - const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") - const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") - bonusSpd.forEach((bonus) => { - statsData.SPD.value += bonus.Value - statsData.SPD.base += bonus.Value - }) - bonusOther.forEach((bonus) => { - const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) - } - }) - } - } - if (avatarSkillTree) { - Object.values(avatarSkillTree).forEach((value) => { - if (value?.["1"] - && value?.["1"]?.PointID - && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" - && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 - && value?.["1"]?.StatusAddList - && value?.["1"].StatusAddList.length > 0) { - value?.["1"]?.StatusAddList.forEach((status) => { - const statsBase = mappingStats?.[status?.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) - } - }) - } - }) - } - - - - if (avatarProfile?.relics && mainAffix && subAffix) { - Object.entries(avatarProfile?.relics).forEach(([key, value]) => { - const mainAffixMap = mainAffix["5" + key] - const subAffixMap = subAffix["5"] - if (!mainAffixMap || !subAffixMap) return - const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat - if (mainStats && statsData[mainStats]) { - statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) - } - value?.sub_affixes.forEach((subValue) => { - const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat - if (subStats && statsData[subStats]) { - statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) - } - }) - }) - } - - if (relicEffects && relicEffects.length > 0) { - relicEffects.forEach((relic) => { - const dataBonus = mapRelicSet?.[relic.key]?.Skills - if (!dataBonus || Object.keys(dataBonus).length === 0) return - Object.entries(dataBonus || {}).forEach(([key, value]) => { - if (relic.count < Number(key)) return - value.Bonus.forEach((bonus) => { - const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) - } - }) - }) - }) - } - - - return statsData - }, [ - avatarSelected, - avatarData, - mapAvatar, - avatarProfile?.lightcone, - avatarProfile?.relics, - mapLightCone, - mainAffix, - subAffix, - relicEffects, - mapRelicSet, - avatarSkillTree - ]) - - return ( -
    -
    -
    - {Object.entries(characterStats || {})?.map(([key, stat], index) => { - if (!stat || (key.includes("Add") && stat.value === 0)) return null - return ( -
    -
    - -
    {stat.name}
    -
    -
    -
    { - stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 - }{stat.unit}
    -
    - ) - })} -
    -
    - -
    - {relicEffects.map((setEffect, index) => { - const relicInfo = mapRelicSet[setEffect.key]; - if (!relicInfo) return null; - return ( -
    -
    -
    - {setEffect.count} -
    -
    - ) - })} -
    -
    - -
    - - {relicStats?.map((relic, index) => { - if (!relic || !avatarSelected) return null - return ( - - ) - })} - - {(!relicStats || !relicStats?.length) && ( -
    -
    - {transI18n("noRelicEquipped")} -
    -
    - )} -
    -
    - ) +"use client" +import NextImage from "next/image" +import useUserDataStore from "@/stores/userDataStore"; +import { useTranslations } from "next-intl"; +import { useMemo } from "react"; +import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper"; +import { mappingStats } from "@/constant/constant"; +import RelicShowcase from "../showcaseCard/relicShowcase"; +import useLocaleStore from "@/stores/localeStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import useCurrentDataStore from "@/stores/currentDataStore"; + +export default function QuickView() { + const { avatars } = useUserDataStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore() + const { avatarSelected, } = useCurrentDataStore() + const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore() + + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarProfile = useMemo(() => { + if (!avatarSelected || !avatarData) return + return avatarData?.profileList?.[avatarData?.profileSelect] + }, [avatarSelected, avatarData]) + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const relicStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return + + return Object.entries(avatarProfile?.relics).map(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + return { + img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, + mainAffix: { + property: mainAffixMap?.[value?.main_affix_id]?.Property, + level: value?.level, + valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), + detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] + }, + subAffix: value?.sub_affixes?.map((subValue) => { + return { + property: subAffixMap?.[subValue?.sub_affix_id]?.Property, + valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), + detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], + step: subValue?.step, + count: subValue?.count + } + }) + } + }) + }, [avatarSelected, avatarProfile, mainAffix, subAffix]) + + const characterStats = useMemo(() => { + if (!avatarSelected || !avatarData) return + const charPromotion = calcPromotion(avatarData.level) + + const statsData: Record = { + HP: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + name: "HP", + icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + round: 0 + }, + ATK: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + name: "ATK", + icon: "spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + round: 0 + }, + DEF: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + name: "DEF", + icon: "spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + round: 0 + }, + SPD: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + name: "SPD", + icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + round: 1 + }, + CRITRate: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + name: "CRIT Rate", + icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + round: 1 + }, + CRITDmg: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + name: "CRIT DMG", + icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + round: 1 + }, + BreakEffect: { + value: 0, + base: 0, + name: "Break Effect", + icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + round: 1 + }, + EffectRES: { + value: 0, + base: 0, + name: "Effect RES", + icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + round: 1 + }, + EnergyRate: { + value: 0, + base: 0, + name: "Energy Rate", + icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + round: 1 + }, + EffectHitRate: { + value: 0, + base: 0, + name: "Effect Hit Rate", + icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + round: 1 + }, + HealBoost: { + value: 0, + base: 0, + name: "Healing Boost", + icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + round: 1 + }, + PhysicalAdd: { + value: 0, + base: 0, + name: "Physical Boost", + icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + round: 1 + }, + FireAdd: { + value: 0, + base: 0, + name: "Fire Boost", + icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + round: 1 + }, + IceAdd: { + value: 0, + base: 0, + name: "Ice Boost", + icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + round: 1 + }, + ThunderAdd: { + value: 0, + base: 0, + name: "Thunder Boost", + icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + round: 1 + }, + WindAdd: { + value: 0, + base: 0, + name: "Wind Boost", + icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + round: 1 + }, + QuantumAdd: { + value: 0, + base: 0, + name: "Quantum Boost", + icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + round: 1 + }, + ImaginaryAdd: { + value: 0, + base: 0, + name: "Imaginary Boost", + icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + round: 1 + }, + ElationAdd: { + value: 0, + base: 0, + name: "Elation Boost", + icon: "spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + round: 1 + } + } + + if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { + const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) + statsData.HP.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.HP.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats?.[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + + const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus + if (bonusData && bonusData.length > 0) { + const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") + const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") + bonusSpd.forEach((bonus) => { + statsData.SPD.value += bonus.Value + statsData.SPD.base += bonus.Value + }) + bonusOther.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + } + } + if (avatarSkillTree) { + Object.values(avatarSkillTree).forEach((value) => { + if (value?.["1"] + && value?.["1"]?.PointID + && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" + && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 + && value?.["1"]?.StatusAddList + && value?.["1"].StatusAddList.length > 0) { + value?.["1"]?.StatusAddList.forEach((status) => { + const statsBase = mappingStats?.[status?.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) + } + }) + } + }) + } + + + + if (avatarProfile?.relics && mainAffix && subAffix) { + Object.entries(avatarProfile?.relics).forEach(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat + if (mainStats && statsData[mainStats]) { + statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) + } + value?.sub_affixes.forEach((subValue) => { + const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat + if (subStats && statsData[subStats]) { + statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) + } + }) + }) + } + + if (relicEffects && relicEffects.length > 0) { + relicEffects.forEach((relic) => { + const dataBonus = mapRelicSet?.[relic.key]?.Skills + if (!dataBonus || Object.keys(dataBonus).length === 0) return + Object.entries(dataBonus || {}).forEach(([key, value]) => { + if (relic.count < Number(key)) return + value.Bonus.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + }) + }) + } + + + return statsData + }, [ + avatarSelected, + avatarData, + mapAvatar, + avatarProfile?.lightcone, + avatarProfile?.relics, + mapLightCone, + mainAffix, + subAffix, + relicEffects, + mapRelicSet, + avatarSkillTree + ]) + + return ( +
    +
    +
    + {Object.entries(characterStats || {})?.map(([key, stat], index) => { + if (!stat || (key.includes("Add") && stat.value === 0)) return null + return ( +
    +
    + +
    {stat.name}
    +
    +
    +
    { + stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 + }{stat.unit}
    +
    + ) + })} +
    +
    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count} +
    +
    + ) + })} +
    +
    + +
    + + {relicStats?.map((relic, index) => { + if (!relic || !avatarSelected) return null + return ( + + ) + })} + + {(!relicStats || !relicStats?.length) && ( +
    +
    + {transI18n("noRelicEquipped")} +
    +
    + )} +
    +
    + ) } \ No newline at end of file diff --git a/src/components/relicBar/index.tsx b/src/components/relicBar/index.tsx index f027aca..7bd76b6 100644 --- a/src/components/relicBar/index.tsx +++ b/src/components/relicBar/index.tsx @@ -1,498 +1,498 @@ -"use client"; -import useUserDataStore from '@/stores/userDataStore'; -import { useEffect, useMemo, useState } from 'react'; -import SelectCustomImage from '../select/customSelectImage'; -import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper'; -import { mappingStats } from '@/constant/constant'; -import useModelStore from '@/stores/modelStore'; -import useRelicMakerStore from '@/stores/relicMakerStore'; -import { toast } from 'react-toastify'; -import { useTranslations } from 'next-intl' -import { ChevronDown, ChevronUp } from 'lucide-react'; -import { AnimatePresence, motion } from 'framer-motion'; -import useDetailDataStore from '@/stores/detailDataStore'; -import useCurrentDataStore from '@/stores/currentDataStore'; -import { RelicSetDetail, SubAffixData } from '@/types'; -import useLocaleStore from '@/stores/localeStore'; -import { mappingRelicSlot } from "@/constant/constant"; - -export default function RelicMaker() { - const { avatars, setAvatars } = useUserDataStore() - const { avatarSelected } = useCurrentDataStore() - const { setIsOpenRelic } = useModelStore() - const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore() - const { locale } = useLocaleStore() - const transI18n = useTranslations("DataPage") - const { - selectedRelicSlot, - selectedRelicSet, - selectedMainStat, - listSelectedSubStats, - selectedRelicLevel, - preSelectedSubStats, - setSelectedRelicSet, - setSelectedMainStat, - setSelectedRelicLevel, - setListSelectedSubStats, - resetHistory, - popHistory, - addHistory, - } = useRelicMakerStore() - const [error, setError] = useState(""); - - const relicSets = useMemo(() => { - const listSet: Record = {}; - for (const [key, value] of Object.entries(mapRelicSet || {})) { - let isOk = false; - for (const key2 of Object.keys(value.Parts)) { - if (key2 == mappingRelicSlot?.[selectedRelicSlot]) { - isOk = true; - break; - } - } - if (isOk) { - listSet[key] = value; - } - } - return listSet; - }, [mapRelicSet , selectedRelicSlot]); - - const subAffixOptions = useMemo(() => { - const listSet: Record = {}; - const subAffixMap = subAffix["5"]; - const mainAffixMap = mainAffix["5" + selectedRelicSlot] - - if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet; - - for (const [key, value] of Object.entries(subAffixMap)) { - if (value.Property !== mainAffixMap[selectedMainStat]?.Property) { - listSet[key] = value; - } - } - return listSet; - }, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]); - - useEffect(() => { - const subAffixMap = subAffix["5"]; - const mainAffixMap = mainAffix["5" + selectedRelicSlot]; - - if (!subAffixMap || !mainAffixMap) return; - - const mainProp = mainAffixMap[selectedMainStat]?.Property; - if (!mainProp) return; - - const newSubAffixes = structuredClone(listSelectedSubStats); - let updated = false; - - for (let i = 0; i < newSubAffixes.length; i++) { - if (newSubAffixes[i].property === mainProp) { - newSubAffixes[i].affixId = ""; - newSubAffixes[i].property = ""; - newSubAffixes[i].rollCount = 0; - newSubAffixes[i].stepCount = 0; - updated = true; - } - } - - if (updated) setListSelectedSubStats(newSubAffixes); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]); - - const exSubAffixOptions = useMemo(() => { - const listSet: Record = {}; - const subAffixMap = subAffix["5"]; - const mainAffixMap = mainAffix["5" + selectedRelicSlot]; - - if (!subAffixMap || !mainAffixMap) return listSet; - - for (const [key, value] of Object.entries(subAffixMap)) { - const subAffix = listSelectedSubStats.find((item) => item.property === value.Property); - if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) { - listSet[key] = value; - } - } - return listSet; - }, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]); - - const effectBonus = useMemo(() => { - const affixSet = mainAffix?.["5" + selectedRelicSlot]; - if (!affixSet) return 0; - - const data = affixSet[selectedMainStat]; - if (!data) return 0; - - return calcMainAffixBonus(data, selectedRelicLevel); - }, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]); - - const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => { - setError(""); - const newSubAffixes = structuredClone(listSelectedSubStats); - if (!subAffixOptions[key]) { - newSubAffixes[index].affixId = ""; - newSubAffixes[index].property = ""; - newSubAffixes[index].rollCount = rollCount; - newSubAffixes[index].stepCount = stepCount; - setListSelectedSubStats(newSubAffixes); - addHistory(index, newSubAffixes[index]); - return; - } - newSubAffixes[index].affixId = key; - newSubAffixes[index].property = subAffixOptions[key].Property; - newSubAffixes[index].rollCount = rollCount; - newSubAffixes[index].stepCount = stepCount; - setListSelectedSubStats(newSubAffixes); - addHistory(index, newSubAffixes[index]); - }; - - const handlerRollback = (index: number) => { - setError(""); - if (!preSelectedSubStats[index]) return; - - const keys = Object.keys(preSelectedSubStats[index]); - if (keys.length <= 1) return; - - const newSubAffixes = structuredClone(listSelectedSubStats); - const listHistory = structuredClone(preSelectedSubStats[index]); - const secondLastKey = listHistory.length - 2; - const preSubAffixes = { ...listHistory[secondLastKey] }; - newSubAffixes[index].rollCount = preSubAffixes.rollCount; - newSubAffixes[index].stepCount = preSubAffixes.stepCount; - setListSelectedSubStats(newSubAffixes); - popHistory(index); - }; - - const resetSubStat = (index: number) => { - const newSubAffixes = structuredClone(listSelectedSubStats); - resetHistory(index); - newSubAffixes[index].affixId = ""; - newSubAffixes[index].property = ""; - newSubAffixes[index].rollCount = 0; - newSubAffixes[index].stepCount = 0; - setListSelectedSubStats(newSubAffixes); - }; - - const randomizeStats = () => { - const newSubAffixes = structuredClone(listSelectedSubStats); - const exKeys = Object.keys(exSubAffixOptions); - for (let i = 0; i < newSubAffixes.length; i++) { - const keys = Object.keys(subAffixOptions).filter((key) => !exKeys.includes(key)); - const randomKey = keys[Math.floor(Math.random() * keys.length)]; - exKeys.push(randomKey); - const randomValue = subAffixOptions[randomKey]; - newSubAffixes[i].affixId = randomKey; - newSubAffixes[i].property = randomValue.Property; - newSubAffixes[i].rollCount = 0; - newSubAffixes[i].stepCount = 0; - } - for (let i = 0; i < newSubAffixes.length; i++) { - addHistory(i, newSubAffixes[i]); - } - setListSelectedSubStats(newSubAffixes); - - }; - - const randomizeRolls = () => { - const newSubAffixes = structuredClone(listSelectedSubStats); - const randomRolls = randomPartition(9, listSelectedSubStats.length); - for (let i = 0; i < listSelectedSubStats.length; i++) { - newSubAffixes[i].rollCount = randomRolls[i]; - newSubAffixes[i].stepCount = randomStep(randomRolls[i]); - } - setListSelectedSubStats(newSubAffixes); - for (let i = 0; i < newSubAffixes.length; i++) { - addHistory(i, newSubAffixes[i]); - } - }; - - const handlerSaveRelic = () => { - setError(""); - const avatar = avatars[avatarSelected?.ID?.toString() || ""]; - if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) { - setError(transI18n("pleaseSelectAllOptions")); - return; - }; - - if (listSelectedSubStats.find((item) => item.affixId === "")) { - setError(transI18n("pleaseSelectAllSubStats")); - return; - }; - - if (avatar) { - avatar.profileList[avatar.profileSelect].relics[selectedRelicSlot] = { - level: selectedRelicLevel, - relic_id: Number(`6${selectedRelicSet}${selectedRelicSlot}`), - relic_set_id: Number(selectedRelicSet), - main_affix_id: Number(selectedMainStat), - sub_affixes: listSelectedSubStats.map((item) => { - return { - sub_affix_id: Number(item.affixId), - count: item.rollCount, - step: item.stepCount - } - }) - } - } - setAvatars({ ...avatars }); - setIsOpenRelic(false); - - toast.success(transI18n("relicSavedSuccessfully")); - } - - return ( -
    -
    -

    - {transI18n("relicMaker")} -

    -
    -
    - - {/* Left Panel */} -
    - - {/* Set Configuration */} -
    -

    {transI18n("mainSettings")}

    - -
    - {/* Main Stat */} -
    - - ({ - value: key, - label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, - imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` - }))} - excludeSet={[]} - selectedCustomSet={selectedMainStat} - placeholder={transI18n("selectAMainStat")} - setSelectedCustomSet={setSelectedMainStat} - /> -
    - {/* Relic Set Selection */} -
    - - ({ - value: key, - label: getLocaleName(locale, value.Name), - imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}` - }))} - excludeSet={[]} - selectedCustomSet={selectedRelicSet} - placeholder={transI18n("selectASet")} - setSelectedCustomSet={setSelectedRelicSet} - /> -
    -
    - - {/* Set Bonus Display */} -
    - {selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => ( -
    - {key}-Pc: -
    -
    - )) :

    {transI18n("pleaseSelectASet")}

    } -
    - - - {/* Rarity */} -
    - - -
    - - {/* Level */} -
    - -
    - setSelectedRelicLevel(parseInt(e.target.value))} - className="range range-primary w-full" - /> -
    {selectedRelicLevel}
    -
    -
    - - {error && ( - - {error}! - - )} - - - - - {/* Save Button */} - -
    -
    - - {/* Right Panel - Sub Stats */} -
    - {/* Total Roll */} -
    -

    {transI18n("totalRoll")} {listSelectedSubStats.reduce((a, b) => a + b.rollCount, 0)}

    - -
    - - -
    -
    - {listSelectedSubStats.map((v, index) => ( -
    -
    - - {/* Stat Selection */} -
    - ({ - value: key, - label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, - imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` - }))} - excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({ - value: key, - label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, - imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` - }))} - selectedCustomSet={v.affixId} - placeholder={transI18n("selectASubStat")} - setSelectedCustomSet={(key) => handleSubStatChange(key, index, 0, 0)} - /> -
    - - {/* Current Value */} -
    - +{ } -
    {calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
    -
    - - {/* Up Roll Values */} -
    -
    - - {transI18n("upRoll")} -
    -
    - - - -
    -
    - - {/* Down Roll Values */} -
    -
    - - {transI18n("downRoll")} -
    -
    - - - -
    -
    - - {/* Reset Button & Roll Info */} -
    -
    -
    - - -
    - -
    - {transI18n("roll")}: {v.rollCount} - {transI18n("step")}: {v.stepCount} -
    -
    -
    - -
    -
    - ))} - - -
    -
    -
    - ); -}; +"use client"; +import useUserDataStore from '@/stores/userDataStore'; +import { useEffect, useMemo, useState } from 'react'; +import SelectCustomImage from '../select/customSelectImage'; +import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper'; +import { mappingStats } from '@/constant/constant'; +import useModelStore from '@/stores/modelStore'; +import useRelicMakerStore from '@/stores/relicMakerStore'; +import { toast } from 'react-toastify'; +import { useTranslations } from 'next-intl' +import { ChevronDown, ChevronUp } from 'lucide-react'; +import { AnimatePresence, motion } from 'framer-motion'; +import useDetailDataStore from '@/stores/detailDataStore'; +import useCurrentDataStore from '@/stores/currentDataStore'; +import { RelicSetDetail, SubAffixData } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import { mappingRelicSlot } from "@/constant/constant"; + +export default function RelicMaker() { + const { avatars, setAvatars } = useUserDataStore() + const { avatarSelected } = useCurrentDataStore() + const { setIsOpenRelic } = useModelStore() + const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const { + selectedRelicSlot, + selectedRelicSet, + selectedMainStat, + listSelectedSubStats, + selectedRelicLevel, + preSelectedSubStats, + setSelectedRelicSet, + setSelectedMainStat, + setSelectedRelicLevel, + setListSelectedSubStats, + resetHistory, + popHistory, + addHistory, + } = useRelicMakerStore() + const [error, setError] = useState(""); + + const relicSets = useMemo(() => { + const listSet: Record = {}; + for (const [key, value] of Object.entries(mapRelicSet || {})) { + let isOk = false; + for (const key2 of Object.keys(value.Parts)) { + if (key2 == mappingRelicSlot?.[selectedRelicSlot]) { + isOk = true; + break; + } + } + if (isOk) { + listSet[key] = value; + } + } + return listSet; + }, [mapRelicSet , selectedRelicSlot]); + + const subAffixOptions = useMemo(() => { + const listSet: Record = {}; + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot] + + if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet; + + for (const [key, value] of Object.entries(subAffixMap)) { + if (value.Property !== mainAffixMap[selectedMainStat]?.Property) { + listSet[key] = value; + } + } + return listSet; + }, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]); + + useEffect(() => { + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot]; + + if (!subAffixMap || !mainAffixMap) return; + + const mainProp = mainAffixMap[selectedMainStat]?.Property; + if (!mainProp) return; + + const newSubAffixes = structuredClone(listSelectedSubStats); + let updated = false; + + for (let i = 0; i < newSubAffixes.length; i++) { + if (newSubAffixes[i].property === mainProp) { + newSubAffixes[i].affixId = ""; + newSubAffixes[i].property = ""; + newSubAffixes[i].rollCount = 0; + newSubAffixes[i].stepCount = 0; + updated = true; + } + } + + if (updated) setListSelectedSubStats(newSubAffixes); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]); + + const exSubAffixOptions = useMemo(() => { + const listSet: Record = {}; + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot]; + + if (!subAffixMap || !mainAffixMap) return listSet; + + for (const [key, value] of Object.entries(subAffixMap)) { + const subAffix = listSelectedSubStats.find((item) => item.property === value.Property); + if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) { + listSet[key] = value; + } + } + return listSet; + }, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]); + + const effectBonus = useMemo(() => { + const affixSet = mainAffix?.["5" + selectedRelicSlot]; + if (!affixSet) return 0; + + const data = affixSet[selectedMainStat]; + if (!data) return 0; + + return calcMainAffixBonus(data, selectedRelicLevel); + }, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]); + + const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => { + setError(""); + const newSubAffixes = structuredClone(listSelectedSubStats); + if (!subAffixOptions[key]) { + newSubAffixes[index].affixId = ""; + newSubAffixes[index].property = ""; + newSubAffixes[index].rollCount = rollCount; + newSubAffixes[index].stepCount = stepCount; + setListSelectedSubStats(newSubAffixes); + addHistory(index, newSubAffixes[index]); + return; + } + newSubAffixes[index].affixId = key; + newSubAffixes[index].property = subAffixOptions[key].Property; + newSubAffixes[index].rollCount = rollCount; + newSubAffixes[index].stepCount = stepCount; + setListSelectedSubStats(newSubAffixes); + addHistory(index, newSubAffixes[index]); + }; + + const handlerRollback = (index: number) => { + setError(""); + if (!preSelectedSubStats[index]) return; + + const keys = Object.keys(preSelectedSubStats[index]); + if (keys.length <= 1) return; + + const newSubAffixes = structuredClone(listSelectedSubStats); + const listHistory = structuredClone(preSelectedSubStats[index]); + const secondLastKey = listHistory.length - 2; + const preSubAffixes = { ...listHistory[secondLastKey] }; + newSubAffixes[index].rollCount = preSubAffixes.rollCount; + newSubAffixes[index].stepCount = preSubAffixes.stepCount; + setListSelectedSubStats(newSubAffixes); + popHistory(index); + }; + + const resetSubStat = (index: number) => { + const newSubAffixes = structuredClone(listSelectedSubStats); + resetHistory(index); + newSubAffixes[index].affixId = ""; + newSubAffixes[index].property = ""; + newSubAffixes[index].rollCount = 0; + newSubAffixes[index].stepCount = 0; + setListSelectedSubStats(newSubAffixes); + }; + + const randomizeStats = () => { + const newSubAffixes = structuredClone(listSelectedSubStats); + const exKeys = Object.keys(exSubAffixOptions); + for (let i = 0; i < newSubAffixes.length; i++) { + const keys = Object.keys(subAffixOptions).filter((key) => !exKeys.includes(key)); + const randomKey = keys[Math.floor(Math.random() * keys.length)]; + exKeys.push(randomKey); + const randomValue = subAffixOptions[randomKey]; + newSubAffixes[i].affixId = randomKey; + newSubAffixes[i].property = randomValue.Property; + newSubAffixes[i].rollCount = 0; + newSubAffixes[i].stepCount = 0; + } + for (let i = 0; i < newSubAffixes.length; i++) { + addHistory(i, newSubAffixes[i]); + } + setListSelectedSubStats(newSubAffixes); + + }; + + const randomizeRolls = () => { + const newSubAffixes = structuredClone(listSelectedSubStats); + const randomRolls = randomPartition(9, listSelectedSubStats.length); + for (let i = 0; i < listSelectedSubStats.length; i++) { + newSubAffixes[i].rollCount = randomRolls[i]; + newSubAffixes[i].stepCount = randomStep(randomRolls[i]); + } + setListSelectedSubStats(newSubAffixes); + for (let i = 0; i < newSubAffixes.length; i++) { + addHistory(i, newSubAffixes[i]); + } + }; + + const handlerSaveRelic = () => { + setError(""); + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) { + setError(transI18n("pleaseSelectAllOptions")); + return; + }; + + if (listSelectedSubStats.find((item) => item.affixId === "")) { + setError(transI18n("pleaseSelectAllSubStats")); + return; + }; + + if (avatar) { + avatar.profileList[avatar.profileSelect].relics[selectedRelicSlot] = { + level: selectedRelicLevel, + relic_id: Number(`6${selectedRelicSet}${selectedRelicSlot}`), + relic_set_id: Number(selectedRelicSet), + main_affix_id: Number(selectedMainStat), + sub_affixes: listSelectedSubStats.map((item) => { + return { + sub_affix_id: Number(item.affixId), + count: item.rollCount, + step: item.stepCount + } + }) + } + } + setAvatars({ ...avatars }); + setIsOpenRelic(false); + + toast.success(transI18n("relicSavedSuccessfully")); + } + + return ( +
    +
    +

    + {transI18n("relicMaker")} +

    +
    +
    + + {/* Left Panel */} +
    + + {/* Set Configuration */} +
    +

    {transI18n("mainSettings")}

    + +
    + {/* Main Stat */} +
    + + ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + excludeSet={[]} + selectedCustomSet={selectedMainStat} + placeholder={transI18n("selectAMainStat")} + setSelectedCustomSet={setSelectedMainStat} + /> +
    + {/* Relic Set Selection */} +
    + + ({ + value: key, + label: getLocaleName(locale, value.Name), + imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}` + }))} + excludeSet={[]} + selectedCustomSet={selectedRelicSet} + placeholder={transI18n("selectASet")} + setSelectedCustomSet={setSelectedRelicSet} + /> +
    +
    + + {/* Set Bonus Display */} +
    + {selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => ( +
    + {key}-Pc: +
    +
    + )) :

    {transI18n("pleaseSelectASet")}

    } +
    + + + {/* Rarity */} +
    + + +
    + + {/* Level */} +
    + +
    + setSelectedRelicLevel(parseInt(e.target.value))} + className="range range-primary w-full" + /> +
    {selectedRelicLevel}
    +
    +
    + + {error && ( + + {error}! + + )} + + + + + {/* Save Button */} + +
    +
    + + {/* Right Panel - Sub Stats */} +
    + {/* Total Roll */} +
    +

    {transI18n("totalRoll")} {listSelectedSubStats.reduce((a, b) => a + b.rollCount, 0)}

    + +
    + + +
    +
    + {listSelectedSubStats.map((v, index) => ( +
    +
    + + {/* Stat Selection */} +
    + ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + selectedCustomSet={v.affixId} + placeholder={transI18n("selectASubStat")} + setSelectedCustomSet={(key) => handleSubStatChange(key, index, 0, 0)} + /> +
    + + {/* Current Value */} +
    + +{ } +
    {calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
    +
    + + {/* Up Roll Values */} +
    +
    + + {transI18n("upRoll")} +
    +
    + + + +
    +
    + + {/* Down Roll Values */} +
    +
    + + {transI18n("downRoll")} +
    +
    + + + +
    +
    + + {/* Reset Button & Roll Info */} +
    +
    +
    + + +
    + +
    + {transI18n("roll")}: {v.rollCount} + {transI18n("step")}: {v.stepCount} +
    +
    +
    + +
    +
    + ))} + + +
    +
    +
    + ); +}; diff --git a/src/components/relicsInfo/index.tsx b/src/components/relicsInfo/index.tsx index 8273bdc..04432bc 100644 --- a/src/components/relicsInfo/index.tsx +++ b/src/components/relicsInfo/index.tsx @@ -1,366 +1,366 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client"; -import { useCallback, useEffect, useMemo } from "react"; -import RelicMaker from "../relicBar"; -import { motion } from "framer-motion"; -import useUserDataStore from "@/stores/userDataStore"; -import { useTranslations } from "next-intl"; -import RelicCard from "../card/relicCard"; -import useModelStore from '@/stores/modelStore'; -import { replaceByParam } from '@/helper'; -import useRelicMakerStore from '@/stores/relicMakerStore'; -import QuickView from "../quickView"; -import { ModalConfig } from "@/types"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import { getLocaleName } from '@/helper'; -import useLocaleStore from "@/stores/localeStore"; - -export default function RelicsInfo() { - const { avatars, setAvatars } = useUserDataStore() - const { - setSelectedRelicSlot, - selectedRelicSlot, - setSelectedMainStat, - setSelectedRelicSet, - setSelectedRelicLevel, - setListSelectedSubStats, - resetHistory, - resetSubStat, - listSelectedSubStats, - } = useRelicMakerStore() - - const { - isOpenRelic, - setIsOpenRelic, - isOpenQuickView, - setIsOpenQuickView - } = useModelStore() - const transI18n = useTranslations("DataPage") - const { avatarSelected } = useCurrentDataStore() - const { mapRelicSet, subAffix } = useDetailDataStore() - const { locale } = useLocaleStore() - - const handleShow = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.showModal(); - } - }; - - // Close modal handler - const handleCloseModal = (modalId: string) => { - const modal = document.getElementById(modalId) as HTMLDialogElement | null; - if (modal) { - modal.close(); - } - }; - - - const getRelic = useCallback((slot: string) => { - const avatar = avatars[avatarSelected?.ID.toString() || ""]; - if (avatar) { - return avatar.profileList[avatar.profileSelect]?.relics[slot] || null; - } - return null; - }, [avatars, avatarSelected]); - - const handlerDeleteRelic = (slot: string) => { - const avatar = avatars[avatarSelected?.ID.toString() || ""]; - if (avatar) { - delete avatar.profileList[avatar.profileSelect].relics[slot] - setAvatars({ ...avatars }); - } - } - - const handlerChangeRelic = (slot: string) => { - const relic = getRelic(slot) - setSelectedRelicSlot(slot) - resetSubStat() - resetHistory(null) - if (relic) { - setSelectedMainStat(relic.main_affix_id.toString()) - setSelectedRelicSet(relic.relic_set_id.toString()) - setSelectedRelicLevel(relic.level) - const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; - relic.sub_affixes.forEach((item, index) => { - newSubAffixes[index].affixId = item.sub_affix_id.toString(); - newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || ""; - newSubAffixes[index].rollCount = item.count || 0; - newSubAffixes[index].stepCount = item.step || 0; - }) - setListSelectedSubStats(newSubAffixes) - } else { - setSelectedMainStat("") - setSelectedRelicSet("") - setSelectedRelicLevel(15) - const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; - newSubAffixes.forEach((item) => { - item.affixId = "" - item.property = "" - item.rollCount = 0 - item.stepCount = 0 - }) - - setListSelectedSubStats(newSubAffixes) - } - setIsOpenRelic(true) - handleShow("action_detail_modal") - } - - const relicEffects = useMemo(() => { - const avatar = avatars[avatarSelected?.ID.toString() || ""]; - const relicCount: { [key: string]: number } = {}; - if (avatar) { - for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { - if (relicCount[relic.relic_set_id]) { - relicCount[relic.relic_set_id]++; - } else { - relicCount[relic.relic_set_id] = 1; - } - } - } - const listEffects: { key: string, count: number }[] = []; - Object.entries(relicCount).forEach(([key, value]) => { - if (value >= 2) { - listEffects.push({ key: key, count: value }); - } - }); - return listEffects; - }, [avatars, avatarSelected]); - - const modalConfigs: ModalConfig[] = [ - { - id: "action_detail_modal", - title: "", - isOpen: isOpenRelic, - onClose: () => { - setIsOpenRelic(false) - handleCloseModal("action_detail_modal") - }, - content: - }, - { - id: "quick_view_modal", - title: transI18n("quickView").toUpperCase(), - isOpen: isOpenQuickView, - onClose: () => { - setIsOpenQuickView(false) - handleCloseModal("quick_view_modal") - }, - content: - } - ] - - // Handle ESC key to close modal - useEffect(() => { - for (const item of modalConfigs) { - if (!item?.isOpen) { - handleCloseModal(item?.id || "") - } - } - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - for (const item of modalConfigs) { - handleCloseModal(item?.id || "") - } - } - }; - - window.addEventListener('keydown', handleEscKey); - return () => window.removeEventListener('keydown', handleEscKey); - }, [isOpenRelic]); - - return ( -
    -
    - - {/* Left Section - Items Grid */} -
    -
    -

    -
    - {transI18n("relics")} -

    - -
    - {["1", "2", "3", "4", "5", "6"].map((item, index) => ( -
    -
    { - if (item === selectedRelicSlot) { - setSelectedRelicSlot("") - } else { - setSelectedRelicSlot(item) - } - handlerChangeRelic(item) - }} - className="cursor-pointer" - > - -
    - -
    - - - {getRelic(item) && ( - - )} -
    -
    - ))} -
    -
    - - -
    - -
    -
    - - {/* Right Section - Stats and Set Effects */} -
    - - {/* Set Effects Panel */} -
    -

    -
    - {transI18n("setEffects")} -

    - -
    - {relicEffects.map((setEffect, index) => { - const relicInfo = mapRelicSet[setEffect.key]; - if (!relicInfo) return null; - return ( -
    -
    -
    - {setEffect.count && ( - - ({setEffect.count}) - - )} -
    - -
    - {Object.entries(relicInfo.Skills).map(([requireNum, value]) => { - if (Number(requireNum) > Number(setEffect.count)) return null; - return ( -
    -
    - {requireNum}-PC: -
    -
    -
    - ) - })} -
    -
    - ) - })} -
    -
    -
    -
    - - {modalConfigs.map(({ id, title, onClose, content }) => ( - -
    -
    - - ✕ - -
    - - {title && ( -
    -

    - {title} -

    -
    - )} - - {content} -
    -
    - ))} - -
    - ); -} +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; +import { useCallback, useEffect, useMemo } from "react"; +import RelicMaker from "../relicBar"; +import { motion } from "framer-motion"; +import useUserDataStore from "@/stores/userDataStore"; +import { useTranslations } from "next-intl"; +import RelicCard from "../card/relicCard"; +import useModelStore from '@/stores/modelStore'; +import { replaceByParam } from '@/helper'; +import useRelicMakerStore from '@/stores/relicMakerStore'; +import QuickView from "../quickView"; +import { ModalConfig } from "@/types"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { getLocaleName } from '@/helper'; +import useLocaleStore from "@/stores/localeStore"; + +export default function RelicsInfo() { + const { avatars, setAvatars } = useUserDataStore() + const { + setSelectedRelicSlot, + selectedRelicSlot, + setSelectedMainStat, + setSelectedRelicSet, + setSelectedRelicLevel, + setListSelectedSubStats, + resetHistory, + resetSubStat, + listSelectedSubStats, + } = useRelicMakerStore() + + const { + isOpenRelic, + setIsOpenRelic, + isOpenQuickView, + setIsOpenQuickView + } = useModelStore() + const transI18n = useTranslations("DataPage") + const { avatarSelected } = useCurrentDataStore() + const { mapRelicSet, subAffix } = useDetailDataStore() + const { locale } = useLocaleStore() + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + + const getRelic = useCallback((slot: string) => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + if (avatar) { + return avatar.profileList[avatar.profileSelect]?.relics[slot] || null; + } + return null; + }, [avatars, avatarSelected]); + + const handlerDeleteRelic = (slot: string) => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + if (avatar) { + delete avatar.profileList[avatar.profileSelect].relics[slot] + setAvatars({ ...avatars }); + } + } + + const handlerChangeRelic = (slot: string) => { + const relic = getRelic(slot) + setSelectedRelicSlot(slot) + resetSubStat() + resetHistory(null) + if (relic) { + setSelectedMainStat(relic.main_affix_id.toString()) + setSelectedRelicSet(relic.relic_set_id.toString()) + setSelectedRelicLevel(relic.level) + const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; + relic.sub_affixes.forEach((item, index) => { + newSubAffixes[index].affixId = item.sub_affix_id.toString(); + newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || ""; + newSubAffixes[index].rollCount = item.count || 0; + newSubAffixes[index].stepCount = item.step || 0; + }) + setListSelectedSubStats(newSubAffixes) + } else { + setSelectedMainStat("") + setSelectedRelicSet("") + setSelectedRelicLevel(15) + const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; + newSubAffixes.forEach((item) => { + item.affixId = "" + item.property = "" + item.rollCount = 0 + item.stepCount = 0 + }) + + setListSelectedSubStats(newSubAffixes) + } + setIsOpenRelic(true) + handleShow("action_detail_modal") + } + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const modalConfigs: ModalConfig[] = [ + { + id: "action_detail_modal", + title: "", + isOpen: isOpenRelic, + onClose: () => { + setIsOpenRelic(false) + handleCloseModal("action_detail_modal") + }, + content: + }, + { + id: "quick_view_modal", + title: transI18n("quickView").toUpperCase(), + isOpen: isOpenQuickView, + onClose: () => { + setIsOpenQuickView(false) + handleCloseModal("quick_view_modal") + }, + content: + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenRelic]); + + return ( +
    +
    + + {/* Left Section - Items Grid */} +
    +
    +

    +
    + {transI18n("relics")} +

    + +
    + {["1", "2", "3", "4", "5", "6"].map((item, index) => ( +
    +
    { + if (item === selectedRelicSlot) { + setSelectedRelicSlot("") + } else { + setSelectedRelicSlot(item) + } + handlerChangeRelic(item) + }} + className="cursor-pointer" + > + +
    + +
    + + + {getRelic(item) && ( + + )} +
    +
    + ))} +
    +
    + + +
    + +
    +
    + + {/* Right Section - Stats and Set Effects */} +
    + + {/* Set Effects Panel */} +
    +

    +
    + {transI18n("setEffects")} +

    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count && ( + + ({setEffect.count}) + + )} +
    + +
    + {Object.entries(relicInfo.Skills).map(([requireNum, value]) => { + if (Number(requireNum) > Number(setEffect.count)) return null; + return ( +
    +
    + {requireNum}-PC: +
    +
    +
    + ) + })} +
    +
    + ) + })} +
    +
    +
    +
    + + {modalConfigs.map(({ id, title, onClose, content }) => ( + +
    +
    + + ✕ + +
    + + {title && ( +
    +

    + {title} +

    +
    + )} + + {content} +
    +
    + ))} + +
    + ); +} diff --git a/src/components/select/customSelectImage.tsx b/src/components/select/customSelectImage.tsx index ea23a8b..4fb0a72 100644 --- a/src/components/select/customSelectImage.tsx +++ b/src/components/select/customSelectImage.tsx @@ -1,112 +1,112 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' -import dynamic from "next/dynamic" -import type { SingleValue } from "react-select" -import Image from "next/image" -import useLocaleStore from "@/stores/localeStore" -import ParseText from "../parseText" -import { themeColors } from "@/constant/constant" -import type { Props as SelectProps } from "react-select" -import { JSX } from "react" - -const Select = dynamic( - () => import("react-select").then(m => m.default), - { ssr: false } -) as ( - props: SelectProps -) => JSX.Element - -export type SelectOption = { - value: string - label: string - imageUrl: string -} - -type SelectCustomProp = { - customSet: SelectOption[] - excludeSet: SelectOption[] - selectedCustomSet: string - placeholder: string - setSelectedCustomSet: (value: string) => void -} - -export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) { - const options: SelectOption[] = customSet - const { locale, theme } = useLocaleStore() - - const c = themeColors[theme] || themeColors.winter - - const customStyles = { - option: (p: any, s: any) => ({ - ...p, - display: 'flex', - alignItems: 'center', - gap: '8px', - padding: '8px 12px', - backgroundColor: s.isFocused ? c.bgHover : c.bg, - color: c.text, - cursor: 'pointer' - }), - - singleValue: (p: any) => ({ - ...p, - display: 'flex', - alignItems: 'center', - gap: '8px', - color: c.text - }), - - control: (p: any) => ({ - ...p, - backgroundColor: c.bg, - borderColor: c.border, - boxShadow: 'none' - }), - - menu: (p: any) => ({ - ...p, - backgroundColor: c.bg, - color: c.text, - zIndex: 9999 - }), - - menuPortal: (p: any) => ({ - ...p, - zIndex: 9999 - }) - } - - const formatOptionLabel = (option: SelectOption) => ( -
    - - -
    - ) - - return ( - !excludeSet.some(ex => ex.value === opt.value))} + value={options.find(opt => { + return opt.value === selectedCustomSet + }) || null} + onChange={(selected: SingleValue) => { + setSelectedCustomSet(selected?.value || '') + }} + formatOptionLabel={formatOptionLabel} + styles={customStyles} + placeholder={placeholder} + className="my-react-select-container" + classNamePrefix="my-react-select" + isSearchable + isClearable + /> + ) +} diff --git a/src/components/select/customSelectText.tsx b/src/components/select/customSelectText.tsx index e92d1fe..5432b54 100644 --- a/src/components/select/customSelectText.tsx +++ b/src/components/select/customSelectText.tsx @@ -1,118 +1,118 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' -import dynamic from "next/dynamic" -import type { SingleValue } from "react-select" -import { replaceByParam } from '@/helper' -import useLocaleStore from '@/stores/localeStore' -import { themeColors } from '@/constant/constant' -import type { Props as SelectProps } from "react-select" -import { JSX } from "react" - -const Select = dynamic( - () => import("react-select").then(m => m.default), - { ssr: false } -) as ( - props: SelectProps -) => JSX.Element - -export type SelectOption = { - id: string - name: string - time?: string - description?: string -} - -type SelectCustomProp = { - customSet: SelectOption[] - excludeSet: SelectOption[] - selectedCustomSet: string - placeholder: string - setSelectedCustomSet: (value: string) => void -} - -export default function SelectCustomText({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) { - const options: SelectOption[] = customSet - const { theme } = useLocaleStore() - const c = themeColors[theme] || themeColors.winter - - const customStyles = { - option: (p: any, s: any) => ({ - ...p, - display: 'flex', - alignItems: 'center', - gap: '8px', - padding: '8px 12px', - backgroundColor: s.isFocused ? c.bgHover : c.bg, - color: c.text, - cursor: 'pointer' - }), - - singleValue: (p: any) => ({ - ...p, - display: 'flex', - alignItems: 'center', - gap: '8px', - color: c.text - }), - - control: (p: any) => ({ - ...p, - backgroundColor: c.bg, - borderColor: c.border, - boxShadow: 'none' - }), - - menu: (p: any) => ({ - ...p, - backgroundColor: c.bg, - color: c.text, - zIndex: 9999 - }), - - menuPortal: (p: any) => ({ - ...p, - zIndex: 9999 - }) - } - - - const formatOptionLabel = (option: SelectOption) => ( -
    -
    - {option.time &&
    {option.time}
    } - {option.description &&
    } -
    - ) - - return ( - !excludeSet.some(ex => ex.id === opt.id))} + value={options.find(opt => { + return opt.id === selectedCustomSet + }) || null} + onChange={(selected: SingleValue) => { + setSelectedCustomSet(selected?.id || '') + }} + formatOptionLabel={formatOptionLabel} + styles={customStyles} + placeholder={placeholder} + className="my-react-select-container" + classNamePrefix="my-react-select" + isSearchable + isClearable + /> + ) +} diff --git a/src/components/showcaseCard/index.tsx b/src/components/showcaseCard/index.tsx index bd0b3a4..3d9d986 100644 --- a/src/components/showcaseCard/index.tsx +++ b/src/components/showcaseCard/index.tsx @@ -1,1006 +1,1006 @@ -"use client"; -import { useEffect, useState, useRef, useMemo, useCallback } from 'react'; -import { FastAverageColor } from 'fast-average-color'; -import NextImage from 'next/image'; -import ParseText from '../parseText'; -import useLocaleStore from '@/stores/localeStore'; -import { calcAffixBonus, calcBaseStat, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, convertToRoman, getNameChar, replaceByParam } from '@/helper'; -import useUserDataStore from '@/stores/userDataStore'; -import { traceShowCaseMap } from '@/constant/traceConstant'; -import { mappingStats } from '@/constant/constant'; -import { useTranslations } from 'next-intl'; -import { toast } from 'react-toastify'; -import RelicShowcase from './relicShowcase'; -import useDetailDataStore from '@/stores/detailDataStore'; -import useCurrentDataStore from '@/stores/currentDataStore'; -import { getLocaleName } from '@/helper'; - - -export default function ShowCaseInfo() { - const { avatarSelected } = useCurrentDataStore() - const { mainAffix, subAffix, mapRelicSet, mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore() - const { avatars } = useUserDataStore() - const [avgColor, setAvgColor] = useState('#222'); - const imgRef = useRef(null); - const cardRef = useRef(null) - const { locale } = useLocaleStore() - const transI18n = useTranslations("DataPage") - - const handleSaveImage = useCallback(() => { - if (cardRef.current === null || !avatarSelected) { - toast.error("Avatar showcase not found!"); - return; - } - - import("html2canvas-pro") - .then(({ default: html2canvas }) => - html2canvas(cardRef.current!, { - scale: 2, - backgroundColor: "#000000", - allowTaint: false, - useCORS: true - }) - ) - .then((canvas: HTMLCanvasElement) => { - const link = document.createElement("a"); - link.download = `${getNameChar(locale, transI18n, avatarSelected)}_showcase.png`; - link.href = canvas.toDataURL("image/png"); - link.click(); - }) - .catch(() => { - toast.error("Error generating showcase card!"); - }); - }, [avatarSelected, locale, transI18n]); - - useEffect(() => { - if (!avatarSelected) return; - const fac = new FastAverageColor(); - const img = new Image(); - - img.crossOrigin = 'anonymous'; - img.src = `${process.env.CDN_URL}/${avatarSelected?.Image?.AvatarCutinFrontImgPath}`; - - img.onload = () => { - fac.getColorAsync(img) - .then((color) => { - setAvgColor(color.hex); - }) - .catch(e => console.error("Error:", e)); - }; - return () => { - fac.destroy(); - img.onload = null; - }; - }, [avatarSelected]); - - const avatarSkillTree = useMemo(() => { - if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} - if (avatars[avatarSelected?.ID?.toString()].enhanced) { - return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} - } - return avatarSelected?.SkillTrees || {} - }, [avatarSelected, avatars]) - - const avatarData = useMemo(() => { - if (!avatarSelected) return - return avatars[avatarSelected?.ID?.toString()] - }, [avatarSelected, avatars]) - - const avatarProfile = useMemo(() => { - if (!avatarSelected || !avatarData) return - return avatarData?.profileList?.[avatarData?.profileSelect] - }, [avatarSelected, avatarData]) - - const lightconeStats = useMemo(() => { - if (!avatarSelected || !avatarProfile?.lightcone || !mapLightCone[avatarProfile?.lightcone?.item_id]) return - const promotion = calcPromotion(avatarProfile?.lightcone?.level) - const atkStat = calcBaseStat( - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseAttack, - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseAttackAdd, - 0, - avatarProfile?.lightcone?.level - ) - const hpStat = calcBaseStat( - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseHP, - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseHPAdd, - 0, - avatarProfile?.lightcone?.level - ) - const defStat = calcBaseStat( - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseDefence, - mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseDefenceAdd, - 0, - avatarProfile?.lightcone?.level - ) - return { - attack: atkStat, - hp: hpStat, - def: defStat, - } - }, [avatarSelected, mapLightCone, avatarProfile]) - - const relicEffects = useMemo(() => { - const avatar = avatars[avatarSelected?.ID?.toString() || ""]; - const relicCount: { [key: string]: number } = {}; - if (avatar) { - for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { - if (relicCount[relic.relic_set_id]) { - relicCount[relic.relic_set_id]++; - } else { - relicCount[relic.relic_set_id] = 1; - } - } - } - const listEffects: { key: string, count: number }[] = []; - Object.entries(relicCount).forEach(([key, value]) => { - if (value >= 2) { - listEffects.push({ key: key, count: value }); - } - }); - return listEffects; - }, [avatars, avatarSelected]); - - const relicStats = useMemo(() => { - if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return - - return Object.entries(avatarProfile?.relics).map(([key, value]) => { - const mainAffixMap = mainAffix["5" + key] - const subAffixMap = subAffix["5"] - if (!mainAffixMap || !subAffixMap) return - return { - img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, - mainAffix: { - property: mainAffixMap?.[value?.main_affix_id]?.Property, - level: value?.level, - valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), - detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] - }, - subAffix: value?.sub_affixes?.map((subValue) => { - return { - property: subAffixMap?.[subValue?.sub_affix_id]?.Property, - valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), - detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], - step: subValue?.step, - count: subValue?.count - } - }) - } - }) - }, [avatarSelected, avatarProfile, mainAffix, subAffix]) - - const totalSubStats = useMemo(() => { - if (!relicStats?.length) return 0 - return (relicStats ?? []).reduce((acc, relic) => { - const subAffixList = relic?.subAffix ?? [] - return acc + subAffixList.reduce((subAcc, subAffix) => { - if (avatarSelected?.Relics?.SubAffixPropertyList.findIndex(it => it === subAffix.property) !== -1) { - return subAcc + (subAffix?.count ?? 0) - } - return subAcc - }, 0) - }, 0) - }, [relicStats, avatarSelected]) - - const characterStats = useMemo(() => { - if (!avatarSelected || !avatarData) return - const charPromotion = calcPromotion(avatarData.level) - - const statsData: Record = { - HP: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, - avatarData.level - ), - name: "HP", - icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", - unit: "", - round: 0 - }, - ATK: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, - avatarData.level - ), - name: "ATK", - icon: "spriteoutput/ui/avatar/icon/IconAttack.png", - unit: "", - round: 0 - }, - DEF: { - value: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, - avatarData.level - ), - base: calcBaseStatRaw( - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, - mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, - avatarData.level - ), - name: "DEF", - icon: "spriteoutput/ui/avatar/icon/IconDefence.png", - unit: "", - round: 0 - }, - SPD: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, - name: "SPD", - icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", - unit: "", - round: 1 - }, - CRITRate: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, - name: "CRIT Rate", - icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", - unit: "%", - round: 1 - }, - CRITDmg: { - value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, - base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, - name: "CRIT DMG", - icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", - unit: "%", - round: 1 - }, - BreakEffect: { - value: 0, - base: 0, - name: "Break Effect", - icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", - unit: "%", - round: 1 - }, - EffectRES: { - value: 0, - base: 0, - name: "Effect RES", - icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", - unit: "%", - round: 1 - }, - EnergyRate: { - value: 0, - base: 0, - name: "Energy Rate", - icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", - unit: "%", - round: 1 - }, - EffectHitRate: { - value: 0, - base: 0, - name: "Effect Hit Rate", - icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", - unit: "%", - round: 1 - }, - HealBoost: { - value: 0, - base: 0, - name: "Healing Boost", - icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", - unit: "%", - round: 1 - }, - PhysicalAdd: { - value: 0, - base: 0, - name: "Physical Boost", - icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", - unit: "%", - round: 1 - }, - FireAdd: { - value: 0, - base: 0, - name: "Fire Boost", - icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", - unit: "%", - round: 1 - }, - IceAdd: { - value: 0, - base: 0, - name: "Ice Boost", - icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", - unit: "%", - round: 1 - }, - ThunderAdd: { - value: 0, - base: 0, - name: "Thunder Boost", - icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", - unit: "%", - round: 1 - }, - WindAdd: { - value: 0, - base: 0, - name: "Wind Boost", - icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", - unit: "%", - round: 1 - }, - QuantumAdd: { - value: 0, - base: 0, - name: "Quantum Boost", - icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", - unit: "%", - round: 1 - }, - ImaginaryAdd: { - value: 0, - base: 0, - name: "Imaginary Boost", - icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", - unit: "%", - round: 1 - }, - ElationAdd: { - value: 0, - base: 0, - name: "Elation Boost", - icon: "spriteoutput/ui/avatar/icon/IconJoy.png", - unit: "%", - round: 1 - } - } - - if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { - const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) - statsData.HP.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, - avatarProfile?.lightcone?.level - ) - statsData.HP.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, - avatarProfile?.lightcone?.level - ) - statsData.ATK.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, - avatarProfile?.lightcone?.level - ) - statsData.ATK.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, - avatarProfile?.lightcone?.level - ) - statsData.DEF.value += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, - avatarProfile?.lightcone?.level - ) - statsData.DEF.base += calcBaseStatRaw( - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, - mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, - avatarProfile?.lightcone?.level - ) - - const bonusData = mapLightCone[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus - if (bonusData && bonusData.length > 0) { - const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") - const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") - bonusSpd.forEach((bonus) => { - statsData.SPD.value += bonus.Value - statsData.SPD.base += bonus.Value - }) - bonusOther.forEach((bonus) => { - const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) - } - }) - } - } - if (avatarSkillTree) { - Object.values(avatarSkillTree).forEach((value) => { - if (value?.["1"] - && value?.["1"]?.PointID - && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" - && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 - && value?.["1"]?.StatusAddList - && value?.["1"].StatusAddList.length > 0) { - value?.["1"]?.StatusAddList.forEach((status) => { - const statsBase = mappingStats?.[status?.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) - } - }) - } - }) - } - - if (avatarProfile?.relics && mainAffix && subAffix) { - Object.entries(avatarProfile?.relics).forEach(([key, value]) => { - const mainAffixMap = mainAffix["5" + key] - const subAffixMap = subAffix["5"] - if (!mainAffixMap || !subAffixMap) return - const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat - if (mainStats && statsData[mainStats]) { - statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) - } - value?.sub_affixes.forEach((subValue) => { - const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat - if (subStats && statsData[subStats]) { - statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) - } - }) - }) - } - - if (relicEffects && relicEffects.length > 0) { - relicEffects.forEach((relic) => { - const dataBonus = mapRelicSet?.[relic.key]?.Skills - if (!dataBonus || Object.keys(dataBonus).length === 0) return - Object.entries(dataBonus || {}).forEach(([key, value]) => { - if (relic.count < Number(key)) return - value.Bonus.forEach((bonus) => { - const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat - if (statsBase && statsData[statsBase]) { - statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) - } - }) - }) - }) - } - - - return statsData - }, [ - avatarSelected, - avatarData, - mapAvatar, - avatarProfile?.lightcone, - avatarProfile?.relics, - mapLightCone, - mainAffix, - subAffix, - relicEffects, - mapRelicSet, - avatarSkillTree - ]) - - const applyBrightness = useCallback((hex: string, brightness: number): string => { - const r = Math.round(parseInt(hex.slice(1, 3), 16) * brightness); - const g = Math.round(parseInt(hex.slice(3, 5), 16) * brightness); - const b = Math.round(parseInt(hex.slice(5, 7), 16) * brightness); - - return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; - }, []) - - return ( -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    -
    - {avatarSelected && ( - - )} -
    -
    - -
    - -
    - {avatarSelected && avatarSelected && avatarData?.data && typeof avatarData?.data?.rank === "number" && ( -
    - {Object.values(avatarSelected?.Ranks || {})?.map((rank, index) => { - const isActive = avatarData?.data?.rank > index; - return ( -
    - - {isActive && ( -
    - )} - -
    -
    - -
    -
    -
    - ); - })} -
    - )} - -
    - -
    -
    -
    -
    - -
    -
    -
    Lv. {avatarData?.level}/80
    - - {totalSubStats} - - - {avatarSelected && ( -
    - - - -
    - )} - -
    -
    - -
    - {avatarSelected && ( -
    - -
    - )} - -
    - {avatarData && avatarSelected && avatarSkillTree && traceShowCaseMap[avatarSelected?.BaseType || ""] - && Object.values(traceShowCaseMap[avatarSelected?.BaseType || ""] || []).map((item, index) => { - - return ( -
    - {item.map((btn, idx) => { - const size = btn.size || "small"; - const isBig = size === "big"; - const isMedium = size === "medium"; - const isBigMemory = size === "big-memory"; - - const sizeClass = isBigMemory ? "w-12 h-12 mx-1" : isBig ? "w-12 h-12 mx-1" : isMedium ? "w-10 h-10" : "w-8 h-8"; - - const imageSize = isBigMemory ? "w-10" : isBig ? "w-10" : isMedium ? "w-8" : "w-6"; - - const bgColor = isBigMemory - ? "bg-[#2a1a39]/80" - : isBig - ? "bg-[#2b1d00]/80" - : isMedium - ? "bg-[#2b1d00]/50" - : "bg-[#000000]/50"; - - const filterClass = isBigMemory - ? "filter sepia brightness-80 hue-rotate-[280deg] saturate-400 contrast-130" - : isBig - ? "filter sepia brightness-150 hue-rotate-15 saturate-200" - : ""; - - if (!avatarSkillTree?.[btn.id]) { - return null; - } - return ( -
    -
    - - - {(isBig || isBigMemory) && ( - - {avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] ? avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] : 1} - - )} -
    - - {btn.isLink && idx < item.length - 1 && ( -
    - )} -
    - ); - })} -
    - - ) - })} -
    - -
    - {avatarProfile && avatarProfile?.lightcone && lightconeStats ? ( -
    - -
    - {/* Background SVG Border (offset top-left) */} - - - - - {/* Card Image */} - - - {/* Top SVG Border (offset bottom-right) */} - - - - - - {/* Stars */} -
    - {[...Array( - Number( - mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity?.[ - mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity.length - 1 - ] || 0 - ) - )].map((_, i) => ( - - ))} -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    - {convertToRoman(avatarProfile?.lightcone?.rank)} -
    - - Lv. {avatarProfile.lightcone.level}/80 - -
    -
    - -
    -
    -
    - - { - lightconeStats?.hp - } -
    -
    - - {lightconeStats?.attack} -
    - -
    -
    - - {lightconeStats?.def} -
    -
    -
    - -
    - ) : (
    - {transI18n("noLightconeEquipped")} -
    )} -
    - -
    - -
    -
    - {Object.entries(characterStats || {})?.map(([key, stat], index) => { - if (!stat || (key.includes("Add") && stat.value === 0)) return null - return ( -
    -
    - - {stat.name} -
    -
    -
    { - stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 - }{stat.unit}
    -
    - ) - })} -
    -
    - -
    - {relicEffects.map((setEffect, index) => { - const relicInfo = mapRelicSet[setEffect.key]; - if (!relicInfo) return null; - return ( -
    -
    -
    - {setEffect.count} -
    -
    - ) - })} -
    -
    - -
    -
    - - {relicStats?.map((relic, index) => { - if (!relic || !avatarSelected) return null - return ( - - ) - })} - - {(!relicStats || !relicStats?.length) && ( -
    -
    - {transI18n("noRelicEquipped")} -
    -
    - )} -
    -
    - -
    -
    - -
    -
    - -
    - ); -} +"use client"; +import { useEffect, useState, useRef, useMemo, useCallback } from 'react'; +import { FastAverageColor } from 'fast-average-color'; +import NextImage from 'next/image'; +import ParseText from '../parseText'; +import useLocaleStore from '@/stores/localeStore'; +import { calcAffixBonus, calcBaseStat, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, convertToRoman, getNameChar, replaceByParam } from '@/helper'; +import useUserDataStore from '@/stores/userDataStore'; +import { traceShowCaseMap } from '@/constant/traceConstant'; +import { mappingStats } from '@/constant/constant'; +import { useTranslations } from 'next-intl'; +import { toast } from 'react-toastify'; +import RelicShowcase from './relicShowcase'; +import useDetailDataStore from '@/stores/detailDataStore'; +import useCurrentDataStore from '@/stores/currentDataStore'; +import { getLocaleName } from '@/helper'; + + +export default function ShowCaseInfo() { + const { avatarSelected } = useCurrentDataStore() + const { mainAffix, subAffix, mapRelicSet, mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore() + const { avatars } = useUserDataStore() + const [avgColor, setAvgColor] = useState('#222'); + const imgRef = useRef(null); + const cardRef = useRef(null) + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + + const handleSaveImage = useCallback(() => { + if (cardRef.current === null || !avatarSelected) { + toast.error("Avatar showcase not found!"); + return; + } + + import("html2canvas-pro") + .then(({ default: html2canvas }) => + html2canvas(cardRef.current!, { + scale: 2, + backgroundColor: "#000000", + allowTaint: false, + useCORS: true + }) + ) + .then((canvas: HTMLCanvasElement) => { + const link = document.createElement("a"); + link.download = `${getNameChar(locale, transI18n, avatarSelected)}_showcase.png`; + link.href = canvas.toDataURL("image/png"); + link.click(); + }) + .catch(() => { + toast.error("Error generating showcase card!"); + }); + }, [avatarSelected, locale, transI18n]); + + useEffect(() => { + if (!avatarSelected) return; + const fac = new FastAverageColor(); + const img = new Image(); + + img.crossOrigin = 'anonymous'; + img.src = `${process.env.CDN_URL}/${avatarSelected?.Image?.AvatarCutinFrontImgPath}`; + + img.onload = () => { + fac.getColorAsync(img) + .then((color) => { + setAvgColor(color.hex); + }) + .catch(e => console.error("Error:", e)); + }; + return () => { + fac.destroy(); + img.onload = null; + }; + }, [avatarSelected]); + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarProfile = useMemo(() => { + if (!avatarSelected || !avatarData) return + return avatarData?.profileList?.[avatarData?.profileSelect] + }, [avatarSelected, avatarData]) + + const lightconeStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.lightcone || !mapLightCone[avatarProfile?.lightcone?.item_id]) return + const promotion = calcPromotion(avatarProfile?.lightcone?.level) + const atkStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseAttack, + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseAttackAdd, + 0, + avatarProfile?.lightcone?.level + ) + const hpStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseHP, + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseHPAdd, + 0, + avatarProfile?.lightcone?.level + ) + const defStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseDefence, + mapLightCone[avatarProfile?.lightcone?.item_id]?.Stats?.[promotion]?.BaseDefenceAdd, + 0, + avatarProfile?.lightcone?.level + ) + return { + attack: atkStat, + hp: hpStat, + def: defStat, + } + }, [avatarSelected, mapLightCone, avatarProfile]) + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const relicStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return + + return Object.entries(avatarProfile?.relics).map(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + return { + img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, + mainAffix: { + property: mainAffixMap?.[value?.main_affix_id]?.Property, + level: value?.level, + valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), + detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] + }, + subAffix: value?.sub_affixes?.map((subValue) => { + return { + property: subAffixMap?.[subValue?.sub_affix_id]?.Property, + valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), + detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], + step: subValue?.step, + count: subValue?.count + } + }) + } + }) + }, [avatarSelected, avatarProfile, mainAffix, subAffix]) + + const totalSubStats = useMemo(() => { + if (!relicStats?.length) return 0 + return (relicStats ?? []).reduce((acc, relic) => { + const subAffixList = relic?.subAffix ?? [] + return acc + subAffixList.reduce((subAcc, subAffix) => { + if (avatarSelected?.Relics?.SubAffixPropertyList.findIndex(it => it === subAffix.property) !== -1) { + return subAcc + (subAffix?.count ?? 0) + } + return subAcc + }, 0) + }, 0) + }, [relicStats, avatarSelected]) + + const characterStats = useMemo(() => { + if (!avatarSelected || !avatarData) return + const charPromotion = calcPromotion(avatarData.level) + + const statsData: Record = { + HP: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + name: "HP", + icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + round: 0 + }, + ATK: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + name: "ATK", + icon: "spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + round: 0 + }, + DEF: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + name: "DEF", + icon: "spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + round: 0 + }, + SPD: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + name: "SPD", + icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + round: 1 + }, + CRITRate: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + name: "CRIT Rate", + icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + round: 1 + }, + CRITDmg: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + name: "CRIT DMG", + icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + round: 1 + }, + BreakEffect: { + value: 0, + base: 0, + name: "Break Effect", + icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + round: 1 + }, + EffectRES: { + value: 0, + base: 0, + name: "Effect RES", + icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + round: 1 + }, + EnergyRate: { + value: 0, + base: 0, + name: "Energy Rate", + icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + round: 1 + }, + EffectHitRate: { + value: 0, + base: 0, + name: "Effect Hit Rate", + icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + round: 1 + }, + HealBoost: { + value: 0, + base: 0, + name: "Healing Boost", + icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + round: 1 + }, + PhysicalAdd: { + value: 0, + base: 0, + name: "Physical Boost", + icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + round: 1 + }, + FireAdd: { + value: 0, + base: 0, + name: "Fire Boost", + icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + round: 1 + }, + IceAdd: { + value: 0, + base: 0, + name: "Ice Boost", + icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + round: 1 + }, + ThunderAdd: { + value: 0, + base: 0, + name: "Thunder Boost", + icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + round: 1 + }, + WindAdd: { + value: 0, + base: 0, + name: "Wind Boost", + icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + round: 1 + }, + QuantumAdd: { + value: 0, + base: 0, + name: "Quantum Boost", + icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + round: 1 + }, + ImaginaryAdd: { + value: 0, + base: 0, + name: "Imaginary Boost", + icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + round: 1 + }, + ElationAdd: { + value: 0, + base: 0, + name: "Elation Boost", + icon: "spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + round: 1 + } + } + + if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { + const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) + statsData.HP.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.HP.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + + const bonusData = mapLightCone[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus + if (bonusData && bonusData.length > 0) { + const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") + const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") + bonusSpd.forEach((bonus) => { + statsData.SPD.value += bonus.Value + statsData.SPD.base += bonus.Value + }) + bonusOther.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + } + } + if (avatarSkillTree) { + Object.values(avatarSkillTree).forEach((value) => { + if (value?.["1"] + && value?.["1"]?.PointID + && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" + && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 + && value?.["1"]?.StatusAddList + && value?.["1"].StatusAddList.length > 0) { + value?.["1"]?.StatusAddList.forEach((status) => { + const statsBase = mappingStats?.[status?.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) + } + }) + } + }) + } + + if (avatarProfile?.relics && mainAffix && subAffix) { + Object.entries(avatarProfile?.relics).forEach(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat + if (mainStats && statsData[mainStats]) { + statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) + } + value?.sub_affixes.forEach((subValue) => { + const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat + if (subStats && statsData[subStats]) { + statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) + } + }) + }) + } + + if (relicEffects && relicEffects.length > 0) { + relicEffects.forEach((relic) => { + const dataBonus = mapRelicSet?.[relic.key]?.Skills + if (!dataBonus || Object.keys(dataBonus).length === 0) return + Object.entries(dataBonus || {}).forEach(([key, value]) => { + if (relic.count < Number(key)) return + value.Bonus.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + }) + }) + } + + + return statsData + }, [ + avatarSelected, + avatarData, + mapAvatar, + avatarProfile?.lightcone, + avatarProfile?.relics, + mapLightCone, + mainAffix, + subAffix, + relicEffects, + mapRelicSet, + avatarSkillTree + ]) + + const applyBrightness = useCallback((hex: string, brightness: number): string => { + const r = Math.round(parseInt(hex.slice(1, 3), 16) * brightness); + const g = Math.round(parseInt(hex.slice(3, 5), 16) * brightness); + const b = Math.round(parseInt(hex.slice(5, 7), 16) * brightness); + + return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; + }, []) + + return ( +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    + {avatarSelected && ( + + )} +
    +
    + +
    + +
    + {avatarSelected && avatarSelected && avatarData?.data && typeof avatarData?.data?.rank === "number" && ( +
    + {Object.values(avatarSelected?.Ranks || {})?.map((rank, index) => { + const isActive = avatarData?.data?.rank > index; + return ( +
    + + {isActive && ( +
    + )} + +
    +
    + +
    +
    +
    + ); + })} +
    + )} + +
    + +
    +
    +
    +
    + +
    +
    +
    Lv. {avatarData?.level}/80
    + + {totalSubStats} + + + {avatarSelected && ( +
    + + + +
    + )} + +
    +
    + +
    + {avatarSelected && ( +
    + +
    + )} + +
    + {avatarData && avatarSelected && avatarSkillTree && traceShowCaseMap[avatarSelected?.BaseType || ""] + && Object.values(traceShowCaseMap[avatarSelected?.BaseType || ""] || []).map((item, index) => { + + return ( +
    + {item.map((btn, idx) => { + const size = btn.size || "small"; + const isBig = size === "big"; + const isMedium = size === "medium"; + const isBigMemory = size === "big-memory"; + + const sizeClass = isBigMemory ? "w-12 h-12 mx-1" : isBig ? "w-12 h-12 mx-1" : isMedium ? "w-10 h-10" : "w-8 h-8"; + + const imageSize = isBigMemory ? "w-10" : isBig ? "w-10" : isMedium ? "w-8" : "w-6"; + + const bgColor = isBigMemory + ? "bg-[#2a1a39]/80" + : isBig + ? "bg-[#2b1d00]/80" + : isMedium + ? "bg-[#2b1d00]/50" + : "bg-[#000000]/50"; + + const filterClass = isBigMemory + ? "filter sepia brightness-80 hue-rotate-[280deg] saturate-400 contrast-130" + : isBig + ? "filter sepia brightness-150 hue-rotate-15 saturate-200" + : ""; + + if (!avatarSkillTree?.[btn.id]) { + return null; + } + return ( +
    +
    + + + {(isBig || isBigMemory) && ( + + {avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] ? avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] : 1} + + )} +
    + + {btn.isLink && idx < item.length - 1 && ( +
    + )} +
    + ); + })} +
    + + ) + })} +
    + +
    + {avatarProfile && avatarProfile?.lightcone && lightconeStats ? ( +
    + +
    + {/* Background SVG Border (offset top-left) */} + + + + + {/* Card Image */} + + + {/* Top SVG Border (offset bottom-right) */} + + + + + + {/* Stars */} +
    + {[...Array( + Number( + mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity?.[ + mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity.length - 1 + ] || 0 + ) + )].map((_, i) => ( + + ))} +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + {convertToRoman(avatarProfile?.lightcone?.rank)} +
    + + Lv. {avatarProfile.lightcone.level}/80 + +
    +
    + +
    +
    +
    + + { + lightconeStats?.hp + } +
    +
    + + {lightconeStats?.attack} +
    + +
    +
    + + {lightconeStats?.def} +
    +
    +
    + +
    + ) : (
    + {transI18n("noLightconeEquipped")} +
    )} +
    + +
    + +
    +
    + {Object.entries(characterStats || {})?.map(([key, stat], index) => { + if (!stat || (key.includes("Add") && stat.value === 0)) return null + return ( +
    +
    + + {stat.name} +
    +
    +
    { + stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 + }{stat.unit}
    +
    + ) + })} +
    +
    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count} +
    +
    + ) + })} +
    +
    + +
    +
    + + {relicStats?.map((relic, index) => { + if (!relic || !avatarSelected) return null + return ( + + ) + })} + + {(!relicStats || !relicStats?.length) && ( +
    +
    + {transI18n("noRelicEquipped")} +
    +
    + )} +
    +
    + +
    +
    + +
    +
    + +
    + ); +} diff --git a/src/components/showcaseCard/relicShowcase.tsx b/src/components/showcaseCard/relicShowcase.tsx index 795da02..1d60d93 100644 --- a/src/components/showcaseCard/relicShowcase.tsx +++ b/src/components/showcaseCard/relicShowcase.tsx @@ -1,108 +1,108 @@ - -"use client" - -import NextImage from "next/image" -import { AvatarDetail, RelicShowcaseType } from "@/types"; - -export default function RelicShowcase({ - relic, - avatarInfo, -}: { - relic: RelicShowcaseType; - avatarInfo: AvatarDetail; -}) { - return ( - <> -
    - {/* Subtle glow overlay */} -
    - -
    -
    - - -
    - ✦✦✦✦✦ -
    -
    - -
    -
    -
    - -
    - - {relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit} - - - +{relic?.mainAffix?.level} - -
    - -
    - -
    - {relic?.subAffix?.map((subAffix, index) => { - if (!subAffix) return null - return ( -
    -
    - {subAffix?.detail?.icon ? ( - - ) : ( -
    - ? -
    - )} - - +{subAffix?.valueAffix + subAffix?.detail?.unit} - - { - (avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && ( - - {subAffix?.count} - - )} -
    -
    - ) - })} -
    -
    - - ) + +"use client" + +import NextImage from "next/image" +import { AvatarDetail, RelicShowcaseType } from "@/types"; + +export default function RelicShowcase({ + relic, + avatarInfo, +}: { + relic: RelicShowcaseType; + avatarInfo: AvatarDetail; +}) { + return ( + <> +
    + {/* Subtle glow overlay */} +
    + +
    +
    + + +
    + ✦✦✦✦✦ +
    +
    + +
    +
    +
    + +
    + + {relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit} + + + +{relic?.mainAffix?.level} + +
    + +
    + +
    + {relic?.subAffix?.map((subAffix, index) => { + if (!subAffix) return null + return ( +
    +
    + {subAffix?.detail?.icon ? ( + + ) : ( +
    + ? +
    + )} + + +{subAffix?.valueAffix + subAffix?.detail?.unit} + + { + (avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && ( + + {subAffix?.count} + + )} +
    +
    + ) + })} +
    +
    + + ) } \ No newline at end of file diff --git a/src/components/skillsInfo/index.tsx b/src/components/skillsInfo/index.tsx index 28ad8ea..568653d 100644 --- a/src/components/skillsInfo/index.tsx +++ b/src/components/skillsInfo/index.tsx @@ -1,385 +1,385 @@ -"use client" - -import { useTranslations } from "next-intl"; -import { useMemo } from "react"; -import { traceButtonsInfo, traceLink } from "@/constant/traceConstant"; -import useUserDataStore from "@/stores/userDataStore"; -import useLocaleStore from "@/stores/localeStore"; -import Image from "next/image"; -import { replaceByParam, getLocaleName } from '@/helper'; -import { mappingStats } from "@/constant/constant"; -import { toast } from "react-toastify"; -import useCurrentDataStore from "@/stores/currentDataStore"; -import { StatusAdd } from '@/types/avatarDetail'; -import { SkillDescription } from "./skillDescription"; - -export default function SkillsInfo() { - const transI18n = useTranslations("DataPage") - const { theme } = useLocaleStore() - const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore() - const { avatars, setAvatar } = useUserDataStore() - const { locale } = useLocaleStore() - const traceButtons = useMemo(() => { - if (!avatarSelected) return - return traceButtonsInfo[avatarSelected.BaseType] - }, [avatarSelected]) - - const avatarData = useMemo(() => { - if (!avatarSelected) return - return avatars[avatarSelected?.ID?.toString()] - }, [avatarSelected, avatars]) - - const avatarSkillTree = useMemo(() => { - if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} - if (avatars[avatarSelected?.ID?.toString()].enhanced) { - return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} - } - return avatarSelected?.SkillTrees || {} - }, [avatarSelected, avatars]) - - const skillInfo = useMemo(() => { - if (!avatarSelected || !skillIDSelected) return - return avatarSkillTree?.[skillIDSelected || ""]?.["1"] - }, [avatarSelected, avatarSkillTree, skillIDSelected]) - - const getTraceBuffDisplay = (status: StatusAdd) => { - const dataDisplay = mappingStats[status.PropertyType] - if (!dataDisplay) return "" - if (dataDisplay.unit === "%") { - return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}` - } - if (dataDisplay.name === "SPD") { - return `${status.Value.toFixed(1)}${dataDisplay.unit}` - } - return `${status.Value.toFixed(0)}${dataDisplay.unit}` - } - - const dataLevelUpSkill = useMemo(() => { - const skillIds: number[] = skillInfo?.LevelUpSkillID || [] - if (!avatarSelected || !avatarData) return undefined - let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) - if (avatarData.enhanced) { - result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) - } - - if (result && result.length > 0) { - return { - isServant: false, - data: result, - servantData: null, - } - } - const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {}) - ?.filter((skill) => skillIds.includes(skill.ID)) - - if (resultServant && resultServant.length > 0) { - return { - isServant: true, - data: resultServant, - servantData: avatarSelected.Memosprite, - } - } - return undefined - }, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData]) - - - const handlerMaxAll = () => { - if (!avatarData || !avatarSkillTree) { - toast.error(transI18n("maxAllFailed")) - return - } - const newData = structuredClone(avatarData) - newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => { - const firstEntry = Object.values(dataPointEntry)[0]; - if (firstEntry) { - acc[firstEntry.PointID] = firstEntry.MaxLevel; - } - return acc; - }, {} as Record) - toast.success(transI18n("maxAllSuccess")) - setAvatar(newData) - } - - const handlerChangeStatusTrace = (status: boolean) => { - if (!avatarData || !skillInfo) return - const newData = structuredClone(avatarData) - newData.data.skills[skillInfo?.PointID] = status ? 1 : 0 - - if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) { - traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => { - if (avatarSkillTree?.[pointId]?.["1"]) { - newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0 - } - }) - } - setAvatar(newData) - } - - return ( -
    -
    -
    -

    -
    - {transI18n("skills")} -

    -
    - - {traceButtons && avatarSelected && ( -
    - - {traceButtons.map((btn, index) => { - if (!avatarSelected?.SkillTrees?.[btn.id]) { - return null - } - return ( -
    { - setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id) - }} - style={{ - left: btn.left, - top: btn.top, - transform: "translate(-50%, -50%)", - }} - > - {btn.id.replaceAll("Point", - {(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && ( -

    - {`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] || 0}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`} -

    - - )} - {btn.size === "special" && ( -
    - )} - {btn.size === "memory" && ( -
    - )} - {btn.size === "elation" && ( -
    - )} - {btn.size === "big" && ( -
    - )} -
    - - - ) - })} -
    - )} - - {!traceButtons && avatarSelected && ( -
    - -
    - )} -
    - -
    -
    -

    -
    - {transI18n("details")} -

    - {skillIDSelected && avatarSelected?.SkillTrees && avatarData && ( -
    - {skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? ( -
    -
    {transI18n("level")}
    -
    - { - const newData = structuredClone(avatarData) - newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value) - setAvatar(newData) - }} - className="range range-success" - step="1" /> -
    - {Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => ( - {index} - ))} -
    -
    -
    - ) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? ( -
    - { - if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") { - if (e.target.checked) { - const newData = structuredClone(avatarData) - newData.data.skills[skillInfo?.PointID] = 1 - setAvatar(newData) - return - } - const newData = structuredClone(avatarData) - delete newData.data.skills[skillInfo?.PointID] - setAvatar(newData) - return - } - handlerChangeStatusTrace(e.target.checked) - }} - /> -
    - {avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")} -
    -
    - ) : ( - null - )} - - {((skillInfo?.PointName && skillInfo?.PointDesc) || - (skillInfo?.PointName && skillInfo?.StatusAddList.length > 0)) - && ( -
    - {getLocaleName(locale, skillInfo.PointName)} - {skillInfo.StatusAddList.length > 0 && ( -
    - {skillInfo.StatusAddList.map((status, index) => ( -
    -
    {getTraceBuffDisplay(status)}
    -
    - ))} -
    - )} -
    - )} - - {skillInfo?.PointDesc && ( -
    - )} - - {skillInfo?.LevelUpSkillID - && skillInfo?.LevelUpSkillID.length > 0 - && dataLevelUpSkill - && ( -
    - - {dataLevelUpSkill?.data?.map((skill, index) => ( -
    - -
    - {transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)} - {` (${transI18n(skill?.SkillEffect?.toLowerCase())})`} -
    - - -
    - ))} - -
    - )} -
    - )} -
    -
    -
    - ); +"use client" + +import { useTranslations } from "next-intl"; +import { useMemo } from "react"; +import { traceButtonsInfo, traceLink } from "@/constant/traceConstant"; +import useUserDataStore from "@/stores/userDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import Image from "next/image"; +import { replaceByParam, getLocaleName } from '@/helper'; +import { mappingStats } from "@/constant/constant"; +import { toast } from "react-toastify"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import { StatusAdd } from '@/types/avatarDetail'; +import { SkillDescription } from "./skillDescription"; + +export default function SkillsInfo() { + const transI18n = useTranslations("DataPage") + const { theme } = useLocaleStore() + const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore() + const { avatars, setAvatar } = useUserDataStore() + const { locale } = useLocaleStore() + const traceButtons = useMemo(() => { + if (!avatarSelected) return + return traceButtonsInfo[avatarSelected.BaseType] + }, [avatarSelected]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const skillInfo = useMemo(() => { + if (!avatarSelected || !skillIDSelected) return + return avatarSkillTree?.[skillIDSelected || ""]?.["1"] + }, [avatarSelected, avatarSkillTree, skillIDSelected]) + + const getTraceBuffDisplay = (status: StatusAdd) => { + const dataDisplay = mappingStats[status.PropertyType] + if (!dataDisplay) return "" + if (dataDisplay.unit === "%") { + return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}` + } + if (dataDisplay.name === "SPD") { + return `${status.Value.toFixed(1)}${dataDisplay.unit}` + } + return `${status.Value.toFixed(0)}${dataDisplay.unit}` + } + + const dataLevelUpSkill = useMemo(() => { + const skillIds: number[] = skillInfo?.LevelUpSkillID || [] + if (!avatarSelected || !avatarData) return undefined + let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) + if (avatarData.enhanced) { + result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) + } + + if (result && result.length > 0) { + return { + isServant: false, + data: result, + servantData: null, + } + } + const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {}) + ?.filter((skill) => skillIds.includes(skill.ID)) + + if (resultServant && resultServant.length > 0) { + return { + isServant: true, + data: resultServant, + servantData: avatarSelected.Memosprite, + } + } + return undefined + }, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData]) + + + const handlerMaxAll = () => { + if (!avatarData || !avatarSkillTree) { + toast.error(transI18n("maxAllFailed")) + return + } + const newData = structuredClone(avatarData) + newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record) + toast.success(transI18n("maxAllSuccess")) + setAvatar(newData) + } + + const handlerChangeStatusTrace = (status: boolean) => { + if (!avatarData || !skillInfo) return + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = status ? 1 : 0 + + if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) { + traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => { + if (avatarSkillTree?.[pointId]?.["1"]) { + newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0 + } + }) + } + setAvatar(newData) + } + + return ( +
    +
    +
    +

    +
    + {transI18n("skills")} +

    +
    + + {traceButtons && avatarSelected && ( +
    + + {traceButtons.map((btn, index) => { + if (!avatarSelected?.SkillTrees?.[btn.id]) { + return null + } + return ( +
    { + setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id) + }} + style={{ + left: btn.left, + top: btn.top, + transform: "translate(-50%, -50%)", + }} + > + {btn.id.replaceAll("Point", + {(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && ( +

    + {`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] || 0}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`} +

    + + )} + {btn.size === "special" && ( +
    + )} + {btn.size === "memory" && ( +
    + )} + {btn.size === "elation" && ( +
    + )} + {btn.size === "big" && ( +
    + )} +
    + + + ) + })} +
    + )} + + {!traceButtons && avatarSelected && ( +
    + +
    + )} +
    + +
    +
    +

    +
    + {transI18n("details")} +

    + {skillIDSelected && avatarSelected?.SkillTrees && avatarData && ( +
    + {skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? ( +
    +
    {transI18n("level")}
    +
    + { + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value) + setAvatar(newData) + }} + className="range range-success" + step="1" /> +
    + {Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => ( + {index} + ))} +
    +
    +
    + ) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? ( +
    + { + if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") { + if (e.target.checked) { + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = 1 + setAvatar(newData) + return + } + const newData = structuredClone(avatarData) + delete newData.data.skills[skillInfo?.PointID] + setAvatar(newData) + return + } + handlerChangeStatusTrace(e.target.checked) + }} + /> +
    + {avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")} +
    +
    + ) : ( + null + )} + + {((skillInfo?.PointName && skillInfo?.PointDesc) || + (skillInfo?.PointName && skillInfo?.StatusAddList.length > 0)) + && ( +
    + {getLocaleName(locale, skillInfo.PointName)} + {skillInfo.StatusAddList.length > 0 && ( +
    + {skillInfo.StatusAddList.map((status, index) => ( +
    +
    {getTraceBuffDisplay(status)}
    +
    + ))} +
    + )} +
    + )} + + {skillInfo?.PointDesc && ( +
    + )} + + {skillInfo?.LevelUpSkillID + && skillInfo?.LevelUpSkillID.length > 0 + && dataLevelUpSkill + && ( +
    + + {dataLevelUpSkill?.data?.map((skill, index) => ( +
    + +
    + {transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)} + {` (${transI18n(skill?.SkillEffect?.toLowerCase())})`} +
    + + +
    + ))} + +
    + )} +
    + )} +
    +
    +
    + ); } \ No newline at end of file diff --git a/src/components/skillsInfo/skillDescription.tsx b/src/components/skillsInfo/skillDescription.tsx index 8abc2ac..328544b 100644 --- a/src/components/skillsInfo/skillDescription.tsx +++ b/src/components/skillsInfo/skillDescription.tsx @@ -1,41 +1,41 @@ -import { getLocaleName, replaceByParam } from "@/helper"; -import { AvatarStore, SkillDetail, SkillTreePoint } from "@/types"; -import ExtraEffectList from "../extraInfo"; - -export const SkillDescription = ({ skill, locale, avatarData, skillInfo }: { - skill: SkillDetail, - locale: string, - avatarData: AvatarStore, - skillInfo: SkillTreePoint -}) => { - const levelKey = avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || ""; - const params = skill.Level[levelKey]?.Param || []; - const descHtml = getLocaleName(locale, skill.Desc) || getLocaleName(locale, skill.SimpleDesc); - - const extraList = Object.values(skill.Extra).length > 0 - ? skill.Extra - : skill?.SimpleExtra || {}; - - return ( -
    -
    -
    -
    -
    -
    - -
    -
    - - {Object.keys(extraList).length > 0 && ( - - )} -
    - ); +import { getLocaleName, replaceByParam } from "@/helper"; +import { AvatarStore, SkillDetail, SkillTreePoint } from "@/types"; +import ExtraEffectList from "../extraInfo"; + +export const SkillDescription = ({ skill, locale, avatarData, skillInfo }: { + skill: SkillDetail, + locale: string, + avatarData: AvatarStore, + skillInfo: SkillTreePoint +}) => { + const levelKey = avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || ""; + const params = skill.Level[levelKey]?.Param || []; + const descHtml = getLocaleName(locale, skill.Desc) || getLocaleName(locale, skill.SimpleDesc); + + const extraList = Object.values(skill.Extra).length > 0 + ? skill.Extra + : skill?.SimpleExtra || {}; + + return ( +
    +
    +
    +
    +
    +
    + +
    +
    + + {Object.keys(extraList).length > 0 && ( + + )} +
    + ); }; \ No newline at end of file diff --git a/src/components/themeController/clientThemeWrapper.tsx b/src/components/themeController/clientThemeWrapper.tsx index 704e8f5..09f456b 100644 --- a/src/components/themeController/clientThemeWrapper.tsx +++ b/src/components/themeController/clientThemeWrapper.tsx @@ -1,8 +1,8 @@ -"use client"; -import { PropsWithChildren, useContext } from "react"; -import { ThemeContext } from "./themeContext"; - -export function ClientThemeWrapper({ children }: PropsWithChildren) { - const { theme } = useContext(ThemeContext); - return
    {children}
    ; +"use client"; +import { PropsWithChildren, useContext } from "react"; +import { ThemeContext } from "./themeContext"; + +export function ClientThemeWrapper({ children }: PropsWithChildren) { + const { theme } = useContext(ThemeContext); + return
    {children}
    ; } \ No newline at end of file diff --git a/src/components/themeController/index.ts b/src/components/themeController/index.ts index 5600303..fbba780 100644 --- a/src/components/themeController/index.ts +++ b/src/components/themeController/index.ts @@ -1,2 +1,2 @@ -export * from "./clientThemeWrapper" -export * from "./themeContext" +export * from "./clientThemeWrapper" +export * from "./themeContext" diff --git a/src/components/themeController/themeContext.tsx b/src/components/themeController/themeContext.tsx index 0033644..651bbd0 100644 --- a/src/components/themeController/themeContext.tsx +++ b/src/components/themeController/themeContext.tsx @@ -1,41 +1,41 @@ -"use client"; -import { createContext, PropsWithChildren, useEffect } from "react"; -import useLocaleStore from "@/stores/localeStore"; - -interface ThemeContextType { - theme?: string; - changeTheme?: (nextTheme: string | null) => void; -} -export const ThemeContext = createContext({}); - -export const ThemeProvider = ({ children }: PropsWithChildren) => { - - const { theme, setTheme } = useLocaleStore() - - useEffect(() => { - if (typeof window !== "undefined") { - const storedTheme = localStorage.getItem("theme"); - if (storedTheme) setTheme(storedTheme); - } - }, [setTheme]); - - const changeTheme = (nextTheme: string | null) => { - if (nextTheme) { - setTheme(nextTheme); - if (typeof window !== "undefined") { - localStorage.setItem("theme", nextTheme); - } - } else { - setTheme(theme === "winter" ? "night" : "winter"); - if (typeof window !== "undefined") { - localStorage.setItem("theme", theme); - } - } - }; - - return ( - - {children} - - ); +"use client"; +import { createContext, PropsWithChildren, useEffect } from "react"; +import useLocaleStore from "@/stores/localeStore"; + +interface ThemeContextType { + theme?: string; + changeTheme?: (nextTheme: string | null) => void; +} +export const ThemeContext = createContext({}); + +export const ThemeProvider = ({ children }: PropsWithChildren) => { + + const { theme, setTheme } = useLocaleStore() + + useEffect(() => { + if (typeof window !== "undefined") { + const storedTheme = localStorage.getItem("theme"); + if (storedTheme) setTheme(storedTheme); + } + }, [setTheme]); + + const changeTheme = (nextTheme: string | null) => { + if (nextTheme) { + setTheme(nextTheme); + if (typeof window !== "undefined") { + localStorage.setItem("theme", nextTheme); + } + } else { + setTheme(theme === "winter" ? "night" : "winter"); + if (typeof window !== "undefined") { + localStorage.setItem("theme", theme); + } + } + }; + + return ( + + {children} + + ); }; \ No newline at end of file diff --git a/src/constant/constant.ts b/src/constant/constant.ts index 8a5c198..fd935a5 100644 --- a/src/constant/constant.ts +++ b/src/constant/constant.ts @@ -1,220 +1,220 @@ -export const listCurrentLanguage = { - en: { label: "English", flag: "🇬🇧" }, - vi: { label: "Tiếng Việt", flag: "🇻🇳" }, - ja: { label: "日本語", flag: "🇯🇵" }, - ko: { label: "한국어", flag: "🇰🇷" }, - zh: { label: "中文", flag: "🇨🇳" }, - de: { label: "Deutsch", flag: "🇩🇪" }, - es: { label: "Español", flag: "🇪🇸" }, - fr: { label: "Français", flag: "🇫🇷" }, - id: { label: "Bahasa Indonesia", flag: "🇮🇩" }, - pt: { label: "Português", flag: "🇵🇹" }, - ru: { label: "Русский", flag: "🇷🇺" }, - th: { label: "ไทย", flag: "🇹🇭" } -}; - -export const listCurrentLanguageApi : Record = { - ja: "jp", - ko: "kr", - en: "en", - vi: "vi", - zh: "cn", - de: "de", - es: "es", - fr: "fr", - id: "id", - pt: "pt", - ru: "ru", - th: "th" -}; - -export const mappingStats = > { - "HPDelta": { - name:"HP", - icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", - unit: "", - baseStat: "HP" - }, - "AttackDelta": { - name:"ATK", - icon:"spriteoutput/ui/avatar/icon/IconAttack.png", - unit: "", - baseStat: "ATK" - }, - "HPAddedRatio": { - name:"HP", - icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", - unit: "%", - baseStat: "HP" - }, - "AttackAddedRatio": { - name:"ATK", - icon:"spriteoutput/ui/avatar/icon/IconAttack.png", - unit: "%", - baseStat: "ATK" - }, - "DefenceDelta": { - name:"DEF", - icon:"spriteoutput/ui/avatar/icon/IconDefence.png", - unit: "", - baseStat: "DEF" - }, - "DefenceAddedRatio": { - name:"DEF", - icon:"spriteoutput/ui/avatar/icon/IconDefence.png", - unit: "%", - baseStat: "DEF" - }, - "SpeedAddedRatio": { - name:"SPD", - icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", - unit: "%", - baseStat: "SPD" - }, - "BaseSpeed": { - name:"SPD", - icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", - unit: "", - baseStat: "SPD" - }, - "CriticalChanceBase": { - name:"CRIT Rate", - icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png", - unit: "%", - baseStat: "CRITRate" - }, - "CriticalDamageBase": { - name:"CRIT DMG", - icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png", - unit: "%", - baseStat: "CRITDmg" - }, - "HealRatioBase": { - name:"Outgoing Healing Boost", - icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png", - unit: "%", - baseStat: "HealBoost" - }, - "StatusProbabilityBase": { - name:"Effect Hit Rate", - icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png", - unit: "%", - baseStat: "EffectHitRate" - }, - "StatusResistanceBase": { - name:"Effect RES", - icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png", - unit: "%", - baseStat: "EffectRES" - }, - "BreakDamageAddedRatioBase": { - name:"Break Effect", - icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png", - unit: "%", - baseStat: "BreakEffect" - }, - "SpeedDelta": { - name:"SPD", - icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", - unit: "", - baseStat: "SPD" - }, - "PhysicalAddedRatio": { - name:"Physical DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", - unit: "%", - baseStat: "PhysicalAdd" - }, - "FireAddedRatio": { - name:"Fire DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", - unit: "%", - baseStat: "FireAdd" - }, - "IceAddedRatio": { - name:"Ice DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", - unit: "%", - baseStat: "IceAdd" - }, - "ThunderAddedRatio": { - name:"Thunder DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", - unit: "%", - baseStat: "ThunderAdd" - }, - "WindAddedRatio": { - name:"Wind DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", - unit: "%", - baseStat: "WindAdd" - }, - "QuantumAddedRatio": { - name:"Quantum DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", - unit: "%", - baseStat: "QuantumAdd" - }, - "ImaginaryAddedRatio": { - name:"Imaginary DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", - unit: "%", - baseStat: "ImaginaryAdd" - }, - "ElationDamageAddedRatioBase": { - name:"Elation DMG Boost", - icon:"spriteoutput/ui/avatar/icon/IconJoy.png", - unit: "%", - baseStat: "ElationAdd" - }, - "SPRatioBase": { - name:"Energy Regeneration Rate", - icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", - unit: "%", - baseStat: "EnergyRate" - } -} - - -export const ratioStats = [ - "HPAddedRatio", - "AttackAddedRatio", - "DefenceAddedRatio", - "SpeedAddedRatio", -] - -export const mappingRelicSlot: Record = { - "1": "HEAD", - "2": "HAND", - "3": "BODY", - "4": "FOOT", - "5": "NECK", - "6": "OBJECT", -} - -export const themeColors: Record = { - winter: { - bg: '#ffffff', - bgHover: '#f1f5f9', - text: '#3a4f6b', - border: '#cbd5e1' - }, - night: { - bg: '#1d232a', - bgHover: '#2a323c', - text: '#cbcdd1', - border: '#3f3f46' - }, - cupcake: { - bg: '#faf7f5', - bgHover: '#f3eae6', - text: '#281333', - border: '#e5d3cb' - }, - coffee: { - bg: '#20161f', - bgHover: '#2a1d29', - text: '#c4a051', - border: '#3a2a36' - } -} +export const listCurrentLanguage = { + en: { label: "English", flag: "🇬🇧" }, + vi: { label: "Tiếng Việt", flag: "🇻🇳" }, + ja: { label: "日本語", flag: "🇯🇵" }, + ko: { label: "한국어", flag: "🇰🇷" }, + zh: { label: "中文", flag: "🇨🇳" }, + de: { label: "Deutsch", flag: "🇩🇪" }, + es: { label: "Español", flag: "🇪🇸" }, + fr: { label: "Français", flag: "🇫🇷" }, + id: { label: "Bahasa Indonesia", flag: "🇮🇩" }, + pt: { label: "Português", flag: "🇵🇹" }, + ru: { label: "Русский", flag: "🇷🇺" }, + th: { label: "ไทย", flag: "🇹🇭" } +}; + +export const listCurrentLanguageApi : Record = { + ja: "jp", + ko: "kr", + en: "en", + vi: "vi", + zh: "cn", + de: "de", + es: "es", + fr: "fr", + id: "id", + pt: "pt", + ru: "ru", + th: "th" +}; + +export const mappingStats = > { + "HPDelta": { + name:"HP", + icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + baseStat: "HP" + }, + "AttackDelta": { + name:"ATK", + icon:"spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + baseStat: "ATK" + }, + "HPAddedRatio": { + name:"HP", + icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "%", + baseStat: "HP" + }, + "AttackAddedRatio": { + name:"ATK", + icon:"spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "%", + baseStat: "ATK" + }, + "DefenceDelta": { + name:"DEF", + icon:"spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + baseStat: "DEF" + }, + "DefenceAddedRatio": { + name:"DEF", + icon:"spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "%", + baseStat: "DEF" + }, + "SpeedAddedRatio": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "%", + baseStat: "SPD" + }, + "BaseSpeed": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + baseStat: "SPD" + }, + "CriticalChanceBase": { + name:"CRIT Rate", + icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + baseStat: "CRITRate" + }, + "CriticalDamageBase": { + name:"CRIT DMG", + icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + baseStat: "CRITDmg" + }, + "HealRatioBase": { + name:"Outgoing Healing Boost", + icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + baseStat: "HealBoost" + }, + "StatusProbabilityBase": { + name:"Effect Hit Rate", + icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + baseStat: "EffectHitRate" + }, + "StatusResistanceBase": { + name:"Effect RES", + icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + baseStat: "EffectRES" + }, + "BreakDamageAddedRatioBase": { + name:"Break Effect", + icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + baseStat: "BreakEffect" + }, + "SpeedDelta": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + baseStat: "SPD" + }, + "PhysicalAddedRatio": { + name:"Physical DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + baseStat: "PhysicalAdd" + }, + "FireAddedRatio": { + name:"Fire DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + baseStat: "FireAdd" + }, + "IceAddedRatio": { + name:"Ice DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + baseStat: "IceAdd" + }, + "ThunderAddedRatio": { + name:"Thunder DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + baseStat: "ThunderAdd" + }, + "WindAddedRatio": { + name:"Wind DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + baseStat: "WindAdd" + }, + "QuantumAddedRatio": { + name:"Quantum DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + baseStat: "QuantumAdd" + }, + "ImaginaryAddedRatio": { + name:"Imaginary DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + baseStat: "ImaginaryAdd" + }, + "ElationDamageAddedRatioBase": { + name:"Elation DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + baseStat: "ElationAdd" + }, + "SPRatioBase": { + name:"Energy Regeneration Rate", + icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + baseStat: "EnergyRate" + } +} + + +export const ratioStats = [ + "HPAddedRatio", + "AttackAddedRatio", + "DefenceAddedRatio", + "SpeedAddedRatio", +] + +export const mappingRelicSlot: Record = { + "1": "HEAD", + "2": "HAND", + "3": "BODY", + "4": "FOOT", + "5": "NECK", + "6": "OBJECT", +} + +export const themeColors: Record = { + winter: { + bg: '#ffffff', + bgHover: '#f1f5f9', + text: '#3a4f6b', + border: '#cbd5e1' + }, + night: { + bg: '#1d232a', + bgHover: '#2a323c', + text: '#cbcdd1', + border: '#3f3f46' + }, + cupcake: { + bg: '#faf7f5', + bgHover: '#f3eae6', + text: '#281333', + border: '#e5d3cb' + }, + coffee: { + bg: '#20161f', + bgHover: '#2a1d29', + text: '#c4a051', + border: '#3a2a36' + } +} diff --git a/src/constant/traceConstant.ts b/src/constant/traceConstant.ts index 5522426..336d34e 100644 --- a/src/constant/traceConstant.ts +++ b/src/constant/traceConstant.ts @@ -1,1148 +1,1148 @@ - - -export const traceButtonsInfo: Record = { - Knight: [ - { id: 'Point03', size: 'big', left: '50%', top: '52%' }, - { id: 'Point04', size: 'big', left: '50%', top: '35%' }, - { id: 'Point02', size: 'big', left: '67%', top: '55%' }, - { id: 'Point05', size: 'big', left: '50%', top: '69.5%' }, - { id: 'Point01', size: 'big', left: '33%', top: '55%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '21.5%' }, - { id: 'Point07', size: 'medium', left: '71%', top: '86%' }, - { id: 'Point06', size: 'medium', left: '29%', top: '86%' }, - { id: 'Point16', size: 'small', left: '50%', top: '9%' }, - { id: 'Point18', size: 'small', left: '66%', top: '13%' }, - { id: 'Point17', size: 'small', left: '34%', top: '13%' }, - { id: 'Point15', size: 'small', left: '79.5%', top: '44.5%' }, - { id: 'Point12', size: 'small', left: '20.5%', top: '44.5%' }, - { id: 'Point09', size: 'small', left: '50%', top: '82.5%' }, - { id: 'Point13', size: 'small', left: '81%', top: '78%' }, - { id: 'Point10', size: 'small', left: '19.5%', top: '78.5%' }, - { id: 'Point14', size: 'small', left: '89%', top: '70%' }, - { id: 'Point11', size: 'small', left: '11%', top: '70%' } - ], - Mage: [ - { id: 'Point03', size: 'big', left: '50%', top: '52%' }, - { id: 'Point04', size: 'big', left: '50%', top: '30%' }, - { id: 'Point02', size: 'big', left: '67%', top: '52%' }, - { id: 'Point05', size: 'big', left: '50%', top: '82.5%' }, - { id: 'Point01', size: 'big', left: '33.5%', top: '52%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '14.5%' }, - { id: 'Point07', size: 'medium', left: '80.5%', top: '53%' }, - { id: 'Point06', size: 'medium', left: '19.5%', top: '53%' }, - { id: 'Point17', size: 'small', left: '66%', top: '17.5%' }, - { id: 'Point16', size: 'small', left: '34%', top: '17.5%' }, - { id: 'Point18', size: 'small', left: '66%', top: '79%' }, - { id: 'Point09', size: 'small', left: '34%', top: '79%' }, - { id: 'Point13', size: 'small', left: '93%', top: '52%' }, - { id: 'Point14', size: 'small', left: '89%', top: '63%' }, - { id: 'Point15', size: 'small', left: '89%', top: '41.5%' }, - { id: 'Point10', size: 'small', left: '7%', top: '52%' }, - { id: 'Point11', size: 'small', left: '11.5%', top: '63%' }, - { id: 'Point12', size: 'small', left: '12%', top: '41.5%' } - ], - Priest: [ - { id: 'Point03', size: 'big', left: '50%', top: '47%' }, - { id: 'Point04', size: 'big', left: '50%', top: '30%' }, - { id: 'Point02', size: 'big', left: '67%', top: '46%' }, - { id: 'Point05', size: 'big', left: '50%', top: '65%' }, - { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '13%' }, - { id: 'Point07', size: 'medium', left: '68%', top: '79%' }, - { id: 'Point06', size: 'medium', left: '33%', top: '79%' }, - { id: 'Point16', size: 'small', left: '65%', top: '18%' }, - { id: 'Point17', size: 'small', left: '35%', top: '18%' }, - { id: 'Point09', size: 'small', left: '57%', top: '89%' }, - { id: 'Point18', size: 'small', left: '43%', top: '89%' }, - { id: 'Point10', size: 'small', left: '80.5%', top: '66%' }, - { id: 'Point11', size: 'small', left: '93%', top: '52%' }, - { id: 'Point12', size: 'small', left: '81%', top: '39%' }, - { id: 'Point13', size: 'small', left: '20.5%', top: '66%' }, - { id: 'Point14', size: 'small', left: '7%', top: '52%' }, - { id: 'Point15', size: 'small', left: '20%', top: '39%' } - ], - Rogue: [ - { id: 'Point03', size: 'big', left: '50%', top: '52%' }, - { id: 'Point04', size: 'big', left: '50%', top: '35%' }, - { id: 'Point02', size: 'big', left: '67%', top: '46%' }, - { id: 'Point05', size: 'big', left: '50%', top: '69%' }, - { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, - { id: 'Point07', size: 'medium', left: '68%', top: '71%' }, - { id: 'Point06', size: 'medium', left: '30%', top: '71%' }, - { id: 'Point16', size: 'small', left: '50%', top: '9%' }, - { id: 'Point18', size: 'small', left: '66%', top: '14%' }, - { id: 'Point17', size: 'small', left: '34%', top: '14%' }, - { id: 'Point09', size: 'small', left: '50%', top: '87%' }, - { id: 'Point15', size: 'small', left: '81%', top: '35%' }, - { id: 'Point12', size: 'small', left: '19%', top: '35%' }, - { id: 'Point13', size: 'small', left: '81%', top: '57%' }, - { id: 'Point10', size: 'small', left: '20%', top: '57%' }, - { id: 'Point14', size: 'small', left: '93%', top: '44%' }, - { id: 'Point11', size: 'small', left: '7%', top: '44%' } - ], - Shaman: [ - { id: 'Point03', size: 'big', left: '50%', top: '56%' }, - { id: 'Point04', size: 'big', left: '50%', top: '35%' }, - { id: 'Point02', size: 'big', left: '67%', top: '41%' }, - { id: 'Point05', size: 'big', left: '50%', top: '74%' }, - { id: 'Point01', size: 'big', left: '33.5%', top: '41%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, - { id: 'Point07', size: 'medium', left: '89.5%', top: '56.5%' }, - { id: 'Point06', size: 'medium', left: '17.5%', top: '56.5%' }, - { id: 'Point16', size: 'small', left: '50%', top: '10%' }, - { id: 'Point18', size: 'small', left: '66%', top: '14%' }, - { id: 'Point17', size: 'small', left: '34%', top: '14%' }, - { id: 'Point09', size: 'small', left: '50%', top: '87%' }, - { id: 'Point15', size: 'small', left: '62%', top: '83%' }, - { id: 'Point12', size: 'small', left: '38%', top: '83%' }, - { id: 'Point13', size: 'small', left: '77%', top: '70%' }, - { id: 'Point14', size: 'small', left: '67%', top: '61%' }, - { id: 'Point10', size: 'small', left: '7%', top: '44%' }, - { id: 'Point11', size: 'small', left: '20%', top: '31%' } - ], - Warlock: [ - { id: 'Point03', size: 'big', left: '50%', top: '44%' }, - { id: 'Point04', size: 'big', left: '50%', top: '24%' }, - { id: 'Point02', size: 'big', left: '67%', top: '47%' }, - { id: 'Point05', size: 'big', left: '50%', top: '61%' }, - { id: 'Point01', size: 'big', left: '33%', top: '47%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '8%' }, - { id: 'Point07', size: 'medium', left: '81.5%', top: '37%' }, - { id: 'Point06', size: 'medium', left: '20.5%', top: '37%' }, - { id: 'Point17', size: 'small', left: '66%', top: '14%' }, - { id: 'Point16', size: 'small', left: '34%', top: '14%' }, - { id: 'Point09', size: 'small', left: '50%', top: '74%' }, - { id: 'Point18', size: 'small', left: '50%', top: '87%' }, - { id: 'Point13', size: 'small', left: '94%', top: '48%' }, - { id: 'Point14', size: 'small', left: '81%', top: '61%' }, - { id: 'Point15', size: 'small', left: '68%', top: '74%' }, - { id: 'Point10', size: 'small', left: '6%', top: '48%' }, - { id: 'Point11', size: 'small', left: '20%', top: '61%' }, - { id: 'Point12', size: 'small', left: '33%', top: '74%' } - ], - Warrior: [ - { id: 'Point03', size: 'big', left: '50%', top: '52%' }, - { id: 'Point04', size: 'big', left: '50%', top: '35%' }, - { id: 'Point02', size: 'big', left: '69%', top: '48%' }, - { id: 'Point05', size: 'big', left: '50%', top: '69%' }, - { id: 'Point01', size: 'big', left: '33%', top: '48%' }, - { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, - { id: 'Point07', size: 'medium', left: '67%', top: '83%' }, - { id: 'Point06', size: 'medium', left: '33%', top: '83%' }, - { id: 'Point16', size: 'small', left: '50%', top: '9%' }, - { id: 'Point18', size: 'small', left: '66%', top: '14%' }, - { id: 'Point17', size: 'small', left: '34%', top: '14%' }, - { id: 'Point09', size: 'small', left: '50%', top: '87%' }, - { id: 'Point15', size: 'small', left: '81%', top: '43.5%' }, - { id: 'Point12', size: 'small', left: '19%', top: '43.5%' }, - { id: 'Point13', size: 'small', left: '81%', top: '70%' }, - { id: 'Point10', size: 'small', left: '19%', top: '70%' }, - { id: 'Point14', size: 'small', left: '93%', top: '56.5%' }, - { id: 'Point11', size: 'small', left: '7%', top: '56.5%' } - ], - Memory: [ - { id: 'Point03', size: 'big', left: '50%', top: '72%' }, - { id: 'Point04', size: 'big', left: '75%', top: '53%' }, - { id: 'Point02', size: 'big', left: '67%', top: '67%' }, - { id: 'Point05', size: 'big', left: '27%', top: '53%' }, - { id: 'Point01', size: 'big', left: '35%', top: '67%' }, - { id: 'Point08', size: 'medium', left: '34%', top: '34%' }, - { id: 'Point07', size: 'medium', left: '50%', top: '87%' }, - { id: 'Point06', size: 'medium', left: '90%', top: '50%' }, - { id: 'Point16', size: 'small', left: '27.5%', top: '22%' }, - { id: 'Point17', size: 'small', left: '43%', top: '14%' }, - { id: 'Point18', size: 'small', left: '59%', top: '14%' }, - { id: 'Point19', size: 'memory', left: '50%', top: '48%' }, - { id: 'Point20', size: 'memory', left: '50%', top: '28%' }, - { id: 'Point12', size: 'small', left: '86%', top: '40%' }, - { id: 'Point13', size: 'small', left: '86%', top: '63%' }, - { id: 'Point14', size: 'small', left: '35%', top: '82%' }, - { id: 'Point15', size: 'small', left: '65%', top: '82%' }, - { id: 'Point09', size: 'small', left: '9%', top: '50%' }, - { id: 'Point10', size: 'small', left: '13%', top: '40%' }, - { id: 'Point11', size: 'small', left: '13%', top: '63%' }, - { id: 'Point21', size: 'special', left: '70%', top: '34%'} - ], - Elation: [ - { id: 'Point04', size: 'big', left: '50%', top: '50.5%' }, - { id: 'Point03', size: 'big', left: '50%', top: '35%' }, - { id: 'Point02', size: 'big', left: '67.5%', top: '20%' }, - { id: 'Point05', size: 'big', left: '50%', top: '68.5%' }, - { id: 'Point01', size: 'big', left: '32.5%', top: '20%' }, - { id: 'Point08', size: 'medium', left: '85%', top: '30%' }, - { id: 'Point07', size: 'medium', left: '15%', top: '30%' }, - { id: 'Point06', size: 'medium', left: '50%', top: '87%' }, - { id: 'Point12', size: 'small', left: '65%', top: '88%' }, - { id: 'Point15', size: 'small', left: '23%', top: '53%' }, - { id: 'Point17', size: 'small', left: '88%', top: '63%' }, - { id: 'Point18', size: 'small', left: '76%', top: '53%' }, - { id: 'Point13', size: 'small', left: '9%', top: '48.5%' }, - { id: 'Point16', size: 'small', left: '90.5%', top: '48.5%' }, - { id: 'Point09', size: 'small', left: '36%', top: '64%' }, - { id: 'Point10', size: 'small', left: '64%', top: '64%' }, - { id: 'Point14', size: 'small', left: '11.5%', top: '63%' }, - { id: 'Point11', size: 'small', left: '35%', top: '88%' }, - { id: 'Point22', size: 'elation', left: '50%', top: '17%' } - ], -} - -export const traceLink : Record> = { - Knight: { - Point06: ["Point12", "Point11"], - - Point08: ["Point16", "Point17", "Point18"], - Point16: ["Point17", "Point18"], - Point17: ["Point18"], - - Point07: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point14: ["Point15"], - }, - Mage: { - Point08: ["Point16", "Point17"], - Point07: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point06: ["Point10", "Point11", "Point12"], - Point10: ["Point11", "Point12"], - }, - Priest: { - Point08: ["Point16", "Point17"], - Point07: ["Point10", "Point11", "Point12"], - Point10: ["Point11", "Point12"], - Point11: ["Point12"], - Point06: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point14: ["Point15"], - }, - Rogue: { - Point08: ["Point16", "Point17", "Point18"], - Point16: ["Point17", "Point18"], - Point07: ["Point13", "Point14"], - Point13: ["Point14"], - Point06: ["Point10", "Point11"], - Point10: ["Point11"], - }, - Shaman: { - Point08: ["Point16", "Point17", "Point18"], - Point16: ["Point17", "Point18"], - Point07: ["Point13", "Point14"], - Point13: ["Point14"], - Point06: ["Point10", "Point11"], - Point10: ["Point11"], - Point09: ["Point15", "Point12"], - }, - Warlock: { - Point08: ["Point16", "Point17"], - Point07: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point14: ["Point15"], - Point06: ["Point10", "Point11", "Point12"], - Point10: ["Point11", "Point12"], - Point11: ["Point12"], - Point09: ["Point18"], - }, - Warrior: { - Point08: ["Point16", "Point17", "Point18"], - Point16: ["Point17", "Point18"], - Point07: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point14: ["Point15"], - Point06: ["Point10", "Point11", "Point12"], - Point10: ["Point11", "Point12"], - Point11: ["Point12"], - }, - Memory: { - Point16: ["Point17", "Point18"], - Point08: ["Point16", "Point17", "Point18"], - Point17: ["Point18"], - Point09: ["Point10", "Point11"], - Point07: ["Point14", "Point15"], - Point06: ["Point12", "Point13"], - }, - Elation: { - Point08: ["Point16", "Point17", "Point18"], - Point16: ["Point17", "Point18"], - Point07: ["Point13", "Point14", "Point15"], - Point13: ["Point14", "Point15"], - Point14: ["Point15"], - Point06: ["Point10", "Point11", "Point12"], - Point10: ["Point11", "Point12"], - Point11: ["Point12"], - }, -} - -export const traceShowCaseMap : Record> = { - Knight: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point15", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - }, - { - id: "Point09", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - } - ] - }, - Mage: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - } - ] - }, - Priest: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - } - ] - }, - Rogue: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point15", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - }, - { - id: "Point09", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - } - ] - }, - Shaman: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point15", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - }, - { - id: "Point09", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - } - ] - }, - Warlock: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - } - ] - }, - Warrior: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - } - ] - }, - Memory: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - } - ], - "5": [ - { - id: "Point19", - size: "big-memory", - isLink: false - }, - { - id: "Point20", - size: "big-memory", - isLink: false - }, - { - id: "Point21", - size: "big", - isLink: false - } - ], - }, - Elation: { - "1": [ - { - id: "Point01", - size: "big", - isLink: false - }, - { - id: "Point09", - size: "small", - isLink: true - } - ], - "2": [ - { - id: "Point02", - size: "big", - isLink: false - }, - { - id: "Point06", - size: "medium", - isLink: true - }, - { - id: "Point10", - size: "small", - isLink: true - }, - { - id: "Point11", - size: "small", - isLink: true - }, - { - id: "Point12", - size: "small", - isLink: true - } - ], - "3": [ - { - id: "Point03", - size: "big", - isLink: false - }, - { - id: "Point07", - size: "medium", - isLink: true - }, - { - id: "Point13", - size: "small", - isLink: true - }, - { - id: "Point14", - size: "small", - isLink: true - }, - { - id: "Point15", - size: "small", - isLink: true - } - ], - "4": [ - { - id: "Point04", - size: "big", - isLink: false - }, - { - id: "Point08", - size: "medium", - isLink: true - }, - { - id: "Point16", - size: "small", - isLink: true - }, - { - id: "Point18", - size: "small", - isLink: true - }, - { - id: "Point17", - size: "small", - isLink: true - } - ] - }, + + +export const traceButtonsInfo: Record = { + Knight: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '55%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69.5%' }, + { id: 'Point01', size: 'big', left: '33%', top: '55%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '21.5%' }, + { id: 'Point07', size: 'medium', left: '71%', top: '86%' }, + { id: 'Point06', size: 'medium', left: '29%', top: '86%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '13%' }, + { id: 'Point17', size: 'small', left: '34%', top: '13%' }, + { id: 'Point15', size: 'small', left: '79.5%', top: '44.5%' }, + { id: 'Point12', size: 'small', left: '20.5%', top: '44.5%' }, + { id: 'Point09', size: 'small', left: '50%', top: '82.5%' }, + { id: 'Point13', size: 'small', left: '81%', top: '78%' }, + { id: 'Point10', size: 'small', left: '19.5%', top: '78.5%' }, + { id: 'Point14', size: 'small', left: '89%', top: '70%' }, + { id: 'Point11', size: 'small', left: '11%', top: '70%' } + ], + Mage: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '30%' }, + { id: 'Point02', size: 'big', left: '67%', top: '52%' }, + { id: 'Point05', size: 'big', left: '50%', top: '82.5%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '52%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '14.5%' }, + { id: 'Point07', size: 'medium', left: '80.5%', top: '53%' }, + { id: 'Point06', size: 'medium', left: '19.5%', top: '53%' }, + { id: 'Point17', size: 'small', left: '66%', top: '17.5%' }, + { id: 'Point16', size: 'small', left: '34%', top: '17.5%' }, + { id: 'Point18', size: 'small', left: '66%', top: '79%' }, + { id: 'Point09', size: 'small', left: '34%', top: '79%' }, + { id: 'Point13', size: 'small', left: '93%', top: '52%' }, + { id: 'Point14', size: 'small', left: '89%', top: '63%' }, + { id: 'Point15', size: 'small', left: '89%', top: '41.5%' }, + { id: 'Point10', size: 'small', left: '7%', top: '52%' }, + { id: 'Point11', size: 'small', left: '11.5%', top: '63%' }, + { id: 'Point12', size: 'small', left: '12%', top: '41.5%' } + ], + Priest: [ + { id: 'Point03', size: 'big', left: '50%', top: '47%' }, + { id: 'Point04', size: 'big', left: '50%', top: '30%' }, + { id: 'Point02', size: 'big', left: '67%', top: '46%' }, + { id: 'Point05', size: 'big', left: '50%', top: '65%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '13%' }, + { id: 'Point07', size: 'medium', left: '68%', top: '79%' }, + { id: 'Point06', size: 'medium', left: '33%', top: '79%' }, + { id: 'Point16', size: 'small', left: '65%', top: '18%' }, + { id: 'Point17', size: 'small', left: '35%', top: '18%' }, + { id: 'Point09', size: 'small', left: '57%', top: '89%' }, + { id: 'Point18', size: 'small', left: '43%', top: '89%' }, + { id: 'Point10', size: 'small', left: '80.5%', top: '66%' }, + { id: 'Point11', size: 'small', left: '93%', top: '52%' }, + { id: 'Point12', size: 'small', left: '81%', top: '39%' }, + { id: 'Point13', size: 'small', left: '20.5%', top: '66%' }, + { id: 'Point14', size: 'small', left: '7%', top: '52%' }, + { id: 'Point15', size: 'small', left: '20%', top: '39%' } + ], + Rogue: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '46%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '68%', top: '71%' }, + { id: 'Point06', size: 'medium', left: '30%', top: '71%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '81%', top: '35%' }, + { id: 'Point12', size: 'small', left: '19%', top: '35%' }, + { id: 'Point13', size: 'small', left: '81%', top: '57%' }, + { id: 'Point10', size: 'small', left: '20%', top: '57%' }, + { id: 'Point14', size: 'small', left: '93%', top: '44%' }, + { id: 'Point11', size: 'small', left: '7%', top: '44%' } + ], + Shaman: [ + { id: 'Point03', size: 'big', left: '50%', top: '56%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '41%' }, + { id: 'Point05', size: 'big', left: '50%', top: '74%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '41%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '89.5%', top: '56.5%' }, + { id: 'Point06', size: 'medium', left: '17.5%', top: '56.5%' }, + { id: 'Point16', size: 'small', left: '50%', top: '10%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '62%', top: '83%' }, + { id: 'Point12', size: 'small', left: '38%', top: '83%' }, + { id: 'Point13', size: 'small', left: '77%', top: '70%' }, + { id: 'Point14', size: 'small', left: '67%', top: '61%' }, + { id: 'Point10', size: 'small', left: '7%', top: '44%' }, + { id: 'Point11', size: 'small', left: '20%', top: '31%' } + ], + Warlock: [ + { id: 'Point03', size: 'big', left: '50%', top: '44%' }, + { id: 'Point04', size: 'big', left: '50%', top: '24%' }, + { id: 'Point02', size: 'big', left: '67%', top: '47%' }, + { id: 'Point05', size: 'big', left: '50%', top: '61%' }, + { id: 'Point01', size: 'big', left: '33%', top: '47%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '8%' }, + { id: 'Point07', size: 'medium', left: '81.5%', top: '37%' }, + { id: 'Point06', size: 'medium', left: '20.5%', top: '37%' }, + { id: 'Point17', size: 'small', left: '66%', top: '14%' }, + { id: 'Point16', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '74%' }, + { id: 'Point18', size: 'small', left: '50%', top: '87%' }, + { id: 'Point13', size: 'small', left: '94%', top: '48%' }, + { id: 'Point14', size: 'small', left: '81%', top: '61%' }, + { id: 'Point15', size: 'small', left: '68%', top: '74%' }, + { id: 'Point10', size: 'small', left: '6%', top: '48%' }, + { id: 'Point11', size: 'small', left: '20%', top: '61%' }, + { id: 'Point12', size: 'small', left: '33%', top: '74%' } + ], + Warrior: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '69%', top: '48%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69%' }, + { id: 'Point01', size: 'big', left: '33%', top: '48%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '67%', top: '83%' }, + { id: 'Point06', size: 'medium', left: '33%', top: '83%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '81%', top: '43.5%' }, + { id: 'Point12', size: 'small', left: '19%', top: '43.5%' }, + { id: 'Point13', size: 'small', left: '81%', top: '70%' }, + { id: 'Point10', size: 'small', left: '19%', top: '70%' }, + { id: 'Point14', size: 'small', left: '93%', top: '56.5%' }, + { id: 'Point11', size: 'small', left: '7%', top: '56.5%' } + ], + Memory: [ + { id: 'Point03', size: 'big', left: '50%', top: '72%' }, + { id: 'Point04', size: 'big', left: '75%', top: '53%' }, + { id: 'Point02', size: 'big', left: '67%', top: '67%' }, + { id: 'Point05', size: 'big', left: '27%', top: '53%' }, + { id: 'Point01', size: 'big', left: '35%', top: '67%' }, + { id: 'Point08', size: 'medium', left: '34%', top: '34%' }, + { id: 'Point07', size: 'medium', left: '50%', top: '87%' }, + { id: 'Point06', size: 'medium', left: '90%', top: '50%' }, + { id: 'Point16', size: 'small', left: '27.5%', top: '22%' }, + { id: 'Point17', size: 'small', left: '43%', top: '14%' }, + { id: 'Point18', size: 'small', left: '59%', top: '14%' }, + { id: 'Point19', size: 'memory', left: '50%', top: '48%' }, + { id: 'Point20', size: 'memory', left: '50%', top: '28%' }, + { id: 'Point12', size: 'small', left: '86%', top: '40%' }, + { id: 'Point13', size: 'small', left: '86%', top: '63%' }, + { id: 'Point14', size: 'small', left: '35%', top: '82%' }, + { id: 'Point15', size: 'small', left: '65%', top: '82%' }, + { id: 'Point09', size: 'small', left: '9%', top: '50%' }, + { id: 'Point10', size: 'small', left: '13%', top: '40%' }, + { id: 'Point11', size: 'small', left: '13%', top: '63%' }, + { id: 'Point21', size: 'special', left: '70%', top: '34%'} + ], + Elation: [ + { id: 'Point04', size: 'big', left: '50%', top: '50.5%' }, + { id: 'Point03', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67.5%', top: '20%' }, + { id: 'Point05', size: 'big', left: '50%', top: '68.5%' }, + { id: 'Point01', size: 'big', left: '32.5%', top: '20%' }, + { id: 'Point08', size: 'medium', left: '85%', top: '30%' }, + { id: 'Point07', size: 'medium', left: '15%', top: '30%' }, + { id: 'Point06', size: 'medium', left: '50%', top: '87%' }, + { id: 'Point12', size: 'small', left: '65%', top: '88%' }, + { id: 'Point15', size: 'small', left: '23%', top: '53%' }, + { id: 'Point17', size: 'small', left: '88%', top: '63%' }, + { id: 'Point18', size: 'small', left: '76%', top: '53%' }, + { id: 'Point13', size: 'small', left: '9%', top: '48.5%' }, + { id: 'Point16', size: 'small', left: '90.5%', top: '48.5%' }, + { id: 'Point09', size: 'small', left: '36%', top: '64%' }, + { id: 'Point10', size: 'small', left: '64%', top: '64%' }, + { id: 'Point14', size: 'small', left: '11.5%', top: '63%' }, + { id: 'Point11', size: 'small', left: '35%', top: '88%' }, + { id: 'Point22', size: 'elation', left: '50%', top: '17%' } + ], +} + +export const traceLink : Record> = { + Knight: { + Point06: ["Point12", "Point11"], + + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point17: ["Point18"], + + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + }, + Mage: { + Point08: ["Point16", "Point17"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + }, + Priest: { + Point08: ["Point16", "Point17"], + Point07: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + Point06: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + }, + Rogue: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14"], + Point13: ["Point14"], + Point06: ["Point10", "Point11"], + Point10: ["Point11"], + }, + Shaman: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14"], + Point13: ["Point14"], + Point06: ["Point10", "Point11"], + Point10: ["Point11"], + Point09: ["Point15", "Point12"], + }, + Warlock: { + Point08: ["Point16", "Point17"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + Point09: ["Point18"], + }, + Warrior: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + }, + Memory: { + Point16: ["Point17", "Point18"], + Point08: ["Point16", "Point17", "Point18"], + Point17: ["Point18"], + Point09: ["Point10", "Point11"], + Point07: ["Point14", "Point15"], + Point06: ["Point12", "Point13"], + }, + Elation: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + }, +} + +export const traceShowCaseMap : Record> = { + Knight: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Mage: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Priest: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Rogue: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Shaman: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Warlock: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Warrior: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Memory: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "5": [ + { + id: "Point19", + size: "big-memory", + isLink: false + }, + { + id: "Point20", + size: "big-memory", + isLink: false + }, + { + id: "Point21", + size: "big", + isLink: false + } + ], + }, + Elation: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, } \ No newline at end of file diff --git a/src/helper/connect.ts b/src/helper/connect.ts index 683a4d8..269ca72 100644 --- a/src/helper/connect.ts +++ b/src/helper/connect.ts @@ -1,105 +1,105 @@ -"use client" -import { SendDataThroughProxy, SendDataToServer } from "@/lib/api/api" -import useConnectStore from "@/stores/connectStore" -import useUserDataStore from "@/stores/userDataStore" -import { converterToFreeSRJson } from "./converterToFreeSRJson" -import { psResponseSchema } from "@/zod" -import useGlobalStore from "@/stores/globalStore" -import { ActionResult, ExtraData, ProxyPayload, ProxyResponse, PSConnectType, PSResponse } from "@/types" - - -const getUrlQuery = (connectionType: PSConnectType | string, serverUrl: string): string => { - if (connectionType === PSConnectType.FireflyGo) return "http://localhost:21000/sync" - if (connectionType === PSConnectType.RobinSR) return "http://localhost:21000/srtools" - - if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) { - return `http://${serverUrl}` - } - return serverUrl -} - -const handleProxyRequest = async (payload: ProxyPayload): Promise => { - const response = await SendDataThroughProxy({ - data: { ...payload, method: "POST" } - }) as ProxyResponse | Error - - if (response instanceof Error) { - return { success: false, message: response.message } - } - if (response.error) { - return { success: false, message: response.error } - } - - const parsed = psResponseSchema.safeParse(response.data) - if (!parsed.success) { - return { success: false, message: "Invalid response schema" } - } - - return { success: true, message: "" } -} - -const handleDirectServerResponse = ( - response: PSResponse | string, - setIsConnectPS: (val: boolean) => void, - onSuccess: (extraData?: ExtraData) => void -): ActionResult => { - if (typeof response === "string") { - setIsConnectPS(false) - return { success: false, message: response } - } - if (response.status !== 200) { - setIsConnectPS(false) - return { success: false, message: response.message } - } - - setIsConnectPS(true) - onSuccess(response?.extra_data) - return { success: true, message: "" } -} - - -export const connectToPS = async (): Promise => { - const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState() - const { setExtraData, setIsConnectPS } = useGlobalStore.getState() - - if (connectionType === "Other" && privateType === "Server") { - return handleProxyRequest({ username, password, serverUrl, data: undefined }) - } - - const urlQuery = getUrlQuery(connectionType, serverUrl) - const response = await SendDataToServer(username, password, urlQuery, undefined) - - return handleDirectServerResponse(response, setIsConnectPS, (extraData) => { - setExtraData(extraData) - }) -} - -export const syncDataToPS = async (): Promise => { - const { connectionType, privateType, serverUrl, username, password } = useConnectStore.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 data = converterToFreeSRJson(avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config) - - if (connectionType === "Other" && privateType === "Server") { - return handleProxyRequest({ username, password, serverUrl, data }) - } - - const urlQuery = getUrlQuery(connectionType, serverUrl) - - const payloadExtra: PSResponse['extra_data'] = structuredClone(extraData) - if (payloadExtra) { - if (!isEnableChangePath) payloadExtra.multi_path = undefined - if (!isEnableLua) payloadExtra.lua = null - } - - const response = await SendDataToServer(username, password, urlQuery, data, payloadExtra) - - return handleDirectServerResponse(response, setIsConnectPS, (responseExtraData) => { - const newData = structuredClone(responseExtraData) - if (newData) { - newData.lua = extraData?.lua || null - } - setExtraData(newData) - }) +"use client" +import { SendDataThroughProxy, SendDataToServer } from "@/lib/api/api" +import useConnectStore from "@/stores/connectStore" +import useUserDataStore from "@/stores/userDataStore" +import { converterToFreeSRJson } from "./converterToFreeSRJson" +import { psResponseSchema } from "@/zod" +import useGlobalStore from "@/stores/globalStore" +import { ActionResult, ExtraData, ProxyPayload, ProxyResponse, PSConnectType, PSResponse } from "@/types" + + +const getUrlQuery = (connectionType: PSConnectType | string, serverUrl: string): string => { + if (connectionType === PSConnectType.FireflyGo) return "http://localhost:21000/sync" + if (connectionType === PSConnectType.RobinSR) return "http://localhost:21000/srtools" + + if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) { + return `http://${serverUrl}` + } + return serverUrl +} + +const handleProxyRequest = async (payload: ProxyPayload): Promise => { + const response = await SendDataThroughProxy({ + data: { ...payload, method: "POST" } + }) as ProxyResponse | Error + + if (response instanceof Error) { + return { success: false, message: response.message } + } + if (response.error) { + return { success: false, message: response.error } + } + + const parsed = psResponseSchema.safeParse(response.data) + if (!parsed.success) { + return { success: false, message: "Invalid response schema" } + } + + return { success: true, message: "" } +} + +const handleDirectServerResponse = ( + response: PSResponse | string, + setIsConnectPS: (val: boolean) => void, + onSuccess: (extraData?: ExtraData) => void +): ActionResult => { + if (typeof response === "string") { + setIsConnectPS(false) + return { success: false, message: response } + } + if (response.status !== 200) { + setIsConnectPS(false) + return { success: false, message: response.message } + } + + setIsConnectPS(true) + onSuccess(response?.extra_data) + return { success: true, message: "" } +} + + +export const connectToPS = async (): Promise => { + const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState() + const { setExtraData, setIsConnectPS } = useGlobalStore.getState() + + if (connectionType === "Other" && privateType === "Server") { + return handleProxyRequest({ username, password, serverUrl, data: undefined }) + } + + const urlQuery = getUrlQuery(connectionType, serverUrl) + const response = await SendDataToServer(username, password, urlQuery, undefined) + + return handleDirectServerResponse(response, setIsConnectPS, (extraData) => { + setExtraData(extraData) + }) +} + +export const syncDataToPS = async (): Promise => { + const { connectionType, privateType, serverUrl, username, password } = useConnectStore.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 data = converterToFreeSRJson(avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config) + + if (connectionType === "Other" && privateType === "Server") { + return handleProxyRequest({ username, password, serverUrl, data }) + } + + const urlQuery = getUrlQuery(connectionType, serverUrl) + + const payloadExtra: PSResponse['extra_data'] = structuredClone(extraData) + if (payloadExtra) { + if (!isEnableChangePath) payloadExtra.multi_path = undefined + if (!isEnableLua) payloadExtra.lua = null + } + + const response = await SendDataToServer(username, password, urlQuery, data, payloadExtra) + + return handleDirectServerResponse(response, setIsConnectPS, (responseExtraData) => { + const newData = structuredClone(responseExtraData) + if (newData) { + newData.lua = extraData?.lua || null + } + setExtraData(newData) + }) } \ No newline at end of file diff --git a/src/helper/convertData.ts b/src/helper/convertData.ts index c0cf832..3867cd3 100644 --- a/src/helper/convertData.ts +++ b/src/helper/convertData.ts @@ -1,15 +1,15 @@ -export function convertToRoman(num: number): string { - const roman: [number, string][] = [ - [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], - [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], - [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'] - ]; - let result = ''; - for (const [val, sym] of roman) { - while (num >= val) { - result += sym; - num -= val; - } - } - return result; +export function convertToRoman(num: number): string { + const roman: [number, string][] = [ + [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], + [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], + [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'] + ]; + let result = ''; + for (const [val, sym] of roman) { + while (num >= val) { + result += sym; + num -= val; + } + } + return result; } \ No newline at end of file diff --git a/src/helper/converterToFreeSRJson.ts b/src/helper/converterToFreeSRJson.ts index 958ced3..3583e39 100644 --- a/src/helper/converterToFreeSRJson.ts +++ b/src/helper/converterToFreeSRJson.ts @@ -1,89 +1,89 @@ - -import useConnectStore from "@/stores/connectStore"; -import useDetailDataStore from "@/stores/detailDataStore"; -import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, PSConnectType, RelicJson } from "@/types"; - - -export function converterToFreeSRJson( - avatars: Record, - battle_type: string, - moc_config: MOCConfigStore, - pf_config: PFConfigStore, - as_config: ASConfigStore, - ce_config: CEConfigStore, - peak_config: PEAKConfigStore, -): FreeSRJson { - const { skillConfig } = useDetailDataStore.getState() - const { connectionType } = useConnectStore.getState() - const lightcones: LightconeJson[] = [] - const relics: RelicJson[] = [] - let battleJson: BattleConfigJson - if (battle_type === "MOC") { - battleJson = { - battle_type: battle_type, - blessings: moc_config.blessings, - custom_stats: [], - cycle_count: moc_config.cycle_count, - stage_id: moc_config.stage_id, - path_resonance_id: 0, - monsters: moc_config.monsters, - } - } else if (battle_type === "PF") { - battleJson = { - battle_type: battle_type, - blessings: pf_config.blessings, - custom_stats: [], - cycle_count: pf_config.cycle_count, - stage_id: pf_config.stage_id, - path_resonance_id: 0, - monsters: pf_config.monsters, - } - } else if (battle_type === "AS") { - battleJson = { - battle_type: battle_type, - blessings: as_config.blessings, - custom_stats: [], - cycle_count: as_config.cycle_count, - stage_id: as_config.stage_id, - path_resonance_id: 0, - monsters: as_config.monsters, - } - } else if (battle_type === "CE") { - battleJson = { - battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", - blessings: ce_config.blessings, - custom_stats: [], - cycle_count: ce_config.cycle_count, - stage_id: ce_config.stage_id, - path_resonance_id: 0, - monsters: ce_config.monsters, - } - } else if (battle_type === "PEAK") { - battleJson = { - battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", - blessings: peak_config.blessings, - custom_stats: [], - cycle_count: peak_config.cycle_count, - stage_id: peak_config.stage_id, - path_resonance_id: 0, - monsters: peak_config.monsters, - } - } else { - battleJson = { - battle_type: battle_type, - blessings: [], - custom_stats: [], - cycle_count: 0, - stage_id: 0, - path_resonance_id: 0, - monsters: [], - } - } - - const avatarsJson: { [key: string]: AvatarJson } = {} - let internalUidLightcone = 0 - let internalUidRelic = 0 - + +import useConnectStore from "@/stores/connectStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, PSConnectType, RelicJson } from "@/types"; + + +export function converterToFreeSRJson( + avatars: Record, + battle_type: string, + moc_config: MOCConfigStore, + pf_config: PFConfigStore, + as_config: ASConfigStore, + ce_config: CEConfigStore, + peak_config: PEAKConfigStore, +): FreeSRJson { + const { skillConfig } = useDetailDataStore.getState() + const { connectionType } = useConnectStore.getState() + const lightcones: LightconeJson[] = [] + const relics: RelicJson[] = [] + let battleJson: BattleConfigJson + if (battle_type === "MOC") { + battleJson = { + battle_type: battle_type, + blessings: moc_config.blessings, + custom_stats: [], + cycle_count: moc_config.cycle_count, + stage_id: moc_config.stage_id, + path_resonance_id: 0, + monsters: moc_config.monsters, + } + } else if (battle_type === "PF") { + battleJson = { + battle_type: battle_type, + blessings: pf_config.blessings, + custom_stats: [], + cycle_count: pf_config.cycle_count, + stage_id: pf_config.stage_id, + path_resonance_id: 0, + monsters: pf_config.monsters, + } + } else if (battle_type === "AS") { + battleJson = { + battle_type: battle_type, + blessings: as_config.blessings, + custom_stats: [], + cycle_count: as_config.cycle_count, + stage_id: as_config.stage_id, + path_resonance_id: 0, + monsters: as_config.monsters, + } + } else if (battle_type === "CE") { + battleJson = { + battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", + blessings: ce_config.blessings, + custom_stats: [], + cycle_count: ce_config.cycle_count, + stage_id: ce_config.stage_id, + path_resonance_id: 0, + monsters: ce_config.monsters, + } + } else if (battle_type === "PEAK") { + battleJson = { + battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", + blessings: peak_config.blessings, + custom_stats: [], + cycle_count: peak_config.cycle_count, + stage_id: peak_config.stage_id, + path_resonance_id: 0, + monsters: peak_config.monsters, + } + } else { + battleJson = { + battle_type: battle_type, + blessings: [], + custom_stats: [], + cycle_count: 0, + stage_id: 0, + path_resonance_id: 0, + monsters: [], + } + } + + const avatarsJson: { [key: string]: AvatarJson } = {} + let internalUidLightcone = 0 + let internalUidRelic = 0 + Object.entries(avatars ?? {}).forEach(([avatarId, avatar]) => { if (!avatar) return @@ -91,10 +91,10 @@ export function converterToFreeSRJson( for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) { if (skillConfig?.[skillId]) { skillsByAnchorType[skillConfig[skillId].IndexSlot] = level > skillConfig[skillId].MaxLevel ? skillConfig[skillId].MaxLevel : level - } - } - avatarsJson[avatarId] = { - owner_uid: Number(avatar.owner_uid || 0), + } + } + avatarsJson[avatarId] = { + owner_uid: Number(avatar.owner_uid || 0), avatar_id: Number(avatar.avatar_id || 0), data: { rank: Number(avatar.data?.rank || 0), @@ -115,21 +115,21 @@ export function converterToFreeSRJson( const newLightcone: LightconeJson = { level: Number(currentProfile.lightcone.level || 0), item_id: Number(currentProfile.lightcone.item_id || 0), - rank: Number(currentProfile.lightcone.rank || 0), - promotion: Number(currentProfile.lightcone.promotion || 0), - internal_uid: internalUidLightcone, - equip_avatar: Number(avatar.avatar_id || 0), - } - internalUidLightcone++ - lightcones.push(newLightcone) - } - - if (currentProfile.relics) { - ["1", "2", "3", "4", "5", "6"].forEach(slot => { - const relic = currentProfile.relics[slot] - if (relic && relic.relic_id !== 0) { - const newRelic: RelicJson = { - level: Number(relic.level || 0), + rank: Number(currentProfile.lightcone.rank || 0), + promotion: Number(currentProfile.lightcone.promotion || 0), + internal_uid: internalUidLightcone, + equip_avatar: Number(avatar.avatar_id || 0), + } + internalUidLightcone++ + lightcones.push(newLightcone) + } + + if (currentProfile.relics) { + ["1", "2", "3", "4", "5", "6"].forEach(slot => { + const relic = currentProfile.relics[slot] + if (relic && relic.relic_id !== 0) { + const newRelic: RelicJson = { + level: Number(relic.level || 0), relic_id: Number(relic.relic_id || 0), relic_set_id: Number(relic.relic_set_id || 0), main_affix_id: Number(relic.main_affix_id || 0), @@ -137,18 +137,18 @@ export function converterToFreeSRJson( internal_uid: internalUidRelic, equip_avatar: Number(avatar.avatar_id || 0), } - internalUidRelic++ - relics.push(newRelic) - } - }) - } - - }) - - return { - lightcones, - relics, - avatars: avatarsJson, - battle_config: battleJson, - } + internalUidRelic++ + relics.push(newRelic) + } + }) + } + + }) + + return { + lightcones, + relics, + avatars: avatarsJson, + battle_config: battleJson, + } } diff --git a/src/helper/getName.ts b/src/helper/getName.ts index 273f461..307c94c 100644 --- a/src/helper/getName.ts +++ b/src/helper/getName.ts @@ -1,62 +1,62 @@ -import { listCurrentLanguageApi } from "@/constant/constant"; -import { AvatarDetail } from "@/types"; -import { useTranslations } from "next-intl" - -type TFunc = ReturnType -function cleanText(text: string): string { - if (!text) return "" - return text.replace(/(.*?)<\/unbreak>/g, "$1") -} - -export function getNameChar( - locale: string, - t: TFunc, - data: AvatarDetail | undefined -): string { - if (!data) return ""; - - if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) { - return ""; - } - - const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase(); - - let text = data.Name[langKey] ?? ""; - - if (!text) { - text = data.Name["en"] ?? ""; - } - - if (data.ID > 8000) { - text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`; - } - - return cleanText(text) -} - -export function getLocaleName(locale: string, data: Record | undefined | null): string { - if (!data) { - return "" - } - - if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) { - return "" - } - - const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase(); - - let text = data[langKey] ?? ""; - - if (!text) { - text = data["en"] ?? ""; - } - - return cleanText(text) -} - -export function parseRuby(text: string): string { - const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs; - return text.replace(rubyRegex, (_match, furigana, kanji) => { - return `${kanji}${furigana}`; - }); +import { listCurrentLanguageApi } from "@/constant/constant"; +import { AvatarDetail } from "@/types"; +import { useTranslations } from "next-intl" + +type TFunc = ReturnType +function cleanText(text: string): string { + if (!text) return "" + return text.replace(/(.*?)<\/unbreak>/g, "$1") +} + +export function getNameChar( + locale: string, + t: TFunc, + data: AvatarDetail | undefined +): string { + if (!data) return ""; + + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) { + return ""; + } + + const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase(); + + let text = data.Name[langKey] ?? ""; + + if (!text) { + text = data.Name["en"] ?? ""; + } + + if (data.ID > 8000) { + text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`; + } + + return cleanText(text) +} + +export function getLocaleName(locale: string, data: Record | undefined | null): string { + if (!data) { + return "" + } + + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguageApi, locale)) { + return "" + } + + const langKey = listCurrentLanguageApi[locale as keyof typeof listCurrentLanguageApi].toLowerCase(); + + let text = data[langKey] ?? ""; + + if (!text) { + text = data["en"] ?? ""; + } + + return cleanText(text) +} + +export function parseRuby(text: string): string { + const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs; + return text.replace(rubyRegex, (_match, furigana, kanji) => { + return `${kanji}${furigana}`; + }); } \ No newline at end of file diff --git a/src/helper/getSkillTree.ts b/src/helper/getSkillTree.ts index e886e5e..6a96f54 100644 --- a/src/helper/getSkillTree.ts +++ b/src/helper/getSkillTree.ts @@ -1,23 +1,23 @@ -import { AvatarDetail } from "@/types"; - -export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) { - if (!avatarSelected) return null; - if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) { - return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => { - const firstEntry = Object.values(dataPointEntry)[0]; - if (firstEntry) { - acc[firstEntry.PointID] = firstEntry.MaxLevel; - } - return acc; - }, {} as Record) - } - +import { AvatarDetail } from "@/types"; + +export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) { + if (!avatarSelected) return null; + if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) { + return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record) + } + return Object.values(avatarSelected?.SkillTrees ?? {}).reduce((acc, dataPointEntry) => { const firstEntry = Object.values(dataPointEntry)[0]; if (firstEntry) { acc[firstEntry.PointID] = firstEntry.MaxLevel; - } - return acc; - }, {} as Record); -} - + } + return acc; + }, {} as Record); +} + diff --git a/src/helper/index.ts b/src/helper/index.ts index ba8080d..480350c 100644 --- a/src/helper/index.ts +++ b/src/helper/index.ts @@ -1,8 +1,8 @@ -export * from "./getName" -export * from "./replaceByParam" -export * from "./converterToAvatarStore" -export * from "./calcData" -export * from "./random" -export * from "./json" -export * from "./convertData" +export * from "./getName" +export * from "./replaceByParam" +export * from "./converterToAvatarStore" +export * from "./calcData" +export * from "./random" +export * from "./json" +export * from "./convertData" export * from "./connect" \ No newline at end of file diff --git a/src/helper/random.ts b/src/helper/random.ts index b7a5156..6fdc995 100644 --- a/src/helper/random.ts +++ b/src/helper/random.ts @@ -8,27 +8,27 @@ export function randomPartition(sum: number, parts: number): number[] { const raw = Array.from({ length: parts }, () => Math.random()); const total = raw.reduce((a, b) => a + b, 0); - const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1); - let diff = sum - result.reduce((a, b) => a + b, 0); - while (diff !== 0) { - for (let i = 0; i < result.length && diff !== 0; i++) { - if (diff > 0) { - result[i]++; - diff--; - } else if (result[i] > 1) { - result[i]--; - diff++; - } - } - } - - return result; -} - -export function randomStep(x: number): number { - let total = 0; - for (let i = 0; i < x; i++) { - total += Math.floor(Math.random() * 3); - } - return total; + const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1); + let diff = sum - result.reduce((a, b) => a + b, 0); + while (diff !== 0) { + for (let i = 0; i < result.length && diff !== 0; i++) { + if (diff > 0) { + result[i]++; + diff--; + } else if (result[i] > 1) { + result[i]--; + diff++; + } + } + } + + return result; +} + +export function randomStep(x: number): number { + let total = 0; + for (let i = 0; i < x; i++) { + total += Math.floor(Math.random() * 3); + } + return total; } diff --git a/src/helper/replaceByParam.ts b/src/helper/replaceByParam.ts index 2f8dd38..c4b0ab1 100644 --- a/src/helper/replaceByParam.ts +++ b/src/helper/replaceByParam.ts @@ -1,39 +1,39 @@ -const formatValue = (value: number, format: string, floatDigits?: string, hasPercent?: boolean): string => { - if (format.startsWith('f')) { - const digits = parseInt(floatDigits || "1", 10); - const num = hasPercent ? value * 100 : value; - return `${num.toFixed(digits)}${hasPercent ? "%" : ""}`; - } - - if (format === 'i') { - const num = hasPercent ? value * 100 : value; - return `${Math.round(num)}${hasPercent ? "%" : ""}`; - } - - return String(value); -}; - +const formatValue = (value: number, format: string, floatDigits?: string, hasPercent?: boolean): string => { + if (format.startsWith('f')) { + const digits = parseInt(floatDigits || "1", 10); + const num = hasPercent ? value * 100 : value; + return `${num.toFixed(digits)}${hasPercent ? "%" : ""}`; + } + + if (format === 'i') { + const num = hasPercent ? value * 100 : value; + return `${Math.round(num)}${hasPercent ? "%" : ""}`; + } + + return String(value); +}; + export function replaceByParam(desc?: string, params: number[] = []): string { if (!desc) return ""; const PARAM_REGEX = /#(\d+)\[(f(\d+)|i)\](%)?/g; - - const processor = (_match: string, index: string, format: string, digits?: string, percent?: string): string => { - const i = parseInt(index, 10) - 1; - const val = params[i]; - return val !== undefined ? formatValue(val, format, digits, !!percent) : ""; - }; - - let result = desc.replace(/(.*?)<\/color>/g, (_, color, inner) => { - const processedInner = inner.replace(PARAM_REGEX, processor); - return `${processedInner}`; - }); - - result = result.replace(/(.*?)<\/unbreak>/g, (_, inner) => { - return inner.replace(PARAM_REGEX, processor); - }); - - result = result.replace(PARAM_REGEX, processor); - - return result.split("\\n").join("
    "); + + const processor = (_match: string, index: string, format: string, digits?: string, percent?: string): string => { + const i = parseInt(index, 10) - 1; + const val = params[i]; + return val !== undefined ? formatValue(val, format, digits, !!percent) : ""; + }; + + let result = desc.replace(/(.*?)<\/color>/g, (_, color, inner) => { + const processedInner = inner.replace(PARAM_REGEX, processor); + return `${processedInner}`; + }); + + result = result.replace(/(.*?)<\/unbreak>/g, (_, inner) => { + return inner.replace(PARAM_REGEX, processor); + }); + + result = result.replace(PARAM_REGEX, processor); + + return result.split("\\n").join("
    "); } diff --git a/src/hooks/index.ts b/src/hooks/index.ts index c13fec0..3e8dc56 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1 +1 @@ -export * from "./useChangeTheme"; +export * from "./useChangeTheme"; diff --git a/src/hooks/useChangeTheme.ts b/src/hooks/useChangeTheme.ts index 9007ded..ef0e42a 100644 --- a/src/hooks/useChangeTheme.ts +++ b/src/hooks/useChangeTheme.ts @@ -1,4 +1,4 @@ -import { useContext } from "react"; -import { ThemeContext } from "@/components/themeController"; - +import { useContext } from "react"; +import { ThemeContext } from "@/components/themeController"; + export const useChangeTheme = () => useContext(ThemeContext); \ No newline at end of file diff --git a/src/lib/api/api.ts b/src/lib/api/api.ts index 6830fa6..14d7559 100644 --- a/src/lib/api/api.ts +++ b/src/lib/api/api.ts @@ -1,142 +1,142 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types"; -import axios from 'axios'; -import { psResponseSchema } from "@/zod"; -import { ExtraData, Metadata } from "@/types"; - -export async function getMetadataApi(): Promise { - try { - const res = await axios.get(`/api/data/metadata`); - return res.data as Metadata; - } catch (error: unknown) { - console.error('Failed to fetch metadata:', error); - return { - BaseType: {}, - DamageType: {}, - MainAffix: {}, - SubAffix: {}, - SkillConfig: {}, - Stage: {}, - HardLevelConfig: {}, - EliteConfig: {} - }; - } -} - -export async function getAvatarListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/avatar`); - return res.data; - } catch (error) { - console.error('Failed to fetch Avatars:', error); - return {}; - } -} - -export async function getLightconeListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/lightcone`); - return res.data; - } catch (error) { - console.error('Failed to fetch lightcones:', error); - return {}; - } -} - -export async function getRelicSetListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/relic`); - return res.data; - } catch (error) { - console.error('Failed to fetch relics:', error); - return {}; - } -} - -export async function getMonsterListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/monster`); - return res.data; - } catch (error) { - console.error('Failed to fetch monster:', error); - return {}; - } -} - -export async function getASEventListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/as`); - return res.data; - } catch (error) { - console.error('Failed to fetch AS:', error); - return {}; - } -} - -export async function getPFEventListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/pf`); - return res.data; - } catch (error) { - console.error('Failed to fetch PF:', error); - return {}; - } -} - -export async function getMOCEventListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/moc`); - return res.data; - } catch (error) { - console.error('Failed to fetch MOC:', error); - return {}; - } -} - -export async function getPeakEventListApi(): Promise> { - try { - const res = await axios.get>(`/api/data/peak`); - return res.data; - } catch (error) { - console.error('Failed to fetch peak:', error); - return {}; - } -} - -export async function getChangelog(): Promise { - try { - const res = await axios.get(`/api/data/changelog`); - return res.data; - } catch (error) { - console.error('Failed to fetch monster:', error); - return []; - } -} - -export async function SendDataToServer( - username: string, - password: string, - serverUrl: string, - data?: FreeSRJson, - extraData?: ExtraData -): Promise { - try { - const response = await axios.post(`${serverUrl}`, { username, password, data, extra_data: extraData }) - const parsed = psResponseSchema.safeParse(response.data) - if (!parsed.success) { - return "Invalid response schema"; - } - return parsed.data; - } catch (error: any) { - return error?.message || "Unknown error"; - } -} - -export async function SendDataThroughProxy({ data }: { data: any }) { - try { - const response = await axios.post(`/api/proxy`, { ...data }) - return response.data; - } catch (error: any) { - return error; - } +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types"; +import axios from 'axios'; +import { psResponseSchema } from "@/zod"; +import { ExtraData, Metadata } from "@/types"; + +export async function getMetadataApi(): Promise { + try { + const res = await axios.get(`/api/data/metadata`); + return res.data as Metadata; + } catch (error: unknown) { + console.error('Failed to fetch metadata:', error); + return { + BaseType: {}, + DamageType: {}, + MainAffix: {}, + SubAffix: {}, + SkillConfig: {}, + Stage: {}, + HardLevelConfig: {}, + EliteConfig: {} + }; + } +} + +export async function getAvatarListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/avatar`); + return res.data; + } catch (error) { + console.error('Failed to fetch Avatars:', error); + return {}; + } +} + +export async function getLightconeListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/lightcone`); + return res.data; + } catch (error) { + console.error('Failed to fetch lightcones:', error); + return {}; + } +} + +export async function getRelicSetListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/relic`); + return res.data; + } catch (error) { + console.error('Failed to fetch relics:', error); + return {}; + } +} + +export async function getMonsterListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/monster`); + return res.data; + } catch (error) { + console.error('Failed to fetch monster:', error); + return {}; + } +} + +export async function getASEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/as`); + return res.data; + } catch (error) { + console.error('Failed to fetch AS:', error); + return {}; + } +} + +export async function getPFEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/pf`); + return res.data; + } catch (error) { + console.error('Failed to fetch PF:', error); + return {}; + } +} + +export async function getMOCEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/moc`); + return res.data; + } catch (error) { + console.error('Failed to fetch MOC:', error); + return {}; + } +} + +export async function getPeakEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/peak`); + return res.data; + } catch (error) { + console.error('Failed to fetch peak:', error); + return {}; + } +} + +export async function getChangelog(): Promise { + try { + const res = await axios.get(`/api/data/changelog`); + return res.data; + } catch (error) { + console.error('Failed to fetch monster:', error); + return []; + } +} + +export async function SendDataToServer( + username: string, + password: string, + serverUrl: string, + data?: FreeSRJson, + extraData?: ExtraData +): Promise { + try { + const response = await axios.post(`${serverUrl}`, { username, password, data, extra_data: extraData }) + const parsed = psResponseSchema.safeParse(response.data) + if (!parsed.success) { + return "Invalid response schema"; + } + return parsed.data; + } catch (error: any) { + return error?.message || "Unknown error"; + } +} + +export async function SendDataThroughProxy({ data }: { data: any }) { + try { + const response = await axios.post(`/api/proxy`, { ...data }) + return response.data; + } catch (error: any) { + return error; + } } \ No newline at end of file diff --git a/src/lib/cache/cache.ts b/src/lib/cache/cache.ts index de5b5a2..edb14c5 100644 --- a/src/lib/cache/cache.ts +++ b/src/lib/cache/cache.ts @@ -1,38 +1,38 @@ -import { readFileSync, readdirSync } from "fs" -import path from "path" - -type CacheItem = { - buf: Uint8Array - type: "json" | "br" -} - -const cache = new Map() - -const dir = path.join(process.cwd(), "data") - -for (const f of readdirSync(dir)) { - const file = path.join(dir, f) - - if (f.endsWith(".json.br")) { - const name = f.replace(".json.br", "") - const buf = new Uint8Array(readFileSync(file)) - cache.set(name, { - buf, - type: "br" - }) - } - - if (f.endsWith(".json")) { - const name = f.replace(".json", "") - const buf = new Uint8Array(readFileSync(file)) - - cache.set(name, { - buf, - type: "json" - }) - } -} - -export function getDataCache(name: string) { - return cache.get(name) +import { readFileSync, readdirSync } from "fs" +import path from "path" + +type CacheItem = { + buf: Uint8Array + type: "json" | "br" +} + +const cache = new Map() + +const dir = path.join(process.cwd(), "data") + +for (const f of readdirSync(dir)) { + const file = path.join(dir, f) + + if (f.endsWith(".json.br")) { + const name = f.replace(".json.br", "") + const buf = new Uint8Array(readFileSync(file)) + cache.set(name, { + buf, + type: "br" + }) + } + + if (f.endsWith(".json")) { + const name = f.replace(".json", "") + const buf = new Uint8Array(readFileSync(file)) + + cache.set(name, { + buf, + type: "json" + }) + } +} + +export function getDataCache(name: string) { + return cache.get(name) } \ No newline at end of file diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts index b376ad3..af97bdb 100644 --- a/src/lib/hooks/index.ts +++ b/src/lib/hooks/index.ts @@ -1,10 +1,10 @@ -export * from "./useFetchMetaData"; -export * from "./useFetchAvatarData"; -export * from "./useFetchLightconeData"; -export * from "./useFetchRelicData"; -export * from "./useFetchMonsterData"; -export * from "./useFetchPFData"; -export * from "./useFetchMOCData"; -export * from "./useFetchASData"; -export * from "./useFetchPEAKData"; +export * from "./useFetchMetaData"; +export * from "./useFetchAvatarData"; +export * from "./useFetchLightconeData"; +export * from "./useFetchRelicData"; +export * from "./useFetchMonsterData"; +export * from "./useFetchPFData"; +export * from "./useFetchMOCData"; +export * from "./useFetchASData"; +export * from "./useFetchPEAKData"; export * from "./useFetchChangelog"; \ No newline at end of file diff --git a/src/lib/hooks/useFetchASData.ts b/src/lib/hooks/useFetchASData.ts index 7ba42c7..3a8175e 100644 --- a/src/lib/hooks/useFetchASData.ts +++ b/src/lib/hooks/useFetchASData.ts @@ -1,26 +1,26 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getASEventListApi } from '@/lib/api' -import { useEffect } from 'react' -import { toast } from 'react-toastify' -import useDetailDataStore from '@/stores/detailDataStore' - -export const useFetchASGroupData = () => { - const { setMapAS } = useDetailDataStore() - - const query = useQuery({ - queryKey: ['ASGroupData'], - queryFn: getASEventListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data) { - setMapAS(query.data) - } else if (query.error) { - toast.error("Failed to load ASGroup data") - } - }, [query.data, query.error, setMapAS]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getASEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore' + +export const useFetchASGroupData = () => { + const { setMapAS } = useDetailDataStore() + + const query = useQuery({ + queryKey: ['ASGroupData'], + queryFn: getASEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data) { + setMapAS(query.data) + } else if (query.error) { + toast.error("Failed to load ASGroup data") + } + }, [query.data, query.error, setMapAS]) + + return query +} diff --git a/src/lib/hooks/useFetchAvatarData.ts b/src/lib/hooks/useFetchAvatarData.ts index 983dc9f..6814009 100644 --- a/src/lib/hooks/useFetchAvatarData.ts +++ b/src/lib/hooks/useFetchAvatarData.ts @@ -1,43 +1,43 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getAvatarListApi } from '@/lib/api' -import { useEffect } from 'react' -import { toast } from 'react-toastify' -import useDetailDataStore from '@/stores/detailDataStore' -import useCurrentDataStore from '@/stores/currentDataStore' -import useUserDataStore from '@/stores/userDataStore' -import { converterToAvatarStore } from '@/helper' - -export const useFetchAvatarData = () => { - const { setMapAvatar, mapAvatar } = useDetailDataStore() - const { setAvatar, avatars } = useUserDataStore() - const { avatarSelected, setAvatarSelected } = useCurrentDataStore() - - const query = useQuery({ - queryKey: ['AvatarData'], - queryFn: getAvatarListApi, - staleTime: 1000 * 60 * 5, - }) - useEffect(() => { - const listAvatarId = Object.keys(avatars) - const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId)) - const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist)) - if (Object.keys(avatarStore).length === 0) return - for (const avatar of Object.values(avatarStore)) { - setAvatar(avatar) - } - }, [mapAvatar]) - - useEffect(() => { - if (query.data) { - setMapAvatar(query.data) - if (!avatarSelected) { - setAvatarSelected(Object.values(query.data)[0]) - } - } - if (query.error) toast.error("Failed to load Avatar data") - }, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected]) - - return query +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getAvatarListApi } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore' +import useCurrentDataStore from '@/stores/currentDataStore' +import useUserDataStore from '@/stores/userDataStore' +import { converterToAvatarStore } from '@/helper' + +export const useFetchAvatarData = () => { + const { setMapAvatar, mapAvatar } = useDetailDataStore() + const { setAvatar, avatars } = useUserDataStore() + const { avatarSelected, setAvatarSelected } = useCurrentDataStore() + + const query = useQuery({ + queryKey: ['AvatarData'], + queryFn: getAvatarListApi, + staleTime: 1000 * 60 * 5, + }) + useEffect(() => { + const listAvatarId = Object.keys(avatars) + const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId)) + const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist)) + if (Object.keys(avatarStore).length === 0) return + for (const avatar of Object.values(avatarStore)) { + setAvatar(avatar) + } + }, [mapAvatar]) + + useEffect(() => { + if (query.data) { + setMapAvatar(query.data) + if (!avatarSelected) { + setAvatarSelected(Object.values(query.data)[0]) + } + } + if (query.error) toast.error("Failed to load Avatar data") + }, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected]) + + return query } \ No newline at end of file diff --git a/src/lib/hooks/useFetchChangelog.ts b/src/lib/hooks/useFetchChangelog.ts index ea30889..21a9fa2 100644 --- a/src/lib/hooks/useFetchChangelog.ts +++ b/src/lib/hooks/useFetchChangelog.ts @@ -1,32 +1,32 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getChangelog } from '@/lib/api' -import { useEffect } from 'react' -import { toast } from 'react-toastify' -import useModelStore from '@/stores/modelStore' -import useLocaleStore from '@/stores/localeStore' - -export const useFetchChangelog = () => { - const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore() - const { setIsChangelog } = useModelStore() - const query = useQuery({ - queryKey: ['changelog'], - queryFn: getChangelog, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setChangelog(query.data) - if (query.data?.[0] && query.data[0].version != currentVersion) { - setIsChangelog(true) - setCurrentVersion(query.data[0].version) - } - } else if (query.error) { - toast.error("Failed to load changelog data") - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getChangelog } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useModelStore from '@/stores/modelStore' +import useLocaleStore from '@/stores/localeStore' + +export const useFetchChangelog = () => { + const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore() + const { setIsChangelog } = useModelStore() + const query = useQuery({ + queryKey: ['changelog'], + queryFn: getChangelog, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setChangelog(query.data) + if (query.data?.[0] && query.data[0].version != currentVersion) { + setIsChangelog(true) + setCurrentVersion(query.data[0].version) + } + } else if (query.error) { + toast.error("Failed to load changelog data") + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog]) + + return query +} diff --git a/src/lib/hooks/useFetchLightconeData.ts b/src/lib/hooks/useFetchLightconeData.ts index 8481ef3..e83f8d3 100644 --- a/src/lib/hooks/useFetchLightconeData.ts +++ b/src/lib/hooks/useFetchLightconeData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getLightconeListApi } from '@/lib/api'; -import { useEffect } from 'react' -import { toast } from 'react-toastify' -import useDetailDataStore from '@/stores/detailDataStore'; - -export const useFetchLightconeData = () => { - const { setMapLightCone } = useDetailDataStore() - const query = useQuery({ - queryKey: ['lightconeData'], - queryFn: getLightconeListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapLightCone(query.data) - } else if (query.error) { - toast.error("Failed to load lightcone data") - } - }, [query.data, query.error, setMapLightCone]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getLightconeListApi } from '@/lib/api'; +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore'; + +export const useFetchLightconeData = () => { + const { setMapLightCone } = useDetailDataStore() + const query = useQuery({ + queryKey: ['lightconeData'], + queryFn: getLightconeListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapLightCone(query.data) + } else if (query.error) { + toast.error("Failed to load lightcone data") + } + }, [query.data, query.error, setMapLightCone]) + + return query +} diff --git a/src/lib/hooks/useFetchMOCData.ts b/src/lib/hooks/useFetchMOCData.ts index 6f91533..869afc6 100644 --- a/src/lib/hooks/useFetchMOCData.ts +++ b/src/lib/hooks/useFetchMOCData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getMOCEventListApi } from '@/lib/api' -import { useEffect } from 'react' -import useDetailDataStore from '@/stores/detailDataStore' -import { toast } from 'react-toastify' - -export const useFetchMOCGroupData = () => { - const { setMapMoc } = useDetailDataStore() - const query = useQuery({ - queryKey: ['MOCGroupData'], - queryFn: getMOCEventListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapMoc(query.data) - } else if (query.error) { - toast.error("Failed to load MOCGroup data") - } - }, [query.data, query.error, setMapMoc]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getMOCEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchMOCGroupData = () => { + const { setMapMoc } = useDetailDataStore() + const query = useQuery({ + queryKey: ['MOCGroupData'], + queryFn: getMOCEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapMoc(query.data) + } else if (query.error) { + toast.error("Failed to load MOCGroup data") + } + }, [query.data, query.error, setMapMoc]) + + return query +} diff --git a/src/lib/hooks/useFetchMetaData.ts b/src/lib/hooks/useFetchMetaData.ts index 9d0ec50..2261987 100644 --- a/src/lib/hooks/useFetchMetaData.ts +++ b/src/lib/hooks/useFetchMetaData.ts @@ -1,33 +1,33 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { useEffect } from 'react' -import { toast } from 'react-toastify' -import { getMetadataApi } from '@/lib/api' -import useCurrentDataStore from '@/stores/currentDataStore' -import useDetailDataStore from '@/stores/detailDataStore' - -export const useFetchConfigData = () => { - const { setMetaData } = useDetailDataStore() - const { setSettingData } = useCurrentDataStore() - - const query = useQuery({ - queryKey: ['initialConfigData'], - queryFn: async () => { - const metaData = await getMetadataApi() - return { metaData } - }, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setSettingData(query.data.metaData) - setMetaData(query.data.metaData) - } - else if (query.error) { - toast.error("Failed to load initial config data") - } - }, [query.data, query.error, setMetaData, setSettingData]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import { getMetadataApi } from '@/lib/api' +import useCurrentDataStore from '@/stores/currentDataStore' +import useDetailDataStore from '@/stores/detailDataStore' + +export const useFetchConfigData = () => { + const { setMetaData } = useDetailDataStore() + const { setSettingData } = useCurrentDataStore() + + const query = useQuery({ + queryKey: ['initialConfigData'], + queryFn: async () => { + const metaData = await getMetadataApi() + return { metaData } + }, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setSettingData(query.data.metaData) + setMetaData(query.data.metaData) + } + else if (query.error) { + toast.error("Failed to load initial config data") + } + }, [query.data, query.error, setMetaData, setSettingData]) + + return query +} diff --git a/src/lib/hooks/useFetchMonsterData.ts b/src/lib/hooks/useFetchMonsterData.ts index 5794928..e9b12f4 100644 --- a/src/lib/hooks/useFetchMonsterData.ts +++ b/src/lib/hooks/useFetchMonsterData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getMonsterListApi } from '@/lib/api' -import { useEffect } from 'react' -import useDetailDataStore from '@/stores/detailDataStore' -import { toast } from 'react-toastify' - -export const useFetchMonsterData = () => { - const { setMapMonster } = useDetailDataStore() - const query = useQuery({ - queryKey: ['MonsterData'], - queryFn: getMonsterListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapMonster(query.data) - } else if (query.error) { - toast.error("Failed to load Monster data") - } - }, [query.data, query.error, setMapMonster]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getMonsterListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchMonsterData = () => { + const { setMapMonster } = useDetailDataStore() + const query = useQuery({ + queryKey: ['MonsterData'], + queryFn: getMonsterListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapMonster(query.data) + } else if (query.error) { + toast.error("Failed to load Monster data") + } + }, [query.data, query.error, setMapMonster]) + + return query +} diff --git a/src/lib/hooks/useFetchPEAKData.ts b/src/lib/hooks/useFetchPEAKData.ts index d5da8d5..a1c59b3 100644 --- a/src/lib/hooks/useFetchPEAKData.ts +++ b/src/lib/hooks/useFetchPEAKData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getPeakEventListApi } from '@/lib/api' -import { useEffect } from 'react' -import useDetailDataStore from '@/stores/detailDataStore' -import { toast } from 'react-toastify' - -export const useFetchPeakGroupData = () => { - const { setMapPeak } = useDetailDataStore() - const query = useQuery({ - queryKey: ['PeakGroupData'], - queryFn: getPeakEventListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapPeak(query.data) - } else if (query.error) { - toast.error("Failed to load PeakGroup data") - } - }, [query.data, query.error, setMapPeak]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getPeakEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchPeakGroupData = () => { + const { setMapPeak } = useDetailDataStore() + const query = useQuery({ + queryKey: ['PeakGroupData'], + queryFn: getPeakEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapPeak(query.data) + } else if (query.error) { + toast.error("Failed to load PeakGroup data") + } + }, [query.data, query.error, setMapPeak]) + + return query +} diff --git a/src/lib/hooks/useFetchPFData.ts b/src/lib/hooks/useFetchPFData.ts index aed6e3f..2bb3a75 100644 --- a/src/lib/hooks/useFetchPFData.ts +++ b/src/lib/hooks/useFetchPFData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getPFEventListApi } from '@/lib/api' -import { useEffect } from 'react' -import useDetailDataStore from '@/stores/detailDataStore' -import { toast } from 'react-toastify' - -export const useFetchPFGroupData = () => { - const { setMapPF } = useDetailDataStore() - const query = useQuery({ - queryKey: ['PFGroupData'], - queryFn: getPFEventListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapPF(query.data) - } else if (query.error) { - toast.error("Failed to load PFGroup data") - } - }, [query.data, query.error, setMapPF]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getPFEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchPFGroupData = () => { + const { setMapPF } = useDetailDataStore() + const query = useQuery({ + queryKey: ['PFGroupData'], + queryFn: getPFEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapPF(query.data) + } else if (query.error) { + toast.error("Failed to load PFGroup data") + } + }, [query.data, query.error, setMapPF]) + + return query +} diff --git a/src/lib/hooks/useFetchRelicData.ts b/src/lib/hooks/useFetchRelicData.ts index 6fdf35d..1c7aa29 100644 --- a/src/lib/hooks/useFetchRelicData.ts +++ b/src/lib/hooks/useFetchRelicData.ts @@ -1,25 +1,25 @@ -"use client" -import { useQuery } from '@tanstack/react-query' -import { getRelicSetListApi } from '@/lib/api' -import { useEffect } from 'react' -import useDetailDataStore from '@/stores/detailDataStore' -import { toast } from 'react-toastify' - -export const useFetchRelicSetData = () => { - const { setMapRelicSet } = useDetailDataStore() - const query = useQuery({ - queryKey: ['RelicSetData'], - queryFn: getRelicSetListApi, - staleTime: 1000 * 60 * 5, - }) - - useEffect(() => { - if (query.data && !query.error) { - setMapRelicSet(query.data) - } else if (query.error) { - toast.error("Failed to load RelicSet data") - } - }, [query.data, query.error, setMapRelicSet]) - - return query -} +"use client" +import { useQuery } from '@tanstack/react-query' +import { getRelicSetListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchRelicSetData = () => { + const { setMapRelicSet } = useDetailDataStore() + const query = useQuery({ + queryKey: ['RelicSetData'], + queryFn: getRelicSetListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapRelicSet(query.data) + } else if (query.error) { + toast.error("Failed to load RelicSet data") + } + }, [query.data, query.error, setMapRelicSet]) + + return query +} diff --git a/src/stores/connectStore.ts b/src/stores/connectStore.ts index a7d9c77..5f9e653 100644 --- a/src/stores/connectStore.ts +++ b/src/stores/connectStore.ts @@ -2,19 +2,19 @@ import { create } from 'zustand' import { persist } from 'zustand/middleware'; import { connectPersistedSchema } from '@/zod'; import { createValidatedJSONStorage } from './persistStorage'; - - + + interface ConnectState { - connectionType: string; - privateType: string; - serverUrl: string; - username: string; - password: string; - setConnectionType: (newConnectionType: string) => void; - setPrivateType: (newPrivateType: string) => void; - setServerUrl: (newServerUrl: string) => void; - setUsername: (newUsername: string) => void; - setPassword: (newPassword: string) => void; + connectionType: string; + privateType: string; + serverUrl: string; + username: string; + password: string; + setConnectionType: (newConnectionType: string) => void; + setPrivateType: (newPrivateType: string) => void; + setServerUrl: (newServerUrl: string) => void; + setUsername: (newUsername: string) => void; + setPassword: (newPassword: string) => void; } type ConnectPersistedState = Pick; @@ -22,17 +22,17 @@ type ConnectPersistedState = Pick()( persist( (set) => ({ - connectionType: "FireflyGo", - privateType: "Local", - serverUrl: "http://localhost:21000", - username: "", - password: "", - setConnectionType: (newConnectionType: string) => set({ connectionType: newConnectionType }), - setPrivateType: (newPrivateType: string) => set({ privateType: newPrivateType }), - setServerUrl: (newServerUrl: string) => set({ serverUrl: newServerUrl }), - setUsername: (newUsername: string) => set({ username: newUsername }), - setPassword: (newPassword: string) => set({ password: newPassword }), - }), + connectionType: "FireflyGo", + privateType: "Local", + serverUrl: "http://localhost:21000", + username: "", + password: "", + setConnectionType: (newConnectionType: string) => set({ connectionType: newConnectionType }), + setPrivateType: (newPrivateType: string) => set({ privateType: newPrivateType }), + setServerUrl: (newServerUrl: string) => set({ serverUrl: newServerUrl }), + setUsername: (newUsername: string) => set({ username: newUsername }), + setPassword: (newPassword: string) => set({ password: newPassword }), + }), { name: 'connect-storage', storage: createValidatedJSONStorage(() => localStorage, connectPersistedSchema), @@ -46,5 +46,5 @@ const useConnectStore = create()( } ) ); - + export default useConnectStore; diff --git a/src/stores/copyProfile.ts b/src/stores/copyProfile.ts index 612a95a..834e556 100644 --- a/src/stores/copyProfile.ts +++ b/src/stores/copyProfile.ts @@ -1,41 +1,41 @@ -import { AvatarDetail, AvatarProfileCardType, BaseTypeData, DamageTypeData } from '@/types'; -import { create } from 'zustand' - -interface CopyProfileState { - selectedProfiles: AvatarProfileCardType[]; - avatarCopySelected: AvatarDetail | null; - listElement: Record; - listPath: Record; - listRank: Record; - setListElement: (newListElement: Record) => void; - setListPath: (newListPath: Record) => void; - setListRank: (newListRank: Record) => void; - setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void; - setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => void; - setResetData: (baseType: Record, damageType: Record) => void; -} - -const useCopyProfileStore = create((set) => ({ - selectedProfiles: [], - avatarCopySelected: null, - listElement: {}, - listPath: {}, - listRank: { "4": false, "5": false }, - listCopyAvatar: [], - listRawCopyAvatar: [], - - setListElement: (newListElement: Record) => set({ listElement: newListElement }), - setListPath: (newListPath: Record) => set({ listPath: newListPath }), - setListRank: (newListRank: Record) => set({ listRank: newListRank }), - setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => set({ avatarCopySelected: newAvatarSelected }), - setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => set({ selectedProfiles: newListAvatar }), - setResetData: (baseType: Record, damageType: Record) => { - set({ - listElement: Object.fromEntries(Object.keys(damageType).map(key => [key, false])) as Record, - listPath: Object.fromEntries(Object.keys(baseType).map(key => [key, false])) as Record, - listRank: { "4": false, "5": false } - }) - } -})); - +import { AvatarDetail, AvatarProfileCardType, BaseTypeData, DamageTypeData } from '@/types'; +import { create } from 'zustand' + +interface CopyProfileState { + selectedProfiles: AvatarProfileCardType[]; + avatarCopySelected: AvatarDetail | null; + listElement: Record; + listPath: Record; + listRank: Record; + setListElement: (newListElement: Record) => void; + setListPath: (newListPath: Record) => void; + setListRank: (newListRank: Record) => void; + setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void; + setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => void; + setResetData: (baseType: Record, damageType: Record) => void; +} + +const useCopyProfileStore = create((set) => ({ + selectedProfiles: [], + avatarCopySelected: null, + listElement: {}, + listPath: {}, + listRank: { "4": false, "5": false }, + listCopyAvatar: [], + listRawCopyAvatar: [], + + setListElement: (newListElement: Record) => set({ listElement: newListElement }), + setListPath: (newListPath: Record) => set({ listPath: newListPath }), + setListRank: (newListRank: Record) => set({ listRank: newListRank }), + setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => set({ avatarCopySelected: newAvatarSelected }), + setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => set({ selectedProfiles: newListAvatar }), + setResetData: (baseType: Record, damageType: Record) => { + set({ + listElement: Object.fromEntries(Object.keys(damageType).map(key => [key, false])) as Record, + listPath: Object.fromEntries(Object.keys(baseType).map(key => [key, false])) as Record, + listRank: { "4": false, "5": false } + }) + } +})); + export default useCopyProfileStore; \ No newline at end of file diff --git a/src/stores/currentDataStore.ts b/src/stores/currentDataStore.ts index c0be7cd..9ecdfc0 100644 --- a/src/stores/currentDataStore.ts +++ b/src/stores/currentDataStore.ts @@ -1,58 +1,58 @@ -import { AvatarDetail, BaseTypeData, Metadata } from '@/types' -import { create } from 'zustand' - -interface CurrentDataState { - avatarSelected: AvatarDetail | null; - skillIDSelected: string | null; - avatarSearch: string; - lightconeSearch: string; - mapAvatarElementActive: Record; - mapAvatarPathActive: Record; - mapLightconePathActive: Record; - mapLightconeRankActive: Record; - setSettingData: (newMetaData: Metadata) => void; - setAvatarSelected: (avatar: AvatarDetail | null) => void; - setSkillIDSelected: (skillID: string | null) => void; - setAvatarSearch: (avatarSearch: string) => void; - setLightconeSearch: (lightconeSearch: string) => void; - setMapAvatarElementActive: (mapAvatarElementActive: Record) => void; - setMapAvatarPathActive: (mapAvatarPathActive: Record) => void; - setMapLightconePathActive: (mapLightconePathActive: Record) => void; - setMapLightconeRankActive: (mapLightconeRankActive: Record) => void; - setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => void; -} - -const useCurrentDataStore = create((set) => ({ - avatarSelected: null, - skillIDSelected: null, - mapAvatarElementActive: {}, - mapAvatarPathActive: {}, - mapLightconePathActive: {}, - mapLightconeRankActive: {"3": false, "4": false, "5": false }, - avatarSearch: "", - lightconeSearch: "", - setSettingData: (newMetaData: Metadata) => set({ - mapAvatarElementActive: Object.fromEntries(Object.keys(newMetaData.DamageType).map(key => [key, false])) as Record, - mapAvatarPathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, - mapLightconePathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, - }), - setAvatarSearch: (avatarSearch: string) => set({ avatarSearch }), - setLightconeSearch: (lightconeSearch: string) => set({ lightconeSearch }), - setAvatarSelected: (avatar: AvatarDetail | null) => set({ avatarSelected: avatar }), - setSkillIDSelected: (skillID: string | null) => set({ skillIDSelected: skillID }), - setMapAvatarElementActive: (mapAvatarElementActive: Record) => set({ mapAvatarElementActive }), - setMapAvatarPathActive: (mapAvatarPathActive: Record) => set({ mapAvatarPathActive }), - setMapLightconePathActive: (mapLightconePathActive: Record) => set({ mapLightconePathActive }), - setMapLightconeRankActive: (mapLightconeRankActive: Record) => set({ mapLightconeRankActive }), - setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => { - const newListPath: Record = Object.fromEntries(Object.keys(baseType).map(key => [key, false])) - const newListRank: Record = {"3": false, "4": false, "5": false } - if (avtarSeleted) { - newListPath[avtarSeleted.BaseType] = true - } - set({ mapLightconeRankActive: newListRank }) - set({ mapLightconePathActive: newListPath }) - } -})) - +import { AvatarDetail, BaseTypeData, Metadata } from '@/types' +import { create } from 'zustand' + +interface CurrentDataState { + avatarSelected: AvatarDetail | null; + skillIDSelected: string | null; + avatarSearch: string; + lightconeSearch: string; + mapAvatarElementActive: Record; + mapAvatarPathActive: Record; + mapLightconePathActive: Record; + mapLightconeRankActive: Record; + setSettingData: (newMetaData: Metadata) => void; + setAvatarSelected: (avatar: AvatarDetail | null) => void; + setSkillIDSelected: (skillID: string | null) => void; + setAvatarSearch: (avatarSearch: string) => void; + setLightconeSearch: (lightconeSearch: string) => void; + setMapAvatarElementActive: (mapAvatarElementActive: Record) => void; + setMapAvatarPathActive: (mapAvatarPathActive: Record) => void; + setMapLightconePathActive: (mapLightconePathActive: Record) => void; + setMapLightconeRankActive: (mapLightconeRankActive: Record) => void; + setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => void; +} + +const useCurrentDataStore = create((set) => ({ + avatarSelected: null, + skillIDSelected: null, + mapAvatarElementActive: {}, + mapAvatarPathActive: {}, + mapLightconePathActive: {}, + mapLightconeRankActive: {"3": false, "4": false, "5": false }, + avatarSearch: "", + lightconeSearch: "", + setSettingData: (newMetaData: Metadata) => set({ + mapAvatarElementActive: Object.fromEntries(Object.keys(newMetaData.DamageType).map(key => [key, false])) as Record, + mapAvatarPathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, + mapLightconePathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, + }), + setAvatarSearch: (avatarSearch: string) => set({ avatarSearch }), + setLightconeSearch: (lightconeSearch: string) => set({ lightconeSearch }), + setAvatarSelected: (avatar: AvatarDetail | null) => set({ avatarSelected: avatar }), + setSkillIDSelected: (skillID: string | null) => set({ skillIDSelected: skillID }), + setMapAvatarElementActive: (mapAvatarElementActive: Record) => set({ mapAvatarElementActive }), + setMapAvatarPathActive: (mapAvatarPathActive: Record) => set({ mapAvatarPathActive }), + setMapLightconePathActive: (mapLightconePathActive: Record) => set({ mapLightconePathActive }), + setMapLightconeRankActive: (mapLightconeRankActive: Record) => set({ mapLightconeRankActive }), + setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => { + const newListPath: Record = Object.fromEntries(Object.keys(baseType).map(key => [key, false])) + const newListRank: Record = {"3": false, "4": false, "5": false } + if (avtarSeleted) { + newListPath[avtarSeleted.BaseType] = true + } + set({ mapLightconeRankActive: newListRank }) + set({ mapLightconePathActive: newListPath }) + } +})) + export default useCurrentDataStore; \ No newline at end of file diff --git a/src/stores/detailDataStore.ts b/src/stores/detailDataStore.ts index fe5cd98..d54d68f 100644 --- a/src/stores/detailDataStore.ts +++ b/src/stores/detailDataStore.ts @@ -1,69 +1,69 @@ -import { ASGroupDetail, AvatarDetail, BaseTypeData, DamageTypeData, EliteData, HardLevelData, LightConeDetail, MainAffixData, Metadata, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, RelicSetDetail, SkillMaxLevelData, StageData, SubAffixData } from '@/types' -import { create } from 'zustand' - -interface DetailDataState { - mapMonster: Record - mapRelicSet: Record - mapAvatar: Record - mapLightCone: Record - mapAS: Record - mapMoc: Record - mapPF: Record - mapPeak: Record - baseType: Record; - damageType: Record; - mainAffix: Record>; - subAffix: Record>; - skillConfig: Record; - stage: Record; - hardLevelConfig: Record>; - eliteConfig: Record; - setMetaData: (newMetaData: Metadata) => void; - setMapMonster: (newMonster: Record) => void - setMapRelicSet: (newRelicSet: Record) => void - setMapAvatar: (newAvatar: Record) => void - setMapLightCone: (newLightCone: Record) => void - setMapAS: (newAS: Record) => void - setMapMoc: (newMoc: Record) => void - setMapPF: (newPF: Record) => void - setMapPeak: (newPeak: Record) => void -} - -const useDetailDataStore = create((set) => ({ - mapMonster: {}, - mapRelicSet: {}, - mapAvatar: {}, - mapLightCone: {}, - mapAS: {}, - mapMoc: {}, - mapPF: {}, - mapPeak: {}, - baseType: {}, - damageType: {}, - mainAffix: {}, - subAffix: {}, - skillConfig: {}, - stage: {}, - hardLevelConfig: {}, - eliteConfig: {}, - setMapMonster: (newMonster) => set({ mapMonster: newMonster }), - setMapRelicSet: (newRelicSet) => set({ mapRelicSet: newRelicSet }), - setMapAvatar: (newAvatar) => set({ mapAvatar: newAvatar }), - setMapLightCone: (newLightCone) => set({ mapLightCone: newLightCone }), - setMapAS: (newAS) => set({ mapAS: newAS }), - setMapMoc: (newMoc) => set({ mapMoc: newMoc }), - setMapPF: (newPF) => set({ mapPF: newPF }), - setMapPeak: (newPeak) => set({ mapPeak: newPeak }), - setMetaData: (newMetaData: Metadata) => set({ - baseType: newMetaData.BaseType, - damageType: newMetaData.DamageType, - mainAffix: newMetaData.MainAffix, - subAffix: newMetaData.SubAffix, - skillConfig: newMetaData.SkillConfig, - stage: newMetaData.Stage, - hardLevelConfig: newMetaData.HardLevelConfig, - eliteConfig: newMetaData.EliteConfig, - }), -})) - -export default useDetailDataStore +import { ASGroupDetail, AvatarDetail, BaseTypeData, DamageTypeData, EliteData, HardLevelData, LightConeDetail, MainAffixData, Metadata, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, RelicSetDetail, SkillMaxLevelData, StageData, SubAffixData } from '@/types' +import { create } from 'zustand' + +interface DetailDataState { + mapMonster: Record + mapRelicSet: Record + mapAvatar: Record + mapLightCone: Record + mapAS: Record + mapMoc: Record + mapPF: Record + mapPeak: Record + baseType: Record; + damageType: Record; + mainAffix: Record>; + subAffix: Record>; + skillConfig: Record; + stage: Record; + hardLevelConfig: Record>; + eliteConfig: Record; + setMetaData: (newMetaData: Metadata) => void; + setMapMonster: (newMonster: Record) => void + setMapRelicSet: (newRelicSet: Record) => void + setMapAvatar: (newAvatar: Record) => void + setMapLightCone: (newLightCone: Record) => void + setMapAS: (newAS: Record) => void + setMapMoc: (newMoc: Record) => void + setMapPF: (newPF: Record) => void + setMapPeak: (newPeak: Record) => void +} + +const useDetailDataStore = create((set) => ({ + mapMonster: {}, + mapRelicSet: {}, + mapAvatar: {}, + mapLightCone: {}, + mapAS: {}, + mapMoc: {}, + mapPF: {}, + mapPeak: {}, + baseType: {}, + damageType: {}, + mainAffix: {}, + subAffix: {}, + skillConfig: {}, + stage: {}, + hardLevelConfig: {}, + eliteConfig: {}, + setMapMonster: (newMonster) => set({ mapMonster: newMonster }), + setMapRelicSet: (newRelicSet) => set({ mapRelicSet: newRelicSet }), + setMapAvatar: (newAvatar) => set({ mapAvatar: newAvatar }), + setMapLightCone: (newLightCone) => set({ mapLightCone: newLightCone }), + setMapAS: (newAS) => set({ mapAS: newAS }), + setMapMoc: (newMoc) => set({ mapMoc: newMoc }), + setMapPF: (newPF) => set({ mapPF: newPF }), + setMapPeak: (newPeak) => set({ mapPeak: newPeak }), + setMetaData: (newMetaData: Metadata) => set({ + baseType: newMetaData.BaseType, + damageType: newMetaData.DamageType, + mainAffix: newMetaData.MainAffix, + subAffix: newMetaData.SubAffix, + skillConfig: newMetaData.SkillConfig, + stage: newMetaData.Stage, + hardLevelConfig: newMetaData.HardLevelConfig, + eliteConfig: newMetaData.EliteConfig, + }), +})) + +export default useDetailDataStore diff --git a/src/stores/enkaStore.ts b/src/stores/enkaStore.ts index 8a6c35a..bf4970c 100644 --- a/src/stores/enkaStore.ts +++ b/src/stores/enkaStore.ts @@ -1,22 +1,22 @@ -import { CharacterInfoCardType, EnkaResponse } from '@/types'; -import { create } from 'zustand' - -interface EnkaState { - selectedCharacters: CharacterInfoCardType[]; - enkaData: EnkaResponse | null; +import { CharacterInfoCardType, EnkaResponse } from '@/types'; +import { create } from 'zustand' + +interface EnkaState { + selectedCharacters: CharacterInfoCardType[]; + enkaData: EnkaResponse | null; uidInput: string; setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => void; setEnkaData: (newEnkaData: EnkaResponse | null) => void; - setUidInput: (newUidInput: string) => void; -} - -const useEnkaStore = create((set) => ({ - selectedCharacters: [], - enkaData: null, - uidInput: "", - setUidInput: (newUidInput: string) => set({ uidInput: newUidInput }), - setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), + setUidInput: (newUidInput: string) => void; +} + +const useEnkaStore = create((set) => ({ + selectedCharacters: [], + enkaData: null, + uidInput: "", + setUidInput: (newUidInput: string) => set({ uidInput: newUidInput }), + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), setEnkaData: (newEnkaData: EnkaResponse | null) => set({ enkaData: newEnkaData }), })); - + export default useEnkaStore; diff --git a/src/stores/freesrStore.ts b/src/stores/freesrStore.ts index 47bc707..4458683 100644 --- a/src/stores/freesrStore.ts +++ b/src/stores/freesrStore.ts @@ -1,18 +1,18 @@ -import { CharacterInfoCardType, FreeSRJson } from '@/types'; -import { create } from 'zustand' - -interface FreeSRState { - selectedCharacters: CharacterInfoCardType[]; - freeSRData: FreeSRJson | null; - setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => void; - setFreeSRData: (newFreeSRData: FreeSRJson | null) => void; -} - -const useFreeSRStore = create((set) => ({ - selectedCharacters: [], - freeSRData: null, - setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), - setFreeSRData: (newFreeSRData: FreeSRJson | null) => set({ freeSRData: newFreeSRData }), -})); - +import { CharacterInfoCardType, FreeSRJson } from '@/types'; +import { create } from 'zustand' + +interface FreeSRState { + selectedCharacters: CharacterInfoCardType[]; + freeSRData: FreeSRJson | null; + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => void; + setFreeSRData: (newFreeSRData: FreeSRJson | null) => void; +} + +const useFreeSRStore = create((set) => ({ + selectedCharacters: [], + freeSRData: null, + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), + setFreeSRData: (newFreeSRData: FreeSRJson | null) => set({ freeSRData: newFreeSRData }), +})); + export default useFreeSRStore; \ No newline at end of file diff --git a/src/stores/globalStore.ts b/src/stores/globalStore.ts index b0c46e5..a028a29 100644 --- a/src/stores/globalStore.ts +++ b/src/stores/globalStore.ts @@ -1,26 +1,26 @@ -import { create } from 'zustand' -import { ExtraData } from '@/types' - -interface GlobalState { - isConnectPS: boolean; - extraData?: ExtraData; - isEnableChangePath: boolean - isEnableLua: boolean - setIsEnableChangePath: (newIsEnableChangePath: boolean) => void; - setIsEnableLua: (newIsEnableLua: boolean) => void; - setExtraData: (newExtraData: ExtraData | undefined) => void; - setIsConnectPS: (newIsConnectPS: boolean) => void; -} - -const useGlobalStore = create((set) => ({ - isConnectPS: false, - extraData: undefined, - isEnableChangePath: false, - isEnableLua: false, - setIsEnableChangePath: (newIsEnableChangePath: boolean) => set({ isEnableChangePath: newIsEnableChangePath }), - setIsEnableLua: (newIsEnableLua: boolean) => set({ isEnableLua: newIsEnableLua }), - setExtraData: (newExtraData: ExtraData | undefined) => set({ extraData: newExtraData }), - setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }), -})); - +import { create } from 'zustand' +import { ExtraData } from '@/types' + +interface GlobalState { + isConnectPS: boolean; + extraData?: ExtraData; + isEnableChangePath: boolean + isEnableLua: boolean + setIsEnableChangePath: (newIsEnableChangePath: boolean) => void; + setIsEnableLua: (newIsEnableLua: boolean) => void; + setExtraData: (newExtraData: ExtraData | undefined) => void; + setIsConnectPS: (newIsConnectPS: boolean) => void; +} + +const useGlobalStore = create((set) => ({ + isConnectPS: false, + extraData: undefined, + isEnableChangePath: false, + isEnableLua: false, + setIsEnableChangePath: (newIsEnableChangePath: boolean) => set({ isEnableChangePath: newIsEnableChangePath }), + setIsEnableLua: (newIsEnableLua: boolean) => set({ isEnableLua: newIsEnableLua }), + setExtraData: (newExtraData: ExtraData | undefined) => set({ extraData: newExtraData }), + setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }), +})); + export default useGlobalStore; \ No newline at end of file diff --git a/src/stores/localeStore.ts b/src/stores/localeStore.ts index 38da530..d7a547a 100644 --- a/src/stores/localeStore.ts +++ b/src/stores/localeStore.ts @@ -3,17 +3,17 @@ import { create } from 'zustand' import { persist } from 'zustand/middleware'; import { localePersistedSchema } from '@/zod'; import { createValidatedJSONStorage } from './persistStorage'; - - + + interface LocaleState { - locale: string; - theme: string; - currentVersion: string - changelog: ChangelogItemType[] - setCurrentVersion: (currentVersion: string) => void; - setChangelog: (newChangelog: ChangelogItemType[]) => void; - setTheme: (newTheme: string) => void; - setLocale: (newLocale: string) => void; + locale: string; + theme: string; + currentVersion: string + changelog: ChangelogItemType[] + setCurrentVersion: (currentVersion: string) => void; + setChangelog: (newChangelog: ChangelogItemType[]) => void; + setTheme: (newTheme: string) => void; + setLocale: (newLocale: string) => void; } type LocalePersistedState = Pick; @@ -21,15 +21,15 @@ type LocalePersistedState = Pick()( persist( (set) => ({ - locale: "en", - theme: "night", - currentVersion: "", - changelog: [], - setCurrentVersion: (currentVersion: string) => set({ currentVersion }), - setChangelog: (newChangelog: ChangelogItemType[]) => set({ changelog: newChangelog }), - setTheme: (newTheme: string) => set({ theme: newTheme }), - setLocale: (newLocale: string) => set({ locale: newLocale }), - }), + locale: "en", + theme: "night", + currentVersion: "", + changelog: [], + setCurrentVersion: (currentVersion: string) => set({ currentVersion }), + setChangelog: (newChangelog: ChangelogItemType[]) => set({ changelog: newChangelog }), + setTheme: (newTheme: string) => set({ theme: newTheme }), + setLocale: (newLocale: string) => set({ locale: newLocale }), + }), { name: 'locale-storage', storage: createValidatedJSONStorage(() => localStorage, localePersistedSchema), @@ -42,5 +42,5 @@ const useLocaleStore = create()( } ) ); - + export default useLocaleStore; diff --git a/src/stores/modelStore.ts b/src/stores/modelStore.ts index cb0ff3f..2acd47b 100644 --- a/src/stores/modelStore.ts +++ b/src/stores/modelStore.ts @@ -1,58 +1,58 @@ -import { create } from 'zustand' - - -interface ModelState { - isOpenLightcone: boolean; - isOpenRelic: boolean; - isOpenAvatar: boolean; - isOpenCreateProfile: boolean; - isOpenImport: boolean; - isOpenCopy: boolean; - isOpenMonster: boolean; - isOpenConnect: boolean; - isOpenAvatars: boolean; - isOpenQuickView: boolean; - isOpenExtra: boolean; - isChangelog: boolean; - setIsOpenExtra: (newIsOpenExtra: boolean) => void; - setIsOpenQuickView: (newIsOpenQuickView: boolean) => void; - setIsOpenAvatars: (newIsOpenAvatars: boolean) => void; - setIsOpenConnect: (newIsOpenConnect: boolean) => void; - setIsOpenMonster: (newIsOpenMonster: boolean) => void; - setIsOpenLightcone: (newIsOpenLightcone: boolean) => void; - setIsOpenRelic: (newIsOpenRelic: boolean) => void; - setIsOpenAvatar: (newIsOpenAvatar: boolean) => void; - setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => void; - setIsOpenImport: (newIsOpenImport: boolean) => void; - setIsOpenCopy: (newIsOpenCopy: boolean) => void; - setIsChangelog: (newChangelog: boolean) => void; -} - -const useModelStore = create((set) => ({ - isOpenLightcone: false, - isOpenRelic: false, - isOpenAvatar: false, - isOpenCreateProfile: false, - isOpenImport: false, - isOpenCopy: false, - isOpenMonster: false, - isOpenConnect: false, - isOpenAvatars: false, - isOpenQuickView: false, - isOpenExtra: false, - isChangelog: false, - setIsOpenExtra: (newIsOpenExtra: boolean) => set({ isOpenExtra: newIsOpenExtra }), - setIsOpenQuickView: (newIsOpenQuickView: boolean) => set({ isOpenQuickView: newIsOpenQuickView }), - setIsOpenAvatars: (newIsOpenAvatars: boolean) => set({ isOpenAvatars: newIsOpenAvatars }), - setIsOpenConnect: (newIsOpenConnect: boolean) => set({ isOpenConnect: newIsOpenConnect }), - setIsOpenMonster: (newIsOpenMonster: boolean) => set({ isOpenMonster: newIsOpenMonster }), - setIsOpenLightcone: (newIsOpenLightcone: boolean) => set({ isOpenLightcone: newIsOpenLightcone }), - setIsOpenRelic: (newIsOpenRelic: boolean) => set({ isOpenRelic: newIsOpenRelic }), - setIsOpenAvatar: (newIsOpenAvatar: boolean) => set({ isOpenAvatar: newIsOpenAvatar }), - setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => set({ isOpenCreateProfile: newIsOpenCreateProfile }), - setIsOpenImport: (newIsOpenImport: boolean) => set({ isOpenImport: newIsOpenImport }), - setIsOpenCopy: (newIsOpenCopy: boolean) => set({ isOpenCopy: newIsOpenCopy }), - setIsChangelog: (newChangelog: boolean) => set({ isChangelog: newChangelog }), -})); - +import { create } from 'zustand' + + +interface ModelState { + isOpenLightcone: boolean; + isOpenRelic: boolean; + isOpenAvatar: boolean; + isOpenCreateProfile: boolean; + isOpenImport: boolean; + isOpenCopy: boolean; + isOpenMonster: boolean; + isOpenConnect: boolean; + isOpenAvatars: boolean; + isOpenQuickView: boolean; + isOpenExtra: boolean; + isChangelog: boolean; + setIsOpenExtra: (newIsOpenExtra: boolean) => void; + setIsOpenQuickView: (newIsOpenQuickView: boolean) => void; + setIsOpenAvatars: (newIsOpenAvatars: boolean) => void; + setIsOpenConnect: (newIsOpenConnect: boolean) => void; + setIsOpenMonster: (newIsOpenMonster: boolean) => void; + setIsOpenLightcone: (newIsOpenLightcone: boolean) => void; + setIsOpenRelic: (newIsOpenRelic: boolean) => void; + setIsOpenAvatar: (newIsOpenAvatar: boolean) => void; + setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => void; + setIsOpenImport: (newIsOpenImport: boolean) => void; + setIsOpenCopy: (newIsOpenCopy: boolean) => void; + setIsChangelog: (newChangelog: boolean) => void; +} + +const useModelStore = create((set) => ({ + isOpenLightcone: false, + isOpenRelic: false, + isOpenAvatar: false, + isOpenCreateProfile: false, + isOpenImport: false, + isOpenCopy: false, + isOpenMonster: false, + isOpenConnect: false, + isOpenAvatars: false, + isOpenQuickView: false, + isOpenExtra: false, + isChangelog: false, + setIsOpenExtra: (newIsOpenExtra: boolean) => set({ isOpenExtra: newIsOpenExtra }), + setIsOpenQuickView: (newIsOpenQuickView: boolean) => set({ isOpenQuickView: newIsOpenQuickView }), + setIsOpenAvatars: (newIsOpenAvatars: boolean) => set({ isOpenAvatars: newIsOpenAvatars }), + setIsOpenConnect: (newIsOpenConnect: boolean) => set({ isOpenConnect: newIsOpenConnect }), + setIsOpenMonster: (newIsOpenMonster: boolean) => set({ isOpenMonster: newIsOpenMonster }), + setIsOpenLightcone: (newIsOpenLightcone: boolean) => set({ isOpenLightcone: newIsOpenLightcone }), + setIsOpenRelic: (newIsOpenRelic: boolean) => set({ isOpenRelic: newIsOpenRelic }), + setIsOpenAvatar: (newIsOpenAvatar: boolean) => set({ isOpenAvatar: newIsOpenAvatar }), + setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => set({ isOpenCreateProfile: newIsOpenCreateProfile }), + setIsOpenImport: (newIsOpenImport: boolean) => set({ isOpenImport: newIsOpenImport }), + setIsOpenCopy: (newIsOpenCopy: boolean) => set({ isOpenCopy: newIsOpenCopy }), + setIsChangelog: (newChangelog: boolean) => set({ isChangelog: newChangelog }), +})); + export default useModelStore; \ No newline at end of file diff --git a/src/stores/relicMakerStore.ts b/src/stores/relicMakerStore.ts index 2c16960..e017f6b 100644 --- a/src/stores/relicMakerStore.ts +++ b/src/stores/relicMakerStore.ts @@ -1,213 +1,213 @@ -import { create } from 'zustand' -interface RelicMakerState { - selectedRelicSlot: string; - selectedMainStat: string; - selectedRelicSet: string; - selectedRelicLevel: number; - listSelectedSubStats: { - property: string, - affixId: string, - rollCount: number, - stepCount: number - }[]; - preSelectedSubStats: Record; - setSelectedRelicSlot: (newSelectedRelicSlot: string) => void; - setSelectedRelicSet: (newSelectedRelicSet: string) => void; - setSelectedMainStat: (newSelectedMainStat: string) => void; - setSelectedRelicLevel: (newSelectedRelicLevel: number) => void; - setListSelectedSubStats: (newSubAffixes: { property: string, affixId: string, rollCount: number, stepCount: number }[]) => void; - resetHistory: (index: number | null) => void; - resetSubStat: () => void; - popHistory: (index: number) => void; - addHistory: (index: number, affix: { property: string, affixId: string, rollCount: number, stepCount: number }) => void; - -} - -const useRelicMakerStore = create((set) => ({ - selectedRelicSlot: "", - selectedMainStat: "", - selectedRelicSet: "", - selectedRelicLevel: 15, - listSelectedSubStats: [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - preSelectedSubStats: { - "0": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "1": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "2": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "3": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - }, - resetSubStat: () => set({ - listSelectedSubStats: [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - }), - setSelectedRelicSlot: (newSelectedRelicSlot: string) => set({ selectedRelicSlot: newSelectedRelicSlot }), - setSelectedRelicSet: (newSelectedRelicSet: string) => set({ selectedRelicSet: newSelectedRelicSet }), - setSelectedMainStat: (newSelectedMainStat: string) => set({ selectedMainStat: newSelectedMainStat }), - setSelectedRelicLevel: (newSelectedRelicLevel: number) => set({ selectedRelicLevel: newSelectedRelicLevel }), - setListSelectedSubStats: (newSubAffixes) => set(() => ({ - listSelectedSubStats: newSubAffixes.map(item => ({ ...item })) - })), - resetHistory: (index: number | null) => set((state) => { - if (index !== null) { - return { - preSelectedSubStats: { - "0": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "1": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "2": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - "3": [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ], - } - } - } else if (index !== null) { - const newPreSelectedSubStats = { ...state.preSelectedSubStats }; - newPreSelectedSubStats[index] = [ - { - property: "", - affixId: "", - rollCount: 0, - stepCount: 0 - }, - ] - return { - preSelectedSubStats: newPreSelectedSubStats - } - } - return {} - }), - popHistory: (index: number) => set((state) => { - const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); - const copied = state.preSelectedSubStats[index].map(item => ({ ...item })); - copied.pop(); - newPreSelectedSubStats[index] = copied; - return { preSelectedSubStats: newPreSelectedSubStats }; - }), - addHistory: (index, affix) => set((state) => { - const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); - - const currentList = state.preSelectedSubStats[index]; - const copied = currentList ? currentList.map(item => ({ ...item })) : [ - { property: "", affixId: "", rollCount: 0, stepCount: 0 }, - { property: "", affixId: "", rollCount: 0, stepCount: 0 }, - { property: "", affixId: "", rollCount: 0, stepCount: 0 }, - { property: "", affixId: "", rollCount: 0, stepCount: 0 }, - ]; - - if (copied) { - const newAffix = structuredClone(affix); - copied.push(newAffix); - newPreSelectedSubStats[index] = copied; - return { preSelectedSubStats: newPreSelectedSubStats }; - } - return {} - }), -})); - +import { create } from 'zustand' +interface RelicMakerState { + selectedRelicSlot: string; + selectedMainStat: string; + selectedRelicSet: string; + selectedRelicLevel: number; + listSelectedSubStats: { + property: string, + affixId: string, + rollCount: number, + stepCount: number + }[]; + preSelectedSubStats: Record; + setSelectedRelicSlot: (newSelectedRelicSlot: string) => void; + setSelectedRelicSet: (newSelectedRelicSet: string) => void; + setSelectedMainStat: (newSelectedMainStat: string) => void; + setSelectedRelicLevel: (newSelectedRelicLevel: number) => void; + setListSelectedSubStats: (newSubAffixes: { property: string, affixId: string, rollCount: number, stepCount: number }[]) => void; + resetHistory: (index: number | null) => void; + resetSubStat: () => void; + popHistory: (index: number) => void; + addHistory: (index: number, affix: { property: string, affixId: string, rollCount: number, stepCount: number }) => void; + +} + +const useRelicMakerStore = create((set) => ({ + selectedRelicSlot: "", + selectedMainStat: "", + selectedRelicSet: "", + selectedRelicLevel: 15, + listSelectedSubStats: [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + preSelectedSubStats: { + "0": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "1": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "2": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "3": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + }, + resetSubStat: () => set({ + listSelectedSubStats: [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + }), + setSelectedRelicSlot: (newSelectedRelicSlot: string) => set({ selectedRelicSlot: newSelectedRelicSlot }), + setSelectedRelicSet: (newSelectedRelicSet: string) => set({ selectedRelicSet: newSelectedRelicSet }), + setSelectedMainStat: (newSelectedMainStat: string) => set({ selectedMainStat: newSelectedMainStat }), + setSelectedRelicLevel: (newSelectedRelicLevel: number) => set({ selectedRelicLevel: newSelectedRelicLevel }), + setListSelectedSubStats: (newSubAffixes) => set(() => ({ + listSelectedSubStats: newSubAffixes.map(item => ({ ...item })) + })), + resetHistory: (index: number | null) => set((state) => { + if (index !== null) { + return { + preSelectedSubStats: { + "0": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "1": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "2": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "3": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + } + } + } else if (index !== null) { + const newPreSelectedSubStats = { ...state.preSelectedSubStats }; + newPreSelectedSubStats[index] = [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ] + return { + preSelectedSubStats: newPreSelectedSubStats + } + } + return {} + }), + popHistory: (index: number) => set((state) => { + const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); + const copied = state.preSelectedSubStats[index].map(item => ({ ...item })); + copied.pop(); + newPreSelectedSubStats[index] = copied; + return { preSelectedSubStats: newPreSelectedSubStats }; + }), + addHistory: (index, affix) => set((state) => { + const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); + + const currentList = state.preSelectedSubStats[index]; + const copied = currentList ? currentList.map(item => ({ ...item })) : [ + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + ]; + + if (copied) { + const newAffix = structuredClone(affix); + copied.push(newAffix); + newPreSelectedSubStats[index] = copied; + return { preSelectedSubStats: newPreSelectedSubStats }; + } + return {} + }), +})); + export default useRelicMakerStore; \ No newline at end of file diff --git a/src/stores/userDataStore.ts b/src/stores/userDataStore.ts index 63b0d35..286ff91 100644 --- a/src/stores/userDataStore.ts +++ b/src/stores/userDataStore.ts @@ -3,24 +3,24 @@ import { create } from 'zustand' import { persist } from 'zustand/middleware'; import { micsSchema } from '@/zod'; import { createValidatedJSONStorage } from './persistStorage'; - - + + interface UserDataState { - avatars: { [key: string]: AvatarStore }; - battle_type: string; - moc_config: MOCConfigStore; - pf_config: PFConfigStore; - as_config: ASConfigStore; - peak_config: PEAKConfigStore; - ce_config: CEConfigStore; - setAvatars: (newAvatars: { [key: string]: AvatarStore }) => void; - setAvatar: (newAvatar: AvatarStore) => void; - setBattleType: (newBattleType: string) => void; - setMocConfig: (newMocConfig: MOCConfigStore) => void; - setPfConfig: (newPfConfig: PFConfigStore) => void; - setAsConfig: (newAsConfig: ASConfigStore) => void; - setPeakConfig: (newPeakConfig: PEAKConfigStore) => void; - setCeConfig: (newCeConfig: CEConfigStore) => void; + avatars: { [key: string]: AvatarStore }; + battle_type: string; + moc_config: MOCConfigStore; + pf_config: PFConfigStore; + as_config: ASConfigStore; + peak_config: PEAKConfigStore; + ce_config: CEConfigStore; + setAvatars: (newAvatars: { [key: string]: AvatarStore }) => void; + setAvatar: (newAvatar: AvatarStore) => void; + setBattleType: (newBattleType: string) => void; + setMocConfig: (newMocConfig: MOCConfigStore) => void; + setPfConfig: (newPfConfig: PFConfigStore) => void; + setAsConfig: (newAsConfig: ASConfigStore) => void; + setPeakConfig: (newPeakConfig: PEAKConfigStore) => void; + setCeConfig: (newCeConfig: CEConfigStore) => void; } type UserDataPersistedState = Pick< @@ -37,64 +37,64 @@ type UserDataPersistedState = Pick< const useUserDataStore = create()( persist( (set) => ({ - avatars: {}, - battle_type: "", - moc_config: { - event_id: 0, - challenge_id: 0, - floor_side: "Upper", - use_turbulence_buff: true, - use_cycle_count: true, - blessings: [], - cycle_count: 0, - stage_id: 0, - monsters: [], - }, - pf_config: { - event_id: 0, - challenge_id: 0, - buff_id: 0, - floor_side: "Upper", - blessings: [], - cycle_count: 0, - stage_id: 0, - monsters: [], - }, - as_config: { - event_id: 0, - challenge_id: 0, - buff_id: 0, - floor_side: "Upper", - blessings: [], - cycle_count: 0, - stage_id: 0, - monsters: [], - }, - ce_config: { - blessings: [], - cycle_count: 30, - stage_id: 0, - monsters: [], - }, - peak_config: { - event_id: 0, - challenge_id: 0, - buff_id: 0, - boss_mode: "Normal", - blessings: [], - cycle_count: 0, - stage_id: 0, - monsters: [], - }, - setAvatars: (newAvatars: { [key: string]: AvatarStore }) => set({ avatars: newAvatars }), - setAvatar: (newAvatar: AvatarStore) => set((state) => ({ avatars: { ...state.avatars, [newAvatar.avatar_id.toString()]: newAvatar } })), - setBattleType: (newBattleType: string) => set({ battle_type: newBattleType }), - setMocConfig: (newMocConfig: MOCConfigStore) => set({ moc_config: newMocConfig }), - setPfConfig: (newPfConfig: PFConfigStore) => set({ pf_config: newPfConfig }), - setAsConfig: (newAsConfig: ASConfigStore) => set({ as_config: newAsConfig }), - setPeakConfig: (newPeakConfig: PEAKConfigStore) => set({ peak_config: newPeakConfig }), - setCeConfig: (newCeConfig: CEConfigStore) => set({ ce_config: newCeConfig }), - }), + avatars: {}, + battle_type: "", + moc_config: { + event_id: 0, + challenge_id: 0, + floor_side: "Upper", + use_turbulence_buff: true, + use_cycle_count: true, + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + pf_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + floor_side: "Upper", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + as_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + floor_side: "Upper", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + ce_config: { + blessings: [], + cycle_count: 30, + stage_id: 0, + monsters: [], + }, + peak_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + boss_mode: "Normal", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + setAvatars: (newAvatars: { [key: string]: AvatarStore }) => set({ avatars: newAvatars }), + setAvatar: (newAvatar: AvatarStore) => set((state) => ({ avatars: { ...state.avatars, [newAvatar.avatar_id.toString()]: newAvatar } })), + setBattleType: (newBattleType: string) => set({ battle_type: newBattleType }), + setMocConfig: (newMocConfig: MOCConfigStore) => set({ moc_config: newMocConfig }), + setPfConfig: (newPfConfig: PFConfigStore) => set({ pf_config: newPfConfig }), + setAsConfig: (newAsConfig: ASConfigStore) => set({ as_config: newAsConfig }), + setPeakConfig: (newPeakConfig: PEAKConfigStore) => set({ peak_config: newPeakConfig }), + setCeConfig: (newCeConfig: CEConfigStore) => set({ ce_config: newCeConfig }), + }), { name: 'user-data-storage', storage: createValidatedJSONStorage(() => localStorage, micsSchema), @@ -110,5 +110,5 @@ const useUserDataStore = create()( } ) ); - + export default useUserDataStore; diff --git a/src/types/asDetail.ts b/src/types/asDetail.ts index 989dc19..d8e36f2 100644 --- a/src/types/asDetail.ts +++ b/src/types/asDetail.ts @@ -1,129 +1,129 @@ -import { ExtraEffect } from "./avatarDetail"; -import { InfiniteWave, MazeBuff } from "./pfDetail"; - -export interface ASGroupDetail { - ID: number; - ChallengeGroupType: string; - Name: Record; - BeginTime: string; - EndTime: string; - BuffList1: ASBuff[]; - BuffList2: ASBuff[]; - BuffList3: ASBuff[] | null; - Level: ASLevel[]; - Tierce: ASTierceLevel | null; -} - -export interface ASBuff { - ID: number; - Param: number[]; - Icon: string; - Name: Record; - Desc: Record; - ExtraList?: ExtraEffect[]; -} - -export interface ASTierceLevel { - ID: number; - PreChallenge: number; - Name: Record; - Target: ASTarget[]; - DamageType: string[]; - TurnLimit: number; - EventList: ASEvent[]; -} - -export interface ASLevel { - Floor: number; - ID: number; - StageNum: number; - Name: Record; - Target: ASTarget[]; - DamageType1: string[]; - DamageType2: string[]; - MazeBuff: MazeBuff[]; - TurnLimit: number; - EventList1: ASEvent[]; - EventList2: ASEvent[]; - Monster1: ASMonster; - Monster2: ASMonster; - Monster3: ASMonster | null; -} - -export interface ASTarget { - ID: number; - Name: Record; - Param: number[]; -} - -export interface ASEvent { - ID: number; - Name: Record; - HardLevelGroup: number; - EliteGroup: number; - Level: number; - Release: boolean; - MonsterList: number[][]; - Infinite: InfiniteWave[] | null; -} - -export interface ASMonster { - ID: number; - Tag: ASTag[]; - Phase: ASPhase[]; - DifficultyGuide: ASDifficultyGuide[]; - TextGuide: ASTextGuide[]; -} - -export interface ASTag { - ID: number; - Name: Record; - BriefDescription: Record; - Param: number[]; - SkillID: number | null; - Effect: ASEffect[]; -} - -export interface ASEffect { - ID: number; - Desc: Record; - Name: Record; - Icon: string; - Param: number[]; - EffectType: number; -} - -export interface ASPhase { - ID: number; - Name: Record; - Answer: Record; - Description: Record; - SkillList: ASSkillList[]; -} - -export interface ASSkillList { - ID: number; - SkillType: string; - Name: Record; - TextList: ASTextList[]; -} - -export interface ASTextList { - ID: number; - Description: Record; - Param: number[]; - Effect: ASEffect[]; -} - -export interface ASDifficultyGuide { - ID: number; - Description: Record; - Param: number[]; - SkillID: number | null; -} - -export interface ASTextGuide { - ID: number; - Description: Record; - Param: number[]; +import { ExtraEffect } from "./avatarDetail"; +import { InfiniteWave, MazeBuff } from "./pfDetail"; + +export interface ASGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + BeginTime: string; + EndTime: string; + BuffList1: ASBuff[]; + BuffList2: ASBuff[]; + BuffList3: ASBuff[] | null; + Level: ASLevel[]; + Tierce: ASTierceLevel | null; +} + +export interface ASBuff { + ID: number; + Param: number[]; + Icon: string; + Name: Record; + Desc: Record; + ExtraList?: ExtraEffect[]; +} + +export interface ASTierceLevel { + ID: number; + PreChallenge: number; + Name: Record; + Target: ASTarget[]; + DamageType: string[]; + TurnLimit: number; + EventList: ASEvent[]; +} + +export interface ASLevel { + Floor: number; + ID: number; + StageNum: number; + Name: Record; + Target: ASTarget[]; + DamageType1: string[]; + DamageType2: string[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList1: ASEvent[]; + EventList2: ASEvent[]; + Monster1: ASMonster; + Monster2: ASMonster; + Monster3: ASMonster | null; +} + +export interface ASTarget { + ID: number; + Name: Record; + Param: number[]; +} + +export interface ASEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite: InfiniteWave[] | null; +} + +export interface ASMonster { + ID: number; + Tag: ASTag[]; + Phase: ASPhase[]; + DifficultyGuide: ASDifficultyGuide[]; + TextGuide: ASTextGuide[]; +} + +export interface ASTag { + ID: number; + Name: Record; + BriefDescription: Record; + Param: number[]; + SkillID: number | null; + Effect: ASEffect[]; +} + +export interface ASEffect { + ID: number; + Desc: Record; + Name: Record; + Icon: string; + Param: number[]; + EffectType: number; +} + +export interface ASPhase { + ID: number; + Name: Record; + Answer: Record; + Description: Record; + SkillList: ASSkillList[]; +} + +export interface ASSkillList { + ID: number; + SkillType: string; + Name: Record; + TextList: ASTextList[]; +} + +export interface ASTextList { + ID: number; + Description: Record; + Param: number[]; + Effect: ASEffect[]; +} + +export interface ASDifficultyGuide { + ID: number; + Description: Record; + Param: number[]; + SkillID: number | null; +} + +export interface ASTextGuide { + ID: number; + Description: Record; + Param: number[]; } \ No newline at end of file diff --git a/src/types/card.ts b/src/types/card.ts index c4ca0bc..38c012e 100644 --- a/src/types/card.ts +++ b/src/types/card.ts @@ -1,25 +1,25 @@ -import { LightconeStore, RelicStore } from "./mics"; - -export interface CharacterInfoCardType { - key: number; - avatar_id: number; - rank: number; - level: number; - lightcone: { - level: number; - rank: number; - item_id: number; - }; - relics: { - level: number; - relic_id: number; - relic_set_id: number; - }[]; -} - -export interface AvatarProfileCardType { - key: number; - profile_name: string, - lightcone?: LightconeStore | null, - relics: Record +import { LightconeStore, RelicStore } from "./mics"; + +export interface CharacterInfoCardType { + key: number; + avatar_id: number; + rank: number; + level: number; + lightcone: { + level: number; + rank: number; + item_id: number; + }; + relics: { + level: number; + relic_id: number; + relic_set_id: number; + }[]; +} + +export interface AvatarProfileCardType { + key: number; + profile_name: string, + lightcone?: LightconeStore | null, + relics: Record } \ No newline at end of file diff --git a/src/types/changelog.ts b/src/types/changelog.ts index 05923f0..aaae5c6 100644 --- a/src/types/changelog.ts +++ b/src/types/changelog.ts @@ -1,7 +1,7 @@ - -export interface ChangelogItemType { - version: string, - date: string, - type: string, - items: string[] + +export interface ChangelogItemType { + version: string, + date: string, + type: string, + items: string[] } \ No newline at end of file diff --git a/src/types/enka.ts b/src/types/enka.ts index 4551bcc..50b3249 100644 --- a/src/types/enka.ts +++ b/src/types/enka.ts @@ -1,96 +1,96 @@ -interface PrivacySettingInfo { - displayCollection: boolean - displayRecord: boolean - displayRecordTeam: boolean - displayOnlineStatus: boolean - displayDiary: boolean -} - -interface RecordInfo { - achievementCount: number - bookCount: number - avatarCount: number - equipmentCount: number - musicCount: number - relicCount: number - challengeInfo: unknown; - maxRogueChallengeScore: number -} - - -interface SubAffix { - affixId: number - cnt: number - step?: number -} - -interface FlatProp { - type: string - value: number -} - +interface PrivacySettingInfo { + displayCollection: boolean + displayRecord: boolean + displayRecordTeam: boolean + displayOnlineStatus: boolean + displayDiary: boolean +} + +interface RecordInfo { + achievementCount: number + bookCount: number + avatarCount: number + equipmentCount: number + musicCount: number + relicCount: number + challengeInfo: unknown; + maxRogueChallengeScore: number +} + + +interface SubAffix { + affixId: number + cnt: number + step?: number +} + +interface FlatProp { + type: string + value: number +} + interface RelicFlat { props: FlatProp[] setName: string | number setID: number } - -interface Relic { - mainAffixId: number - subAffixList: SubAffix[] - tid: number - type: number - level: number - _flat: RelicFlat -} - -interface SkillTree { - pointId: number - level: number -} - + +interface Relic { + mainAffixId: number + subAffixList: SubAffix[] + tid: number + type: number + level: number + _flat: RelicFlat +} + +interface SkillTree { + pointId: number + level: number +} + interface EquipmentFlat { props: FlatProp[] name: string | number } - -interface Equipment { - rank: number - tid: number - promotion: number - level: number - _flat: EquipmentFlat -} - -export interface AvatarEnkaDetail { - relicList: Relic[] - level: number - promotion: number - rank?: number - skillTreeList: SkillTree[] - equipment: Equipment - avatarId: number - _assist?: boolean -} - -interface DetailInfo { - worldLevel: number - privacySettingInfo: PrivacySettingInfo - headIcon: number - signature: string - avatarDetailList: AvatarEnkaDetail[] - platform: string - recordInfo: RecordInfo - uid: number - level: number - nickname: string - isDisplayAvatar: boolean - friendCount: number - personalCardId: number -} - -export interface EnkaResponse { - detailInfo: DetailInfo - ttl: number - uid: string -} + +interface Equipment { + rank: number + tid: number + promotion: number + level: number + _flat: EquipmentFlat +} + +export interface AvatarEnkaDetail { + relicList: Relic[] + level: number + promotion: number + rank?: number + skillTreeList: SkillTree[] + equipment: Equipment + avatarId: number + _assist?: boolean +} + +interface DetailInfo { + worldLevel: number + privacySettingInfo: PrivacySettingInfo + headIcon: number + signature: string + avatarDetailList: AvatarEnkaDetail[] + platform: string + recordInfo: RecordInfo + uid: number + level: number + nickname: string + isDisplayAvatar: boolean + friendCount: number + personalCardId: number +} + +export interface EnkaResponse { + detailInfo: DetailInfo + ttl: number + uid: string +} diff --git a/src/types/extraData.ts b/src/types/extraData.ts index ef042f8..00d3e26 100644 --- a/src/types/extraData.ts +++ b/src/types/extraData.ts @@ -1,27 +1,27 @@ -export interface ExtraData { - theory_craft?: { - hp: Record - cycle_count: number - mode: boolean - stage_id: number - custom_lineup?: string[] - } - setting?: { - censorship: boolean - cm: boolean - first_person: boolean - hide_ui: boolean - } - challenge?: { - skip_node: number - challenge_peak_group_id: number - challenge_peak_group_id_list: number[] - } - multi_path?: { - main: number - march_7: number - multi_path_main: number[] - multi_path_march_7: number[] - }, - lua?: string | null -} +export interface ExtraData { + theory_craft?: { + hp: Record + cycle_count: number + mode: boolean + stage_id: number + custom_lineup?: string[] + } + setting?: { + censorship: boolean + cm: boolean + first_person: boolean + hide_ui: boolean + } + challenge?: { + skip_node: number + challenge_peak_group_id: number + challenge_peak_group_id_list: number[] + } + multi_path?: { + main: number + march_7: number + multi_path_main: number[] + multi_path_march_7: number[] + }, + lua?: string | null +} diff --git a/src/types/gloval.d.ts b/src/types/gloval.d.ts index 847afa7..46cfe71 100644 --- a/src/types/gloval.d.ts +++ b/src/types/gloval.d.ts @@ -1,4 +1,4 @@ -declare module '*.css'; -declare module '*.scss'; -declare module '*.module.css'; +declare module '*.css'; +declare module '*.scss'; +declare module '*.module.css'; declare module '*.module.scss'; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index e8a0944..1b7d263 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,21 +1,21 @@ -export * from "./avatarDetail" -export * from "./srtools" -export * from "./mics" -export * from "./lightconeDetail" -export * from "./relicDetail" -export * from "./enka" -export * from "./card" -export * from "./pfDetail" -export * from "./asDetail" -export * from "./mocDetail" -export * from "./peakDetail" -export * from "./extraData" -export * from "./showcase" -export * from "./srtools" -export * from "./changelog" -export * from "./modelConfig" -export * from "./metaData" -export * from "./monsterDetail" -export * from "./filter" -export * from "./metaData" +export * from "./avatarDetail" +export * from "./srtools" +export * from "./mics" +export * from "./lightconeDetail" +export * from "./relicDetail" +export * from "./enka" +export * from "./card" +export * from "./pfDetail" +export * from "./asDetail" +export * from "./mocDetail" +export * from "./peakDetail" +export * from "./extraData" +export * from "./showcase" +export * from "./srtools" +export * from "./changelog" +export * from "./modelConfig" +export * from "./metaData" +export * from "./monsterDetail" +export * from "./filter" +export * from "./metaData" export * from "./psConnect" \ No newline at end of file diff --git a/src/types/lightconeDetail.ts b/src/types/lightconeDetail.ts index 6d084fc..75e9989 100644 --- a/src/types/lightconeDetail.ts +++ b/src/types/lightconeDetail.ts @@ -1,52 +1,52 @@ -export interface LightConeDetail { - ID: number; - Release: boolean; - Name: Record; - Desc: Record; - Rarity: string; - BaseType: string; - MaxPromotionLevel: number; - MaxRank: number; - Skills: LightconeSkill; - Image: LightconeImage; - Stats: Record; -} - -export interface LightconeSkill { - ID: number; - Name: Record; - Desc: Record; - AbilityName: string; - Level: Record; -} - -export interface LightconeSkillLevel { - Level: number; - Param: number[]; - Bonus: LightconeSkillBonus[]; -} - -export interface LightconeSkillBonus { - PropertyType: string; - Value: number; -} - -export interface LightconeImage { - ThumbnailPath: string; - ImagePath: string; - IconPath: string; - FigureIconPath: string; -} - -export interface LightconeStatLevel { - Promotion: number; - PlayerLevelRequire: number; - WorldLevelRequire: number; - MaxLevel: number; - BaseHP: number; - BaseHPAdd: number; - BaseAttack: number; - BaseAttackAdd: number; - BaseDefence: number; - BaseDefenceAdd: number; +export interface LightConeDetail { + ID: number; + Release: boolean; + Name: Record; + Desc: Record; + Rarity: string; + BaseType: string; + MaxPromotionLevel: number; + MaxRank: number; + Skills: LightconeSkill; + Image: LightconeImage; + Stats: Record; +} + +export interface LightconeSkill { + ID: number; + Name: Record; + Desc: Record; + AbilityName: string; + Level: Record; +} + +export interface LightconeSkillLevel { + Level: number; + Param: number[]; + Bonus: LightconeSkillBonus[]; +} + +export interface LightconeSkillBonus { + PropertyType: string; + Value: number; +} + +export interface LightconeImage { + ThumbnailPath: string; + ImagePath: string; + IconPath: string; + FigureIconPath: string; +} + +export interface LightconeStatLevel { + Promotion: number; + PlayerLevelRequire: number; + WorldLevelRequire: number; + MaxLevel: number; + BaseHP: number; + BaseHPAdd: number; + BaseAttack: number; + BaseAttackAdd: number; + BaseDefence: number; + BaseDefenceAdd: number; } \ No newline at end of file diff --git a/src/types/metaData.ts b/src/types/metaData.ts index ef2d077..2148934 100644 --- a/src/types/metaData.ts +++ b/src/types/metaData.ts @@ -1,59 +1,59 @@ -export interface Metadata { - BaseType: Record; - DamageType: Record; - MainAffix: Record>; - SubAffix: Record>; - SkillConfig: Record; - Stage: Record; - HardLevelConfig: Record>; - EliteConfig: Record; -} - -export interface HardLevelData { - AttackRatio: number - DefenceRatio: number - HPRatio: number - SpeedRatio: number - StanceRatio: number -} - -export interface EliteData { - AttackRatio: number - DefenceRatio: number - HPRatio: number - SpeedRatio: number - StanceRatio: number -} - -export interface StageData { - ID: number; - Name: Record; -} - -export interface BaseTypeData { - Text: Record; - Icon: string; -} - -export interface DamageTypeData { - Name: Record; - Icon: string; -} - -export interface MainAffixData { - Property: string; - BaseValue: number; - LevelAdd: number; -} - -export interface SubAffixData { - Property: string; - BaseValue: number; - StepValue: number; - StepNum: number; -} - -export interface SkillMaxLevelData { - MaxLevel: number; - IndexSlot: number; -} +export interface Metadata { + BaseType: Record; + DamageType: Record; + MainAffix: Record>; + SubAffix: Record>; + SkillConfig: Record; + Stage: Record; + HardLevelConfig: Record>; + EliteConfig: Record; +} + +export interface HardLevelData { + AttackRatio: number + DefenceRatio: number + HPRatio: number + SpeedRatio: number + StanceRatio: number +} + +export interface EliteData { + AttackRatio: number + DefenceRatio: number + HPRatio: number + SpeedRatio: number + StanceRatio: number +} + +export interface StageData { + ID: number; + Name: Record; +} + +export interface BaseTypeData { + Text: Record; + Icon: string; +} + +export interface DamageTypeData { + Name: Record; + Icon: string; +} + +export interface MainAffixData { + Property: string; + BaseValue: number; + LevelAdd: number; +} + +export interface SubAffixData { + Property: string; + BaseValue: number; + StepValue: number; + StepNum: number; +} + +export interface SkillMaxLevelData { + MaxLevel: number; + IndexSlot: number; +} diff --git a/src/types/mics.ts b/src/types/mics.ts index 08ae3ad..d93d2e4 100644 --- a/src/types/mics.ts +++ b/src/types/mics.ts @@ -1,122 +1,122 @@ -export type AvatarDataStore = { - rank: number, - skills: Record -} -export type LightconeStore = { - level: number; - item_id: number; - rank: number; - promotion: number; -} - -export type SubAffixStore = { - sub_affix_id: number; - count: number; - step: number; -} -export type RelicStore = { - level: number; - relic_id: number; - relic_set_id: number; - main_affix_id: number; - sub_affixes: SubAffixStore[]; -} - -export type AvatarProfileStore = { - profile_name: string, - lightcone: LightconeStore | null, - relics: Record -} - -export type AvatarStore = { - owner_uid?: number; - avatar_id: number; - data: AvatarDataStore; - level: number; - promotion: number; - techniques: number[]; - sp_value: number; - sp_max: number; - can_change_sp: boolean; - enhanced: string; - profileSelect: number; - profileList: AvatarProfileStore[] -} - -export type MonsterStore = { - monster_id: number; - level: number; - amount: number; -} - -export type DynamicKeyStore = { - key: string; - value: number; -} - -export type BattleBuffStore = { - level: number; - id: number; - dynamic_key?: DynamicKeyStore; -} -export type MOCConfigStore = { - event_id: number; - challenge_id: number; - floor_side: string; - use_turbulence_buff: boolean; - use_cycle_count: boolean; - blessings: BattleBuffStore[] - cycle_count: number; - stage_id: number; - monsters: MonsterStore[][]; -} - -export type PFConfigStore = { - event_id: number; - challenge_id: number; - buff_id: number; - floor_side: string; - blessings: BattleBuffStore[] - cycle_count: number; - stage_id: number; - monsters: MonsterStore[][]; -} - -export type ASConfigStore = { - event_id: number; - challenge_id: number; - buff_id: number; - floor_side: string; - blessings: BattleBuffStore[] - cycle_count: number; - stage_id: number; - monsters: MonsterStore[][]; -} - -export type PEAKConfigStore = { - event_id: number; - challenge_id: number; - buff_id: number; - boss_mode: string; - blessings: BattleBuffStore[] - cycle_count: number; - stage_id: number; - monsters: MonsterStore[][]; -} - -export type CEConfigStore = { - blessings: BattleBuffStore[] - cycle_count: number; - stage_id: number; - monsters: MonsterStore[][]; -} - -export interface Mics { - avatars: Record, - battle_type: string; - moc_config: MOCConfigStore; - pf_config: PFConfigStore; - as_config: ASConfigStore; - ce_config: CEConfigStore; - peak_config: PEAKConfigStore +export type AvatarDataStore = { + rank: number, + skills: Record +} +export type LightconeStore = { + level: number; + item_id: number; + rank: number; + promotion: number; +} + +export type SubAffixStore = { + sub_affix_id: number; + count: number; + step: number; +} +export type RelicStore = { + level: number; + relic_id: number; + relic_set_id: number; + main_affix_id: number; + sub_affixes: SubAffixStore[]; +} + +export type AvatarProfileStore = { + profile_name: string, + lightcone: LightconeStore | null, + relics: Record +} + +export type AvatarStore = { + owner_uid?: number; + avatar_id: number; + data: AvatarDataStore; + level: number; + promotion: number; + techniques: number[]; + sp_value: number; + sp_max: number; + can_change_sp: boolean; + enhanced: string; + profileSelect: number; + profileList: AvatarProfileStore[] +} + +export type MonsterStore = { + monster_id: number; + level: number; + amount: number; +} + +export type DynamicKeyStore = { + key: string; + value: number; +} + +export type BattleBuffStore = { + level: number; + id: number; + dynamic_key?: DynamicKeyStore; +} +export type MOCConfigStore = { + event_id: number; + challenge_id: number; + floor_side: string; + use_turbulence_buff: boolean; + use_cycle_count: boolean; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type PFConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + floor_side: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type ASConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + floor_side: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type PEAKConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + boss_mode: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type CEConfigStore = { + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export interface Mics { + avatars: Record, + battle_type: string; + moc_config: MOCConfigStore; + pf_config: PFConfigStore; + as_config: ASConfigStore; + ce_config: CEConfigStore; + peak_config: PEAKConfigStore } \ No newline at end of file diff --git a/src/types/modelConfig.ts b/src/types/modelConfig.ts index ba7001d..3a34b28 100644 --- a/src/types/modelConfig.ts +++ b/src/types/modelConfig.ts @@ -1,7 +1,7 @@ -export type ModalConfig = { - id: string - title: string - isOpen: boolean - onClose: () => void - content: React.ReactNode +export type ModalConfig = { + id: string + title: string + isOpen: boolean + onClose: () => void + content: React.ReactNode } \ No newline at end of file diff --git a/src/types/monsterDetail.ts b/src/types/monsterDetail.ts index 5fe6a24..f22947a 100644 --- a/src/types/monsterDetail.ts +++ b/src/types/monsterDetail.ts @@ -1,45 +1,45 @@ - -export interface MonsterDetail { - ID: number; - Name: Record; - EliteGroup: number; - HardLevelGroup: number; - StanceWeakList: string[]; - Modify: MonsterModify; - Rank: string; - StanceCount: number; - StanceType: string; - Image: MonsterImage; - Base: MonsterBase; -} - - -export interface MonsterModify { - AttackModifyRatio: number; - DefenceModifyRatio: number; - HPModifyRatio: number; - SpeedModifyRatio: number; - StanceModifyRatio: number; - SpeedModifyValue: number; - StanceModifyValue: number; -} - -export interface MonsterImage { - IconPath: string; - RoundIconPath: string; - ImagePath: string; - ManikinImagePath: string; -} - -export interface MonsterBase { - AttackBase: number; - DefenceBase: number; - HPBase: number; - SpeedBase: number; - StanceBase: number; - CriticalDamageBase: number; - StatusResistanceBase: number; - SpeedModifyValue: number; - StanceModifyValue: number; - InitialDelayRatio: number; + +export interface MonsterDetail { + ID: number; + Name: Record; + EliteGroup: number; + HardLevelGroup: number; + StanceWeakList: string[]; + Modify: MonsterModify; + Rank: string; + StanceCount: number; + StanceType: string; + Image: MonsterImage; + Base: MonsterBase; +} + + +export interface MonsterModify { + AttackModifyRatio: number; + DefenceModifyRatio: number; + HPModifyRatio: number; + SpeedModifyRatio: number; + StanceModifyRatio: number; + SpeedModifyValue: number; + StanceModifyValue: number; +} + +export interface MonsterImage { + IconPath: string; + RoundIconPath: string; + ImagePath: string; + ManikinImagePath: string; +} + +export interface MonsterBase { + AttackBase: number; + DefenceBase: number; + HPBase: number; + SpeedBase: number; + StanceBase: number; + CriticalDamageBase: number; + StatusResistanceBase: number; + SpeedModifyValue: number; + StanceModifyValue: number; + InitialDelayRatio: number; } \ No newline at end of file diff --git a/src/types/peakDetail.ts b/src/types/peakDetail.ts index 5eb453c..468f08e 100644 --- a/src/types/peakDetail.ts +++ b/src/types/peakDetail.ts @@ -1,41 +1,41 @@ -import { BattleTarget, InfiniteWave, MazeBuff } from "./pfDetail"; - -export interface PeakGroupDetail { - ID: number; - ChallengeGroupType: string; - Name: Record; - PreLevel: PeakMazeConfig[]; - BossLevel: PeakMazeConfig | null; - BossConfig: PeakBossConfig | null; -} - -export interface PeakMazeConfig { - ID: number; - Name: Record; - BattleTarget: BattleTarget[]; - DamageType: string[]; - MazeBuff: MazeBuff[]; - TurnLimit: number; - EventList: PeakEvent[]; -} - -export interface PeakBossConfig { - ID: number; - Name: Record; - BuffList: MazeBuff[]; - BattleTarget: BattleTarget[]; - MazeBuff: MazeBuff[]; - TurnLimit: number; - EventList: PeakEvent[]; -} - -export interface PeakEvent { - ID: number; - Name: Record; - HardLevelGroup: number; - EliteGroup: number; - Level: number; - Release: boolean; - MonsterList: number[][]; - Infinite: InfiniteWave[] | null; +import { BattleTarget, InfiniteWave, MazeBuff } from "./pfDetail"; + +export interface PeakGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + PreLevel: PeakMazeConfig[]; + BossLevel: PeakMazeConfig | null; + BossConfig: PeakBossConfig | null; +} + +export interface PeakMazeConfig { + ID: number; + Name: Record; + BattleTarget: BattleTarget[]; + DamageType: string[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList: PeakEvent[]; +} + +export interface PeakBossConfig { + ID: number; + Name: Record; + BuffList: MazeBuff[]; + BattleTarget: BattleTarget[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList: PeakEvent[]; +} + +export interface PeakEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite: InfiniteWave[] | null; } \ No newline at end of file diff --git a/src/types/pfDetail.ts b/src/types/pfDetail.ts index 3866c8b..ed58259 100644 --- a/src/types/pfDetail.ts +++ b/src/types/pfDetail.ts @@ -1,75 +1,75 @@ -export interface PFGroupDetail { - ID: number; - ChallengeGroupType: string; - Name: Record; - BeginTime: string; - EndTime: string; - SubOption: MazeBuff[]; - Option: MazeBuff[]; - Level: PFLevel[]; - Tierce: PFTierceLevel | null; -} - -export interface MazeBuff { - ID: number; - Param: number[]; - Icon: string; - Name: Record; - Desc: Record; -} - -export interface PFTierceLevel { - ID: number; - PreChallenge: number; - Name: Record; - Target: StoryTarget[]; - DamageType: string[]; - TurnLimit: number; - EventList: PFEvent[]; -} - -export interface PFLevel { - Floor: number; - ID: number; - StageNum: number; - Name: Record; - Target: StoryTarget[]; - DamageType1: string[]; - DamageType2: string[]; - MazeBuff: MazeBuff[]; - EventList1: PFEvent[]; - EventList2: PFEvent[]; - TurnLimit: number; - BattleTarget: BattleTarget[]; - ClearScore: number; -} - -export interface StoryTarget { - ID: number; - Name: Record; - Param: number[]; -} - -export interface BattleTarget { - ID: number; - Param: number[]; - Name: Record; -} - -export interface PFEvent { - ID: number; - Name: Record; - HardLevelGroup: number; - EliteGroup: number; - Level: number; - Release: boolean; - MonsterList: number[][]; - Infinite?: InfiniteWave[] | null; -} - -export interface InfiniteWave { - ID: number; - MaxMonsterCount: number; - MonsterList: number[]; - EliteGroup: number; +export interface PFGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + BeginTime: string; + EndTime: string; + SubOption: MazeBuff[]; + Option: MazeBuff[]; + Level: PFLevel[]; + Tierce: PFTierceLevel | null; +} + +export interface MazeBuff { + ID: number; + Param: number[]; + Icon: string; + Name: Record; + Desc: Record; +} + +export interface PFTierceLevel { + ID: number; + PreChallenge: number; + Name: Record; + Target: StoryTarget[]; + DamageType: string[]; + TurnLimit: number; + EventList: PFEvent[]; +} + +export interface PFLevel { + Floor: number; + ID: number; + StageNum: number; + Name: Record; + Target: StoryTarget[]; + DamageType1: string[]; + DamageType2: string[]; + MazeBuff: MazeBuff[]; + EventList1: PFEvent[]; + EventList2: PFEvent[]; + TurnLimit: number; + BattleTarget: BattleTarget[]; + ClearScore: number; +} + +export interface StoryTarget { + ID: number; + Name: Record; + Param: number[]; +} + +export interface BattleTarget { + ID: number; + Param: number[]; + Name: Record; +} + +export interface PFEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite?: InfiniteWave[] | null; +} + +export interface InfiniteWave { + ID: number; + MaxMonsterCount: number; + MonsterList: number[]; + EliteGroup: number; } \ No newline at end of file diff --git a/src/types/psConnect.ts b/src/types/psConnect.ts index 7402f49..918795c 100644 --- a/src/types/psConnect.ts +++ b/src/types/psConnect.ts @@ -1,32 +1,32 @@ -import { ExtraData } from "./extraData"; -import { FreeSRJson } from "./srtools"; - -export enum PSConnectType { - FireflyGo = "FireflyGo", - RobinSR = "RobinSR", - Other = "Other", -} - -export interface ProxyPayload { - username?: string; - password?: string; - serverUrl: string; - data?: FreeSRJson; -} - -export interface ProxyResponse { - error?: string; - message?: string; - data?: unknown; -} - -export interface PSResponse { - status: number; - message: string; - extra_data?: ExtraData -} - -export interface ActionResult { - success: boolean; - message: string; +import { ExtraData } from "./extraData"; +import { FreeSRJson } from "./srtools"; + +export enum PSConnectType { + FireflyGo = "FireflyGo", + RobinSR = "RobinSR", + Other = "Other", +} + +export interface ProxyPayload { + username?: string; + password?: string; + serverUrl: string; + data?: FreeSRJson; +} + +export interface ProxyResponse { + error?: string; + message?: string; + data?: unknown; +} + +export interface PSResponse { + status: number; + message: string; + extra_data?: ExtraData +} + +export interface ActionResult { + success: boolean; + message: string; } \ No newline at end of file diff --git a/src/types/relicDetail.ts b/src/types/relicDetail.ts index f760840..f4ef34f 100644 --- a/src/types/relicDetail.ts +++ b/src/types/relicDetail.ts @@ -1,59 +1,59 @@ - -export interface RelicSetDetail { - ID: number; - Name: Record; - Image: RelicSetImage; - Release: boolean; - ReleaseVersion: string; - IsPlanarSuit: boolean; - DisplayItemID: number; - DisplayItemIDRarity4: number; - SkillList: number[]; - Skills: Record; - Parts: Record; -} - -export interface RelicSetImage { - SetIconPath: string; - SetIconFigurePath: string; -} - -export interface RelicSkill { - RequireNum: number; - Desc: Record; - AbilityName: string; - Param: number[]; - Bonus: RelicSkillBonus[]; -} - -export interface RelicSkillBonus { - PropertyType: string; - Value: number; -} - -export interface RelicPart { - Type: string; - Image: RelicPartImage; - Name: Record; - Desc: Record; - Data: Record | null; -} - -export interface RelicPartImage { - IconPath: string; - ItemFigureIconPath: string; -} - -export interface RelicPartData { - ID: number; - SetID: number; - Type: string; - Rarity: string; - MainAffixGroup: number; - SubAffixGroup: number; - MaxLevel: number; - ExpType: number; - ExpProvide: number; - CoinCost: number; - Mode: string; + +export interface RelicSetDetail { + ID: number; + Name: Record; + Image: RelicSetImage; + Release: boolean; + ReleaseVersion: string; + IsPlanarSuit: boolean; + DisplayItemID: number; + DisplayItemIDRarity4: number; + SkillList: number[]; + Skills: Record; + Parts: Record; +} + +export interface RelicSetImage { + SetIconPath: string; + SetIconFigurePath: string; +} + +export interface RelicSkill { + RequireNum: number; + Desc: Record; + AbilityName: string; + Param: number[]; + Bonus: RelicSkillBonus[]; +} + +export interface RelicSkillBonus { + PropertyType: string; + Value: number; +} + +export interface RelicPart { + Type: string; + Image: RelicPartImage; + Name: Record; + Desc: Record; + Data: Record | null; +} + +export interface RelicPartImage { + IconPath: string; + ItemFigureIconPath: string; +} + +export interface RelicPartData { + ID: number; + SetID: number; + Type: string; + Rarity: string; + MainAffixGroup: number; + SubAffixGroup: number; + MaxLevel: number; + ExpType: number; + ExpProvide: number; + CoinCost: number; + Mode: string; } \ No newline at end of file diff --git a/src/types/showcase.ts b/src/types/showcase.ts index c3b0837..61d9b57 100644 --- a/src/types/showcase.ts +++ b/src/types/showcase.ts @@ -1,26 +1,26 @@ -export type RelicShowcaseType = { - img: string; - mainAffix: { - property: string; - level: number; - valueAffix: string; - detail: { - name: string; - icon: string; - unit: string; - baseStat: string; - }; - }; - subAffix: { - property: string; - valueAffix: string; - detail: { - name: string; - icon: string; - unit: string; - baseStat: string; - }; - step: number; - count: number; - }[] +export type RelicShowcaseType = { + img: string; + mainAffix: { + property: string; + level: number; + valueAffix: string; + detail: { + name: string; + icon: string; + unit: string; + baseStat: string; + }; + }; + subAffix: { + property: string; + valueAffix: string; + detail: { + name: string; + icon: string; + unit: string; + baseStat: string; + }; + step: number; + count: number; + }[] } \ No newline at end of file diff --git a/src/types/srtools.ts b/src/types/srtools.ts index 1db5ecb..c93e6c1 100644 --- a/src/types/srtools.ts +++ b/src/types/srtools.ts @@ -1,82 +1,82 @@ -export interface SubAffix { - sub_affix_id: number; - count: number; - step: number; -} - -export interface RelicJson { - level: number; - relic_id: number; - relic_set_id: number; - main_affix_id: number; - sub_affixes: SubAffix[]; - internal_uid: number; - equip_avatar: number; -} - -export interface LightconeJson { - level: number; - item_id: number; - equip_avatar: number; - rank: number; - promotion: number; - internal_uid: number; -} -export interface AvatarData { - rank: number, - skills: Record - skills_by_anchor_type?: Record -} - -export interface AvatarJson { - owner_uid?: number; - avatar_id: number; - data: AvatarData; - level: number; - promotion: number; - techniques: number[]; - sp_value: number; - sp_max: number; - enhanced_id?: number; -} -export interface MonsterJson { - monster_id: number; - level: number; - amount: number; -} - -export interface DynamicKeyJson { - key: string; - value: number; -} - -export interface BattleBuffJson { - level: number; - id: number; - dynamic_key?: DynamicKeyJson; -} - -export interface BattleConfigJson { - battle_type: string; - blessings: BattleBuffJson[] - custom_stats: SubAffix[]; - cycle_count: number; - stage_id: number; - path_resonance_id: number; - monsters: MonsterJson[][]; -} -type LoadoutJson = { - name: string - avatar_id: number - relic_list: string[] -} -export interface FreeSRJson { - key?: string; - lightcones: LightconeJson[]; - relics: RelicJson[]; - avatars: Record; - battle_config: BattleConfigJson; - loadout?: LoadoutJson[]; -} - - +export interface SubAffix { + sub_affix_id: number; + count: number; + step: number; +} + +export interface RelicJson { + level: number; + relic_id: number; + relic_set_id: number; + main_affix_id: number; + sub_affixes: SubAffix[]; + internal_uid: number; + equip_avatar: number; +} + +export interface LightconeJson { + level: number; + item_id: number; + equip_avatar: number; + rank: number; + promotion: number; + internal_uid: number; +} +export interface AvatarData { + rank: number, + skills: Record + skills_by_anchor_type?: Record +} + +export interface AvatarJson { + owner_uid?: number; + avatar_id: number; + data: AvatarData; + level: number; + promotion: number; + techniques: number[]; + sp_value: number; + sp_max: number; + enhanced_id?: number; +} +export interface MonsterJson { + monster_id: number; + level: number; + amount: number; +} + +export interface DynamicKeyJson { + key: string; + value: number; +} + +export interface BattleBuffJson { + level: number; + id: number; + dynamic_key?: DynamicKeyJson; +} + +export interface BattleConfigJson { + battle_type: string; + blessings: BattleBuffJson[] + custom_stats: SubAffix[]; + cycle_count: number; + stage_id: number; + path_resonance_id: number; + monsters: MonsterJson[][]; +} +type LoadoutJson = { + name: string + avatar_id: number + relic_list: string[] +} +export interface FreeSRJson { + key?: string; + lightcones: LightconeJson[]; + relics: RelicJson[]; + avatars: Record; + battle_config: BattleConfigJson; + loadout?: LoadoutJson[]; +} + + diff --git a/src/zod/index.ts b/src/zod/index.ts index 87e0fea..c0cf202 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -1,10 +1,10 @@ -export * from "./srtools.zod" -export * from "./filter.zod" -export * from "./mics.zod" -export * from "./enka.zod" -export * from "./card.zod" +export * from "./srtools.zod" +export * from "./filter.zod" +export * from "./mics.zod" +export * from "./enka.zod" +export * from "./card.zod" export * from "./extraData.zod" export * from "./showcase.zod" export * from "./persistedStore.zod" export * from "./srtools.zod" - + diff --git a/tsconfig.json b/tsconfig.json index 2169551..a86c232 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,4 +38,4 @@ "exclude": [ "node_modules" ] -} +}