change icon stats, new plana, redesign some ui, fix JSON managment
Deploy GaC-Srtool to GitHub Pages / deploy (push) Canceled after 0s
Deploy GaC-Srtool to GitHub Pages / deploy (push) Canceled after 0s
This commit is contained in:
1 parent
083848ee6e
commit
45d8414e60
19 files changed
+911
-222
No files matched your search
@@ -1,10 +1,10 @@
|
||||
import { COMMANDS, usage, inspectCommand, completeCommand, setCommandLanguage, describeCommand } from './command-assistant.mjs';
|
||||
import { AVATAR_METADATA, getAvatarInfo } from './avatars-data.js';
|
||||
import { LIGHTCONE_METADATA, RELIC_SET_METADATA, getLightconeInfo, getRelicSetInfo } from './equipment-data.js?v=2.23';
|
||||
import { initializeItemCatalog } from './item-catalog.js?v=2';
|
||||
import { initializeUnstuck } from './unstuck.js?v=1';
|
||||
import { AVATAR_METADATA, getAvatarInfo } from './avatars-data.js?v=2.21';
|
||||
import { LIGHTCONE_METADATA, RELIC_SET_METADATA, getLightconeInfo, getRelicSetInfo } from './equipment-data.js?v=2.27';
|
||||
import { initializeItemCatalog } from './item-catalog.js?v=5';
|
||||
import { initializeUnstuck } from './unstuck.js?v=4';
|
||||
import { PRYDWEN_BUILD_GUIDES } from './build-guides-data.js';
|
||||
import { SUPPORTED_LANGUAGES, TRANSLATIONS, getCurrentLanguage, setLanguage, t, translateElement, translatePath } from './i18n.js?v=2.17';
|
||||
import { SUPPORTED_LANGUAGES, TRANSLATIONS, getCurrentLanguage, setLanguage, t, translateElement, translatePath } from './i18n.js?v=2.21';
|
||||
import {
|
||||
getAvatarIconUrl,
|
||||
getLightconeArtUrl,
|
||||
@@ -480,6 +480,7 @@ function setConnectionStatus(connected, extraText = '') {
|
||||
if (connected !== toolSessionConnected) syncToolSession(connected);
|
||||
state.isConnected = connected;
|
||||
if (connectionChanged && state.activeTab === 'tab-monster-settings') loadMonsterSettings();
|
||||
updateCameraSaveStatus();
|
||||
if (connected) {
|
||||
el.statusDot.classList.add('connected');
|
||||
el.connectionStatusText.textContent = extraText || t('online');
|
||||
@@ -604,7 +605,7 @@ function areCapturedSubAffixesValid(setId, slot, mainAffixId, subAffixes) {
|
||||
|
||||
// Official in-game property icons (same set used by the overview tab)
|
||||
function getRelicImage(setId, slot = 5) {
|
||||
return RELIC_SET_METADATA[setId]?.images?.[slot] || RELIC_SET_METADATA[setId]?.images?.[5] || `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/relic/${setId}.png`;
|
||||
return RELIC_SET_METADATA[setId]?.setIcon || RELIC_SET_METADATA[setId]?.images?.[slot] || RELIC_SET_METADATA[setId]?.images?.[5] || `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/relic/${setId}.png`;
|
||||
}
|
||||
function getLightconeIconSrc(id) {
|
||||
return LIGHTCONE_METADATA[id]?.images?.thumbnail || `https://static.nanoka.cc/assets/hsr/lightconemediumicon/${id}.webp`;
|
||||
@@ -1015,7 +1016,26 @@ function applyTranslations() {
|
||||
if (icons) {
|
||||
elem.textContent = val.replace(/^[🎯⚡🔨🛡️\s]+/u, '');
|
||||
icons.slice().reverse().forEach(icon => elem.insertAdjacentHTML('afterbegin', getStatIconHtml(icon, 16, '')));
|
||||
} else elem.textContent = val;
|
||||
} else {
|
||||
elem.textContent = val;
|
||||
const equipmentIcons = {
|
||||
tabEquipment: './assets/lightcones/22009-icon.webp',
|
||||
modalAddLcTitle: './assets/lightcones/22009-icon.webp',
|
||||
eqLcTitle: './assets/lightcones/22009-icon.webp',
|
||||
overviewEquippedLc: './assets/lightcones/22009-icon.webp',
|
||||
modalAddRelicTitle: './assets/relic-slots/1.png',
|
||||
eqRelicTitle: './assets/relic-slots/1.png',
|
||||
overviewEquippedRelics: './assets/relic-slots/1.png',
|
||||
activeSetEffects: './assets/relic-slots/1.png'
|
||||
};
|
||||
if (equipmentIcons[key]) {
|
||||
const icon = document.createElement('img');
|
||||
icon.src = equipmentIcons[key];
|
||||
icon.alt = '';
|
||||
icon.className = 'game-inline-icon';
|
||||
elem.prepend(icon, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1071,6 +1091,11 @@ function applyTranslations() {
|
||||
updateAvatarAssignCurrent();
|
||||
renderAvatarAssignGrid();
|
||||
}
|
||||
|
||||
// Keep the camera panel (mode cards, distance readout, save state) translated.
|
||||
renderCameraModeOptions();
|
||||
syncCameraUi();
|
||||
|
||||
loadEquipment();
|
||||
if (analystState.currentReport) {
|
||||
renderDamageAnalyst();
|
||||
@@ -2928,6 +2953,7 @@ const SIGNATURE_LIGHTCONES = {
|
||||
// 1: Flat HP, 2: Flat ATK, 3: Flat DEF, 4: HP%, 5: ATK%, 6: DEF%
|
||||
// 7: SPD, 8: CRIT Rate%, 9: CRIT DMG%, 10: EHR%, 11: Effect RES%, 12: Break Effect%
|
||||
const CHARACTER_OPTIMAL_BUILDS = {
|
||||
1511: { cavern: 133, planar: 329, body: 4, feet: 4, sphere: 9, rope: 4, substats: [8, 9, 7, 5] }, // Aeon ★ Aha
|
||||
1308: { cavern: 117, planar: 313, body: 4, feet: 2, sphere: 7, rope: 4, substats: [8, 9, 7, 5] }, // Acheron
|
||||
1310: { cavern: 119, planar: 315, body: 2, feet: 4, sphere: 2, rope: 1, substats: [12, 7, 5, 11] }, // Firefly
|
||||
1220: { cavern: 120, planar: 314, body: 4, feet: 4, sphere: 8, rope: 4, substats: [8, 9, 7, 5] }, // Feixiao
|
||||
@@ -3369,17 +3395,17 @@ function populateModalChangeLcDropdown(avatarPath) {
|
||||
|
||||
// Slot full definitions for relics
|
||||
const SLOT_NAMES_FULL = {
|
||||
1: { nameKey: 'relicSlotHead', shortKey: 'slotHeadShort', icon: "🧢", defaultStat: "HP (Flat)" },
|
||||
2: { nameKey: 'relicSlotHands', shortKey: 'slotHandsShort', icon: "🧤", defaultStat: "ATK (Flat)" },
|
||||
3: { nameKey: 'relicSlotBody', shortKey: 'slotBodyShort', icon: "🦺", defaultStat: "CRIT Rate / DMG" },
|
||||
4: { nameKey: 'relicSlotFeet', shortKey: 'slotFeetShort', icon: "👢", defaultStat: "SPD / ATK%" },
|
||||
5: { nameKey: 'relicSlotSphere', shortKey: 'slotSphereShort', icon: "🔮", defaultStat: "Elemental DMG%" },
|
||||
6: { nameKey: 'relicSlotRope', shortKey: 'slotRopeShort', icon: "🔗", defaultStat: "ERR / Break / ATK%" }
|
||||
1: { nameKey: 'relicSlotHead', shortKey: 'slotHeadShort', icon: './assets/relic-slots/1.png', defaultStat: "HP (Flat)" },
|
||||
2: { nameKey: 'relicSlotHands', shortKey: 'slotHandsShort', icon: './assets/relic-slots/2.png', defaultStat: "ATK (Flat)" },
|
||||
3: { nameKey: 'relicSlotBody', shortKey: 'slotBodyShort', icon: './assets/relic-slots/3.png', defaultStat: "CRIT Rate / DMG" },
|
||||
4: { nameKey: 'relicSlotFeet', shortKey: 'slotFeetShort', icon: './assets/relic-slots/4.png', defaultStat: "SPD / ATK%" },
|
||||
5: { nameKey: 'relicSlotSphere', shortKey: 'slotSphereShort', icon: './assets/relic-slots/5.png', defaultStat: "Elemental DMG%" },
|
||||
6: { nameKey: 'relicSlotRope', shortKey: 'slotRopeShort', icon: './assets/relic-slots/6.png', defaultStat: "ERR / Break / ATK%" }
|
||||
};
|
||||
|
||||
function getSlotInfo(slot) {
|
||||
const info = SLOT_NAMES_FULL[slot];
|
||||
if (!info) return { name: `${t('slot')} ${slot}`, short: `${t('slot')} ${slot}`, icon: '🛡️', defaultStat: '' };
|
||||
if (!info) return { name: `${t('slot')} ${slot}`, short: `${t('slot')} ${slot}`, icon: '', defaultStat: '' };
|
||||
const name = t(info.nameKey);
|
||||
return { ...info, name, short: t(info.shortKey) };
|
||||
}
|
||||
@@ -3963,7 +3989,7 @@ function renderModalRelicsTab(avatarId) {
|
||||
<div class="relic-slot-icon-wrap">
|
||||
<img src="${getRelicImage(setId, slot)}"
|
||||
onerror="this.src='./assets/logo.png'" alt="Relic" style="width: 100%; height: 100%; object-fit: contain;">
|
||||
<span class="relic-slot-badge">${slotInfo.icon} ${slotInfo.short}</span>
|
||||
<span class="relic-slot-badge" aria-label="${t('relicSlotBadgeLabel', { slot: slotInfo.short })}"><img class="relic-slot-type-icon" src="${slotInfo.icon}" alt=""> ${slotInfo.short}</span>
|
||||
</div>
|
||||
<div class="relic-slot-content">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; padding-right: 1.5rem;">
|
||||
@@ -3995,8 +4021,8 @@ function renderModalRelicsTab(avatarId) {
|
||||
box.className = 'relic-slot-box empty';
|
||||
box.innerHTML = `
|
||||
<div class="relic-slot-icon-wrap" style="opacity: 0.4;">
|
||||
<span style="font-size: 1.5rem;">${slotInfo.icon}</span>
|
||||
<span class="relic-slot-badge">${slotInfo.name.split(' ')[0]}</span>
|
||||
<img class="relic-slot-empty-icon" src="${slotInfo.icon}" alt="">
|
||||
<span class="relic-slot-badge" aria-label="${t('relicSlotBadgeLabel', { slot: slotInfo.short })}">${slotInfo.short}</span>
|
||||
</div>
|
||||
<div class="relic-slot-content">
|
||||
<div class="relic-slot-title" style="color: var(--text-muted);">${slotInfo.name}</div>
|
||||
@@ -4020,9 +4046,9 @@ function renderModalRelicsTab(avatarId) {
|
||||
const cnt = setCountMap[sId];
|
||||
const sInfo = getRelicSetInfo(sId);
|
||||
if (cnt >= 4) {
|
||||
activeSets.push(`🌟 <strong>${sInfo.name} (4-Piece Set)</strong>: ${sInfo.desc4 || t('relicSet4Fallback')}`);
|
||||
activeSets.push(`<img class="game-inline-icon" src="${getRelicImage(sId)}" alt=""> <strong>${sInfo.name} (${t('relicSetPieces', { count: 4 })})</strong>: ${sInfo.desc4 || t('relicSet4Fallback')}`);
|
||||
} else if (cnt >= 2) {
|
||||
activeSets.push(`✨ <strong>${sInfo.name} (2-Piece Set)</strong>: ${sInfo.desc2 || t('relicSet2Fallback')}`);
|
||||
activeSets.push(`<img class="game-inline-icon" src="${getRelicImage(sId)}" alt=""> <strong>${sInfo.name} (${t('relicSetPieces', { count: 2 })})</strong>: ${sInfo.desc2 || t('relicSet2Fallback')}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5164,17 +5190,17 @@ async function renderModalSkillsTab(avatarId) {
|
||||
// Traces
|
||||
if (a2Node) {
|
||||
if (el.traceIconA2) el.traceIconA2.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/${a2Node.icon}`;
|
||||
if (el.traceNameA2) el.traceNameA2.textContent = `🌟 ${a2Node.name || t('traceFallbackA2')}`;
|
||||
if (el.traceNameA2) el.traceNameA2.textContent = a2Node.name || t('traceFallbackA2');
|
||||
if (el.traceDescA2) el.traceDescA2.innerHTML = formatSkillDesc(a2Node.desc, a2Node.params, 1);
|
||||
}
|
||||
if (a4Node) {
|
||||
if (el.traceIconA4) el.traceIconA4.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/${a4Node.icon}`;
|
||||
if (el.traceNameA4) el.traceNameA4.textContent = `🌟 ${a4Node.name || t('traceFallbackA4')}`;
|
||||
if (el.traceNameA4) el.traceNameA4.textContent = a4Node.name || t('traceFallbackA4');
|
||||
if (el.traceDescA4) el.traceDescA4.innerHTML = formatSkillDesc(a4Node.desc, a4Node.params, 1);
|
||||
}
|
||||
if (a6Node) {
|
||||
if (el.traceIconA6) el.traceIconA6.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/${a6Node.icon}`;
|
||||
if (el.traceNameA6) el.traceNameA6.textContent = `🌟 ${a6Node.name || t('traceFallbackA6')}`;
|
||||
if (el.traceNameA6) el.traceNameA6.textContent = a6Node.name || t('traceFallbackA6');
|
||||
if (el.traceDescA6) el.traceDescA6.innerHTML = formatSkillDesc(a6Node.desc, a6Node.params, 1);
|
||||
}
|
||||
}
|
||||
@@ -5657,7 +5683,7 @@ function renderShowcaseCard(avatarId) {
|
||||
}
|
||||
|
||||
if (activeSetBadges.length === 0) {
|
||||
const rec = RECOMMENDED_RELIC_BUILDS[canonPath] || RECOMMENDED_RELIC_BUILDS.Preservation;
|
||||
const rec = getOptimalBuildForAvatar(aid);
|
||||
activeSetBadges.push({ name: getRelicSetInfo(rec.cavern).name, count: 4 });
|
||||
activeSetBadges.push({ name: getRelicSetInfo(rec.planar).name, count: 2 });
|
||||
}
|
||||
@@ -5711,7 +5737,7 @@ function renderShowcaseCard(avatarId) {
|
||||
let relicsHtml = '';
|
||||
for (let slot = 1; slot <= 6; slot++) {
|
||||
const equipped = avatarRelics.find(r => (r.relic_id % 10 === slot));
|
||||
const rec = RECOMMENDED_RELIC_BUILDS[canonPath] || RECOMMENDED_RELIC_BUILDS.Preservation;
|
||||
const rec = getOptimalBuildForAvatar(aid);
|
||||
const setId = equipped ? Math.floor((equipped.relic_id % 10000) / 10) : (slot <= 4 ? rec.cavern : rec.planar);
|
||||
const rInfo = getRelicSetInfo(setId);
|
||||
const slotInfo = slotNames[slot];
|
||||
@@ -8182,10 +8208,7 @@ async function loadPersistentSettings() {
|
||||
}
|
||||
if (!data) return;
|
||||
|
||||
const cameraMode = document.getElementById('camera-mode');
|
||||
const cameraDistance = document.getElementById('camera-distance');
|
||||
if (cameraMode) cameraMode.value = data.camera_settings?.mode || 'freecam';
|
||||
if (cameraDistance) cameraDistance.value = data.camera_settings?.distance ?? 2;
|
||||
applyCameraSettings(data.camera_settings);
|
||||
|
||||
if (data.main_character && el.persistentMcSelect) {
|
||||
el.persistentMcSelect.value = data.main_character;
|
||||
@@ -8202,6 +8225,177 @@ async function loadPersistentSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Freecam / FaceCam settings panel.
|
||||
* The native <select> and range input stay in the DOM as the single source of
|
||||
* truth for the save payload, while the visible controls are custom cards.
|
||||
*/
|
||||
const cameraSettingsCache = { busy: false, dirty: false, error: false };
|
||||
|
||||
function getCameraModeSelect() {
|
||||
return document.getElementById('camera-mode');
|
||||
}
|
||||
|
||||
function getCameraDistanceInput() {
|
||||
return document.getElementById('camera-distance');
|
||||
}
|
||||
|
||||
function clampCameraDistance(value) {
|
||||
const numeric = Number(value);
|
||||
const safe = Number.isFinite(numeric) ? numeric : 2;
|
||||
return Math.min(5, Math.max(0.5, Math.round(safe * 10) / 10));
|
||||
}
|
||||
|
||||
function renderCameraModeOptions() {
|
||||
const container = document.getElementById('camera-mode-options');
|
||||
const select = getCameraModeSelect();
|
||||
if (!container || !select) return;
|
||||
|
||||
const choices = [
|
||||
{ value: 'freecam', icon: '🎥', title: t('cameraModeFreecam'), detail: t('cameraModeFreecamDesc') },
|
||||
{ value: 'facecam', icon: '🙂', title: t('cameraModeFacecam'), detail: t('cameraModeFacecamDesc') }
|
||||
];
|
||||
|
||||
container.replaceChildren();
|
||||
for (const choice of choices) {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'camera-mode-option';
|
||||
button.dataset.value = choice.value;
|
||||
button.setAttribute('aria-pressed', 'false');
|
||||
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'camera-mode-icon';
|
||||
icon.setAttribute('aria-hidden', 'true');
|
||||
icon.textContent = choice.icon;
|
||||
|
||||
const text = document.createElement('span');
|
||||
text.className = 'camera-mode-text';
|
||||
const title = document.createElement('span');
|
||||
title.className = 'camera-mode-title';
|
||||
title.textContent = choice.title;
|
||||
const detail = document.createElement('span');
|
||||
detail.className = 'camera-mode-detail';
|
||||
detail.textContent = choice.detail;
|
||||
text.append(title, detail);
|
||||
|
||||
const check = document.createElement('span');
|
||||
check.className = 'camera-mode-check';
|
||||
check.setAttribute('aria-hidden', 'true');
|
||||
check.textContent = '✓';
|
||||
|
||||
button.append(icon, text, check);
|
||||
container.append(button);
|
||||
}
|
||||
|
||||
container.setAttribute('aria-label', t('cameraMode'));
|
||||
container.onclick = event => {
|
||||
const button = event.target.closest('.camera-mode-option');
|
||||
if (!button) return;
|
||||
select.value = button.dataset.value;
|
||||
syncCameraUi({ dirty: true });
|
||||
};
|
||||
|
||||
syncCameraModeSelection();
|
||||
}
|
||||
|
||||
function syncCameraModeSelection() {
|
||||
const container = document.getElementById('camera-mode-options');
|
||||
const select = getCameraModeSelect();
|
||||
if (!container || !select) return;
|
||||
for (const button of container.querySelectorAll('.camera-mode-option')) {
|
||||
const selected = button.dataset.value === select.value;
|
||||
button.classList.toggle('is-selected', selected);
|
||||
button.setAttribute('aria-pressed', String(selected));
|
||||
}
|
||||
}
|
||||
|
||||
function readCameraForm() {
|
||||
const select = getCameraModeSelect();
|
||||
const input = getCameraDistanceInput();
|
||||
return {
|
||||
mode: select?.value === 'facecam' ? 'facecam' : 'freecam',
|
||||
distance: clampCameraDistance(input?.value)
|
||||
};
|
||||
}
|
||||
|
||||
function setCameraSaveStatus(stateName, messageKey) {
|
||||
const wrapper = document.getElementById('camera-save-state');
|
||||
const text = document.getElementById('camera-save-state-text');
|
||||
if (wrapper) wrapper.dataset.state = stateName;
|
||||
if (text) text.textContent = t(messageKey);
|
||||
}
|
||||
|
||||
function updateCameraSaveStatus() {
|
||||
const button = document.getElementById('btn-save-camera');
|
||||
const online = state.isConnected && state.serverMode === 'gac';
|
||||
|
||||
if (cameraSettingsCache.busy) {
|
||||
setCameraSaveStatus('saving', 'cameraStatusSaving');
|
||||
if (button) button.disabled = true;
|
||||
return;
|
||||
}
|
||||
if (!online) {
|
||||
setCameraSaveStatus('offline', 'cameraStatusOffline');
|
||||
if (button) button.disabled = true;
|
||||
return;
|
||||
}
|
||||
if (cameraSettingsCache.error) setCameraSaveStatus('error', 'cameraStatusError');
|
||||
else if (cameraSettingsCache.dirty) setCameraSaveStatus('unsaved', 'cameraStatusUnsaved');
|
||||
else if (state.persistentData?.camera_settings) setCameraSaveStatus('saved', 'cameraStatusSaved');
|
||||
else setCameraSaveStatus('idle', 'cameraStatusIdle');
|
||||
if (button) button.disabled = false;
|
||||
}
|
||||
|
||||
function cameraFormDiffersFromSaved() {
|
||||
const saved = state.persistentData?.camera_settings;
|
||||
if (!saved) return true;
|
||||
const current = readCameraForm();
|
||||
const savedMode = saved.mode === 'facecam' ? 'facecam' : 'freecam';
|
||||
return current.mode !== savedMode
|
||||
|| Math.abs(current.distance - clampCameraDistance(saved.distance)) > 0.001;
|
||||
}
|
||||
|
||||
function syncCameraUi({ dirty = false } = {}) {
|
||||
const distanceInput = getCameraDistanceInput();
|
||||
const distanceValue = document.getElementById('camera-distance-value');
|
||||
const distanceBlock = document.getElementById('camera-distance-block');
|
||||
const distanceNote = document.getElementById('camera-distance-note');
|
||||
if (!distanceInput) return;
|
||||
|
||||
const settings = readCameraForm();
|
||||
if (dirty) {
|
||||
cameraSettingsCache.dirty = cameraFormDiffersFromSaved();
|
||||
cameraSettingsCache.error = false;
|
||||
}
|
||||
|
||||
if (distanceValue) distanceValue.textContent = t('cameraDistanceValue', { value: settings.distance.toFixed(1) });
|
||||
if (distanceBlock) distanceBlock.classList.toggle('is-inactive', settings.mode !== 'facecam');
|
||||
if (distanceNote) {
|
||||
distanceNote.textContent = t(settings.mode === 'facecam' ? 'cameraDistanceHint' : 'cameraDistanceHintFreecam');
|
||||
}
|
||||
|
||||
syncCameraModeSelection();
|
||||
updateCameraSaveStatus();
|
||||
}
|
||||
|
||||
function applyCameraSettings(settings) {
|
||||
const select = getCameraModeSelect();
|
||||
const distanceInput = getCameraDistanceInput();
|
||||
if (select) select.value = settings?.mode === 'facecam' ? 'facecam' : 'freecam';
|
||||
if (distanceInput) distanceInput.value = String(clampCameraDistance(settings?.distance ?? 2));
|
||||
cameraSettingsCache.dirty = false;
|
||||
cameraSettingsCache.error = false;
|
||||
syncCameraUi();
|
||||
}
|
||||
|
||||
function initCameraSettings() {
|
||||
if (!getCameraModeSelect()) return;
|
||||
renderCameraModeOptions();
|
||||
getCameraDistanceInput()?.addEventListener('input', () => syncCameraUi({ dirty: true }));
|
||||
syncCameraUi();
|
||||
}
|
||||
|
||||
// Render Ex-Rank Cards
|
||||
function renderExRanks(exList) {
|
||||
if (!el.exrankList) return;
|
||||
@@ -8273,19 +8467,30 @@ async function toggleSingleExRank(avatarId, unlock) {
|
||||
}
|
||||
|
||||
// Load and Switch JSON Tab Data
|
||||
let jsonLoadGeneration = 0;
|
||||
let jsonLoadPending = false;
|
||||
async function loadJsonData(mode) {
|
||||
const generation = ++jsonLoadGeneration;
|
||||
jsonLoadPending = true;
|
||||
state.currentJsonMode = mode;
|
||||
// Never leave the previous file visible under the newly selected filename.
|
||||
el.jsonEditor.value = '';
|
||||
el.jsonEditor.readOnly = true;
|
||||
if (el.btnJsonSave) el.btnJsonSave.disabled = true;
|
||||
try {
|
||||
if (mode === 'freesr') {
|
||||
el.btnSelectFreesr.className = 'btn btn-sm btn-primary active';
|
||||
if (el.btnSelectPersistent) el.btnSelectPersistent.className = 'btn btn-sm btn-secondary';
|
||||
if (el.btnSelectDatagame) el.btnSelectDatagame.className = 'btn btn-sm btn-secondary';
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusLoading', { file: 'freesr-data.json' });
|
||||
try {
|
||||
if (state.fullFreesrData) {
|
||||
if (state.fullFreesrData && !state.isConnected) {
|
||||
el.jsonEditor.value = JSON.stringify(state.fullFreesrData, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusMemory', { file: 'freesr-data.json' });
|
||||
} else if (state.isConnected) {
|
||||
const res = await apiCall('/api/freesr');
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
if (!res.data || typeof res.data !== 'object' || Array.isArray(res.data)) throw new Error('invalid freesr response');
|
||||
state.fullFreesrData = res.data;
|
||||
saveFreesrCache(res.data);
|
||||
el.jsonEditor.value = JSON.stringify(res.data, null, 2);
|
||||
@@ -8298,13 +8503,11 @@ async function loadJsonData(mode) {
|
||||
el.jsonEditor.value = JSON.stringify(parsed, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusBrowserCache', { file: 'freesr-data.json' });
|
||||
} else {
|
||||
el.jsonStatusInfo.textContent = t('jsonOfflineNoCache');
|
||||
if (!el.jsonEditor.value) {
|
||||
el.jsonEditor.value = JSON.stringify({ avatars: {}, lightcones: [], relics: [], battle_config: {} }, null, 2);
|
||||
}
|
||||
el.jsonStatusInfo.textContent = t('jsonDataUnavailable', { file: 'freesr-data.json' });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
const cached = localStorage.getItem('gac_srtool_freesr_cache');
|
||||
if (cached) {
|
||||
try {
|
||||
@@ -8314,10 +8517,7 @@ async function loadJsonData(mode) {
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusBrowserCache', { file: 'freesr-data.json' });
|
||||
} catch (e) {}
|
||||
} else {
|
||||
el.jsonStatusInfo.textContent = t('jsonOfflinePaste');
|
||||
if (!el.jsonEditor.value) {
|
||||
el.jsonEditor.value = JSON.stringify({ avatars: {}, lightcones: [], relics: [], battle_config: {} }, null, 2);
|
||||
}
|
||||
el.jsonStatusInfo.textContent = t('jsonDataUnavailable', { file: 'freesr-data.json' });
|
||||
}
|
||||
}
|
||||
} else if (mode === 'datagame') {
|
||||
@@ -8326,11 +8526,12 @@ async function loadJsonData(mode) {
|
||||
if (el.btnSelectDatagame) el.btnSelectDatagame.className = 'btn btn-sm btn-primary active';
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusLoading', { file: 'data-in-game.json' });
|
||||
try {
|
||||
if (state.dataGameData) {
|
||||
if (state.dataGameData && !state.isConnected) {
|
||||
el.jsonEditor.value = JSON.stringify(state.dataGameData, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusMemory', { file: 'data-in-game.json' });
|
||||
} else if (state.isConnected) {
|
||||
const res = await apiCall('/api/data-in-game').catch(() => null);
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
if (res && (res.data || res.lineups)) {
|
||||
const val = res.data || res;
|
||||
state.dataGameData = val;
|
||||
@@ -8354,6 +8555,7 @@ async function loadJsonData(mode) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
const cached = localStorage.getItem('gac_srtool_datagame_cache');
|
||||
if (cached) {
|
||||
try {
|
||||
@@ -8374,14 +8576,17 @@ async function loadJsonData(mode) {
|
||||
if (el.btnSelectDatagame) el.btnSelectDatagame.className = 'btn btn-sm btn-secondary';
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusLoading', { file: 'persistent' });
|
||||
try {
|
||||
if (state.persistentData) {
|
||||
if (state.persistentData && !state.isConnected) {
|
||||
el.jsonEditor.value = JSON.stringify(state.persistentData, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusMemory', { file: 'persistent' });
|
||||
} else if (state.isConnected) {
|
||||
const res = await apiCall('/api/persistent');
|
||||
state.persistentData = res.data;
|
||||
savePersistentCache(res.data);
|
||||
el.jsonEditor.value = JSON.stringify(res.data, null, 2);
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
const persistent = res.persistent ?? res.data;
|
||||
if (!persistent || typeof persistent !== 'object' || Array.isArray(persistent)) throw new Error('invalid persistent response');
|
||||
state.persistentData = persistent;
|
||||
savePersistentCache(persistent);
|
||||
el.jsonEditor.value = JSON.stringify(persistent, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusServer', { file: 'persistent' });
|
||||
} else {
|
||||
const cached = localStorage.getItem('gac_srtool_persistent_cache');
|
||||
@@ -8391,13 +8596,11 @@ async function loadJsonData(mode) {
|
||||
el.jsonEditor.value = JSON.stringify(parsed, null, 2);
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusBrowserCache', { file: 'persistent' });
|
||||
} else {
|
||||
el.jsonStatusInfo.textContent = t('jsonOfflineNoCache');
|
||||
if (!el.jsonEditor.value) {
|
||||
el.jsonEditor.value = JSON.stringify({ lineups: DEFAULT_LINEUP, main_character: "FemaleHarmony", march_type: "MarchHunt" }, null, 2);
|
||||
}
|
||||
el.jsonStatusInfo.textContent = t('jsonDataUnavailable', { file: 'persistent' });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (generation !== jsonLoadGeneration) return;
|
||||
const cached = localStorage.getItem('gac_srtool_persistent_cache');
|
||||
if (cached) {
|
||||
try {
|
||||
@@ -8407,13 +8610,17 @@ async function loadJsonData(mode) {
|
||||
el.jsonStatusInfo.textContent = t('jsonStatusBrowserCache', { file: 'persistent' });
|
||||
} catch (e) {}
|
||||
} else {
|
||||
el.jsonStatusInfo.textContent = t('jsonOfflinePaste');
|
||||
if (!el.jsonEditor.value) {
|
||||
el.jsonEditor.value = JSON.stringify({ lineups: DEFAULT_LINEUP, main_character: "FemaleHarmony", march_type: "MarchHunt" }, null, 2);
|
||||
}
|
||||
el.jsonStatusInfo.textContent = t('jsonDataUnavailable', { file: 'persistent' });
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (generation === jsonLoadGeneration) {
|
||||
jsonLoadPending = false;
|
||||
el.jsonEditor.readOnly = false;
|
||||
if (el.btnJsonSave) el.btnJsonSave.disabled = !el.jsonEditor.value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save JSON Tab Data
|
||||
@@ -9015,18 +9222,46 @@ function setupEvents() {
|
||||
// Persistent Toggles
|
||||
document.getElementById('btn-save-camera')?.addEventListener('click', async () => {
|
||||
const button = document.getElementById('btn-save-camera');
|
||||
const mode = document.getElementById('camera-mode').value;
|
||||
const distance = Number(document.getElementById('camera-distance').value);
|
||||
const select = getCameraModeSelect();
|
||||
const distanceInput = getCameraDistanceInput();
|
||||
if (!button || !select || !distanceInput || cameraSettingsCache.busy) return;
|
||||
|
||||
const mode = select.value === 'facecam' ? 'facecam' : 'freecam';
|
||||
const distance = Number(distanceInput.value);
|
||||
if (!Number.isFinite(distance) || distance < 0.5 || distance > 5) {
|
||||
showToast(t('cameraDistanceError'), 'error'); return;
|
||||
showToast(t('cameraDistanceError'), 'error');
|
||||
return;
|
||||
}
|
||||
button.disabled = true;
|
||||
if (!state.isConnected || state.serverMode !== 'gac') {
|
||||
showToast(t('cameraSaveOfflineError'), 'error');
|
||||
updateCameraSaveStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
cameraSettingsCache.busy = true;
|
||||
updateCameraSaveStatus();
|
||||
button.textContent = t('cameraSaving');
|
||||
try {
|
||||
const saved = await apiCall('/api/camera', 'POST', { mode, distance });
|
||||
if (state.persistentData) state.persistentData.camera_settings = saved;
|
||||
const payload = saved?.camera_settings || saved || {};
|
||||
const settings = {
|
||||
mode: payload.mode === 'facecam' ? 'facecam' : (payload.mode === 'freecam' ? 'freecam' : mode),
|
||||
distance: clampCameraDistance(payload.distance ?? distance)
|
||||
};
|
||||
state.persistentData = state.persistentData || {};
|
||||
state.persistentData.camera_settings = settings;
|
||||
savePersistentCache(state.persistentData);
|
||||
applyCameraSettings(settings);
|
||||
showToast(t('cameraSaved'), 'success');
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
finally { button.disabled = false; }
|
||||
} catch (err) {
|
||||
cameraSettingsCache.dirty = true;
|
||||
cameraSettingsCache.error = true;
|
||||
showToast(t('toastErrorMessage', { message: err.message }), 'error');
|
||||
} finally {
|
||||
cameraSettingsCache.busy = false;
|
||||
button.textContent = t('cameraSave');
|
||||
updateCameraSaveStatus();
|
||||
}
|
||||
});
|
||||
document.getElementById('btn-save-aha-help')?.addEventListener('click', async () => {
|
||||
const select = document.getElementById('aha-help-level');
|
||||
@@ -9192,6 +9427,9 @@ function setupEvents() {
|
||||
});
|
||||
|
||||
el.btnJsonSave.addEventListener('click', saveJsonData);
|
||||
el.jsonEditor.addEventListener('input', () => {
|
||||
if (!jsonLoadPending) el.btnJsonSave.disabled = !el.jsonEditor.value.trim();
|
||||
});
|
||||
|
||||
el.btnJsonDownload.addEventListener('click', () => {
|
||||
const text = el.jsonEditor.value;
|
||||
@@ -9211,6 +9449,7 @@ function setupEvents() {
|
||||
try {
|
||||
const parsed = JSON.parse(event.target.result);
|
||||
el.jsonEditor.value = JSON.stringify(parsed, null, 2);
|
||||
if (!jsonLoadPending) el.btnJsonSave.disabled = false;
|
||||
|
||||
let recognized = false;
|
||||
let isFreesr = false;
|
||||
@@ -9247,7 +9486,7 @@ function setupEvents() {
|
||||
}
|
||||
|
||||
// If persistent or has lineups
|
||||
if (parsed.main_character || parsed.march_type || (parsed.lineups && !parsed.avatars)) {
|
||||
if (!isFreesr && (parsed.main_character || parsed.march_type || parsed.lineups)) {
|
||||
recognized = true;
|
||||
isPersistent = true;
|
||||
state.persistentData = parsed;
|
||||
@@ -10082,9 +10321,9 @@ function renderDamageAnalyst() {
|
||||
|
||||
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap; padding-top: 0.65rem; border-top: 1px solid rgba(255,255,255,0.06); font-size: 0.75rem; color: var(--text-muted);">
|
||||
<div>⏱️ ${t('analystTurnsTaken')}: <strong style="color: #fff;">${hasAvatarStats ? a.total_turns || 0 : '—'}</strong> ${hasAvatarStats && a.extra_turns ? `(+${a.extra_turns})` : ''}</div>
|
||||
<div>🛡️ ${t('analystShield')}: <strong style="color: #38bdf8;">${hasAvatarStats ? formatNumber(a.total_shield) : '—'}</strong></div>
|
||||
<div>💚 ${t('analystHealing')}: <strong style="color: #34d399;">${hasAvatarStats ? formatNumber(a.total_heal) : '—'}</strong></div>
|
||||
<div>💔 ${t('analystDamageTaken')}: <strong style="color: #f87171;">${hasAvatarStats ? formatNumber(a.total_damage_taken) : '—'}</strong></div>
|
||||
<div>${getStatIconHtml('IconDefence', 16)} ${t('analystShield')}: <strong style="color: #38bdf8;">${hasAvatarStats ? formatNumber(a.total_shield) : '—'}</strong></div>
|
||||
<div>${getStatIconHtml('IconHealRatio', 16)} ${t('analystHealing')}: <strong style="color: #34d399;">${hasAvatarStats ? formatNumber(a.total_heal) : '—'}</strong></div>
|
||||
<div>${getStatIconHtml('IconMaxHP', 16)} ${t('analystDamageTaken')}: <strong style="color: #f87171;">${hasAvatarStats ? formatNumber(a.total_damage_taken) : '—'}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -10376,6 +10615,7 @@ async function init() {
|
||||
setupTabs();
|
||||
setupModalSubtabs();
|
||||
setupEvents();
|
||||
initCameraSettings();
|
||||
setupInventoryEvents();
|
||||
setupDamageAnalystEvents();
|
||||
setupGMConsoleEvents();
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getCurrentLanguage, translateElement, translatePath } from "./i18n.js?v=2.17";
|
||||
import { getCurrentLanguage, translateElement, translatePath } from "./i18n.js?v=2.21";
|
||||
|
||||
export const AVATAR_METADATA = {
|
||||
"1001": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCurrentLanguage, translatePath } from "./i18n.js?v=2.17";
|
||||
import { getCurrentLanguage, translatePath } from "./i18n.js?v=2.21";
|
||||
|
||||
export const LIGHTCONE_METADATA = {
|
||||
"20000": {
|
||||
@@ -4325,8 +4325,8 @@ export const RELIC_SET_METADATA = {
|
||||
|
||||
// Set data is backed by the captured Client relic and affix tables documented in the server resources.
|
||||
Object.assign(RELIC_SET_METADATA, {
|
||||
329: { clientCaptureRequired: true, names: { en: "God's Moment of Joy" }, desc2: "Increases the wearer's SPD by 6%. When the wearer's Elation reaches 40% or higher, the wearer's action advances by 40% immediately when entering combat.", images: { 5: './assets/relics/329-5.webp', 6: './assets/relics/329-6.webp' } },
|
||||
330: { clientCaptureRequired: true, names: { en: 'Subspace ASC Center' }, desc2: "Increases the wearer's SPD by 6%. When the wearer's SPD reaches 145 or higher, increases all allies' Break Effect by 18% after entering combat. This effect cannot stack.", images: { 5: './assets/relics/330-5.webp', 6: './assets/relics/330-6.webp' } }
|
||||
329: { clientCaptureRequired: true, names: { en: "God's Moment of Joy" }, desc2: "Increases the wearer's SPD by 6%. When the wearer's Elation reaches 40% or higher, the wearer's action advances by 40% immediately when entering combat.", setIcon: './assets/relics/329-set.png', images: { 5: './assets/relics/329-5.webp', 6: './assets/relics/329-6.webp' } },
|
||||
330: { clientCaptureRequired: true, names: { en: 'Subspace ASC Center' }, desc2: "Increases the wearer's SPD by 6%. When the wearer's SPD reaches 145 or higher, increases all allies' Break Effect by 18% after entering combat. This effect cannot stack.", setIcon: './assets/relics/330-set.png', images: { 5: './assets/relics/330-5.webp', 6: './assets/relics/330-6.webp' } }
|
||||
});
|
||||
|
||||
export function getLightconeInfo(lcId, langOverride = null) {
|
||||
|
||||
+180
-144
@@ -129,8 +129,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "เพิ่มกรวยแสง 5 ดาวประจำตัวละครทั้งหมดเข้าคลังแล้ว",
|
||||
"toastTeleportedTrain": "วาร์ปตัวละครกลับสู่ Astral Express Parlor Car เรียบร้อยแล้ว",
|
||||
"toastLineupCleared": "ล้างตัวละครในทีมเรียบร้อยแล้ว",
|
||||
"modalAddLcTitle": "🗡️ เพิ่มกรวยแสงลง FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ เพิ่มรีลิกส์ลง FreeSR",
|
||||
"modalAddLcTitle": "เพิ่มกรวยแสงลง FreeSR",
|
||||
"modalAddRelicTitle": "เพิ่มรีลิกส์ลง FreeSR",
|
||||
"lblSelectLc": "เลือกกรวยแสง",
|
||||
"lblSelectRelicSet": "เลือกเซ็ตรีลิกส์",
|
||||
"lblRelicSlot": "ชิ้นส่วน (Slot)",
|
||||
@@ -152,7 +152,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "เชื่อมต่อไม่สำเร็จ",
|
||||
"tabDashboard": "📊 แดชบอร์ด & จัดทีม",
|
||||
"tabAvatars": "👥 ตัวละคร",
|
||||
"tabEquipment": "🗡️ กรวยแสง & รีลิกส์",
|
||||
"tabEquipment": "กรวยแสง & รีลิกส์",
|
||||
"tabInventory": "🎒 ไอเทมในกระเป๋า",
|
||||
"inventoryTitle": "🎒 จัดการจำนวนไอเทม",
|
||||
"inventoryDescription": "ค้นหาด้วย Item ID แล้วแก้จำนวนหลายรายการก่อนบันทึกครั้งเดียว รายการมาจาก catalog ของ Client ที่เซิร์ฟเวอร์ใช้ ช่องว่างหมายถึงใช้ค่าเริ่มต้นของเซิร์ฟเวอร์",
|
||||
@@ -211,10 +211,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "ยืนยันปรับตัวละครทั้งหมดให้เป็นเลเวล 80 และปลด E6 หรือไม่?",
|
||||
"maxAllSuccess": "ปรับเต็มทุกตัวละครสำเร็จแล้ว!",
|
||||
"avatarRankSuccess": "ปรับตัวละคร {id} เป็น E{rank} สำเร็จ",
|
||||
"eqLcTitle": "🗡️ กรวยแสงในคลัง",
|
||||
"eqLcTitle": "กรวยแสงในคลัง",
|
||||
"eqLcSubtitle": "รายการกรวยแสงทั้งหมดจาก freesr-data.json",
|
||||
"eqLcLoading": "กำลังโหลดข้อมูลกรวยแสง...",
|
||||
"eqRelicTitle": "🛡️ รีลิกส์ในคลัง",
|
||||
"eqRelicTitle": "รีลิกส์ในคลัง",
|
||||
"eqRelicSubtitle": "รายการรีลิกส์ทั้งหมดจาก freesr-data.json",
|
||||
"eqRelicLoading": "กำลังโหลดข้อมูลรีลิกส์...",
|
||||
"itemCount": "{n} ชิ้น",
|
||||
@@ -296,9 +296,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "พรสวรรค์",
|
||||
"modalTracesTitle": "ร่องรอยและเทคนิค",
|
||||
"modalTraceTechnique": "⚡ เทคนิค — เปิดใช้งานก่อนเริ่มการต่อสู้",
|
||||
"traceA2": "🌟 ร่องรอยพิเศษ A2",
|
||||
"traceA4": "🌟 ร่องรอยพิเศษ A4",
|
||||
"traceA6": "🌟 ร่องรอยพิเศษ A6",
|
||||
"traceA2": "ร่องรอยพิเศษ A2",
|
||||
"traceA4": "ร่องรอยพิเศษ A4",
|
||||
"traceA6": "ร่องรอยพิเศษ A6",
|
||||
"traceStatNodes": "💎 โหนดสเตตัสเสริมทั้งหมด (10 โหนด)",
|
||||
"lblLevelRange": "ระดับเลเวล (1-80)",
|
||||
"lblPromotionRange": "การเลื่อนขั้น (0-6)",
|
||||
@@ -400,13 +400,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "แนะนำ",
|
||||
"overviewStatsTitle": "⚔️ ค่าพลังการต่อสู้ (Combat Stats)",
|
||||
"overviewStatsNote": "รวมโบนัสกรวยแสงและรีลิกส์",
|
||||
"overviewEquippedLc": "🗡️ กรวยแสงที่สวมใส่",
|
||||
"overviewEquippedRelics": "🛡️ รีลิกส์และเซ็ต",
|
||||
"overviewEquippedLc": "กรวยแสงที่สวมใส่",
|
||||
"overviewEquippedRelics": "รีลิกส์และเซ็ต",
|
||||
"modalRelicsTitle": "🛡️ สวมใส่รีลิกส์ (Relics 6 ชิ้น)",
|
||||
"modalRelicsSubtitle": "คลิกแต่ละช่องเพื่อเลือกเปลี่ยน หรือสวมใส่เซ็ตแนะนำ",
|
||||
"btnAutoEquipRelics": "⚡ สวมใส่เซ็ตแนะนำครบชุด",
|
||||
"btnClearAllRelics": "🧹 ถอดทั้งหมด",
|
||||
"activeSetEffects": "✨ เอฟเฟกต์เซ็ตที่เปิดใช้งาน",
|
||||
"activeSetEffects": "เอฟเฟกต์เซ็ตที่เปิดใช้งาน",
|
||||
"modalColLevel": "เลเวล",
|
||||
"lblPromotion": "ขั้นเลื่อนขั้น",
|
||||
"slotPickerTitle": "🛡️ เลือกสวมใส่รีลิกส์",
|
||||
@@ -625,8 +625,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Added all 5★ signature light cones to inventory",
|
||||
"toastTeleportedTrain": "Teleported to Astral Express Parlor Car",
|
||||
"toastLineupCleared": "Lineup cleared successfully",
|
||||
"modalAddLcTitle": "🗡️ Add Light Cone to FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Add Relic to FreeSR",
|
||||
"modalAddLcTitle": "Add Light Cone to FreeSR",
|
||||
"modalAddRelicTitle": "Add Relic to FreeSR",
|
||||
"lblSelectLc": "Select Light Cone",
|
||||
"lblSelectRelicSet": "Select Relic Set",
|
||||
"lblRelicSlot": "Relic Slot",
|
||||
@@ -648,7 +648,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Connection Failed",
|
||||
"tabDashboard": "📊 Dashboard & Party",
|
||||
"tabAvatars": "👥 Characters",
|
||||
"tabEquipment": "🗡️ Lightcones & Relics",
|
||||
"tabEquipment": "Lightcones & Relics",
|
||||
"tabInventory": "🎒 Inventory",
|
||||
"inventoryTitle": "🎒 Item quantities",
|
||||
"inventoryDescription": "Search by item ID, edit several quantities, then save once. Items come from the Client catalog used by the server. Leave a field empty to use the server default.",
|
||||
@@ -707,10 +707,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Upgrade all characters to Level 80 and unlock E6?",
|
||||
"maxAllSuccess": "All characters maximized successfully!",
|
||||
"avatarRankSuccess": "Updated character {id} to E{rank}",
|
||||
"eqLcTitle": "🗡️ Lightcones in Inventory",
|
||||
"eqLcTitle": "Lightcones in Inventory",
|
||||
"eqLcSubtitle": "All lightcones from freesr-data.json",
|
||||
"eqLcLoading": "Loading lightcone data...",
|
||||
"eqRelicTitle": "🛡️ Relics in Inventory",
|
||||
"eqRelicTitle": "Relics in Inventory",
|
||||
"eqRelicSubtitle": "All relics from freesr-data.json",
|
||||
"eqRelicLoading": "Loading relic data...",
|
||||
"itemCount": "{n} items",
|
||||
@@ -792,9 +792,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talent",
|
||||
"modalTracesTitle": "Traces & Technique",
|
||||
"modalTraceTechnique": "⚡ Technique — Pre-battle active",
|
||||
"traceA2": "🌟 Bonus Trace A2",
|
||||
"traceA4": "🌟 Bonus Trace A4",
|
||||
"traceA6": "🌟 Bonus Trace A6",
|
||||
"traceA2": "Bonus Trace A2",
|
||||
"traceA4": "Bonus Trace A4",
|
||||
"traceA6": "Bonus Trace A6",
|
||||
"traceStatNodes": "💎 All Stat Bonus Nodes (10 nodes)",
|
||||
"lblLevelRange": "Level (1-80)",
|
||||
"lblPromotionRange": "Ascension / Promotion (0-6)",
|
||||
@@ -896,13 +896,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Recommended",
|
||||
"overviewStatsTitle": "⚔️ Combat Stats",
|
||||
"overviewStatsNote": "Includes Lightcone & Relic bonuses",
|
||||
"overviewEquippedLc": "🗡️ Equipped Lightcone",
|
||||
"overviewEquippedRelics": "🛡️ Relics & Set Bonuses",
|
||||
"overviewEquippedLc": "Equipped Lightcone",
|
||||
"overviewEquippedRelics": "Relics & Set Bonuses",
|
||||
"modalRelicsTitle": "🛡️ Equip Relics (6 Slots)",
|
||||
"modalRelicsSubtitle": "Click each slot to change or equip recommended sets",
|
||||
"btnAutoEquipRelics": "⚡ Equip Recommended Sets",
|
||||
"btnClearAllRelics": "🧹 Unequip All",
|
||||
"activeSetEffects": "✨ Active Set Bonuses",
|
||||
"activeSetEffects": "Active Set Bonuses",
|
||||
"modalColLevel": "Level",
|
||||
"lblPromotion": "Ascension Rank",
|
||||
"slotPickerTitle": "🛡️ Select Relic to Equip",
|
||||
@@ -1005,8 +1005,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "已添加全部五星光锥至背包",
|
||||
"toastTeleportedTrain": "已传送至星穹列车观景车厢",
|
||||
"toastLineupCleared": "已清空编队角色",
|
||||
"modalAddLcTitle": "🗡️ 添加光锥至 FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ 添加遗器至 FreeSR",
|
||||
"modalAddLcTitle": "添加光锥至 FreeSR",
|
||||
"modalAddRelicTitle": "添加遗器至 FreeSR",
|
||||
"lblSelectLc": "选择光锥",
|
||||
"lblSelectRelicSet": "选择遗器套装",
|
||||
"lblRelicSlot": "遗器部位",
|
||||
@@ -1028,7 +1028,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "连接失败",
|
||||
"tabDashboard": "📊 仪表盘与配队",
|
||||
"tabAvatars": "👥 角色列表",
|
||||
"tabEquipment": "🗡️ 光锥与遗器",
|
||||
"tabEquipment": "光锥与遗器",
|
||||
"tabPersistent": "💾 GaCy 存档管理",
|
||||
"tabExrank": "⚡ Ex-Rank 特殊技能",
|
||||
"tabJson": "📝 JSON 文件管理",
|
||||
@@ -1064,10 +1064,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "确定将全部角色升至80级并解锁满命E6吗?",
|
||||
"maxAllSuccess": "全角色养成满级成功!",
|
||||
"avatarRankSuccess": "已调整角色 {id} 为星魂 {rank}",
|
||||
"eqLcTitle": "🗡️ 背包中的光锥",
|
||||
"eqLcTitle": "背包中的光锥",
|
||||
"eqLcSubtitle": "来自 freesr-data.json 的所有光锥列表",
|
||||
"eqLcLoading": "正在加载光锥数据...",
|
||||
"eqRelicTitle": "🛡️ 背包中的遗器",
|
||||
"eqRelicTitle": "背包中的遗器",
|
||||
"eqRelicSubtitle": "来自 freesr-data.json 的所有遗器列表",
|
||||
"eqRelicLoading": "正在加载遗器数据...",
|
||||
"itemCount": "{n} 件",
|
||||
@@ -1125,9 +1125,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "天赋",
|
||||
"modalTracesTitle": "行迹与秘技",
|
||||
"modalTraceTechnique": "⚡ 秘技 — 进入战斗前生效",
|
||||
"traceA2": "🌟 额外能力 A2",
|
||||
"traceA4": "🌟 额外能力 A4",
|
||||
"traceA6": "🌟 额外能力 A6",
|
||||
"traceA2": "额外能力 A2",
|
||||
"traceA4": "额外能力 A4",
|
||||
"traceA6": "额外能力 A6",
|
||||
"traceStatNodes": "💎 全部属性加成节点 (10个)",
|
||||
"lblLevelRange": "角色等级 (1-80)",
|
||||
"lblPromotionRange": "晋阶等级 (0-6)",
|
||||
@@ -1266,13 +1266,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "推荐",
|
||||
"overviewStatsTitle": "⚔️ 战斗面板 (Combat Stats)",
|
||||
"overviewStatsNote": "已计入光锥与遗器属性加成",
|
||||
"overviewEquippedLc": "🗡️ 当前装备光锥",
|
||||
"overviewEquippedRelics": "🛡️ 遗器套装效果",
|
||||
"overviewEquippedLc": "当前装备光锥",
|
||||
"overviewEquippedRelics": "遗器套装效果",
|
||||
"modalRelicsTitle": "🛡️ 遗器配装 (6 部位)",
|
||||
"modalRelicsSubtitle": "点击单个部位更换,或一键穿戴推荐套装",
|
||||
"btnAutoEquipRelics": "⚡ 一键穿戴推荐套装",
|
||||
"btnClearAllRelics": "🧹 一键卸下全部",
|
||||
"activeSetEffects": "✨ 当前生效的套装效果",
|
||||
"activeSetEffects": "当前生效的套装效果",
|
||||
"modalColLevel": "等级",
|
||||
"lblPromotion": "突破阶段",
|
||||
"slotPickerTitle": "🛡️ 选择要装备的遗器",
|
||||
@@ -1355,8 +1355,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "已新增全部五星光錐至背包",
|
||||
"toastTeleportedTrain": "已傳送至星穹列車觀景車廂",
|
||||
"toastLineupCleared": "已清空隊伍角色",
|
||||
"modalAddLcTitle": "🗡️ 新增光錐至 FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ 新增遺器至 FreeSR",
|
||||
"modalAddLcTitle": "新增光錐至 FreeSR",
|
||||
"modalAddRelicTitle": "新增遺器至 FreeSR",
|
||||
"lblSelectLc": "選擇光錐",
|
||||
"lblSelectRelicSet": "選擇遺器套裝",
|
||||
"lblRelicSlot": "遺器部位",
|
||||
@@ -1378,7 +1378,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "連線失敗",
|
||||
"tabDashboard": "📊 儀表板與配隊",
|
||||
"tabAvatars": "👥 角色列表",
|
||||
"tabEquipment": "🗡️ 光錐與遺器",
|
||||
"tabEquipment": "光錐與遺器",
|
||||
"tabPersistent": "💾 GaCy 存檔管理",
|
||||
"tabExrank": "⚡ Ex-Rank 特殊技能",
|
||||
"tabJson": "📝 JSON 檔案管理",
|
||||
@@ -1414,10 +1414,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "確定將全部角色升至80級並解鎖滿魂E6嗎?",
|
||||
"maxAllSuccess": "全角色養成滿級成功!",
|
||||
"avatarRankSuccess": "已調整角色 {id} 為星魂 {rank}",
|
||||
"eqLcTitle": "🗡️ 背包中的光錐",
|
||||
"eqLcTitle": "背包中的光錐",
|
||||
"eqLcSubtitle": "來自 freesr-data.json 的所有光錐列表",
|
||||
"eqLcLoading": "正在載入光錐資料...",
|
||||
"eqRelicTitle": "🛡️ 背包中的遺器",
|
||||
"eqRelicTitle": "背包中的遺器",
|
||||
"eqRelicSubtitle": "來自 freesr-data.json 的所有遺器列表",
|
||||
"eqRelicLoading": "正在載入遺器資料...",
|
||||
"itemCount": "{n} 件",
|
||||
@@ -1475,9 +1475,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "天賦",
|
||||
"modalTracesTitle": "行跡與秘技",
|
||||
"modalTraceTechnique": "⚡ 秘技 — 進入戰鬥前生效",
|
||||
"traceA2": "🌟 額外能力 A2",
|
||||
"traceA4": "🌟 額外能力 A4",
|
||||
"traceA6": "🌟 額外能力 A6",
|
||||
"traceA2": "額外能力 A2",
|
||||
"traceA4": "額外能力 A4",
|
||||
"traceA6": "額外能力 A6",
|
||||
"traceStatNodes": "💎 全部屬性加成節點 (10個)",
|
||||
"lblLevelRange": "角色等級 (1-80)",
|
||||
"lblPromotionRange": "晉階等級 (0-6)",
|
||||
@@ -1616,13 +1616,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "推薦",
|
||||
"overviewStatsTitle": "⚔️ 戰鬥面板 (Combat Stats)",
|
||||
"overviewStatsNote": "已計入光錐與遺器屬性加成",
|
||||
"overviewEquippedLc": "🗡️ 當前裝備光錐",
|
||||
"overviewEquippedRelics": "🛡️ 遺器套裝效果",
|
||||
"overviewEquippedLc": "當前裝備光錐",
|
||||
"overviewEquippedRelics": "遺器套裝效果",
|
||||
"modalRelicsTitle": "🛡️ 遺器配裝 (6 部位)",
|
||||
"modalRelicsSubtitle": "點擊單一部位更換,或一鍵穿戴推薦套裝",
|
||||
"btnAutoEquipRelics": "⚡ 一鍵穿戴推薦套裝",
|
||||
"btnClearAllRelics": "🧹 一鍵卸下全部",
|
||||
"activeSetEffects": "✨ 當前生效的套裝效果",
|
||||
"activeSetEffects": "當前生效的套裝效果",
|
||||
"modalColLevel": "等級",
|
||||
"lblPromotion": "突破階段",
|
||||
"slotPickerTitle": "🛡️ 選擇要裝備的遺器",
|
||||
@@ -1705,8 +1705,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "全★5光円錐を追加しました",
|
||||
"toastTeleportedTrain": "星穹列車の客車へテレポートしました",
|
||||
"toastLineupCleared": "編成をクリアしました",
|
||||
"modalAddLcTitle": "🗡️ FreeSRに光円錐を追加",
|
||||
"modalAddRelicTitle": "🛡️ FreeSRに遺物を追加",
|
||||
"modalAddLcTitle": "FreeSRに光円錐を追加",
|
||||
"modalAddRelicTitle": "FreeSRに遺物を追加",
|
||||
"lblSelectLc": "光円錐を選択",
|
||||
"lblSelectRelicSet": "遺物セットを選択",
|
||||
"lblRelicSlot": "部位",
|
||||
@@ -1728,7 +1728,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "接続失敗",
|
||||
"tabDashboard": "📊 ダッシュボード・編成",
|
||||
"tabAvatars": "👥 キャラクター",
|
||||
"tabEquipment": "🗡️ 光円錐と遺物",
|
||||
"tabEquipment": "光円錐と遺物",
|
||||
"tabPersistent": "💾 GaCy セーブ管理",
|
||||
"tabExrank": "⚡ Ex-Rank 特殊スキル",
|
||||
"tabJson": "📝 JSON 管理",
|
||||
@@ -1764,10 +1764,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "全キャラクターをレベル80・星魂E6まで育成しますか?",
|
||||
"maxAllSuccess": "全キャラクターの最大化が完了しました!",
|
||||
"avatarRankSuccess": "キャラクター {id} を星魂 {rank} に変更しました",
|
||||
"eqLcTitle": "🗡️ 所持光円錐一覧",
|
||||
"eqLcTitle": "所持光円錐一覧",
|
||||
"eqLcSubtitle": "freesr-data.json からの全光円錐一覧",
|
||||
"eqLcLoading": "光円錐データを読み込み中...",
|
||||
"eqRelicTitle": "🛡️ 所持遺物一覧",
|
||||
"eqRelicTitle": "所持遺物一覧",
|
||||
"eqRelicSubtitle": "freesr-data.json からの全遺物一覧",
|
||||
"eqRelicLoading": "遺物データを読み込み中...",
|
||||
"itemCount": "{n} 個",
|
||||
@@ -1825,9 +1825,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "天賦",
|
||||
"modalTracesTitle": "軌跡と秘技",
|
||||
"modalTraceTechnique": "⚡ 秘技 — 戦闘突入前に有効化",
|
||||
"traceA2": "🌟 追加能力 A2",
|
||||
"traceA4": "🌟 追加能力 A4",
|
||||
"traceA6": "🌟 追加能力 A6",
|
||||
"traceA2": "追加能力 A2",
|
||||
"traceA4": "追加能力 A4",
|
||||
"traceA6": "追加能力 A6",
|
||||
"traceStatNodes": "💎 全ステータスボーナス (10個)",
|
||||
"lblLevelRange": "レベル (1-80)",
|
||||
"lblPromotionRange": "昇格段階 (0-6)",
|
||||
@@ -1966,13 +1966,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "推奨",
|
||||
"overviewStatsTitle": "⚔️ 戦闘ステータス (Combat Stats)",
|
||||
"overviewStatsNote": "光円錐および遺物のボーナスを含みます",
|
||||
"overviewEquippedLc": "🗡️ 装備中の光円錐",
|
||||
"overviewEquippedRelics": "🛡️ 遺物&セット効果",
|
||||
"overviewEquippedLc": "装備中の光円錐",
|
||||
"overviewEquippedRelics": "遺物&セット効果",
|
||||
"modalRelicsTitle": "🛡️ 遺物装備 (全6スロット)",
|
||||
"modalRelicsSubtitle": "各スロットをクリックして変更、または推奨セットを一括装備",
|
||||
"btnAutoEquipRelics": "⚡ 推奨セットを一括装備",
|
||||
"btnClearAllRelics": "🧹 すべて外す",
|
||||
"activeSetEffects": "✨ 発動中のセット効果",
|
||||
"activeSetEffects": "発動中のセット効果",
|
||||
"modalColLevel": "レベル",
|
||||
"lblPromotion": "昇格段階",
|
||||
"slotPickerTitle": "🛡️ 装備する遺物を選択",
|
||||
@@ -2055,8 +2055,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "모든 5★ 광추를 추가했습니다",
|
||||
"toastTeleportedTrain": "은하열차 객차로 워프했습니다",
|
||||
"toastLineupCleared": "파티를 초기화했습니다",
|
||||
"modalAddLcTitle": "🗡️ FreeSR에 광추 추가",
|
||||
"modalAddRelicTitle": "🛡️ FreeSR에 유물 추가",
|
||||
"modalAddLcTitle": "FreeSR에 광추 추가",
|
||||
"modalAddRelicTitle": "FreeSR에 유물 추가",
|
||||
"lblSelectLc": "광추 선택",
|
||||
"lblSelectRelicSet": "유물 세트 선택",
|
||||
"lblRelicSlot": "부위",
|
||||
@@ -2078,7 +2078,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "연결 실패",
|
||||
"tabDashboard": "📊 대시보드 및 파티",
|
||||
"tabAvatars": "👥 캐릭터",
|
||||
"tabEquipment": "🗡️ 광추 및 유물",
|
||||
"tabEquipment": "광추 및 유물",
|
||||
"tabPersistent": "💾 GaCy 세이브 관리",
|
||||
"tabExrank": "⚡ Ex-Rank 특수 스킬",
|
||||
"tabJson": "📝 JSON 관리",
|
||||
@@ -2114,10 +2114,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "모든 캐릭터를 레벨 80 및 성흔 6으로 육성하시겠습니까?",
|
||||
"maxAllSuccess": "모든 캐릭터 최대치 달성 완료!",
|
||||
"avatarRankSuccess": "캐릭터 {id} 성흔을 {rank}(으)로 변경했습니다",
|
||||
"eqLcTitle": "🗡️ 보유 광추 목록",
|
||||
"eqLcTitle": "보유 광추 목록",
|
||||
"eqLcSubtitle": "freesr-data.json의 모든 광추 목록",
|
||||
"eqLcLoading": "광추 데이터 불러오는 중...",
|
||||
"eqRelicTitle": "🛡️ 보유 유물 목록",
|
||||
"eqRelicTitle": "보유 유물 목록",
|
||||
"eqRelicSubtitle": "freesr-data.json의 모든 유물 목록",
|
||||
"eqRelicLoading": "유물 데이터 불러오는 중...",
|
||||
"itemCount": "{n}개",
|
||||
@@ -2175,9 +2175,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "특성",
|
||||
"modalTracesTitle": "행적 및 비술",
|
||||
"modalTraceTechnique": "⚡ 비술 — 전투 진입 전 활성화",
|
||||
"traceA2": "🌟 추가 능력 A2",
|
||||
"traceA4": "🌟 추가 능력 A4",
|
||||
"traceA6": "🌟 추가 능력 A6",
|
||||
"traceA2": "추가 능력 A2",
|
||||
"traceA4": "추가 능력 A4",
|
||||
"traceA6": "추가 능력 A6",
|
||||
"traceStatNodes": "💎 모든 스탯 보너스 노드 (10개)",
|
||||
"lblLevelRange": "레벨 (1-80)",
|
||||
"lblPromotionRange": "승급 단계 (0-6)",
|
||||
@@ -2316,13 +2316,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "추천",
|
||||
"overviewStatsTitle": "⚔️ 전투 스탯 (Combat Stats)",
|
||||
"overviewStatsNote": "광추 및 유물 보너스 포함",
|
||||
"overviewEquippedLc": "🗡️ 장착 중인 광추",
|
||||
"overviewEquippedRelics": "🛡️ 유물 및 세트 효과",
|
||||
"overviewEquippedLc": "장착 중인 광추",
|
||||
"overviewEquippedRelics": "유물 및 세트 효과",
|
||||
"modalRelicsTitle": "🛡️ 유물 장착 (6개 부위)",
|
||||
"modalRelicsSubtitle": "각 부위를 클릭하여 변경하거나 추천 세트를 일괄 장착",
|
||||
"btnAutoEquipRelics": "⚡ 추천 세트 일괄 장착",
|
||||
"btnClearAllRelics": "🧹 모두 해제",
|
||||
"activeSetEffects": "✨ 활성화된 세트 효과",
|
||||
"activeSetEffects": "활성화된 세트 효과",
|
||||
"modalColLevel": "레벨",
|
||||
"lblPromotion": "승급 단계",
|
||||
"slotPickerTitle": "🛡️ 장착할 유물 선택",
|
||||
@@ -2405,8 +2405,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Đã thêm toàn bộ Nón Ánh Sáng 5 sao",
|
||||
"toastTeleportedTrain": "Đã dịch chuyển về Toa Tàu Astral",
|
||||
"toastLineupCleared": "Đã xóa sạch đội hình",
|
||||
"modalAddLcTitle": "🗡️ Thêm Nón Ánh Sáng vào FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Thêm Di Vật vào FreeSR",
|
||||
"modalAddLcTitle": "Thêm Nón Ánh Sáng vào FreeSR",
|
||||
"modalAddRelicTitle": "Thêm Di Vật vào FreeSR",
|
||||
"lblSelectLc": "Chọn Nón Ánh Sáng",
|
||||
"lblSelectRelicSet": "Chọn Bộ Di Vật",
|
||||
"lblRelicSlot": "Vị trí Di Vật",
|
||||
@@ -2428,7 +2428,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Kết nối thất bại",
|
||||
"tabDashboard": "📊 Tổng quan & Đội hình",
|
||||
"tabAvatars": "👥 Nhân vật",
|
||||
"tabEquipment": "🗡️ Nón Ánh Sáng & Di Vật",
|
||||
"tabEquipment": "Nón Ánh Sáng & Di Vật",
|
||||
"tabPersistent": "💾 Quản lý lưu GaCy",
|
||||
"tabExrank": "⚡ Kỹ Năng Ex-Rank",
|
||||
"tabJson": "📝 Quản lý JSON",
|
||||
@@ -2464,10 +2464,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Nâng cấp tất cả nhân vật lên Cấp 80 và Tinh Hồn 6?",
|
||||
"maxAllSuccess": "Tối đa tất cả nhân vật thành công!",
|
||||
"avatarRankSuccess": "Đã chỉnh nhân vật {id} thành Tinh Hồn {rank}",
|
||||
"eqLcTitle": "🗡️ Nón Ánh Sáng trong kho",
|
||||
"eqLcTitle": "Nón Ánh Sáng trong kho",
|
||||
"eqLcSubtitle": "Tất cả Nón Ánh Sáng từ freesr-data.json",
|
||||
"eqLcLoading": "Đang tải dữ liệu Nón Ánh Sáng...",
|
||||
"eqRelicTitle": "🛡️ Di Vật trong kho",
|
||||
"eqRelicTitle": "Di Vật trong kho",
|
||||
"eqRelicSubtitle": "Tất cả Di Vật từ freesr-data.json",
|
||||
"eqRelicLoading": "Đang tải dữ liệu Di Vật...",
|
||||
"itemCount": "{n} món",
|
||||
@@ -2525,9 +2525,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Thiên Phú",
|
||||
"modalTracesTitle": "Vết Tích & Bí Kỹ",
|
||||
"modalTraceTechnique": "⚡ Bí Kỹ — Kích hoạt trước trận",
|
||||
"traceA2": "🌟 Vết Tích Phụ A2",
|
||||
"traceA4": "🌟 Vết Tích Phụ A4",
|
||||
"traceA6": "🌟 Vết Tích Phụ A6",
|
||||
"traceA2": "Vết Tích Phụ A2",
|
||||
"traceA4": "Vết Tích Phụ A4",
|
||||
"traceA6": "Vết Tích Phụ A6",
|
||||
"traceStatNodes": "💎 Tất cả nhánh tăng chỉ số (10 nhánh)",
|
||||
"lblLevelRange": "Cấp độ (1-80)",
|
||||
"lblPromotionRange": "Đột phá (0-6)",
|
||||
@@ -2666,13 +2666,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Đề Xuất",
|
||||
"overviewStatsTitle": "⚔️ Chỉ số chiến đấu (Combat Stats)",
|
||||
"overviewStatsNote": "Bao gồm chỉ số từ Nón Ánh Sáng và Di Vật",
|
||||
"overviewEquippedLc": "🗡️ Nón Ánh Sáng đang trang bị",
|
||||
"overviewEquippedRelics": "🛡️ Hiệu ứng bộ Di Vật",
|
||||
"overviewEquippedLc": "Nón Ánh Sáng đang trang bị",
|
||||
"overviewEquippedRelics": "Hiệu ứng bộ Di Vật",
|
||||
"modalRelicsTitle": "🛡️ Trang bị Di Vật (6 vị trí)",
|
||||
"modalRelicsSubtitle": "Nhấn vào từng ô để thay đổi hoặc trang bị bộ đề xuất",
|
||||
"btnAutoEquipRelics": "⚡ Trang bị bộ đề xuất",
|
||||
"btnClearAllRelics": "🧹 Tháo toàn bộ",
|
||||
"activeSetEffects": "✨ Hiệu ứng bộ đang kích hoạt",
|
||||
"activeSetEffects": "Hiệu ứng bộ đang kích hoạt",
|
||||
"modalColLevel": "Cấp độ",
|
||||
"lblPromotion": "Cấp đột phá",
|
||||
"slotPickerTitle": "🛡️ Chọn Di Vật để trang bị",
|
||||
@@ -2755,8 +2755,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Berhasil menambahkan semua Light Cone 5★",
|
||||
"toastTeleportedTrain": "Teleportasi ke Parlor Car berhasil",
|
||||
"toastLineupCleared": "Susunan tim berhasil dikosongkan",
|
||||
"modalAddLcTitle": "🗡️ Tambah Light Cone ke FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Tambah Relic ke FreeSR",
|
||||
"modalAddLcTitle": "Tambah Light Cone ke FreeSR",
|
||||
"modalAddRelicTitle": "Tambah Relic ke FreeSR",
|
||||
"lblSelectLc": "Pilih Light Cone",
|
||||
"lblSelectRelicSet": "Pilih Set Relic",
|
||||
"lblRelicSlot": "Slot Relic",
|
||||
@@ -2778,7 +2778,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Koneksi Gagal",
|
||||
"tabDashboard": "📊 Dasbor & Tim",
|
||||
"tabAvatars": "👥 Karakter",
|
||||
"tabEquipment": "🗡️ Light Cone & Relic",
|
||||
"tabEquipment": "Light Cone & Relic",
|
||||
"tabPersistent": "💾 Pengelola Simpan GaCy",
|
||||
"tabExrank": "⚡ Keahlian Ex-Rank",
|
||||
"tabJson": "📝 Manajemen JSON",
|
||||
@@ -2814,10 +2814,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Tingkatkan semua karakter ke Lv. 80 dan buka E6?",
|
||||
"maxAllSuccess": "Semua karakter berhasil dimaksimalkan!",
|
||||
"avatarRankSuccess": "Karakter {id} diperbarui ke E{rank}",
|
||||
"eqLcTitle": "🗡️ Light Cone di Inventaris",
|
||||
"eqLcTitle": "Light Cone di Inventaris",
|
||||
"eqLcSubtitle": "Semua light cone dari freesr-data.json",
|
||||
"eqLcLoading": "Memuat data light cone...",
|
||||
"eqRelicTitle": "🛡️ Relic di Inventaris",
|
||||
"eqRelicTitle": "Relic di Inventaris",
|
||||
"eqRelicSubtitle": "Semua relic dari freesr-data.json",
|
||||
"eqRelicLoading": "Memuat data relic...",
|
||||
"itemCount": "{n} item",
|
||||
@@ -2875,9 +2875,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talent",
|
||||
"modalTracesTitle": "Traces & Technique",
|
||||
"modalTraceTechnique": "⚡ Technique — Aktif sebelum tempur",
|
||||
"traceA2": "🌟 Bonus Trace A2",
|
||||
"traceA4": "🌟 Bonus Trace A4",
|
||||
"traceA6": "🌟 Bonus Trace A6",
|
||||
"traceA2": "Bonus Trace A2",
|
||||
"traceA4": "Bonus Trace A4",
|
||||
"traceA6": "Bonus Trace A6",
|
||||
"traceStatNodes": "💎 Semua Node Bonus Stat (10 node)",
|
||||
"lblLevelRange": "Level (1-80)",
|
||||
"lblPromotionRange": "Promosi (0-6)",
|
||||
@@ -3016,13 +3016,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Rekomendasi",
|
||||
"overviewStatsTitle": "⚔️ Statistik Tempur (Combat Stats)",
|
||||
"overviewStatsNote": "Termasuk bonus Light Cone & Relic",
|
||||
"overviewEquippedLc": "🗡️ Light Cone Terpasang",
|
||||
"overviewEquippedRelics": "🛡️ Bonus Set Relic",
|
||||
"overviewEquippedLc": "Light Cone Terpasang",
|
||||
"overviewEquippedRelics": "Bonus Set Relic",
|
||||
"modalRelicsTitle": "🛡️ Pasang Relic (6 Slot)",
|
||||
"modalRelicsSubtitle": "Klik setiap slot untuk mengganti atau pasang set rekomendasi",
|
||||
"btnAutoEquipRelics": "⚡ Pasang Set Rekomendasi",
|
||||
"btnClearAllRelics": "🧹 Lepas Semua",
|
||||
"activeSetEffects": "✨ Efek Set yang Aktif",
|
||||
"activeSetEffects": "Efek Set yang Aktif",
|
||||
"modalColLevel": "Level",
|
||||
"lblPromotion": "Peringkat Ascension",
|
||||
"slotPickerTitle": "🛡️ Pilih Relic untuk Dipasang",
|
||||
@@ -3105,8 +3105,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Todos los conos de 5★ añadidos",
|
||||
"toastTeleportedTrain": "Teletransportado al Expreso Astral",
|
||||
"toastLineupCleared": "Equipo vaciado correctamente",
|
||||
"modalAddLcTitle": "🗡️ Añadir cono de luz a FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Añadir reliquia a FreeSR",
|
||||
"modalAddLcTitle": "Añadir cono de luz a FreeSR",
|
||||
"modalAddRelicTitle": "Añadir reliquia a FreeSR",
|
||||
"lblSelectLc": "Seleccionar cono de luz",
|
||||
"lblSelectRelicSet": "Seleccionar conjunto de reliquias",
|
||||
"lblRelicSlot": "Ranura de reliquia",
|
||||
@@ -3128,7 +3128,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Conexión fallida",
|
||||
"tabDashboard": "📊 Panel y equipo",
|
||||
"tabAvatars": "👥 Personajes",
|
||||
"tabEquipment": "🗡️ Conos de luz y artefactos",
|
||||
"tabEquipment": "Conos de luz y artefactos",
|
||||
"tabPersistent": "💾 Gestor de guardado GaCy",
|
||||
"tabExrank": "⚡ Habilidades Ex-Rank",
|
||||
"tabJson": "📝 Gestión de JSON",
|
||||
@@ -3164,10 +3164,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "¿Subir todos los personajes a nivel 80 y desbloquear E6?",
|
||||
"maxAllSuccess": "¡Todos los personajes maximizados con éxito!",
|
||||
"avatarRankSuccess": "Personaje {id} actualizado a E{rank}",
|
||||
"eqLcTitle": "🗡️ Conos de luz en inventario",
|
||||
"eqLcTitle": "Conos de luz en inventario",
|
||||
"eqLcSubtitle": "Todos los conos de luz de freesr-data.json",
|
||||
"eqLcLoading": "Cargando datos de conos de luz...",
|
||||
"eqRelicTitle": "🛡️ Artefactos en inventario",
|
||||
"eqRelicTitle": "Artefactos en inventario",
|
||||
"eqRelicSubtitle": "Todos los artefactos de freesr-data.json",
|
||||
"eqRelicLoading": "Cargando datos de artefactos...",
|
||||
"itemCount": "{n} objetos",
|
||||
@@ -3225,9 +3225,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talento",
|
||||
"modalTracesTitle": "Rastros y técnica",
|
||||
"modalTraceTechnique": "⚡ Técnica — Activa antes de la batalla",
|
||||
"traceA2": "🌟 Habilidad adicional A2",
|
||||
"traceA4": "🌟 Habilidad adicional A4",
|
||||
"traceA6": "🌟 Habilidad adicional A6",
|
||||
"traceA2": "Habilidad adicional A2",
|
||||
"traceA4": "Habilidad adicional A4",
|
||||
"traceA6": "Habilidad adicional A6",
|
||||
"traceStatNodes": "💎 Todos los nodos de atributos (10 nodos)",
|
||||
"lblLevelRange": "Nivel (1-80)",
|
||||
"lblPromotionRange": "Ascensión (0-6)",
|
||||
@@ -3366,13 +3366,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Recomendado",
|
||||
"overviewStatsTitle": "⚔️ Estadísticas de combate",
|
||||
"overviewStatsNote": "Incluye bonificaciones de Cono y Artefactos",
|
||||
"overviewEquippedLc": "🗡️ Cono de Luz equipado",
|
||||
"overviewEquippedRelics": "🛡️ Artefactos y efectos de set",
|
||||
"overviewEquippedLc": "Cono de Luz equipado",
|
||||
"overviewEquippedRelics": "Artefactos y efectos de set",
|
||||
"modalRelicsTitle": "🛡️ Equipar artefactos (6 ranuras)",
|
||||
"modalRelicsSubtitle": "Haz clic en cada ranura para cambiar o equipar sets recomendados",
|
||||
"btnAutoEquipRelics": "⚡ Equipar sets recomendados",
|
||||
"btnClearAllRelics": "🧹 Desequipar todo",
|
||||
"activeSetEffects": "✨ Efectos de set activos",
|
||||
"activeSetEffects": "Efectos de set activos",
|
||||
"modalColLevel": "Nivel",
|
||||
"lblPromotion": "Rango de ascensión",
|
||||
"slotPickerTitle": "🛡️ Seleccionar artefacto para equipar",
|
||||
@@ -3455,8 +3455,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Tous les cônes de lumière 5★ ajoutés",
|
||||
"toastTeleportedTrain": "Téléporté au wagon-salon de l'Astral Express",
|
||||
"toastLineupCleared": "Équipe réinitialisée",
|
||||
"modalAddLcTitle": "🗡️ Ajouter cône de lumière à FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Ajouter relique à FreeSR",
|
||||
"modalAddLcTitle": "Ajouter cône de lumière à FreeSR",
|
||||
"modalAddRelicTitle": "Ajouter relique à FreeSR",
|
||||
"lblSelectLc": "Sélectionner cône de lumière",
|
||||
"lblSelectRelicSet": "Sélectionner ensemble de reliques",
|
||||
"lblRelicSlot": "Emplacement de relique",
|
||||
@@ -3478,7 +3478,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Échec de connexion",
|
||||
"tabDashboard": "📊 Tableau de bord et équipe",
|
||||
"tabAvatars": "👥 Personnages",
|
||||
"tabEquipment": "🗡️ Cônes de lumière et reliques",
|
||||
"tabEquipment": "Cônes de lumière et reliques",
|
||||
"tabPersistent": "💾 Gestionnaire de sauvegarde GaCy",
|
||||
"tabExrank": "⚡ Compétences Ex-Rank",
|
||||
"tabJson": "📝 Gestion JSON",
|
||||
@@ -3514,10 +3514,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Améliorer tous les personnages au niveau 80 et débloquer E6 ?",
|
||||
"maxAllSuccess": "Tous les personnages sont maximisés avec succès !",
|
||||
"avatarRankSuccess": "Personnage {id} mis à jour au rang E{rank}",
|
||||
"eqLcTitle": "🗡️ Cônes de lumière dans l'inventaire",
|
||||
"eqLcTitle": "Cônes de lumière dans l'inventaire",
|
||||
"eqLcSubtitle": "Tous les cônes de lumière de freesr-data.json",
|
||||
"eqLcLoading": "Chargement des cônes de lumière...",
|
||||
"eqRelicTitle": "🛡️ Reliques dans l'inventaire",
|
||||
"eqRelicTitle": "Reliques dans l'inventaire",
|
||||
"eqRelicSubtitle": "Toutes les reliques de freesr-data.json",
|
||||
"eqRelicLoading": "Chargement des reliques...",
|
||||
"itemCount": "{n} objets",
|
||||
@@ -3575,9 +3575,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talent",
|
||||
"modalTracesTitle": "Traces et technique",
|
||||
"modalTraceTechnique": "⚡ Technique — Active avant le combat",
|
||||
"traceA2": "🌟 Capacité bonus A2",
|
||||
"traceA4": "🌟 Capacité bonus A4",
|
||||
"traceA6": "🌟 Capacité bonus A6",
|
||||
"traceA2": "Capacité bonus A2",
|
||||
"traceA4": "Capacité bonus A4",
|
||||
"traceA6": "Capacité bonus A6",
|
||||
"traceStatNodes": "💎 Tous les nœuds de statistiques (10 nœuds)",
|
||||
"lblLevelRange": "Niveau (1-80)",
|
||||
"lblPromotionRange": "Élévation (0-6)",
|
||||
@@ -3716,13 +3716,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Recommandé",
|
||||
"overviewStatsTitle": "⚔️ Statistiques de combat",
|
||||
"overviewStatsNote": "Inclut les bonus de Cône et de Reliques",
|
||||
"overviewEquippedLc": "🗡️ Cône de Lumière équipé",
|
||||
"overviewEquippedRelics": "🛡️ Reliques et bonus de set",
|
||||
"overviewEquippedLc": "Cône de Lumière équipé",
|
||||
"overviewEquippedRelics": "Reliques et bonus de set",
|
||||
"modalRelicsTitle": "🛡️ Équiper reliques (6 emplacements)",
|
||||
"modalRelicsSubtitle": "Cliquez sur chaque emplacement pour modifier ou équiper les sets recommandés",
|
||||
"btnAutoEquipRelics": "⚡ Équiper sets recommandés",
|
||||
"btnClearAllRelics": "🧹 Tout déséquiper",
|
||||
"activeSetEffects": "✨ Bonus de set actifs",
|
||||
"activeSetEffects": "Bonus de set actifs",
|
||||
"modalColLevel": "Niveau",
|
||||
"lblPromotion": "Rang d'élévation",
|
||||
"slotPickerTitle": "🛡️ Sélectionner une relique à équiper",
|
||||
@@ -3805,8 +3805,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Alle 5★-Lichtkegel hinzugefügt",
|
||||
"toastTeleportedTrain": "Zum Astralexpress teleportiert",
|
||||
"toastLineupCleared": "Team-Plätze geleert",
|
||||
"modalAddLcTitle": "🗡️ Lichtkegel zu FreeSR hinzufügen",
|
||||
"modalAddRelicTitle": "🛡️ Relikt zu FreeSR hinzufügen",
|
||||
"modalAddLcTitle": "Lichtkegel zu FreeSR hinzufügen",
|
||||
"modalAddRelicTitle": "Relikt zu FreeSR hinzufügen",
|
||||
"lblSelectLc": "Lichtkegel auswählen",
|
||||
"lblSelectRelicSet": "Reliktset auswählen",
|
||||
"lblRelicSlot": "Reliktslot",
|
||||
@@ -3828,7 +3828,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Verbindung fehlgeschlagen",
|
||||
"tabDashboard": "📊 Dashboard & Team",
|
||||
"tabAvatars": "👥 Charaktere",
|
||||
"tabEquipment": "🗡️ Lichtkegel & Relikte",
|
||||
"tabEquipment": "Lichtkegel & Relikte",
|
||||
"tabPersistent": "💾 GaCy-Speicher-Manager",
|
||||
"tabExrank": "⚡ Ex-Rank-Fähigkeiten",
|
||||
"tabJson": "📝 JSON-Verwaltung",
|
||||
@@ -3864,10 +3864,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Alle Charaktere auf Stufe 80 aufwerten und E6 freischalten?",
|
||||
"maxAllSuccess": "Alle Charaktere erfolgreich maximiert!",
|
||||
"avatarRankSuccess": "Charakter {id} auf E{rank} aktualisiert",
|
||||
"eqLcTitle": "🗡️ Lichtkegel im Inventar",
|
||||
"eqLcTitle": "Lichtkegel im Inventar",
|
||||
"eqLcSubtitle": "Alle Lichtkegel aus freesr-data.json",
|
||||
"eqLcLoading": "Lichtkegeldaten werden geladen...",
|
||||
"eqRelicTitle": "🛡️ Relikte im Inventar",
|
||||
"eqRelicTitle": "Relikte im Inventar",
|
||||
"eqRelicSubtitle": "Alle Relikte aus freesr-data.json",
|
||||
"eqRelicLoading": "Reliktdaten werden geladen...",
|
||||
"itemCount": "{n} Gegenstände",
|
||||
@@ -3925,9 +3925,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talent",
|
||||
"modalTracesTitle": "Spuren & Technik",
|
||||
"modalTraceTechnique": "⚡ Technik — Vor dem Kampf aktiv",
|
||||
"traceA2": "🌟 Zusatzfähigkeit A2",
|
||||
"traceA4": "🌟 Zusatzfähigkeit A4",
|
||||
"traceA6": "🌟 Zusatzfähigkeit A6",
|
||||
"traceA2": "Zusatzfähigkeit A2",
|
||||
"traceA4": "Zusatzfähigkeit A4",
|
||||
"traceA6": "Zusatzfähigkeit A6",
|
||||
"traceStatNodes": "💎 Alle Attributs-Bonus-Knoten (10 Knoten)",
|
||||
"lblLevelRange": "Stufe (1-80)",
|
||||
"lblPromotionRange": "Aufstieg (0-6)",
|
||||
@@ -4066,13 +4066,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Empfohlen",
|
||||
"overviewStatsTitle": "⚔️ Kampfwerte (Combat Stats)",
|
||||
"overviewStatsNote": "Enthält Boni von Lichtkegel und Relikten",
|
||||
"overviewEquippedLc": "🗡️ Ausgerüsteter Lichtkegel",
|
||||
"overviewEquippedRelics": "🛡️ Relikte & Set-Boni",
|
||||
"overviewEquippedLc": "Ausgerüsteter Lichtkegel",
|
||||
"overviewEquippedRelics": "Relikte & Set-Boni",
|
||||
"modalRelicsTitle": "🛡️ Relikte ausrüsten (6 Plätze)",
|
||||
"modalRelicsSubtitle": "Klicken Sie auf einen Platz zum Wechseln oder empfohlene Sets ausrüsten",
|
||||
"btnAutoEquipRelics": "⚡ Empfohlene Sets anlegen",
|
||||
"btnClearAllRelics": "🧹 Alles ablegen",
|
||||
"activeSetEffects": "✨ Aktive Set-Boni",
|
||||
"activeSetEffects": "Aktive Set-Boni",
|
||||
"modalColLevel": "Stufe",
|
||||
"lblPromotion": "Aufstiegsrang",
|
||||
"slotPickerTitle": "🛡️ Relikt zum Ausrüsten wählen",
|
||||
@@ -4155,8 +4155,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Все 5★ световые конусы добавлены",
|
||||
"toastTeleportedTrain": "Телепортирован в общий вагон экспресса",
|
||||
"toastLineupCleared": "Слоты отряда очищены",
|
||||
"modalAddLcTitle": "🗡️ Добавить световой конус в FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Добавить реликвию в FreeSR",
|
||||
"modalAddLcTitle": "Добавить световой конус в FreeSR",
|
||||
"modalAddRelicTitle": "Добавить реликвию в FreeSR",
|
||||
"lblSelectLc": "Выберите световой конус",
|
||||
"lblSelectRelicSet": "Выберите набор реликвий",
|
||||
"lblRelicSlot": "Слот реликвии",
|
||||
@@ -4178,7 +4178,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Ошибка подключения",
|
||||
"tabDashboard": "📊 Панель и отряд",
|
||||
"tabAvatars": "👥 Персонажи",
|
||||
"tabEquipment": "🗡️ Световые конусы и реликвии",
|
||||
"tabEquipment": "Световые конусы и реликвии",
|
||||
"tabPersistent": "💾 Управление сохранениями GaCy",
|
||||
"tabExrank": "⚡ Особые навыки Ex-Rank",
|
||||
"tabJson": "📝 Управление JSON",
|
||||
@@ -4214,10 +4214,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Повысить всех персонажей до 80 уровня и разблокировать E6?",
|
||||
"maxAllSuccess": "Все персонажи успешно прокачаны!",
|
||||
"avatarRankSuccess": "Персонаж {id} повышен до эйдолона {rank}",
|
||||
"eqLcTitle": "🗡️ Световые конусы в инвентаре",
|
||||
"eqLcTitle": "Световые конусы в инвентаре",
|
||||
"eqLcSubtitle": "Все световые конусы из freesr-data.json",
|
||||
"eqLcLoading": "Загрузка конусов...",
|
||||
"eqRelicTitle": "🛡️ Реликвии в инвентаре",
|
||||
"eqRelicTitle": "Реликвии в инвентаре",
|
||||
"eqRelicSubtitle": "Все реликвии из freesr-data.json",
|
||||
"eqRelicLoading": "Загрузка реликвий...",
|
||||
"itemCount": "{n} шт.",
|
||||
@@ -4275,9 +4275,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Талант",
|
||||
"modalTracesTitle": "Следы и техника",
|
||||
"modalTraceTechnique": "⚡ Техника — Активна перед боем",
|
||||
"traceA2": "🌟 Доп. способность A2",
|
||||
"traceA4": "🌟 Доп. способность A4",
|
||||
"traceA6": "🌟 Доп. способность A6",
|
||||
"traceA2": "Доп. способность A2",
|
||||
"traceA4": "Доп. способность A4",
|
||||
"traceA6": "Доп. способность A6",
|
||||
"traceStatNodes": "💎 Все узлы усиления параметров (10 узлов)",
|
||||
"lblLevelRange": "Уровень (1-80)",
|
||||
"lblPromotionRange": "Возвышение (0-6)",
|
||||
@@ -4416,13 +4416,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Рекомендуется",
|
||||
"overviewStatsTitle": "⚔️ Боевые характеристики",
|
||||
"overviewStatsNote": "Включает бонусы конуса и реликвий",
|
||||
"overviewEquippedLc": "🗡️ Надетый Световой конус",
|
||||
"overviewEquippedRelics": "🛡️ Реликвии и бонусы комплектов",
|
||||
"overviewEquippedLc": "Надетый Световой конус",
|
||||
"overviewEquippedRelics": "Реликвии и бонусы комплектов",
|
||||
"modalRelicsTitle": "🛡️ Экипировка реликвий (6 слотов)",
|
||||
"modalRelicsSubtitle": "Нажмите на слот для смены или наденьте рекомендованные комплекты",
|
||||
"btnAutoEquipRelics": "⚡ Надеть рекомендованные сеты",
|
||||
"btnClearAllRelics": "🧹 Снять всё",
|
||||
"activeSetEffects": "✨ Активные эффекты комплектов",
|
||||
"activeSetEffects": "Активные эффекты комплектов",
|
||||
"modalColLevel": "Уровень",
|
||||
"lblPromotion": "Ранг возвышения",
|
||||
"slotPickerTitle": "🛡️ Выбор реликвии для экипировки",
|
||||
@@ -4505,8 +4505,8 @@ export const TRANSLATIONS = {
|
||||
"toastAddedAllLcs": "Todos os Cones de Luz 5★ adicionados",
|
||||
"toastTeleportedTrain": "Teleportado para o Expresso Astral",
|
||||
"toastLineupCleared": "Equipe limpa com sucesso",
|
||||
"modalAddLcTitle": "🗡️ Adicionar Cone de Luz ao FreeSR",
|
||||
"modalAddRelicTitle": "🛡️ Adicionar Relíquia ao FreeSR",
|
||||
"modalAddLcTitle": "Adicionar Cone de Luz ao FreeSR",
|
||||
"modalAddRelicTitle": "Adicionar Relíquia ao FreeSR",
|
||||
"lblSelectLc": "Selecionar Cone de Luz",
|
||||
"lblSelectRelicSet": "Selecionar Conjunto de Relíquias",
|
||||
"lblRelicSlot": "Espaço de Relíquia",
|
||||
@@ -4528,7 +4528,7 @@ export const TRANSLATIONS = {
|
||||
"connectFailed": "Falha na Conexão",
|
||||
"tabDashboard": "📊 Painel e Equipe",
|
||||
"tabAvatars": "👥 Personagens",
|
||||
"tabEquipment": "🗡️ Cones de Luz e Relíquias",
|
||||
"tabEquipment": "Cones de Luz e Relíquias",
|
||||
"tabPersistent": "💾 Gerenciador de Salvamento GaCy",
|
||||
"tabExrank": "⚡ Habilidades Ex-Rank",
|
||||
"tabJson": "📝 Gerenciamento de JSON",
|
||||
@@ -4564,10 +4564,10 @@ export const TRANSLATIONS = {
|
||||
"confirmMaxAll": "Evoluir todos os personagens para o Nível 80 e desbloquear E6?",
|
||||
"maxAllSuccess": "Todos os personagens foram maximizados com sucesso!",
|
||||
"avatarRankSuccess": "Personagem {id} atualizado para E{rank}",
|
||||
"eqLcTitle": "🗡️ Cones de Luz no Inventário",
|
||||
"eqLcTitle": "Cones de Luz no Inventário",
|
||||
"eqLcSubtitle": "Todos os cones de luz de freesr-data.json",
|
||||
"eqLcLoading": "Carregando dados dos cones de luz...",
|
||||
"eqRelicTitle": "🛡️ Relíquias no Inventário",
|
||||
"eqRelicTitle": "Relíquias no Inventário",
|
||||
"eqRelicSubtitle": "Todas as relíquias de freesr-data.json",
|
||||
"eqRelicLoading": "Carregando dados das relíquias...",
|
||||
"itemCount": "{n} itens",
|
||||
@@ -4625,9 +4625,9 @@ export const TRANSLATIONS = {
|
||||
"modalSkillTalent": "Talento",
|
||||
"modalTracesTitle": "Rastros e Técnica",
|
||||
"modalTraceTechnique": "⚡ Técnica — Ativa pré-combate",
|
||||
"traceA2": "🌟 Habilidade Bônus A2",
|
||||
"traceA4": "🌟 Habilidade Bônus A4",
|
||||
"traceA6": "🌟 Habilidade Bônus A6",
|
||||
"traceA2": "Habilidade Bônus A2",
|
||||
"traceA4": "Habilidade Bônus A4",
|
||||
"traceA6": "Habilidade Bônus A6",
|
||||
"traceStatNodes": "💎 Todos os nós de atributos bônus (10 nós)",
|
||||
"lblLevelRange": "Nível (1-80)",
|
||||
"lblPromotionRange": "Ascensão (0-6)",
|
||||
@@ -4766,13 +4766,13 @@ export const TRANSLATIONS = {
|
||||
"recommendedBadge": "Recomendado",
|
||||
"overviewStatsTitle": "⚔️ Atributos de Combate",
|
||||
"overviewStatsNote": "Inclui bônus de Cone de Luz e Relíquias",
|
||||
"overviewEquippedLc": "🗡️ Cone de Luz Equipado",
|
||||
"overviewEquippedRelics": "🛡️ Relíquias e Efeitos de Conjunto",
|
||||
"overviewEquippedLc": "Cone de Luz Equipado",
|
||||
"overviewEquippedRelics": "Relíquias e Efeitos de Conjunto",
|
||||
"modalRelicsTitle": "🛡️ Equipar Relíquias (6 Espaços)",
|
||||
"modalRelicsSubtitle": "Clique em cada espaço para alterar ou equipar conjuntos recomendados",
|
||||
"btnAutoEquipRelics": "⚡ Equipar Conjuntos Recomendados",
|
||||
"btnClearAllRelics": "🧹 Desequipar Tudo",
|
||||
"activeSetEffects": "✨ Efeitos de Conjunto Ativos",
|
||||
"activeSetEffects": "Efeitos de Conjunto Ativos",
|
||||
"modalColLevel": "Nível",
|
||||
"lblPromotion": "Classificação de Ascensão",
|
||||
"slotPickerTitle": "🛡️ Selecionar Relíquia para Equipar",
|
||||
@@ -6388,7 +6388,7 @@ const UI_COMPLETION_TRANSLATIONS = {
|
||||
id: {
|
||||
appSubtitle: 'Pengelola server dan FreeSR Star Rail', offline: 'Luring', btnPersistent: '💾 Simpanan persistent', slot: 'Posisi',
|
||||
analystDmgNormal: '🗡️ Serangan Dasar', analystDmgSkill: '⚡ Skill Tempur', analystDmgUlt: '💥 Serangan Pamungkas', analystDmgFollowup: '🔄 Serangan Lanjutan', analystMonsterName: 'Musuh #{id}',
|
||||
modalSkillBasic: 'Serangan Dasar', modalSkillSkill: 'Skill Tempur', modalSkillTalent: 'Talenta', traceA2: '🌟 Jejak Bonus A2', traceA4: '🌟 Jejak Bonus A4', traceA6: '🌟 Jejak Bonus A6', btnResetE0: 'Atur Ulang E0', modalEnergy50: 'Atur 50%', modalEnergy100: 'Atur 100%',
|
||||
modalSkillBasic: 'Serangan Dasar', modalSkillSkill: 'Skill Tempur', modalSkillTalent: 'Talenta', traceA2: 'Jejak Bonus A2', traceA4: 'Jejak Bonus A4', traceA6: 'Jejak Bonus A6', btnResetE0: 'Atur Ulang E0', modalEnergy50: 'Atur 50%', modalEnergy100: 'Atur 100%',
|
||||
equipmentSearchPlaceholder: '🔍 Cari Light Cone, Relik, atau ID item…', filter3Star: '3 Bintang', filterAllStatus: 'Semua Status', filterEquipped: 'Dipakai', filterInInventory: 'Di Inventaris', filterAllTypes: 'Semua Jenis', filterAllRelicSlots: 'Semua Slot Relik',
|
||||
globalFeatureSilverWolf: 'Fitur Global Silver Wolf', globalFeatureCastorice: 'Fitur Global Castorice', globalFeatureAha: 'Fitur Global Aeon ★ Aha', lightConeSearchPlaceholder: '🔍 Cari Light Cone berdasarkan nama atau ID…',
|
||||
superimpositionLabel: 'Superimposisi', superimpositionSelectTitle: 'Pilih superimposisi saat dipasang', superimpositionRank: 'S{rank} (Superimposisi {rank})',
|
||||
@@ -7129,7 +7129,24 @@ const MODAL_AND_TOOL_TRANSLATIONS = {
|
||||
cameraFaceCamExperimental: 'FaceCam (ทดลอง)',
|
||||
cameraDistanceError: 'ระยะ FaceCam ต้องอยู่ระหว่าง 0.5 ถึง 5',
|
||||
cameraSaved: 'บันทึกกล้องแล้ว',
|
||||
cameraModeFreecam: 'Freecam',
|
||||
cameraModeFreecamDesc: 'กล้องอิสระ — ขยับมุมมองรอบตัวละครได้ตามต้องการ',
|
||||
cameraModeFacecam: 'FaceCam',
|
||||
cameraModeFacecamDesc: 'กล้องโคลสอัพใบหน้าตัวละคร (ฟีเจอร์ทดลอง)',
|
||||
cameraDistanceValue: '{value}×',
|
||||
cameraDistanceHint: 'ใช้กับโหมด FaceCam เท่านั้น — ล้อเมาส์ในเกมปรับระยะชั่วคราวได้',
|
||||
cameraDistanceHintFreecam: 'เปิดโหมด FaceCam เพื่อใช้ระยะนี้',
|
||||
cameraStatusIdle: 'พร้อมบันทึก',
|
||||
cameraStatusSaved: 'บันทึกแล้ว',
|
||||
cameraStatusUnsaved: 'มีการแก้ไขที่ยังไม่ถูกบันทึก',
|
||||
cameraStatusOffline: 'เชื่อมต่อ GaCy GO เพื่อบันทึก',
|
||||
cameraStatusSaving: 'กำลังบันทึก...',
|
||||
cameraStatusError: 'บันทึกไม่สำเร็จ',
|
||||
cameraSaving: 'กำลังบันทึก...',
|
||||
cameraSaveOfflineError: 'ยังไม่ได้เชื่อมต่อเซิร์ฟเวอร์ GaCy GO จึงยังบันทึกกล้องไม่ได้',
|
||||
lcPassiveFallback: 'เพิ่มค่าสถานะพื้นฐานตามสาย <strong>{path}</strong> และเพิ่มประสิทธิภาพความเสียหายและการสนับสนุน <strong>{value}%</strong> เมื่อเข้าสู่การต่อสู้',
|
||||
relicSetPieces: '{count} ชิ้น',
|
||||
relicSlotBadgeLabel: 'ช่องรีลิกส์: {slot}',
|
||||
slotHeadShort: 'ส่วนหัว',
|
||||
slotHandsShort: 'ส่วนมือ',
|
||||
slotBodyShort: 'ลำตัว',
|
||||
@@ -7150,6 +7167,7 @@ const MODAL_AND_TOOL_TRANSLATIONS = {
|
||||
jsonStatusBrowserCache: '{file} (โหลดจากแคชของเบราว์เซอร์)',
|
||||
jsonStatusCache: '{file} (โหลดจากแคช)',
|
||||
jsonStatusDump: '{file} (โครงสร้าง Firefly dump)',
|
||||
jsonDataUnavailable: 'โหลด {file} ไม่ได้และไม่มีแคช กรุณาตรวจการเชื่อมต่อ หรือวาง JSON เอง',
|
||||
jsonOfflineNoCache: 'Offline: ไม่มีแคช สามารถอัปโหลดหรือพิมพ์ JSON เองได้',
|
||||
jsonOfflinePaste: 'Offline: สามารถพิมพ์หรือวาง JSON เองได้',
|
||||
jsonCacheUnavailable: 'ไม่สามารถตรวจสอบได้',
|
||||
@@ -7242,7 +7260,24 @@ const MODAL_AND_TOOL_TRANSLATIONS = {
|
||||
cameraFaceCamExperimental: 'FaceCam (experimental)',
|
||||
cameraDistanceError: 'FaceCam distance must be between 0.5 and 5',
|
||||
cameraSaved: 'Camera saved',
|
||||
cameraModeFreecam: 'Freecam',
|
||||
cameraModeFreecamDesc: 'Free camera — orbit the view around your character',
|
||||
cameraModeFacecam: 'FaceCam',
|
||||
cameraModeFacecamDesc: 'Close-up face camera (experimental)',
|
||||
cameraDistanceValue: '{value}×',
|
||||
cameraDistanceHint: 'FaceCam only — the mouse wheel in game adjusts the distance temporarily',
|
||||
cameraDistanceHintFreecam: 'Switch to FaceCam to use this distance',
|
||||
cameraStatusIdle: 'Ready to save',
|
||||
cameraStatusSaved: 'Saved',
|
||||
cameraStatusUnsaved: 'Unsaved changes',
|
||||
cameraStatusOffline: 'Connect to GaCy GO to save',
|
||||
cameraStatusSaving: 'Saving...',
|
||||
cameraStatusError: 'Save failed',
|
||||
cameraSaving: 'Saving...',
|
||||
cameraSaveOfflineError: 'Not connected to a GaCy GO server, so the camera settings cannot be saved',
|
||||
lcPassiveFallback: 'Increases basic stats following the <strong>{path}</strong> path and boosts damage and support performance by <strong>{value}%</strong> in battle',
|
||||
relicSetPieces: '{count}-Piece Set',
|
||||
relicSlotBadgeLabel: 'Relic slot: {slot}',
|
||||
slotHeadShort: 'Head',
|
||||
slotHandsShort: 'Hands',
|
||||
slotBodyShort: 'Body',
|
||||
@@ -7263,6 +7298,7 @@ const MODAL_AND_TOOL_TRANSLATIONS = {
|
||||
jsonStatusBrowserCache: '{file} (from browser cache)',
|
||||
jsonStatusCache: '{file} (from cache)',
|
||||
jsonStatusDump: '{file} (Firefly dump structure)',
|
||||
jsonDataUnavailable: 'Could not load {file} and no cache is available. Check the connection or paste JSON.',
|
||||
jsonOfflineNoCache: 'Offline: no cache. You can upload or paste JSON.',
|
||||
jsonOfflinePaste: 'Offline: you can paste or type JSON.',
|
||||
jsonCacheUnavailable: 'Unavailable',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getCurrentLanguage, t, translatePath } from './i18n.js?v=2.17';
|
||||
import { getCurrentLanguage, t, translatePath } from './i18n.js?v=2.21';
|
||||
|
||||
const CATALOG_URL = new URL('../assets/item-catalog.json?v=2', import.meta.url);
|
||||
const PAGE_SIZE = 48;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getCurrentLanguage } from './i18n.js?v=2.17';
|
||||
import { getCurrentLanguage } from './i18n.js?v=2.21';
|
||||
|
||||
const strings = {
|
||||
en: {
|
||||
|
||||
Reference in new issue
Block a user