feat: add material inventory editor
This commit is contained in:
1 parent
fa31704a56
commit
ab01489ea1
4 files changed
+254
-5
No files matched your search
@@ -51,17 +51,23 @@
|
||||
- **ปรับแต่งกรวยแสง:** สวมใส่, ปรับเลเวล 1-80, เลื่อนขั้น 0-6 และระดับการขัดเกลา Superimposition (S1-S5)
|
||||
- **สลับกรวยแสงประจำตัวอัตโนมัติ:** ตรวจจับและสลับใส่ Signature Lightcone ของแต่ละตัวละครได้ในคลิกเดียว
|
||||
|
||||
### 💾 6. จัดการไฟล์เซฟ GaC (Persistent & FreeSR Data)
|
||||
### 🎒 6. คลังไอเทม (Material Inventory)
|
||||
- ค้นหารายการจาก Item ID ใน catalog ของ Client ที่เซิร์ฟเวอร์กำลังใช้ พร้อมแก้จำนวนได้หลายรายการก่อนบันทึกครั้งเดียว
|
||||
- ค่าในช่องที่ว่างใช้ค่าที่เซิร์ฟเวอร์สร้างตามปกติ; ล้างค่าที่เคยกำหนดเองเพื่อกลับไปใช้ค่าเริ่มต้นได้
|
||||
- การบันทึกแก้เฉพาะจำนวนไอเทมใน `freesr-data.json` ไม่เขียนทับตัวละคร, LC, Relic หรือ Battle Config
|
||||
- รองรับเฉพาะ GaC-hkrpg-go ผ่าน `/api/inventory/materials`; บันทึกลงไฟล์ทันทีและต้องเข้าเกมใหม่เพื่อโหลด session พร้อมจำนวนที่แก้แล้ว
|
||||
|
||||
### 💾 7. จัดการไฟล์เซฟ GaC (Persistent & FreeSR Data)
|
||||
- **ตั้งค่าตัวเอก (ผู้บุกเบิก):** สลับเพศ (Caelus / Stelle) และเปลี่ยนธาตุ (กายภาพ, ไฟ, จินตภาพ, ความจำ)
|
||||
- **สลับพาธ March 7th:** เลือกระหว่างพาธการอนุรักษ์ (น้ำแข็ง) และพาธล่าสังหาร (จินตภาพ)
|
||||
- **สวิตช์ระบบพิเศษ Ex-Rank:** จัดการเปิด/ปิดความสามารถพิเศษ Ex-Rank สำหรับตัวละครเฉพาะ (เช่น Jing Yuan, Dan Heng • IL, Acheron)
|
||||
- **ตัวแก้ไข JSON ดิบ:** ตรวจสอบ แก้ไข จัดรูปแบบ และดาวน์โหลดสำรองไฟล์ `freesr-data.json` และ `persistent`
|
||||
|
||||
### 💻 7. คอนโซลคำสั่ง GM (GM & Server Console)
|
||||
### 💻 8. คอนโซลคำสั่ง GM (GM & Server Console)
|
||||
- ส่งคำสั่ง GM ไปยังเซิร์ฟเวอร์ได้โดยตรงผ่านหน้าเว็บ
|
||||
- ดูผลตอบกลับและตรวจสอบสถานะการทำงานของเซิร์ฟเวอร์แบบ Real-time
|
||||
|
||||
### 🌐 8. รองรับ 13 ภาษาทั่วโลก (Multi-Language Support)
|
||||
### 🌐 9. รองรับ 13 ภาษาทั่วโลก (Multi-Language Support)
|
||||
- สลับเปลี่ยนภาษาได้ทันทีโดยไม่ต้องรีเฟรชหน้าเว็บ:
|
||||
- 🇹🇭 ไทย (Thai)
|
||||
- 🇺🇸 English
|
||||
@@ -103,6 +109,7 @@ GaC-Srtool สื่อสารกับเซิร์ฟเวอร์ GaC-h
|
||||
| --- | --- | --- |
|
||||
| `/api/status` | GET | ข้อมูลสถานะเซิร์ฟเวอร์, ทีม, ตัวละคร และพิกัด |
|
||||
| `/api/freesr` | GET, POST | ดึงหรืออัปเดตข้อมูล `freesr-data.json` |
|
||||
| `/api/inventory/materials` | GET, POST | อ่าน Client item catalog และบันทึกจำนวนไอเทมแบบเจาะจงโดยไม่เขียนทับข้อมูลอื่น |
|
||||
| `/api/freesr/avatars` | GET, POST | ดึงหรืออัปเดตข้อมูลตัวละครและสถิติ |
|
||||
| `/api/persistent` | GET, POST | ดึงหรืออัปเดตข้อมูล `persistent` |
|
||||
| `/api/battle_report` | GET, POST | ดึงสถิติดาเมจและผลการต่อสู้ล่าสุด |
|
||||
|
||||
+43
@@ -55,6 +55,7 @@
|
||||
<button class="tab-btn active" data-tab="tab-dashboard" data-i18n="tabDashboard">📊 แดชบอร์ด & จัดทีม</button>
|
||||
<button class="tab-btn" data-tab="tab-avatars" data-i18n="tabAvatars">👥 ตัวละคร</button>
|
||||
<button class="tab-btn" data-tab="tab-equipment" data-i18n="tabEquipment">🗡️ กรวยแสง & รีลิกส์</button>
|
||||
<button class="tab-btn" data-tab="tab-inventory" data-i18n="tabInventory">🎒 ไอเทมในกระเป๋า</button>
|
||||
<button class="tab-btn" data-tab="tab-damage-analyst" id="tab-btn-damage-analyst" data-i18n="tabDamageAnalyst">💥 Damage Analyst</button>
|
||||
<button class="tab-btn" data-tab="tab-persistent" id="tab-btn-persistent" data-i18n="tabPersistent">💾 จัดการเซฟ GaC</button>
|
||||
<button class="tab-btn" data-tab="tab-exrank" id="tab-btn-exrank" data-i18n="tabExrank">⚡ สกิลพิเศษ Ex-Rank</button>
|
||||
@@ -398,6 +399,48 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Material inventory editor: edits only Client-catalog item quantities. -->
|
||||
<section id="tab-inventory" class="tab-content">
|
||||
<div class="card" style="margin-bottom: 1rem;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;flex-wrap:wrap;">
|
||||
<div>
|
||||
<div class="card-title" data-i18n="inventoryTitle">🎒 จัดการจำนวนไอเทม</div>
|
||||
<p class="card-subtitle" data-i18n="inventoryDescription">ค้นหาด้วย Item ID แล้วแก้จำนวนหลายรายการก่อนบันทึกครั้งเดียว รายการมาจาก catalog ของ Client ที่เซิร์ฟเวอร์ใช้ ช่องว่างหมายถึงใช้ค่าเริ่มต้นของเซิร์ฟเวอร์</p>
|
||||
</div>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;">
|
||||
<span id="inventory-count" class="badge-super-tag">—</span>
|
||||
<span id="inventory-dirty-count" class="badge-super-tag" style="display:none;">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:.65rem;align-items:center;flex-wrap:wrap;margin-top:1rem;">
|
||||
<input type="search" id="inventory-search" class="form-input" style="flex:1;min-width:220px;" data-i18n-placeholder="inventorySearchPlaceholder" placeholder="ค้นหา Item ID...">
|
||||
<button type="button" class="btn btn-secondary" id="btn-inventory-discard" disabled data-i18n="inventoryDiscard">ทิ้งการแก้ไข</button>
|
||||
<button type="button" class="btn btn-success" id="btn-inventory-save" disabled data-i18n="inventorySave">บันทึกการเปลี่ยนแปลง</button>
|
||||
</div>
|
||||
<div id="inventory-status" role="status" aria-live="polite" style="font-size:.8rem;color:var(--text-muted);margin-top:.7rem;" data-i18n="inventoryLoadHint">กำลังโหลดรายการจากเซิร์ฟเวอร์...</div>
|
||||
</div>
|
||||
<div class="card" style="padding:0;overflow:hidden;">
|
||||
<div style="overflow:auto;max-height:65vh;">
|
||||
<table style="width:100%;border-collapse:collapse;min-width:420px;">
|
||||
<thead style="position:sticky;top:0;background:var(--bg-card-hover);z-index:1;">
|
||||
<tr>
|
||||
<th style="text-align:left;padding:.8rem 1rem;" data-i18n="inventoryItemId">Item ID</th>
|
||||
<th style="text-align:left;padding:.8rem 1rem;" data-i18n="inventoryDefaultCount">จำนวนในกระเป๋า</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="inventory-materials-body">
|
||||
<tr><td colspan="2" style="padding:1.5rem;text-align:center;color:var(--text-muted);" data-i18n="inventoryLoadHint">กำลังโหลดรายการจากเซิร์ฟเวอร์...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;gap:.75rem;padding:.75rem 1rem;border-top:1px solid var(--border-color);">
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="btn-inventory-prev" disabled data-i18n="inventoryPrevious">ก่อนหน้า</button>
|
||||
<span id="inventory-page-label" style="font-size:.8rem;color:var(--text-muted);"></span>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="btn-inventory-next" disabled data-i18n="inventoryNext">ถัดไป</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3.5. Damage Analyst (In-Game Battle Telemetry & Damage Analysis) -->
|
||||
<section id="tab-damage-analyst" class="tab-content">
|
||||
<!-- Toolbar & Control Card -->
|
||||
|
||||
@@ -43,7 +43,11 @@ const state = {
|
||||
defaultCreative: localStorage.getItem('gac_srtool_default_creative') === 'true',
|
||||
showcaseRolls: localStorage.getItem('gac_srtool_showcase_rolls') !== 'false',
|
||||
showcaseAura: localStorage.getItem('gac_srtool_showcase_aura') !== 'false',
|
||||
eqActiveSubtab: 'lc'
|
||||
eqActiveSubtab: 'lc',
|
||||
inventoryItems: [],
|
||||
inventorySaved: {},
|
||||
inventoryDraft: {},
|
||||
inventoryPage: 0
|
||||
};
|
||||
|
||||
// UI Elements Map
|
||||
@@ -183,6 +187,18 @@ const el = {
|
||||
toggleAhaGlobal: document.getElementById('toggle-aha-global'),
|
||||
btnSavePersistentToggles: document.getElementById('btn-save-persistent-toggles'),
|
||||
|
||||
// Material Inventory
|
||||
inventorySearch: document.getElementById('inventory-search'),
|
||||
inventoryBody: document.getElementById('inventory-materials-body'),
|
||||
inventoryCount: document.getElementById('inventory-count'),
|
||||
inventoryDirtyCount: document.getElementById('inventory-dirty-count'),
|
||||
inventoryStatus: document.getElementById('inventory-status'),
|
||||
inventoryPageLabel: document.getElementById('inventory-page-label'),
|
||||
btnInventorySave: document.getElementById('btn-inventory-save'),
|
||||
btnInventoryDiscard: document.getElementById('btn-inventory-discard'),
|
||||
btnInventoryPrev: document.getElementById('btn-inventory-prev'),
|
||||
btnInventoryNext: document.getElementById('btn-inventory-next'),
|
||||
|
||||
// Ex-Rank
|
||||
tabBtnExrank: document.getElementById('tab-btn-exrank'),
|
||||
exrankList: document.getElementById('exrank-list'),
|
||||
@@ -1070,7 +1086,18 @@ async function refreshStatus() {
|
||||
try {
|
||||
let data;
|
||||
if (state.serverMode === 'gac') {
|
||||
data = await apiCall('/api/status');
|
||||
const [status, freesr] = await Promise.all([
|
||||
apiCall('/api/status'),
|
||||
apiCall('/api/freesr').catch(() => null)
|
||||
]);
|
||||
data = status;
|
||||
// GAC status contains persistent settings and lineup only. Refresh the
|
||||
// FreeSR snapshot separately so the avatar editor shows the server's
|
||||
// latest skills/Trace values instead of a stale browser cache.
|
||||
if (freesr?.data) {
|
||||
state.fullFreesrData = freesr.data;
|
||||
await saveFreesrCache(freesr.data);
|
||||
}
|
||||
} else if (state.serverMode === 'firefly') {
|
||||
// FireflyGo uses data-in-game.json for live state and freesr-data.json for inventory
|
||||
const datagame = await apiCall('/api/data-in-game').catch(() => null);
|
||||
@@ -6820,6 +6847,8 @@ function setupTabs() {
|
||||
renderAvatarsGrid();
|
||||
} else if (targetId === 'tab-equipment') {
|
||||
loadEquipment();
|
||||
} else if (targetId === 'tab-inventory') {
|
||||
loadMaterialsInventory();
|
||||
} else if (targetId === 'tab-persistent') {
|
||||
loadPersistentSettings();
|
||||
} else if (targetId === 'tab-json') {
|
||||
@@ -6832,6 +6861,129 @@ function setupTabs() {
|
||||
});
|
||||
}
|
||||
|
||||
const INVENTORY_PAGE_SIZE = 100;
|
||||
|
||||
function inventoryFilteredIds() {
|
||||
const query = (el.inventorySearch?.value || '').trim();
|
||||
if (!query) return state.inventoryItems;
|
||||
return state.inventoryItems.filter(id => String(id).includes(query));
|
||||
}
|
||||
|
||||
function inventoryDirtyIds() {
|
||||
return state.inventoryItems.filter(id => {
|
||||
const key = String(id);
|
||||
if (!Object.prototype.hasOwnProperty.call(state.inventoryDraft, key)) return false;
|
||||
const hasSaved = Object.prototype.hasOwnProperty.call(state.inventorySaved, key);
|
||||
const draft = state.inventoryDraft[key];
|
||||
return draft === null ? hasSaved : (!hasSaved || draft !== state.inventorySaved[key]);
|
||||
});
|
||||
}
|
||||
|
||||
function renderMaterialsInventory() {
|
||||
if (!el.inventoryBody) return;
|
||||
const ids = inventoryFilteredIds();
|
||||
const dirty = inventoryDirtyIds();
|
||||
const pageCount = Math.max(1, Math.ceil(ids.length / INVENTORY_PAGE_SIZE));
|
||||
state.inventoryPage = Math.min(state.inventoryPage, pageCount - 1);
|
||||
const start = state.inventoryPage * INVENTORY_PAGE_SIZE;
|
||||
const pageIds = ids.slice(start, start + INVENTORY_PAGE_SIZE);
|
||||
|
||||
el.inventoryCount.textContent = t('inventoryItemTotal', { count: state.inventoryItems.length });
|
||||
el.inventoryDirtyCount.textContent = t('inventoryPending', { count: dirty.length });
|
||||
el.inventoryDirtyCount.style.display = dirty.length ? '' : 'none';
|
||||
el.btnInventorySave.disabled = dirty.length === 0;
|
||||
el.btnInventoryDiscard.disabled = dirty.length === 0;
|
||||
el.btnInventoryPrev.disabled = state.inventoryPage === 0;
|
||||
el.btnInventoryNext.disabled = state.inventoryPage >= pageCount - 1;
|
||||
el.inventoryPageLabel.textContent = t('inventoryPage', { page: state.inventoryPage + 1, pages: pageCount, count: ids.length });
|
||||
|
||||
if (!pageIds.length) {
|
||||
el.inventoryBody.innerHTML = `<tr><td colspan="2" style="padding:1.5rem;text-align:center;color:var(--text-muted);">${t('inventoryNoMatch')}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
el.inventoryBody.innerHTML = pageIds.map(id => {
|
||||
const key = String(id);
|
||||
const hasSaved = Object.prototype.hasOwnProperty.call(state.inventorySaved, key);
|
||||
const value = Object.prototype.hasOwnProperty.call(state.inventoryDraft, key)
|
||||
? state.inventoryDraft[key]
|
||||
: (hasSaved ? state.inventorySaved[key] : '');
|
||||
const changed = Object.prototype.hasOwnProperty.call(state.inventoryDraft, key)
|
||||
&& (value === null ? hasSaved : (!hasSaved || value !== state.inventorySaved[key]));
|
||||
const displayValue = value === null ? '' : value;
|
||||
return `<tr style="border-top:1px solid var(--border-color);${changed ? 'background:rgba(245,158,11,.08);' : ''}">
|
||||
<td style="padding:.55rem 1rem;font-family:monospace;">${id}</td>
|
||||
<td style="padding:.4rem 1rem;"><input type="number" class="form-input inventory-count-input" data-item-id="${id}" min="0" max="4294967295" step="1" value="${displayValue}" placeholder="${t('inventoryDefaultPlaceholder')}" aria-label="${t('inventoryCountFor', { id })}" style="max-width:220px;"></td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function loadMaterialsInventory() {
|
||||
if (!el.inventoryStatus) return;
|
||||
if (state.serverMode !== 'gac') {
|
||||
el.inventoryStatus.textContent = t('inventoryGaCOnly');
|
||||
return;
|
||||
}
|
||||
el.inventoryStatus.textContent = t('inventoryLoadHint');
|
||||
try {
|
||||
const result = await apiCall('/api/inventory/materials');
|
||||
if (!Array.isArray(result.item_ids)) throw new Error('Server returned an invalid item catalog');
|
||||
state.inventoryItems = [...new Set([...result.item_ids, 1, 2])].filter(Number.isInteger).sort((a, b) => a - b);
|
||||
state.inventorySaved = result.materials && typeof result.materials === 'object' ? result.materials : {};
|
||||
state.inventoryDraft = {};
|
||||
state.inventoryPage = 0;
|
||||
el.inventoryStatus.textContent = t('inventoryLoadSuccess', { count: state.inventoryItems.length });
|
||||
renderMaterialsInventory();
|
||||
} catch (error) {
|
||||
el.inventoryStatus.textContent = `${t('inventoryLoadFailed')}: ${error.message}`;
|
||||
el.inventoryBody.innerHTML = `<tr><td colspan="2" style="padding:1.5rem;text-align:center;color:var(--text-muted);">${t('inventoryLoadFailed')}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveMaterialsInventory() {
|
||||
const updates = inventoryDirtyIds().map(id => {
|
||||
const count = state.inventoryDraft[String(id)];
|
||||
return count === null ? { item_id: id, reset: true } : { item_id: id, count };
|
||||
});
|
||||
if (!updates.length) return;
|
||||
el.btnInventorySave.disabled = true;
|
||||
el.inventoryStatus.textContent = t('inventorySaving', { count: updates.length });
|
||||
try {
|
||||
const result = await apiCall('/api/inventory/materials', 'POST', { updates });
|
||||
state.inventorySaved = result.materials || state.inventorySaved;
|
||||
state.inventoryDraft = {};
|
||||
if (state.fullFreesrData) {
|
||||
state.fullFreesrData.materials = { ...state.inventorySaved };
|
||||
await saveFreesrCache(state.fullFreesrData);
|
||||
}
|
||||
el.inventoryStatus.textContent = t('inventorySaveSuccess', { count: updates.length });
|
||||
renderMaterialsInventory();
|
||||
} catch (error) {
|
||||
el.inventoryStatus.textContent = `${t('inventorySaveFailed')}: ${error.message}`;
|
||||
renderMaterialsInventory();
|
||||
}
|
||||
}
|
||||
|
||||
function setupInventoryEvents() {
|
||||
el.inventorySearch?.addEventListener('input', () => { state.inventoryPage = 0; renderMaterialsInventory(); });
|
||||
el.inventoryBody?.addEventListener('change', event => {
|
||||
const input = event.target.closest('.inventory-count-input');
|
||||
if (!input) return;
|
||||
const raw = input.value.trim();
|
||||
if (raw === '') {
|
||||
state.inventoryDraft[input.dataset.itemId] = null;
|
||||
} else {
|
||||
const value = Number(raw);
|
||||
if (!Number.isSafeInteger(value) || value < 0 || value > 4294967295) return;
|
||||
state.inventoryDraft[input.dataset.itemId] = value;
|
||||
}
|
||||
renderMaterialsInventory();
|
||||
});
|
||||
el.btnInventorySave?.addEventListener('click', saveMaterialsInventory);
|
||||
el.btnInventoryDiscard?.addEventListener('click', () => { state.inventoryDraft = {}; renderMaterialsInventory(); });
|
||||
el.btnInventoryPrev?.addEventListener('click', () => { state.inventoryPage = Math.max(0, state.inventoryPage - 1); renderMaterialsInventory(); });
|
||||
el.btnInventoryNext?.addEventListener('click', () => { state.inventoryPage++; renderMaterialsInventory(); });
|
||||
}
|
||||
|
||||
// Setup Event Listeners
|
||||
function setupEvents() {
|
||||
// Quick Ping & Refresh
|
||||
@@ -8382,6 +8534,7 @@ async function init() {
|
||||
setupTabs();
|
||||
setupModalSubtabs();
|
||||
setupEvents();
|
||||
setupInventoryEvents();
|
||||
setupDamageAnalystEvents();
|
||||
setupGMConsoleEvents();
|
||||
applyTranslations();
|
||||
|
||||
+46
@@ -133,6 +133,29 @@ export const TRANSLATIONS = {
|
||||
"tabDashboard": "📊 แดชบอร์ด & จัดทีม",
|
||||
"tabAvatars": "👥 ตัวละคร",
|
||||
"tabEquipment": "🗡️ กรวยแสง & รีลิกส์",
|
||||
"tabInventory": "🎒 ไอเทมในกระเป๋า",
|
||||
"inventoryTitle": "🎒 จัดการจำนวนไอเทม",
|
||||
"inventoryDescription": "ค้นหาด้วย Item ID แล้วแก้จำนวนหลายรายการก่อนบันทึกครั้งเดียว รายการมาจาก catalog ของ Client ที่เซิร์ฟเวอร์ใช้ ช่องว่างหมายถึงใช้ค่าเริ่มต้นของเซิร์ฟเวอร์",
|
||||
"inventorySearchPlaceholder": "ค้นหา Item ID...",
|
||||
"inventoryDiscard": "ทิ้งการแก้ไข",
|
||||
"inventorySave": "บันทึกการเปลี่ยนแปลง",
|
||||
"inventoryLoadHint": "กำลังโหลดรายการจากเซิร์ฟเวอร์...",
|
||||
"inventoryItemTotal": "{count} รายการ",
|
||||
"inventoryPending": "รอบันทึก {count}",
|
||||
"inventoryPage": "หน้า {page}/{pages} • {count} รายการ",
|
||||
"inventoryNoMatch": "ไม่พบ Item ID ที่ค้นหา",
|
||||
"inventoryCountFor": "จำนวนไอเทม {id}",
|
||||
"inventoryDefaultPlaceholder": "ค่าเริ่มต้นเซิร์ฟเวอร์",
|
||||
"inventoryGaCOnly": "ตัวจัดการนี้ใช้ได้กับ GaC-hkrpg-go เท่านั้น",
|
||||
"inventoryLoadSuccess": "โหลด catalog แล้ว {count} รายการ • ค่าเริ่มต้นจะใช้จำนวนของเซิร์ฟเวอร์",
|
||||
"inventoryLoadFailed": "โหลด catalog ไม่สำเร็จ",
|
||||
"inventorySaving": "กำลังบันทึก {count} รายการ...",
|
||||
"inventorySaveSuccess": "บันทึก {count} รายการแล้ว • เข้าเกมใหม่เพื่อโหลดค่าเข้ากระเป๋า",
|
||||
"inventorySaveFailed": "บันทึกไม่สำเร็จ",
|
||||
"inventoryItemId": "Item ID",
|
||||
"inventoryDefaultCount": "จำนวนในกระเป๋า",
|
||||
"inventoryPrevious": "ก่อนหน้า",
|
||||
"inventoryNext": "ถัดไป",
|
||||
"tabPersistent": "💾 จัดการเซฟ GaC",
|
||||
"tabExrank": "⚡ สกิลพิเศษ Ex-Rank",
|
||||
"tabJson": "📝 จัดการไฟล์ JSON",
|
||||
@@ -580,6 +603,29 @@ export const TRANSLATIONS = {
|
||||
"tabDashboard": "📊 Dashboard & Party",
|
||||
"tabAvatars": "👥 Characters",
|
||||
"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.",
|
||||
"inventorySearchPlaceholder": "Search item ID...",
|
||||
"inventoryDiscard": "Discard edits",
|
||||
"inventorySave": "Save changes",
|
||||
"inventoryLoadHint": "Loading the server catalog...",
|
||||
"inventoryItemTotal": "{count} items",
|
||||
"inventoryPending": "{count} pending",
|
||||
"inventoryPage": "Page {page}/{pages} • {count} items",
|
||||
"inventoryNoMatch": "No matching item IDs",
|
||||
"inventoryCountFor": "Quantity for item {id}",
|
||||
"inventoryDefaultPlaceholder": "Server default",
|
||||
"inventoryGaCOnly": "This editor is available with GaC-hkrpg-go only.",
|
||||
"inventoryLoadSuccess": "Loaded {count} catalog items • unchanged items use the server default",
|
||||
"inventoryLoadFailed": "Could not load the catalog",
|
||||
"inventorySaving": "Saving {count} items...",
|
||||
"inventorySaveSuccess": "Saved {count} items • log in again to load the updated bag",
|
||||
"inventorySaveFailed": "Could not save inventory",
|
||||
"inventoryItemId": "Item ID",
|
||||
"inventoryDefaultCount": "Bag quantity",
|
||||
"inventoryPrevious": "Previous",
|
||||
"inventoryNext": "Next",
|
||||
"tabPersistent": "💾 GaC Save Manager",
|
||||
"tabExrank": "⚡ Ex-Rank Skills",
|
||||
"tabJson": "📝 JSON Management",
|
||||
|
||||
Reference in new issue
Block a user