feat(server): unlock creative mode for all servers and adapt file mechanisms for firefly and freesr

This commit is contained in:
CAtlas0784 committed 2026-10-17 11:08:20 +07:00
1 parent bd367a11cd
commit 707071c502
3 files changed
+370 -61

No files matched your search

+3 -2
View File
@@ -392,7 +392,7 @@
<div class="grid-2">
<!-- Main Character & March Path -->
<div class="card">
<div class="card" id="card-persistent-chars">
<div class="card-title" data-i18n="persistentTbM7Title">👤 ผู้บุกเบิก & March 7th</div>
<form id="form-persistent-chars">
<div class="form-group">
@@ -424,7 +424,7 @@
</div>
<!-- Global Features & Toggles -->
<div class="card">
<div class="card" id="card-persistent-global">
<div class="card-title" data-i18n="persistentGlobalTitle">⚙️ สวิตช์ระบบพิเศษของ GaC</div>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-top: 1rem;">
<div class="ex-switch-card">
@@ -495,6 +495,7 @@
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-sm btn-primary active" id="btn-select-freesr" data-i18n="btnFreesr">📄 freesr-data.json</button>
<button class="btn btn-sm btn-secondary" id="btn-select-persistent" data-i18n="btnPersistent">💾 ไฟล์เซฟ persistent</button>
<button class="btn btn-sm btn-secondary" id="btn-select-datagame" data-i18n="btnDataGame" style="display: none;">🎮 data-in-game.json</button>
</div>
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-sm btn-secondary" id="btn-json-beautify" data-i18n="btnBeautify">✨ จัดรูปแบบ JSON</button>
+365 -59
View File
@@ -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 {
+2
View File
@@ -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",