From 707071c5022dff5ea6c7139b747d6e7b4243128d Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Sat, 17 Oct 2026 11:08:20 +0700 Subject: [PATCH] feat(server): unlock creative mode for all servers and adapt file mechanisms for firefly and freesr --- index.html | 5 +- js/app.js | 424 +++++++++++++++++++++++++++++++++++++++++++++-------- js/i18n.js | 2 + 3 files changed, 370 insertions(+), 61 deletions(-) diff --git a/index.html b/index.html index d0e7d03..b413906 100644 --- a/index.html +++ b/index.html @@ -392,7 +392,7 @@
-
+
👤 ผู้บุกเบิก & March 7th
@@ -424,7 +424,7 @@
-
+
⚙️ สวิตช์ระบบพิเศษของ GaC
@@ -495,6 +495,7 @@
+
diff --git a/js/app.js b/js/app.js index 9891fad..2c1ee64 100644 --- a/js/app.js +++ b/js/app.js @@ -32,6 +32,7 @@ const state = { avatarsList: [], fullFreesrData: null, persistentData: null, + dataGameData: null, currentJsonMode: 'freesr', activeTab: 'tab-dashboard', selectedLineupSlot: null, @@ -153,6 +154,8 @@ const el = { // Persistent Tab tabBtnPersistent: document.getElementById('tab-btn-persistent'), + cardPersistentChars: document.getElementById('card-persistent-chars'), + cardPersistentGlobal: document.getElementById('card-persistent-global'), formPersistentChars: document.getElementById('form-persistent-chars'), persistentMcSelect: document.getElementById('persistent-mc-select'), persistentMarchSelect: document.getElementById('persistent-march-select'), @@ -170,6 +173,7 @@ const el = { // JSON Tab btnSelectFreesr: document.getElementById('btn-select-freesr'), btnSelectPersistent: document.getElementById('btn-select-persistent'), + btnSelectDatagame: document.getElementById('btn-select-datagame'), btnJsonBeautify: document.getElementById('btn-json-beautify'), btnJsonDownload: document.getElementById('btn-json-download'), btnJsonSave: document.getElementById('btn-json-save'), @@ -654,6 +658,91 @@ async function savePersistentCache(data) { updateCacheUsageDisplay(); } +// FireflyGo data-in-game.json structure and path mappings (from FFGO dumps) +const DEFAULT_DATA_GAME = { + leader: 0, + lineups: { + "0": 1506, + "1": 1502, + "2": 1511, + "3": 1503 + }, + position: { + x: 8000, + z: 1550, + y: 715, + rot_y: 131000 + }, + scene: { + plane_id: 80112, + floor_id: 80112001, + entry_id: 801120102, + map_rotation_group_id: 0 + }, + player_outfit: [ 1003 ], + char_path: { + main: 8010, + march_7: 1224 + }, + char_enhanced: { + "1004": 1, + "1005": 0, + "1006": 1, + "1102": 1, + "1205": 1, + "1212": 1, + "1217": 1, + "1306": 1, + "1307": 1, + "1310": 1 + } +}; + +const FIREFLY_MC_MAP = { + 'MalePhysical': 8001, + 'FemalePhysical': 8002, + 'MalePreservation': 8003, + 'FemalePreservation': 8004, + 'MaleHarmony': 8005, + 'FemaleHarmony': 8010, + 'MaleRemembrance': 8011, + 'FemaleRemembrance': 8012 +}; +const FIREFLY_MC_REVERSE_MAP = { + 8001: 'MalePhysical', + 8002: 'FemalePhysical', + 8003: 'MalePreservation', + 8004: 'FemalePreservation', + 8005: 'MaleHarmony', + 8006: 'FemaleHarmony', + 8010: 'FemaleHarmony', + 8011: 'MaleRemembrance', + 8012: 'FemaleRemembrance' +}; +const FIREFLY_MARCH_MAP = { + 'MarchPreservation': 1001, + 'MarchHunt': 1224 +}; +const FIREFLY_MARCH_REVERSE_MAP = { + 1001: 'MarchPreservation', + 1224: 'MarchHunt' +}; + +async function saveDataGameCache(data) { + if (!data) return; + try { + await idbSet('gac_srtool_datagame_cache', data); + } catch (e) { + console.warn('Failed to save data-in-game cache to IndexedDB:', e); + } + try { + localStorage.setItem('gac_srtool_datagame_cache', JSON.stringify(data)); + } catch (e) { + console.warn('localStorage quota exceeded for data-in-game cache:', e); + } + updateCacheUsageDisplay(); +} + async function loadCachedStorageData() { try { let cachedFreesr = await idbGet('gac_srtool_freesr_cache'); @@ -699,6 +788,23 @@ async function loadCachedStorageData() { } catch (e) { console.warn('Failed to load cached persistent data:', e); } + + try { + let cachedDataGame = await idbGet('gac_srtool_datagame_cache'); + if (!cachedDataGame) { + const raw = localStorage.getItem('gac_srtool_datagame_cache'); + if (raw) cachedDataGame = JSON.parse(raw); + } + if (cachedDataGame) { + state.dataGameData = cachedDataGame; + if (state.serverMode === 'firefly' && cachedDataGame.lineups && (!state.statusData || !state.statusData.lineups)) { + state.statusData = state.statusData || {}; + state.statusData.lineups = cachedDataGame.lineups; + } + } + } catch (e) { + console.warn('Failed to load cached data-in-game data:', e); + } } // Apply multi-language translations across UI @@ -762,28 +868,67 @@ function setupServerModes() { if (mode === 'gac') { if (el.lblActiveMode) el.lblActiveMode.textContent = 'GaC-hkrpg-go'; - if (el.tabBtnPersistent) el.tabBtnPersistent.style.display = ''; + if (el.tabBtnPersistent) { + el.tabBtnPersistent.style.display = ''; + el.tabBtnPersistent.textContent = '💾 จัดการเซฟ GaC'; + } + if (el.cardPersistentChars) el.cardPersistentChars.style.display = ''; + if (el.cardPersistentGlobal) el.cardPersistentGlobal.style.display = ''; if (el.tabBtnExrank) el.tabBtnExrank.style.display = ''; if (el.btnQuickSyncPersistent) el.btnQuickSyncPersistent.style.display = ''; if (el.btnSelectPersistent) el.btnSelectPersistent.style.display = ''; - } else { - if (el.lblActiveMode) el.lblActiveMode.textContent = mode === 'firefly' ? 'FireflyGo' : 'RobinSR'; + if (el.btnSelectDatagame) el.btnSelectDatagame.style.display = 'none'; + if (el.badgePersistenceMode) el.badgePersistenceMode.textContent = 'GaC: persistent.json (/api/persistent)'; + } else if (mode === 'firefly') { + if (el.lblActiveMode) el.lblActiveMode.textContent = 'FireflyGo'; + if (el.tabBtnPersistent) { + el.tabBtnPersistent.style.display = ''; + el.tabBtnPersistent.textContent = '👤 ผู้บุกเบิก & March 7th'; + } + if (el.cardPersistentChars) el.cardPersistentChars.style.display = ''; + if (el.cardPersistentGlobal) el.cardPersistentGlobal.style.display = 'none'; // Hide unsupported GaC switches + if (el.tabBtnExrank) el.tabBtnExrank.style.display = 'none'; // Hide unsupported Ex-ranks + if (el.btnQuickSyncPersistent) el.btnQuickSyncPersistent.style.display = 'none'; + if (el.btnSelectPersistent) el.btnSelectPersistent.style.display = 'none'; + if (el.btnSelectDatagame) el.btnSelectDatagame.style.display = ''; // Show data-in-game for Firefly + if (el.badgePersistenceMode) el.badgePersistenceMode.textContent = 'FireflyGo: data-in-game.json & freesr-data.json'; + + if (state.activeTab === 'tab-exrank') { + const dashboardBtn = document.querySelector('.tab-btn[data-tab="tab-dashboard"]'); + if (dashboardBtn) dashboardBtn.click(); + } + + if (state.currentJsonMode === 'persistent') { + loadJsonData('datagame'); + } + } else { // robin + if (el.lblActiveMode) el.lblActiveMode.textContent = 'RobinSR'; if (el.tabBtnPersistent) el.tabBtnPersistent.style.display = 'none'; + if (el.cardPersistentGlobal) el.cardPersistentGlobal.style.display = 'none'; if (el.tabBtnExrank) el.tabBtnExrank.style.display = 'none'; if (el.btnQuickSyncPersistent) el.btnQuickSyncPersistent.style.display = 'none'; if (el.btnSelectPersistent) el.btnSelectPersistent.style.display = 'none'; + if (el.btnSelectDatagame) el.btnSelectDatagame.style.display = 'none'; + if (el.badgePersistenceMode) el.badgePersistenceMode.textContent = 'RobinSR: freesr-data.json (/api/freesr)'; - // If user is currently viewing a hidden tab, auto switch back to dashboard if (state.activeTab === 'tab-persistent' || state.activeTab === 'tab-exrank') { const dashboardBtn = document.querySelector('.tab-btn[data-tab="tab-dashboard"]'); if (dashboardBtn) dashboardBtn.click(); } - // If user is viewing persistent JSON, switch back to freesr - if (state.currentJsonMode === 'persistent') { + if (state.currentJsonMode === 'persistent' || state.currentJsonMode === 'datagame') { loadJsonData('freesr'); } } + + // Creative Mode is OPEN for all server modes! + if (el.toggleCreativeMode) { + el.toggleCreativeMode.disabled = false; + el.toggleCreativeMode.checked = state.creativeMode; + } + if (el.badgeCreativeMode) { + el.badgeCreativeMode.style.display = state.creativeMode ? 'inline-flex' : 'none'; + } }; if (el.serverModeSelect) { @@ -850,9 +995,44 @@ async function refreshStatus() { let data; if (state.serverMode === 'gac') { data = await apiCall('/api/status'); - } else { + } 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); + if (datagame && (datagame.data || datagame.lineups)) { + state.dataGameData = datagame.data || datagame; + await saveDataGameCache(state.dataGameData); + } const freesr = await apiCall('/api/freesr').catch(() => null); if (freesr && freesr.data) { + state.fullFreesrData = freesr.data; + data = { + status: 200, + avatar_count: Object.keys(freesr.data.avatars || {}).length, + lightcone_count: (freesr.data.lightcones || []).length, + relic_count: (freesr.data.relics || []).length, + lineups: state.dataGameData?.lineups || freesr.data.lineups || freesr.data.battle_config?.custom_battle_lineup || DEFAULT_LINEUP, + scene: state.dataGameData?.scene || freesr.data.scene, + position: state.dataGameData?.position || freesr.data.position, + ex_ranks: [] + }; + } else if (state.dataGameData) { + data = { + status: 200, + avatar_count: state.avatarsList.length, + lightcone_count: 0, + relic_count: 0, + lineups: state.dataGameData.lineups || DEFAULT_LINEUP, + scene: state.dataGameData.scene, + position: state.dataGameData.position, + ex_ranks: [] + }; + } else { + throw new Error('Firefly server not reachable'); + } + } else { // robin + const freesr = await apiCall('/api/freesr').catch(() => null); + if (freesr && freesr.data) { + state.fullFreesrData = freesr.data; data = { status: 200, avatar_count: Object.keys(freesr.data.avatars || {}).length, @@ -890,24 +1070,21 @@ async function refreshStatus() { if (state.serverMode === 'gac') { el.badgePersistenceMode.textContent = 'GaC: persistent.json (/api/persistent)'; el.badgePersistenceMode.className = 'badge-creative'; + } else if (state.serverMode === 'firefly') { + el.badgePersistenceMode.textContent = 'FireflyGo: data-in-game.json & freesr-data.json'; + el.badgePersistenceMode.className = 'badge-super-tag'; } else { - el.badgePersistenceMode.textContent = 'FireflyGo: freesr-data.json (/api/freesr)'; + el.badgePersistenceMode.textContent = 'RobinSR: freesr-data.json (/api/freesr)'; el.badgePersistenceMode.className = 'badge-super-tag'; } } + // Creative Mode is OPEN for all servers if (el.toggleCreativeMode) { - if (state.serverMode !== 'gac') { - el.toggleCreativeMode.disabled = true; - el.toggleCreativeMode.checked = false; - state.creativeMode = false; - if (el.badgeCreativeMode) el.badgeCreativeMode.style.display = 'none'; - } else { - el.toggleCreativeMode.disabled = false; - el.toggleCreativeMode.checked = state.creativeMode; - if (el.badgeCreativeMode) { - el.badgeCreativeMode.style.display = state.creativeMode ? 'inline-flex' : 'none'; - } + el.toggleCreativeMode.disabled = false; + el.toggleCreativeMode.checked = state.creativeMode; + if (el.badgeCreativeMode) { + el.badgeCreativeMode.style.display = state.creativeMode ? 'inline-flex' : 'none'; } } @@ -933,8 +1110,11 @@ async function refreshStatus() { if (state.serverMode === 'gac') { el.badgePersistenceMode.textContent = 'GaC: persistent.json (/api/persistent)'; el.badgePersistenceMode.className = 'badge-creative'; + } else if (state.serverMode === 'firefly') { + el.badgePersistenceMode.textContent = 'FireflyGo: data-in-game.json & freesr-data.json'; + el.badgePersistenceMode.className = 'badge-super-tag'; } else { - el.badgePersistenceMode.textContent = 'FireflyGo: freesr-data.json (/api/freesr)'; + el.badgePersistenceMode.textContent = 'RobinSR: freesr-data.json (/api/freesr)'; el.badgePersistenceMode.className = 'badge-super-tag'; } } @@ -947,12 +1127,9 @@ async function refreshStatus() { // Render party lineup cards function renderLineup(lineups) { - if (state.serverMode !== 'gac') { - state.creativeMode = false; - } if (el.toggleCreativeMode) { el.toggleCreativeMode.checked = state.creativeMode; - el.toggleCreativeMode.disabled = (state.serverMode !== 'gac'); + el.toggleCreativeMode.disabled = false; } if (el.badgeCreativeMode) { el.badgeCreativeMode.style.display = state.creativeMode ? 'inline-flex' : 'none'; @@ -3993,30 +4170,49 @@ async function exportShowcaseImage(avatarId) { showToast(t('showcaseExport') || 'เปิดหน้าต่างสำหรับบันทึกการ์ดเรียบร้อย', 'info'); } -// Server Lineup Persistence Manager (GaC Persistent vs FireflyGo FreeSR) +// Server Lineup Persistence Manager (GaC Persistent vs FireflyGo data-in-game vs FreeSR) async function saveLineupToServer(lineupData) { - if (!state.isConnected) return; + if (!lineupData) return; + + // 1. GaC server handles lineups in persistent if (state.serverMode === 'gac') { - // GaC server handles lineups in persistent - await apiCall('/api/persistent', 'POST', { lineups: lineupData }); - } else { - // FireflyGo / other FreeSR servers handle lineups in freesr-data.json - if (!state.fullFreesrData) { - state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] }; + if (state.persistentData) { + state.persistentData.lineups = lineupData; + savePersistentCache(state.persistentData); } - state.fullFreesrData.lineups = lineupData; - const arr = [ - lineupData[0] || 0, - lineupData[1] || 0, - lineupData[2] || 0, - lineupData[3] || 0 - ]; - if (!state.fullFreesrData.battle_config) { - state.fullFreesrData.battle_config = {}; + if (state.isConnected) { + await apiCall('/api/persistent', 'POST', { lineups: lineupData }).catch(() => {}); } - state.fullFreesrData.battle_config.custom_battle_lineup = arr; - state.fullFreesrData.custom_battle_lineup = arr; - await saveFreesrChangesToServer(); + } + + // 2. FireflyGo / FreeSR servers handle lineups in freesr-data.json + if (!state.fullFreesrData) { + state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] }; + } + state.fullFreesrData.lineups = { ...lineupData }; + if (!state.fullFreesrData.battle_config) { + state.fullFreesrData.battle_config = {}; + } + + // Dynamically map all active slots in order - never truncate slots in Creative Mode! + const slotKeys = Object.keys(lineupData).sort((a, b) => parseInt(a, 10) - parseInt(b, 10)); + const fullArr = slotKeys.map(k => lineupData[k] || 0); + state.fullFreesrData.battle_config.custom_battle_lineup = fullArr; + state.fullFreesrData.custom_battle_lineup = fullArr; + await saveFreesrCache(state.fullFreesrData); + + // 3. Firefly live state in data-in-game.json + if (state.dataGameData || state.serverMode === 'firefly') { + state.dataGameData = state.dataGameData || { ...DEFAULT_DATA_GAME }; + state.dataGameData.lineups = { ...lineupData }; + await saveDataGameCache(state.dataGameData); + if (state.isConnected) { + await apiCall('/api/data-in-game', 'POST', state.dataGameData).catch(() => {}); + } + } + + if (state.isConnected && state.serverMode !== 'gac') { + await saveFreesrChangesToServer().catch(() => {}); } } @@ -5070,9 +5266,27 @@ async function handleAddRelicSubmit(e) { showToast(t('toastRelicAdded'), 'success'); } -// Load Persistent Save Settings (Exclusive to GaC-hkrpg-go) +// Load Persistent / Character Path Settings (GaC persistent vs Firefly data-in-game) async function loadPersistentSettings() { - if (state.serverMode !== 'gac') return; + if (state.serverMode === 'robin') return; + + if (state.serverMode === 'firefly') { + // Firefly uses data-in-game.json char_path { main, march_7 } + let dg = state.dataGameData; + if (!dg) { + dg = await idbGet('gac_srtool_datagame_cache') || JSON.parse(localStorage.getItem('gac_srtool_datagame_cache') || 'null'); + if (dg) state.dataGameData = dg; + } + if (dg?.char_path) { + const mcKey = FIREFLY_MC_REVERSE_MAP[dg.char_path.main]; + if (mcKey && el.persistentMcSelect) el.persistentMcSelect.value = mcKey; + const marchKey = FIREFLY_MARCH_REVERSE_MAP[dg.char_path.march_7]; + if (marchKey && el.persistentMarchSelect) el.persistentMarchSelect.value = marchKey; + } + return; + } + + // GaC mode try { let data = state.persistentData; if (state.isConnected) { @@ -5180,7 +5394,8 @@ async function loadJsonData(mode) { state.currentJsonMode = mode; if (mode === 'freesr') { el.btnSelectFreesr.className = 'btn btn-sm btn-primary active'; - el.btnSelectPersistent.className = 'btn btn-sm btn-secondary'; + 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 = 'กำลังโหลด freesr-data.json...'; try { if (state.fullFreesrData) { @@ -5222,9 +5437,58 @@ async function loadJsonData(mode) { } } } + } else if (mode === 'datagame') { + el.btnSelectFreesr.className = 'btn btn-sm btn-secondary'; + if (el.btnSelectPersistent) el.btnSelectPersistent.className = 'btn btn-sm btn-secondary'; + if (el.btnSelectDatagame) el.btnSelectDatagame.className = 'btn btn-sm btn-primary active'; + el.jsonStatusInfo.textContent = 'กำลังโหลด data-in-game.json...'; + try { + if (state.dataGameData) { + el.jsonEditor.value = JSON.stringify(state.dataGameData, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โหลดจากหน่วยความจำ/แคช)'; + } else if (state.isConnected) { + const res = await apiCall('/api/data-in-game').catch(() => null); + if (res && (res.data || res.lineups)) { + const val = res.data || res; + state.dataGameData = val; + saveDataGameCache(val); + el.jsonEditor.value = JSON.stringify(val, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โหลดจากเซิร์ฟเวอร์)'; + } else { + throw new Error('Not available'); + } + } else { + const cached = localStorage.getItem('gac_srtool_datagame_cache'); + if (cached) { + const parsed = JSON.parse(cached); + state.dataGameData = parsed; + el.jsonEditor.value = JSON.stringify(parsed, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โหลดจากแคช)'; + } else { + state.dataGameData = { ...DEFAULT_DATA_GAME }; + el.jsonEditor.value = JSON.stringify(DEFAULT_DATA_GAME, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โครงสร้าง Firefly dump)'; + } + } + } catch (err) { + const cached = localStorage.getItem('gac_srtool_datagame_cache'); + if (cached) { + try { + const parsed = JSON.parse(cached); + state.dataGameData = parsed; + el.jsonEditor.value = JSON.stringify(parsed, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โหลดจากแคช)'; + } catch (e) {} + } else { + state.dataGameData = { ...DEFAULT_DATA_GAME }; + el.jsonEditor.value = JSON.stringify(DEFAULT_DATA_GAME, null, 2); + el.jsonStatusInfo.textContent = 'data-in-game.json (โครงสร้าง Firefly dump)'; + } + } } else { el.btnSelectFreesr.className = 'btn btn-sm btn-secondary'; - el.btnSelectPersistent.className = 'btn btn-sm btn-primary active'; + if (el.btnSelectPersistent) el.btnSelectPersistent.className = 'btn btn-sm btn-primary active'; + if (el.btnSelectDatagame) el.btnSelectDatagame.className = 'btn btn-sm btn-secondary'; el.jsonStatusInfo.textContent = 'กำลังโหลด persistent...'; try { if (state.persistentData) { @@ -5308,6 +5572,31 @@ async function saveJsonData() { } else { showToast('💾 บันทึก freesr-data.json ลงในแคชแล้ว (โหมดออฟไลน์)', 'info'); } + } else if (state.currentJsonMode === 'datagame') { + state.dataGameData = parsed; + await saveDataGameCache(parsed); + if (parsed.lineups) { + state.statusData = state.statusData || {}; + state.statusData.lineups = parsed.lineups; + renderLineup(parsed.lineups); + } + let serverSaved = false; + let serverError = null; + if (state.isConnected) { + try { + await apiCall('/api/data-in-game', 'POST', parsed); + serverSaved = true; + } catch (err) { + serverError = err.message; + } + } + if (serverSaved) { + showToast('✅ บันทึก data-in-game.json ลงในเซิร์ฟเวอร์และแคชเรียบร้อย', 'success'); + } else if (serverError) { + showToast(`⚠️ บันทึกลงแคชแล้ว แต่ส่งไปยังเซิร์ฟเวอร์ไม่สำเร็จ: ${serverError}`, 'warning'); + } else { + showToast('💾 บันทึก data-in-game.json ลงในแคชแล้ว (โหมดออฟไลน์)', 'info'); + } } else { state.persistentData = parsed; await savePersistentCache(parsed); @@ -5764,15 +6053,9 @@ function setupEvents() { // Creative Mode Toggle if (el.toggleCreativeMode) { - el.toggleCreativeMode.checked = state.creativeMode && state.serverMode === 'gac'; - el.toggleCreativeMode.disabled = (state.serverMode !== 'gac'); + el.toggleCreativeMode.checked = state.creativeMode; + el.toggleCreativeMode.disabled = false; el.toggleCreativeMode.addEventListener('change', (e) => { - if (state.serverMode !== 'gac') { - el.toggleCreativeMode.checked = false; - state.creativeMode = false; - showToast('โหมดสร้างสรรค์รองรับเฉพาะบนเซิร์ฟเวอร์ GaC เท่านั้น', 'warning'); - return; - } state.creativeMode = e.target.checked; localStorage.setItem('gac_srtool_creative_mode', state.creativeMode); if (el.badgeCreativeMode) { @@ -5793,11 +6076,33 @@ function setupEvents() { el.btnQuickClearLineup?.addEventListener('click', quickClearLineup); el.btnMaxAllAvatars?.addEventListener('click', maxAllAvatars); - // Persistent Form Submit + // Persistent / Character Path Form Submit el.formPersistentChars.addEventListener('submit', async (e) => { e.preventDefault(); const mc = el.persistentMcSelect.value; const march = el.persistentMarchSelect.value; + + if (state.serverMode === 'firefly') { + state.dataGameData = state.dataGameData || { ...DEFAULT_DATA_GAME }; + state.dataGameData.char_path = state.dataGameData.char_path || {}; + state.dataGameData.char_path.main = FIREFLY_MC_MAP[mc] || 8010; + state.dataGameData.char_path.march_7 = FIREFLY_MARCH_MAP[march] || 1224; + await saveDataGameCache(state.dataGameData); + + let serverUpdated = false; + if (state.isConnected) { + try { + await apiCall('/api/data-in-game', 'POST', state.dataGameData); + serverUpdated = true; + } catch (e) { + await saveFreesrChangesToServer().catch(() => {}); + } + } + showToast(serverUpdated ? 'บันทึกสายของตัวละคร (Firefly data-in-game) สำเร็จ!' : 'บันทึกสายตัวละครลงในแคชแล้ว (Firefly data-in-game)', 'success'); + refreshStatus(); + return; + } + try { await apiCall('/api/persistent', 'POST', { main_character: mc, @@ -5960,8 +6265,9 @@ function setupEvents() { }); // JSON Tab Controls - el.btnSelectFreesr.addEventListener('click', () => loadJsonData('freesr')); - el.btnSelectPersistent.addEventListener('click', () => loadJsonData('persistent')); + el.btnSelectFreesr?.addEventListener('click', () => loadJsonData('freesr')); + el.btnSelectPersistent?.addEventListener('click', () => loadJsonData('persistent')); + el.btnSelectDatagame?.addEventListener('click', () => loadJsonData('datagame')); el.btnJsonBeautify.addEventListener('click', () => { try { diff --git a/js/i18n.js b/js/i18n.js index cea79a7..59f4eaf 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -149,6 +149,7 @@ export const TRANSLATIONS = { "disabled": "ปิดใช้งาน", "btnFreesr": "📄 freesr-data.json", "btnPersistent": "💾 ไฟล์เซฟ persistent", + "btnDataGame": "🎮 data-in-game.json", "btnBeautify": "✨ จัดรูปแบบ JSON", "btnDownload": "📥 ดาวน์โหลดสำรอง", "btnUpload": "📤 อัปโหลดไฟล์", @@ -429,6 +430,7 @@ export const TRANSLATIONS = { "disabled": "disabled", "btnFreesr": "📄 freesr-data.json", "btnPersistent": "💾 persistent save", + "btnDataGame": "🎮 data-in-game.json", "btnBeautify": "✨ Beautify JSON", "btnDownload": "📥 Download Backup", "btnUpload": "📤 Upload File",