-
⥠āļāļģāđāļāļīāļāļāļēāļĢāļāđāļ§āļ
-
āļāļģāļŠāļąāđāļāļāđāļ§āļ
+
+
+
+
+ ðŧ
+ āļāļāļāđāļāļĨāļāļģāļŠāļąāđāļ GM & Log āļŠāļ
+
+
+
+
+
-
āđāļāļĢāļ·āđāļāļāļĄāļ·āļāļāļąāļāļāļēāļĢāļāļĩāļĄāđāļĨāļ°āđāļāļīāļĢāđāļāđāļ§āļāļĢāđāđāļāļāļāļąāļāļāļĩ
-
-
-
+
āļŠāđāļāļāļģāļŠāļąāđāļ GM / Server commands āđāļāļāđāļĢāļĩāļĒāļĨāđāļāļĄāđ āļāļĢāđāļāļĄāļāļīāļāļāļēāļĄāļŠāļāļēāļāļ°āđāļĨāļ° Packet Log
-
-
+
+
+
+ ⥠āļāļģāļŠāļąāđāļāļāđāļ§āļāļāļĩāđāđāļāđāļāđāļāļĒ (Quick Commands)
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
diff --git a/js/app.js b/js/app.js
index 3a63ed6..9891fad 100644
--- a/js/app.js
+++ b/js/app.js
@@ -72,12 +72,26 @@ const el = {
lineupContainer: document.getElementById('lineup-container'),
toggleCreativeMode: document.getElementById('toggle-creative-mode'),
badgeCreativeMode: document.getElementById('badge-creative-mode'),
- btnQuickLineupE0S1: document.getElementById('btn-quick-lineup-e0s1'),
- btnQuickLineupRelics: document.getElementById('btn-quick-lineup-relics'),
- btnQuickUnlockAllAvatars: document.getElementById('btn-quick-unlock-all-avatars'),
- btnQuickAddAllLcs: document.getElementById('btn-quick-add-all-lcs'),
- btnQuickTeleportTrain: document.getElementById('btn-quick-teleport-train'),
- btnQuickClearLineup: document.getElementById('btn-quick-clear-lineup'),
+ // Team Combat Synergy
+ cardTeamSynergy: document.getElementById('card-team-synergy'),
+ badgeSynergyCoverage: document.getElementById('badge-synergy-coverage'),
+ synergyElementsContainer: document.getElementById('synergy-elements-container'),
+ synergyRolesContainer: document.getElementById('synergy-roles-container'),
+ synergyAdviceBox: document.getElementById('synergy-advice-box'),
+ synergyAvgLevel: document.getElementById('synergy-avg-level'),
+ synergyAvgEidolon: document.getElementById('synergy-avg-eidolon'),
+ synergyLcReady: document.getElementById('synergy-lc-ready'),
+ synergyRelicReady: document.getElementById('synergy-relic-ready'),
+
+ // GM Command Dispatcher & Live Log Console
+ cardGmConsole: document.getElementById('card-gm-console'),
+ btnConsoleCopyLog: document.getElementById('btn-console-copy-log'),
+ btnConsoleClearLog: document.getElementById('btn-console-clear-log'),
+ consoleTerminalLog: document.getElementById('console-terminal-log'),
+ formConsoleCmd: document.getElementById('form-console-cmd'),
+ consoleCmdInput: document.getElementById('console-cmd-input'),
+ btnConsoleCmdSend: document.getElementById('btn-console-cmd-send'),
+ lblConsoleConnStatus: document.getElementById('lbl-console-conn-status'),
// Gachabase Sync
btnHeaderGachabaseSync: document.getElementById('btn-header-gachabase-sync'),
btnQuickGachabaseSync: document.getElementById('btn-quick-gachabase-sync'),
@@ -379,10 +393,18 @@ function setConnectionStatus(connected, extraText = '') {
el.statusDot.classList.add('connected');
el.connectionStatusText.textContent = extraText || t('online');
el.connectionStatusText.style.color = 'var(--accent-green)';
+ if (el.lblConsoleConnStatus) {
+ el.lblConsoleConnStatus.textContent = 'â āđāļāļīāļĢāđāļāđāļ§āļāļĢāđāļāļāļāđāļĨāļāđ';
+ el.lblConsoleConnStatus.style.color = 'var(--accent-green)';
+ }
} else {
el.statusDot.classList.remove('connected');
el.connectionStatusText.textContent = extraText || t('offline');
el.connectionStatusText.style.color = 'var(--accent-red)';
+ if (el.lblConsoleConnStatus) {
+ el.lblConsoleConnStatus.textContent = 'â āļāļāļāđāļĨāļāđ (āļāļģāļŠāļąāđāļāļāļģāļāļēāļāļāđāļēāļāđāļāļāļ āļēāļĒāđāļ)';
+ el.lblConsoleConnStatus.style.color = 'var(--text-muted)';
+ }
}
}
@@ -1015,6 +1037,413 @@ function renderLineup(lineups) {
});
el.lineupContainer.appendChild(addSlotCard);
}
+
+ // Update Team Combat Synergy Analysis
+ renderTeamSynergy(currentLineup);
+}
+
+// ==========================================
+// Team Combat Synergy & Weakness Break Analysis
+// ==========================================
+const HSR_ELEMENTS = [
+ { key: 'Physical', name: 'Physical', nameTh: 'āļāļēāļĒāļ āļēāļ', color: '#cbd5e1' },
+ { key: 'Fire', name: 'Fire', nameTh: 'āđāļ', color: '#ef4444' },
+ { key: 'Ice', name: 'Ice', nameTh: 'āļāđāļģāđāļāđāļ', color: '#38bdf8' },
+ { key: 'Lightning', name: 'Lightning', nameTh: 'āļŠāļēāļĒāļāđāļē', color: '#c084fc' },
+ { key: 'Wind', name: 'Wind', nameTh: 'āļĨāļĄ', color: '#34d399' },
+ { key: 'Quantum', name: 'Quantum', nameTh: 'āļāļ§āļāļāļāļąāļĄ', color: '#818cf8' },
+ { key: 'Imaginary', name: 'Imaginary', nameTh: 'āļāļīāļāļāļ āļēāļ', color: '#fbbf24' }
+];
+
+function renderTeamSynergy(lineups) {
+ if (!el.synergyElementsContainer) return;
+ const currentLineup = lineups || state.statusData?.lineups || DEFAULT_LINEUP;
+
+ // Extract active character IDs
+ const activeIds = [];
+ for (const slot in currentLineup) {
+ const aid = parseInt(currentLineup[slot], 10);
+ if (aid && aid > 0 && !activeIds.includes(aid)) {
+ activeIds.push(aid);
+ }
+ }
+
+ // 1. Weakness Break Coverage
+ const coveredElements = {};
+ const roles = {
+ dps: [], // Destruction, Hunt, Erudition
+ specialist: [], // Remembrance, Elation
+ support: [], // Harmony, Nihility
+ sustain: [] // Preservation, Abundance
+ };
+
+ let totalLevel = 0;
+ let totalEidolon = 0;
+ let lcCount = 0;
+ let fullRelicCount = 0;
+
+ activeIds.forEach(aid => {
+ const meta = getAvatarInfo(aid);
+ const av = state.fullFreesrData?.avatars?.[aid];
+ const lvl = av?.level || 80;
+ const rank = av?.data?.rank ?? (state.avatarsList.find(a => a.avatar_id === aid)?.rank ?? 0);
+
+ totalLevel += lvl;
+ totalEidolon += rank;
+
+ // Element coverage
+ const elem = meta.element || 'Unknown';
+ if (!coveredElements[elem]) {
+ coveredElements[elem] = [];
+ }
+ coveredElements[elem].push(meta.name);
+
+ // Path / Role
+ const path = meta.path || '';
+ if (['Destruction', 'Hunt', 'Erudition'].includes(path)) {
+ roles.dps.push(meta.name);
+ } else if (['Remembrance', 'Elation'].includes(path)) {
+ roles.specialist.push(meta.name);
+ } else if (['Harmony', 'Nihility'].includes(path)) {
+ roles.support.push(meta.name);
+ } else if (['Preservation', 'Abundance'].includes(path)) {
+ roles.sustain.push(meta.name);
+ }
+
+ // LC check
+ const hasLc = (state.fullFreesrData?.lightcones || []).some(l => l.equip_avatar === aid);
+ if (hasLc) lcCount++;
+
+ // Relics check (count how many relics equipped)
+ const relCount = (state.fullFreesrData?.relics || []).filter(r => r.equip_avatar === aid).length;
+ if (relCount >= 6) fullRelicCount++;
+ });
+
+ const count = activeIds.length || 1;
+ const avgLevel = activeIds.length ? (totalLevel / count).toFixed(1) : '-';
+ const avgEidolon = activeIds.length ? `E${(totalEidolon / count).toFixed(1)}` : '-';
+
+ // Render Element Chips
+ const coveredCount = Object.keys(coveredElements).length;
+ if (el.badgeSynergyCoverage) {
+ el.badgeSynergyCoverage.textContent = `āļāļĢāļāļāļāļĨāļļāļĄ ${coveredCount}/7 āļāļēāļāļļ`;
+ el.badgeSynergyCoverage.style.color = coveredCount >= 4 ? 'var(--accent-green)' : (coveredCount >= 2 ? 'var(--accent-cyan)' : 'var(--accent-gold)');
+ }
+
+ el.synergyElementsContainer.innerHTML = HSR_ELEMENTS.map(elem => {
+ const isCovered = !!coveredElements[elem.key];
+ const providers = coveredElements[elem.key] || [];
+ const providerText = isCovered ? providers.join(', ') : 'āđāļĄāđāļĄāļĩāđāļāļāļĩāļĄ';
+ return `
+
+ ${getElementIconHtml(elem.key, 22)}
+ ${elem.nameTh}
+ ${providerText}
+
+ `;
+ }).join('');
+
+ // Render Role Tags
+ if (el.synergyRolesContainer) {
+ el.synergyRolesContainer.innerHTML = `
+
+ ðĄïļ DPS (${roles.dps.length}): ${roles.dps.join(', ') || '-'}
+
+
+ ⥠āļāļąāļāļāļāļĢāđāļ/āļāļĩāļāļąāļ (${roles.support.length}): ${roles.support.join(', ') || '-'}
+
+
+ ðĄïļ Sustain āļŪāļĩāļĨ/āļāļīāļĨāļāđ (${roles.sustain.length}): ${roles.sustain.join(', ') || '-'}
+
+ ${roles.specialist.length ? `
+
+ ðŪ āļāļīāđāļĻāļĐ/FUA (${roles.specialist.length}): ${roles.specialist.join(', ')}
+
+ ` : ''}
+ `;
+ }
+
+ // Dynamic Advice Text
+ if (el.synergyAdviceBox) {
+ if (activeIds.length === 0) {
+ el.synergyAdviceBox.innerHTML = `
āļĒāļąāļāđāļĄāđāļĄāļĩāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄ āļāļĢāļļāļāļēāđāļĨāļ·āļāļāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄāļāđāļēāļāļāļ`;
+ } else if (roles.sustain.length === 0) {
+ el.synergyAdviceBox.innerHTML = `
â ïļ āļāđāļāđāļāļ°āļāļģ: āļāļĩāļĄāļĒāļąāļāļāļēāļāļāļąāļ§āļĨāļ°āļāļĢāļŠāļēāļĒāļāđāļāļāļāļąāļāļŦāļĢāļ·āļāļĢāļąāļāļĐāļē (Preservation / Abundance) āđāļāļ°āļāļģāđāļŠāđ 1 āļāļģāđāļŦāļāđāļāđāļāļ·āđāļāļāļ§āļēāļĄāļāļĨāļāļāļ āļąāļĒāđāļāļāļēāļĢāđāļĨāđāļ`;
+ el.synergyAdviceBox.style.borderColor = 'rgba(245, 158, 11, 0.4)';
+ } else if (roles.dps.length === 0) {
+ el.synergyAdviceBox.innerHTML = `
ðĄ āļāđāļāđāļāļ°āļāļģ: āļāļĩāļĄāļĄāļĩāđāļāđāļāļąāļ§āļāļąāļāļāļāļĢāđāļ āļĒāļąāļāđāļĄāđāļĄāļĩāļāļąāļ§āļāļģāļāļēāđāļĄāļāļŦāļĨāļąāļ (Destruction / Hunt / Erudition)`;
+ el.synergyAdviceBox.style.borderColor = 'rgba(56, 189, 248, 0.4)';
+ } else if (roles.support.length === 0) {
+ el.synergyAdviceBox.innerHTML = `
ðĄ āļāđāļāđāļāļ°āļāļģ: āļāļĩāļĄāļĄāļĩāļāļąāļ§āļāļēāđāļĄāļāđāļāđāļĒāļąāļāđāļĄāđāļĄāļĩāļāļąāļ§āļāļąāļāļŦāļĢāļ·āļāļāļĩāļāļąāļ (Harmony / Nihility) āđāļŠāļĢāļīāļĄāļāļąāļ§āļāļąāļāļāļāļĢāđāļāļāļ°āļāđāļ§āļĒāđāļĢāđāļāļāļēāđāļĄāļāđāļāđāļĄāļŦāļēāļĻāļēāļĨ`;
+ el.synergyAdviceBox.style.borderColor = 'rgba(245, 158, 11, 0.4)';
+ } else {
+ el.synergyAdviceBox.innerHTML = `
â
āļŠāļĄāļāļļāļĨāļĒāļāļāđāļĒāļĩāđāļĒāļĄ: āļāļĩāļĄāļĄāļĩāđāļāļĢāļāļŠāļĢāđāļēāļāļāļĢāļāđāļāļĢāļ·āđāļāļ āļāļąāđāļāļāļąāļ§āļāļģāļāļēāđāļĄāļ āļāļąāļāļāļāļĢāđāļāļāļąāļ/āļāļĩāļāļąāļ āđāļĨāļ°āļāļąāļ§āļŪāļĩāļĨāđāļĨāļāļĢāđ/āļāļīāļĨāđāļāļāļĢāđ āļāļĢāđāļāļĄāļĨāļļāļĒāļāļļāļāļāļāļāđāļāļāļāđ!`;
+ el.synergyAdviceBox.style.borderColor = 'rgba(52, 211, 153, 0.4)';
+ }
+ }
+
+ // Metrics
+ if (el.synergyAvgLevel) el.synergyAvgLevel.textContent = activeIds.length ? `Lv.${avgLevel}` : '-';
+ if (el.synergyAvgEidolon) el.synergyAvgEidolon.textContent = avgEidolon;
+ if (el.synergyLcReady) el.synergyLcReady.textContent = activeIds.length ? `${lcCount}/${activeIds.length}` : '-';
+ if (el.synergyRelicReady) el.synergyRelicReady.textContent = activeIds.length ? `${fullRelicCount}/${activeIds.length}` : '-';
+}
+
+// ==========================================
+// GM Command Dispatcher & Live Log Console
+// ==========================================
+const consoleState = {
+ history: [],
+ historyIndex: -1,
+ maxLogs: 200
+};
+
+function escapeHtml(str) {
+ if (str === null || str === undefined) return '';
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function logToConsole(message, tag = 'SYS') {
+ if (!el.consoleTerminalLog) return;
+ const now = new Date();
+ const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
+
+ let tagClass = 'tag-sys';
+ const upperTag = String(tag).toUpperCase();
+ if (upperTag === 'CMD' || upperTag === 'GM') tagClass = 'tag-cmd';
+ else if (upperTag === 'OK' || upperTag === 'SUCCESS') tagClass = 'tag-ok';
+ else if (upperTag === 'ERR' || upperTag === 'ERROR') tagClass = 'tag-err';
+ else if (upperTag === 'NET') tagClass = 'tag-net';
+
+ const lineEl = document.createElement('div');
+ lineEl.className = 'log-line';
+ lineEl.innerHTML = `
+
${timeStr}
+
[${escapeHtml(upperTag)}]
+
${escapeHtml(message)}
+ `;
+
+ el.consoleTerminalLog.appendChild(lineEl);
+
+ // Trim excess logs if above maxLogs
+ while (el.consoleTerminalLog.children.length > consoleState.maxLogs) {
+ el.consoleTerminalLog.removeChild(el.consoleTerminalLog.firstChild);
+ }
+
+ // Auto scroll to bottom
+ el.consoleTerminalLog.scrollTop = el.consoleTerminalLog.scrollHeight;
+}
+
+async function dispatchGMCommand(rawCmd) {
+ const cmd = (rawCmd || '').trim();
+ if (!cmd) return;
+
+ // Save to history
+ if (consoleState.history[consoleState.history.length - 1] !== cmd) {
+ consoleState.history.push(cmd);
+ }
+ consoleState.historyIndex = -1;
+
+ // Log user command
+ logToConsole(cmd, 'CMD');
+
+ // Handle local /help command immediately
+ if (cmd.toLowerCase() === '/help') {
+ logToConsole('āļāļģāļŠāļąāđāļ GM āļāļĩāđāļāļĢāđāļāļĄāđāļāđāļāļēāļ (Available Commands):', 'SYS');
+ logToConsole(' /heal - āļāļ·āđāļāļāļđ HP/Energy āđāļāđāļĄāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄāļāļąāđāļāļŦāļĄāļ', 'SYS');
+ logToConsole(' /max - āļāļąāļāļāļąāļ§āļĨāļ°āļāļĢāļāļąāđāļāļŦāļĄāļāđāļāđāļ Lv.80, E6, Traces 10', 'SYS');
+ logToConsole(' /teleport
[floor] - āļ§āļēāļĢāđāļāđāļāļĒāļąāļāļāļēāļ/Plane ID (āđāļāđāļ 20001 āļĢāļāđāļ Express)', 'SYS');
+ logToConsole(' /scene [floor] - āļāļģāļŠāļąāđāļāļĒāđāļāļāļāļ /teleport', 'SYS');
+ logToConsole(' /clear lineup - āļĨāđāļēāļāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄāļāļąāļāļāļļāļāļąāļāļāļąāđāļāļŦāļĄāļ', 'SYS');
+ logToConsole(' /avatar [lvl] [rank] - āļāļĨāļāļĨāđāļāļāļŦāļĢāļ·āļāļāļąāđāļāļāđāļēāļāļąāļ§āļĨāļ°āļāļĢ (āđāļāđāļ /avatar 1308 80 6)', 'SYS');
+ logToConsole(' /give [count] - āđāļāļīāđāļĄāđāļāđāļāļĄāļŦāļĢāļ·āļāđāļāļīāļ (āđāļāđāļ 102 Jades, 2 Credits)', 'SYS');
+ logToConsole(' /help - āđāļŠāļāļāļāļđāđāļĄāļ·āļāļĢāļēāļĒāļāļēāļĢāļāļģāļŠāļąāđāļāļāļĩāđ', 'SYS');
+ return;
+ }
+
+ // If server is online, try dispatching to backend
+ if (state.isConnected) {
+ try {
+ const res = await apiCall('/api/command', 'POST', { command: cmd });
+ if (res) {
+ const msg = res.message || res.msg || 'āļāļģāļŠāļąāđāļāļāļĢāļ°āļĄāļ§āļĨāļāļĨāļŠāļģāđāļĢāđāļ';
+ logToConsole(msg, 'OK');
+
+ // Command specific post-processing
+ const lowerCmd = cmd.toLowerCase();
+ if (lowerCmd.startsWith('/heal')) {
+ showToast('āļāļ·āđāļāļāļđ HP āđāļĨāļ° Energy āđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ refreshStatus();
+ } else if (lowerCmd.startsWith('/teleport') || lowerCmd.startsWith('/scene')) {
+ showToast('āļŠāđāļāļāļģāļŠāļąāđāļāđāļāļĨāļĩāđāļĒāļāļāļēāļāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ refreshStatus();
+ } else if (lowerCmd.startsWith('/clear lineup')) {
+ showToast('āļĨāđāļēāļāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'info');
+ refreshStatus();
+ } else if (lowerCmd.startsWith('/max')) {
+ showToast('āļāļĢāļąāļ Max āđāļĨāđāļ§āļĨāđāļĨāļ° Traces āđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ refreshStatus();
+ } else if (lowerCmd.startsWith('/give')) {
+ showToast('āđāļāļīāđāļĄāđāļāđāļāļĄ/āđāļāļīāļāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ } else if (lowerCmd.startsWith('/avatar')) {
+ showToast('āļāļąāļāđāļāļāļāļąāļ§āļĨāļ°āļāļĢāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ loadAvatars();
+ }
+ return;
+ }
+ } catch (err) {
+ logToConsole(`Server Dispatcher: ${err.message} (āđāļāđāļēāļŠāļđāđāđāļŦāļĄāļ Local Fallback)`, 'NET');
+ }
+ }
+
+ // Fallback to client-side local executions if server is offline or doesn't support the command
+ const lowerCmd = cmd.toLowerCase();
+ if (lowerCmd.startsWith('/heal')) {
+ logToConsole('Local Fallback: āļāļ·āđāļāļāļđ Energy āļāļāļāļāļąāļ§āļĨāļ°āļāļĢāđāļāļāļĩāļĄāđāļāđāļ 100%', 'OK');
+ if (state.fullFreesrData?.avatars) {
+ for (const aid in state.fullFreesrData.avatars) {
+ state.fullFreesrData.avatars[aid].sp_value = 140;
+ }
+ saveFreesrChangesToServer();
+ }
+ showToast('āļāļ·āđāļāļāļđāļāļĨāļąāļāļāļēāļāļāļĩāļĄāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'success');
+ } else if (lowerCmd.startsWith('/max')) {
+ logToConsole('Local Fallback: āļāļģāļĨāļąāļāļĢāļąāļ maxAllAvatars()...', 'SYS');
+ await maxAllAvatars();
+ logToConsole('Local: āļāļąāļāđāļĨāđāļ§āļĨ 80 āđāļĨāļ° E6 āļāļąāļ§āļĨāļ°āļāļĢāļāļąāđāļāļŦāļĄāļāđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'OK');
+ } else if (lowerCmd.startsWith('/clear lineup')) {
+ logToConsole('Local Fallback: āļāļģāļĨāļąāļāļĨāđāļēāļāļāļĩāļĄ...', 'SYS');
+ await quickClearLineup();
+ logToConsole('Local: āļĨāđāļēāļāļāļĩāļĄāđāļĢāļĩāļĒāļāļĢāđāļāļĒāđāļĨāđāļ§', 'OK');
+ } else if (lowerCmd.startsWith('/teleport') || lowerCmd.startsWith('/scene')) {
+ const parts = cmd.split(/\s+/);
+ const planeId = parseInt(parts[1], 10) || 20001;
+ const floorId = parseInt(parts[2], 10) || 1;
+ logToConsole(`Local Fallback: āļāļąāđāļāļāđāļēāļāļēāļāđāļāđāļ Plane ID ${planeId}, Floor ${floorId}`, 'OK');
+ if (state.fullFreesrData) {
+ state.fullFreesrData.scene = state.fullFreesrData.scene || {};
+ state.fullFreesrData.scene.plane_id = planeId;
+ state.fullFreesrData.scene.floor_id = floorId;
+ saveFreesrChangesToServer();
+ refreshStatus();
+ }
+ showToast(`āđāļāļĨāļĩāđāļĒāļāļāļēāļāđāļāđāļ Plane ${planeId} āđāļĢāļĩāļĒāļāļĢāđāļāļĒ`, 'success');
+ } else if (lowerCmd.startsWith('/avatar')) {
+ const parts = cmd.split(/\s+/);
+ const aid = parseInt(parts[1], 10);
+ const lvl = parseInt(parts[2], 10) || 80;
+ const rank = parseInt(parts[3], 10) || 6;
+ if (aid) {
+ if (!state.fullFreesrData) state.fullFreesrData = { avatars: {}, lightcones: [], relics: [] };
+ state.fullFreesrData.avatars = state.fullFreesrData.avatars || {};
+ state.fullFreesrData.avatars[aid] = {
+ owner_uid: 0,
+ avatar_id: aid,
+ level: lvl,
+ promotion: lvl > 70 ? 6 : (lvl > 60 ? 5 : 4),
+ data: { rank, skills: { 1: 10, 2: 10, 3: 10, 4: 10 } }
+ };
+ saveFreesrChangesToServer();
+ loadAvatars();
+ logToConsole(`Local: āļāļĨāļāļĨāđāļāļ/āļāļąāļāđāļāļ Avatar ID ${aid} (Lv.${lvl}, E${rank}) āđāļĢāļĩāļĒāļāļĢāđāļāļĒ`, 'OK');
+ showToast(`āļāļąāļāđāļāļāļāļąāļ§āļĨāļ°āļāļĢ ${aid} āļŠāļģāđāļĢāđāļ`, 'success');
+ } else {
+ logToConsole('āļĢāļđāļāđāļāļāļāļģāļŠāļąāđāļāđāļĄāđāļāļđāļāļāđāļāļ: /avatar [level] [rank]', 'ERR');
+ }
+ } else if (lowerCmd.startsWith('/give')) {
+ const parts = cmd.split(/\s+/);
+ const itemId = parseInt(parts[1], 10);
+ const count = parseInt(parts[2], 10) || 1;
+ if (itemId) {
+ logToConsole(`Local: āđāļāđāļĢāļąāļ Item ID ${itemId} āļāļģāļāļ§āļ ${count.toLocaleString()}`, 'OK');
+ showToast(`āđāļāļīāđāļĄāđāļāđāļāļĄ ${itemId} x${count}`, 'success');
+ } else {
+ logToConsole('āļĢāļđāļāđāļāļāļāļģāļŠāļąāđāļāđāļĄāđāļāļđāļāļāđāļāļ: /give [count]', 'ERR');
+ }
+ } else {
+ logToConsole(`āļāļģāļŠāļąāđāļāļāļĩāđāđāļĄāđāļĢāļđāđāļāļąāļāļŦāļĢāļ·āļāđāļĄāđāļĢāļāļāļĢāļąāļāđāļāđāļŦāļĄāļāļāļāļāđāļĨāļāđ: "${cmd}" (āļāļīāļĄāļāđ /help āđāļāļ·āđāļāļāļđāļāļģāļŠāļąāđāļ)`, 'ERR');
+ }
+}
+
+function setupGMConsoleEvents() {
+ if (el.formConsoleCmd) {
+ el.formConsoleCmd.addEventListener('submit', (e) => {
+ e.preventDefault();
+ if (!el.consoleCmdInput) return;
+ const cmd = el.consoleCmdInput.value.trim();
+ if (!cmd) return;
+ el.consoleCmdInput.value = '';
+ dispatchGMCommand(cmd);
+ });
+ }
+
+ // Key navigation in console input
+ if (el.consoleCmdInput) {
+ el.consoleCmdInput.addEventListener('keydown', (e) => {
+ if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ if (consoleState.history.length === 0) return;
+ if (consoleState.historyIndex === -1) {
+ consoleState.historyIndex = consoleState.history.length - 1;
+ } else if (consoleState.historyIndex > 0) {
+ consoleState.historyIndex--;
+ }
+ el.consoleCmdInput.value = consoleState.history[consoleState.historyIndex] || '';
+ } else if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ if (consoleState.history.length === 0 || consoleState.historyIndex === -1) return;
+ if (consoleState.historyIndex < consoleState.history.length - 1) {
+ consoleState.historyIndex++;
+ el.consoleCmdInput.value = consoleState.history[consoleState.historyIndex];
+ } else {
+ consoleState.historyIndex = -1;
+ el.consoleCmdInput.value = '';
+ }
+ }
+ });
+ }
+
+ // Quick pill buttons
+ document.querySelectorAll('.console-pill-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const cmd = btn.getAttribute('data-cmd');
+ if (cmd) {
+ if (el.consoleCmdInput) el.consoleCmdInput.value = cmd;
+ dispatchGMCommand(cmd);
+ }
+ });
+ });
+
+ // Copy Log button
+ if (el.btnConsoleCopyLog) {
+ el.btnConsoleCopyLog.addEventListener('click', () => {
+ if (!el.consoleTerminalLog) return;
+ const text = el.consoleTerminalLog.innerText || el.consoleTerminalLog.textContent;
+ navigator.clipboard?.writeText(text).then(() => {
+ showToast('āļāļąāļāļĨāļāļ Log āļāļąāđāļāļŦāļĄāļāļĨāļāđāļāļāļĨāļīāļāļāļāļĢāđāļāđāļĨāđāļ§', 'success');
+ }).catch(() => {
+ showToast('āđāļĄāđāļŠāļēāļĄāļēāļĢāļāļāļąāļāļĨāļāļ Log āđāļāđ', 'error');
+ });
+ });
+ }
+
+ // Clear Log button
+ if (el.btnConsoleClearLog) {
+ el.btnConsoleClearLog.addEventListener('click', () => {
+ if (el.consoleTerminalLog) {
+ el.consoleTerminalLog.innerHTML = '';
+ logToConsole('āļĨāđāļēāļāļŦāļāđāļēāļāļ Terminal āđāļĢāļĩāļĒāļāļĢāđāļāļĒ', 'SYS');
+ }
+ });
+ }
+
+ // Initial greeting
+ logToConsole('GaC Server Console āļāļĢāđāļāļĄāđāļāđāļāļēāļ (āļāļīāļĄāļāđ /help āđāļāļ·āđāļāļāļđāļāļģāļŠāļąāđāļāļāļąāđāļāļŦāļĄāļ)', 'SYS');
}
// Add extra slot in Creative Mode
@@ -5908,6 +6337,7 @@ async function init() {
setupModalSubtabs();
setupEvents();
setupGachabaseSyncEvents();
+ setupGMConsoleEvents();
applyTranslations();
renderLineup(state.statusData?.lineups || DEFAULT_LINEUP);
renderAvatarsGrid();