From 62a529ef3f064d69526ea3dc1560101d6cf1ce87 Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Sun, 25 Oct 2026 00:45:23 +0700 Subject: [PATCH] feat: select Anomaly Arbitration round --- css/styles.css | 25 +++--- index.html | 4 +- js/app.js | 205 ++++++++++++++++++++++++++++++------------------- js/i18n.js | 20 +++++ 4 files changed, 159 insertions(+), 95 deletions(-) diff --git a/css/styles.css b/css/styles.css index 4e0acca..39140c1 100644 --- a/css/styles.css +++ b/css/styles.css @@ -3661,16 +3661,17 @@ input:checked + .slider:before { .monster-settings-stage-copy strong { color: var(--text-main); font-size: .92rem; line-height: 1.28; overflow-wrap: anywhere; } .monster-settings-stage-card.is-boss .monster-settings-stage-meta:first-of-type { color: #fcd34d; } .monster-settings-view[hidden] { display: none; } -.monster-aa-room-control { display: flex; align-items: center; justify-content: space-between; gap: 1.25rem; padding: 1rem 1.1rem; border: 1px solid rgba(56, 189, 248, .28); border-radius: 14px; background: linear-gradient(120deg, rgba(14, 165, 233, .1), rgba(15, 23, 42, .72)); } -.monster-aa-room-header { display: grid; gap: .6rem; min-width: 0; } -.monster-aa-room-copy h3 { margin: 0; color: var(--text-highlight); font-size: 1rem; } -.monster-aa-room-copy p { margin: .35rem 0 0; color: var(--text-muted); line-height: 1.5; } -.monster-aa-room-status { color: var(--text-muted); font-size: .88rem; line-height: 1.45; overflow-wrap: anywhere; } -.monster-aa-room-status.is-pending { color: #86efac; } -.monster-aa-room-status.is-error { color: #fda4af; } -.monster-aa-room-actions { display: flex; flex: 0 0 auto; align-items: center; gap: .6rem; } -.monster-aa-room-actions .btn { min-height: 42px; white-space: nowrap; } -.monster-aa-room-actions .btn:disabled { opacity: .55; cursor: not-allowed; } +.monster-aa-round-control { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .9fr); align-items: center; gap: 1.25rem; padding: 1rem 1.1rem; border: 1px solid rgba(56, 189, 248, .28); border-radius: 14px; background: linear-gradient(120deg, rgba(14, 165, 233, .1), rgba(15, 23, 42, .72)); } +.monster-aa-round-header { display: grid; gap: .6rem; min-width: 0; } +.monster-aa-round-copy h3 { margin: 0; color: var(--text-highlight); font-size: 1rem; } +.monster-aa-round-copy p { margin: .35rem 0 0; color: var(--text-muted); line-height: 1.5; } +.monster-aa-round-status { color: var(--text-muted); font-size: .88rem; line-height: 1.45; overflow-wrap: anywhere; } +.monster-aa-round-status.is-pending { color: #86efac; } +.monster-aa-round-status.is-error { color: #fda4af; } +.monster-aa-round-picker-wrap { display: grid; gap: .5rem; min-width: 0; } +.monster-aa-round-picker-label { color: var(--text-muted); font-size: .82rem; font-weight: 800; } +.monster-aa-round-picker { min-height: 46px; width: 100%; } +.monster-aa-round-picker:disabled { opacity: .55; cursor: not-allowed; } .monster-settings-stage-heading { display: flex; align-items: center; gap: .65rem; flex-wrap: wrap; } .monster-settings-stage-heading h3 { margin: 0; color: var(--text-highlight); font-size: 1.1rem; } .monster-settings-badge { padding: .25rem .55rem; border: 1px solid rgba(192, 132, 252, .3); border-radius: 999px; color: #d8b4fe; background: rgba(168, 85, 247, .1); font-size: .75rem; font-weight: 700; } @@ -3731,9 +3732,7 @@ input:checked + .slider:before { .monster-settings-wave { flex-direction: column; } .monster-settings-monsters { width: 100%; } .monster-settings-stage-grid { grid-template-columns: 1fr; } - .monster-aa-room-control { align-items: stretch; flex-direction: column; } - .monster-aa-room-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } - .monster-aa-room-actions .btn { width: 100%; } + .monster-aa-round-control { grid-template-columns: 1fr; align-items: stretch; } .monster-custom-toolbar, .monster-custom-save-actions { align-items: stretch; flex-direction: column; } .monster-custom-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); } .monster-custom-save-actions .btn { width: 100%; } diff --git a/index.html b/index.html index 3c0b410..fee81cf 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ GaC-Srtool - + @@ -1806,6 +1806,6 @@
- + diff --git a/js/app.js b/js/app.js index 92dbf3d..c6f2cad 100644 --- a/js/app.js +++ b/js/app.js @@ -51,7 +51,7 @@ const state = { inventorySaved: {}, inventoryDraft: {}, inventoryPage: 0, - monsterSettings: { activeMode: 'moc', catalog: null, groupId: '', stageId: '', waves: [], savedFingerprint: '', ready: false, canEdit: false, aaSelection: null, aaSelectionLoaded: false, aaSelectionBusy: false, aaSelectionError: '', aaSelectionErrorKey: '' }, + monsterSettings: { activeMode: 'moc', catalog: null, groupId: '', stageId: '', waves: [], savedFingerprint: '', ready: false, canEdit: false, aaRoundSelection: null, aaRoundSelectionLoaded: false, aaRoundSelectionBusy: false, aaRoundSelectionError: '', aaRoundSelectionErrorKey: '' }, }; // UI Elements Map @@ -6938,47 +6938,60 @@ function monsterStagePreviewNames(stage) { return (stage?.monster_ids || []).map(monsterName).filter(Boolean); } -function renderAARoomSelection(stage, groups) { +function renderAARoundSelection(groups) { const editor = state.monsterSettings; - const section = monsterElement('section', 'monster-aa-room-control'); - const header = monsterElement('div', 'monster-aa-room-header'); - const copy = monsterElement('div', 'monster-aa-room-copy'); - copy.append(monsterElement('h3', '', t('monsterAaRoomTitle'))); - copy.append(monsterElement('p', '', t('monsterAaRoomDescription'))); + const section = monsterElement('section', 'monster-aa-round-control'); + const header = monsterElement('div', 'monster-aa-round-header'); + const copy = monsterElement('div', 'monster-aa-round-copy'); + copy.append(monsterElement('h3', '', t('monsterAaRoundTitle'))); + copy.append(monsterElement('p', '', t('monsterAaRoundDescription'))); header.append(copy); - const queued = editor.aaSelection; - if (queued) { - const queuedStage = groups.flatMap(group => group.stages || []).find(item => Number(item.id) === Number(queued.peak_id)); - const queuedName = monsterStagePreviewNames(queuedStage).slice(0, 2).join(' / ') || t('monsterSettingsStageTitle', { id: queued.peak_id }); - const status = monsterElement('div', 'monster-aa-room-status is-pending', t('monsterAaRoomPending', { room: queuedName, id: queued.peak_id })); + const selection = editor.aaRoundSelection; + if (editor.aaRoundSelectionError) { + const status = monsterElement('div', 'monster-aa-round-status is-error', t(editor.aaRoundSelectionErrorKey || 'monsterAaRoundLoadError', { error: editor.aaRoundSelectionError })); status.setAttribute('role', 'status'); header.append(status); - } else if (editor.aaSelectionError) { - const status = monsterElement('div', 'monster-aa-room-status is-error', t(editor.aaSelectionErrorKey || 'monsterAaRoomLoadError', { error: editor.aaSelectionError })); + } else if (editor.aaRoundSelectionBusy) { + const status = monsterElement('div', 'monster-aa-round-status', t('monsterAaRoundUpdating')); status.setAttribute('role', 'status'); header.append(status); - } else if (!editor.aaSelectionLoaded && state.isConnected && state.serverMode === 'gac') { - header.append(monsterElement('div', 'monster-aa-room-status', t('monsterAaRoomLoading'))); + } else if (!editor.aaRoundSelectionLoaded && state.isConnected && state.serverMode === 'gac') { + header.append(monsterElement('div', 'monster-aa-round-status', t('monsterAaRoundLoading'))); } else if (!state.isConnected || state.serverMode !== 'gac') { - header.append(monsterElement('div', 'monster-aa-room-status', t('monsterAaRoomConnect'))); + header.append(monsterElement('div', 'monster-aa-round-status', t('monsterAaRoundConnect'))); + } else if (selection) { + const selectedGroup = groups.find(group => Number(group.id) === Number(selection.peak_group_id)); + const selectedName = monsterLocalizedText(selectedGroup?.names) || t('monsterSettingsGroupOption', { id: selection.peak_group_id, count: selectedGroup?.stages?.length || 0 }); + const status = monsterElement('div', 'monster-aa-round-status is-pending', t('monsterAaRoundPending', { round: selectedName, id: selection.peak_group_id })); + status.setAttribute('role', 'status'); + header.append(status); + } else { + header.append(monsterElement('div', 'monster-aa-round-status', t('monsterAaRoundDefault'))); } - const actions = monsterElement('div', 'monster-aa-room-actions'); - const selectButton = monsterElement('button', 'btn btn-primary monster-aa-room-select', t('monsterAaRoomUse')); - selectButton.type = 'button'; - selectButton.dataset.aaRoomAction = 'select'; - selectButton.dataset.aaRoomId = String(stage.id); - selectButton.disabled = editor.aaSelectionBusy || !state.isConnected || state.serverMode !== 'gac' || !editor.catalog; - actions.append(selectButton); - if (queued) { - const clearButton = monsterElement('button', 'btn btn-secondary monster-aa-room-clear', t('monsterAaRoomClear')); - clearButton.type = 'button'; - clearButton.dataset.aaRoomAction = 'clear'; - clearButton.disabled = editor.aaSelectionBusy || !state.isConnected || state.serverMode !== 'gac'; - actions.append(clearButton); + const picker = monsterElement('div', 'monster-aa-round-picker-wrap'); + const label = monsterElement('label', 'monster-aa-round-picker-label', t('monsterAaRoundSelectLabel')); + label.htmlFor = 'monster-aa-round-picker'; + const select = monsterElement('select', 'form-select monster-aa-round-picker'); + select.id = 'monster-aa-round-picker'; + select.dataset.aaRoundPicker = 'true'; + select.setAttribute('aria-label', t('monsterAaRoundSelectLabel')); + select.disabled = editor.aaRoundSelectionBusy || !state.isConnected || state.serverMode !== 'gac' || !editor.catalog; + const defaultOption = document.createElement('option'); + defaultOption.value = ''; + defaultOption.textContent = t('monsterAaRoundDefaultOption'); + select.append(defaultOption); + for (const group of groups) { + const option = document.createElement('option'); + option.value = String(group.id); + const roundName = monsterLocalizedText(group.names) || t('monsterSettingsGroupOption', { id: group.id, count: group.stages.length }); + option.textContent = `${roundName} · ${t('monsterAaRoundGroupId', { id: group.id })}`; + select.append(option); } - section.append(header, actions); + select.value = selection ? String(selection.peak_group_id) : ''; + picker.append(label, select); + section.append(header, picker); return section; } @@ -6992,33 +7005,40 @@ function renderMonsterChallengeMode(mode) { return; } - const selectedGroup = groups.find(group => String(group.id) === String(state.monsterSettings.groupId)) || groups[0]; + const preferredGroupId = mode === 'aa' + ? (state.monsterSettings.aaRoundSelection?.peak_group_id ?? groups[0].id) + : state.monsterSettings.groupId; + const selectedGroup = groups.find(group => String(group.id) === String(preferredGroupId)) || groups[0]; state.monsterSettings.groupId = String(selectedGroup.id); const selectedStage = selectedGroup.stages.find(stage => String(stage.id) === String(state.monsterSettings.stageId)) || selectedGroup.stages[0]; state.monsterSettings.stageId = String(selectedStage.id); - const groupPicker = monsterElement('section', 'monster-settings-picker-section'); - groupPicker.append(monsterElement('h3', 'monster-settings-picker-title', t('monsterSettingsGroup'))); - const groupGrid = monsterElement('div', 'monster-settings-group-grid'); - for (const group of groups) { - const button = monsterElement('button', `monster-settings-group-card${group.id === selectedGroup.id ? ' active' : ''}`); - button.type = 'button'; - button.dataset.monsterGroup = String(group.id); - button.setAttribute('aria-pressed', String(group.id === selectedGroup.id)); - const copy = monsterElement('span', 'monster-settings-group-copy'); - copy.append(monsterElement('strong', 'monster-settings-group-name', monsterLocalizedText(group.names) || t('monsterSettingsGroupOption', { id: group.id, count: group.stages.length }))); - copy.append(monsterElement('span', 'monster-settings-group-meta', t('monsterSettingsGroupOption', { id: group.id, count: group.stages.length }))); - button.append(copy); - const bossStage = group.stages.find(stage => stage.is_boss); - const bossId = bossStage?.monster_ids?.[0]; - if (bossId) appendMonsterIdentity(button, bossId, 'group-feature'); - groupGrid.append(button); + if (mode === 'aa') { + root.append(renderAARoundSelection(groups)); + } else { + const groupPicker = monsterElement('section', 'monster-settings-picker-section'); + groupPicker.append(monsterElement('h3', 'monster-settings-picker-title', t('monsterSettingsGroup'))); + const groupGrid = monsterElement('div', 'monster-settings-group-grid'); + for (const group of groups) { + const button = monsterElement('button', `monster-settings-group-card${group.id === selectedGroup.id ? ' active' : ''}`); + button.type = 'button'; + button.dataset.monsterGroup = String(group.id); + button.setAttribute('aria-pressed', String(group.id === selectedGroup.id)); + const copy = monsterElement('span', 'monster-settings-group-copy'); + copy.append(monsterElement('strong', 'monster-settings-group-name', monsterLocalizedText(group.names) || t('monsterSettingsGroupOption', { id: group.id, count: group.stages.length }))); + copy.append(monsterElement('span', 'monster-settings-group-meta', t('monsterSettingsGroupOption', { id: group.id, count: group.stages.length }))); + button.append(copy); + const bossStage = group.stages.find(stage => stage.is_boss); + const bossId = bossStage?.monster_ids?.[0]; + if (bossId) appendMonsterIdentity(button, bossId, 'group-feature'); + groupGrid.append(button); + } + groupPicker.append(groupGrid); + root.append(groupPicker); } - groupPicker.append(groupGrid); - root.append(groupPicker); const stagePicker = monsterElement('section', 'monster-settings-picker-section'); - stagePicker.append(monsterElement('h3', 'monster-settings-picker-title', t('monsterSettingsChallenge'))); + stagePicker.append(monsterElement('h3', 'monster-settings-picker-title', t(mode === 'aa' ? 'monsterSettingsAARoomPreview' : 'monsterSettingsChallenge'))); const stageGrid = monsterElement('div', 'monster-settings-stage-grid'); for (const stage of selectedGroup.stages) { const button = monsterElement('button', `monster-settings-stage-card${stage.id === selectedStage.id ? ' active' : ''}${stage.is_boss ? ' is-boss' : ''}`); @@ -7039,8 +7059,6 @@ function renderMonsterChallengeMode(mode) { stagePicker.append(stageGrid); root.append(stagePicker); - if (mode === 'aa') root.append(renderAARoomSelection(selectedStage, groups)); - const stageHeader = monsterElement('div', 'monster-settings-stage-heading'); const stageNames = monsterStagePreviewNames(selectedStage); stageHeader.append(monsterElement('h3', '', stageNames.length ? stageNames.slice(0, 2).join(' / ') : t('monsterSettingsStageTitle', { id: selectedStage.id }))); @@ -7341,10 +7359,10 @@ async function loadMonsterSettings() { editor.ready = false; editor.canEdit = false; editor.catalog = null; - editor.aaSelection = null; - editor.aaSelectionLoaded = false; - editor.aaSelectionError = ''; - editor.aaSelectionErrorKey = ''; + editor.aaRoundSelection = null; + editor.aaRoundSelectionLoaded = false; + editor.aaRoundSelectionError = ''; + editor.aaRoundSelectionErrorKey = ''; setMonsterSettingsStatus('monsterSettingsLoading', 'loading'); let catalogError = null; @@ -7353,12 +7371,25 @@ async function loadMonsterSettings() { editor.catalog = await apiCall('/api/monster-settings/catalog'); } catch (error) { catalogError = error; } try { - const result = await apiCall('/api/monster-settings/aa-room'); - editor.aaSelection = result?.selection || null; - editor.aaSelectionLoaded = true; + const result = await apiCall('/api/monster-settings/aa-round'); + editor.aaRoundSelection = result?.selection || null; + editor.aaRoundSelectionLoaded = true; + if (editor.aaRoundSelection) { + const selected = editor.catalog?.modes?.aa?.find(group => Number(group.id) === Number(editor.aaRoundSelection.peak_group_id)); + if (selected) { + editor.groupId = String(selected.id); + if (!selected.stages.some(stage => String(stage.id) === String(editor.stageId))) editor.stageId = String(selected.stages[0]?.id || ''); + } + } else { + const defaultGroup = editor.catalog?.modes?.aa?.[0]; + if (defaultGroup) { + editor.groupId = String(defaultGroup.id); + if (!defaultGroup.stages.some(stage => String(stage.id) === String(editor.stageId))) editor.stageId = String(defaultGroup.stages[0]?.id || ''); + } + } } catch (error) { - editor.aaSelectionError = error.message; - editor.aaSelectionErrorKey = 'monsterAaRoomLoadError'; + editor.aaRoundSelectionError = error.message; + editor.aaRoundSelectionErrorKey = 'monsterAaRoundLoadError'; } } @@ -7410,29 +7441,43 @@ function setupMonsterSettingsEvents() { }); }); const root = document.getElementById('monster-settings-view'); - root?.addEventListener('click', async event => { - const action = event.target.closest('[data-aa-room-action]'); - if (!action || action.disabled) return; + root?.addEventListener('change', async event => { + const picker = event.target.closest('[data-aa-round-picker]'); + if (!picker || picker.disabled) return; const editor = state.monsterSettings; - if (editor.aaSelectionBusy) return; - editor.aaSelectionBusy = true; - editor.aaSelectionError = ''; - editor.aaSelectionErrorKey = ''; + if (editor.aaRoundSelectionBusy) return; + const peakGroupId = picker.value ? Number(picker.value) : 0; + if (peakGroupId) { + const selected = editor.catalog?.modes?.aa?.find(group => Number(group.id) === peakGroupId); + if (!selected) return; + editor.groupId = String(selected.id); + editor.stageId = String(selected.stages[0]?.id || ''); + } else { + const defaultGroup = editor.catalog?.modes?.aa?.[0]; + if (defaultGroup) { + editor.groupId = String(defaultGroup.id); + editor.stageId = String(defaultGroup.stages[0]?.id || ''); + } + } + const previousSelection = editor.aaRoundSelection; + editor.aaRoundSelectionBusy = true; + editor.aaRoundSelectionError = ''; + editor.aaRoundSelectionErrorKey = ''; + editor.aaRoundSelection = peakGroupId ? { peak_group_id: peakGroupId } : null; renderMonsterSettings(); try { - const payload = action.dataset.aaRoomAction === 'clear' - ? { action: 'clear' } - : { peak_id: Number(action.dataset.aaRoomId) }; - const result = await apiCall('/api/monster-settings/aa-room', 'POST', payload); - editor.aaSelection = result?.selection || null; - editor.aaSelectionLoaded = true; - showToast(t(editor.aaSelection ? 'monsterAaRoomSavedToast' : 'monsterAaRoomClearedToast'), 'success'); + const payload = peakGroupId ? { peak_group_id: peakGroupId } : { action: 'clear' }; + const result = await apiCall('/api/monster-settings/aa-round', 'POST', payload); + editor.aaRoundSelection = result?.selection || null; + editor.aaRoundSelectionLoaded = true; + showToast(t(editor.aaRoundSelection ? 'monsterAaRoundSavedToast' : 'monsterAaRoundClearedToast'), 'success'); } catch (error) { - editor.aaSelectionError = error.message; - editor.aaSelectionErrorKey = 'monsterAaRoomSaveError'; - showToast(t('monsterAaRoomSaveError', { error: error.message }), 'error'); + editor.aaRoundSelection = previousSelection; + editor.aaRoundSelectionError = error.message; + editor.aaRoundSelectionErrorKey = 'monsterAaRoundSaveError'; + showToast(t('monsterAaRoundSaveError', { error: error.message }), 'error'); } finally { - editor.aaSelectionBusy = false; + editor.aaRoundSelectionBusy = false; renderMonsterSettings(); } }); diff --git a/js/i18n.js b/js/i18n.js index 33b274a..cd7a54a 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -6786,6 +6786,26 @@ for (const [language, additions] of Object.entries(AA_ROOM_SELECTION_TRANSLATION Object.assign(TRANSLATIONS[language], additions); } +const AA_ROUND_SELECTION_TRANSLATIONS = { + en: { monsterAaRoundTitle:'Choose the AA round', monsterAaRoundDescription:'Select the round the game should show when its AA data loads again. Choose a room in the game; entries below are only previews.', monsterAaRoundPending:'Selected round: {round} (Group {id}). Reopen Anomaly Arbitration in the game to show it.', monsterAaRoundSelectLabel:'Anomaly Arbitration round', monsterAaRoundDefaultOption:'Use the Client default round', monsterAaRoundDefault:'No round override. The Client default round will be shown.', monsterAaRoundUpdating:'Updating the AA round…', monsterAaRoundLoading:'Loading the saved AA round…', monsterAaRoundConnect:'Connect to GaC to choose the AA round shown in game.', monsterAaRoundLoadError:'Could not load the AA round: {error}', monsterAaRoundSavedToast:'AA round selected. Reopen Anomaly Arbitration in the game to show it.', monsterAaRoundClearedToast:'AA round override cleared. The Client default round will be shown.', monsterAaRoundSaveError:'Could not update the AA round: {error}', monsterAaRoundGroupId:'Round {id}', monsterSettingsAARoomPreview:'Rooms in this round · preview only' }, + th: { monsterAaRoundTitle:'เลือกรอบ AA', monsterAaRoundDescription:'เลือกรอบที่จะแสดงในเกมเมื่อโหลดข้อมูล AA อีกครั้ง ส่วนห้องให้เลือกจากในเกม รายการด้านล่างมีไว้ดูตัวอย่างเท่านั้น', monsterAaRoundPending:'เลือกรอบ {round} (กลุ่ม {id}) แล้ว เปิด Anomaly Arbitration ในเกมอีกครั้งเพื่อให้แสดงรอบนี้', monsterAaRoundSelectLabel:'รอบ Anomaly Arbitration', monsterAaRoundDefaultOption:'ใช้รอบเริ่มต้นของ Client', monsterAaRoundDefault:'ไม่ได้แทนรอบ ระบบจะแสดงรอบเริ่มต้นของ Client', monsterAaRoundUpdating:'กำลังเปลี่ยนรอบ AA…', monsterAaRoundLoading:'กำลังโหลดรอบ AA ที่บันทึกไว้…', monsterAaRoundConnect:'เชื่อมต่อ GaC เพื่อเลือกรอบ AA ที่จะแสดงในเกม', monsterAaRoundLoadError:'โหลดรอบ AA ไม่สำเร็จ: {error}', monsterAaRoundSavedToast:'เลือกรอบ AA แล้ว เปิด Anomaly Arbitration ในเกมอีกครั้งเพื่อให้แสดงรอบนี้', monsterAaRoundClearedToast:'ล้างการแทนรอบ AA แล้ว ระบบจะแสดงรอบเริ่มต้นของ Client', monsterAaRoundSaveError:'เปลี่ยนรอบ AA ไม่สำเร็จ: {error}', monsterAaRoundGroupId:'รอบ {id}', monsterSettingsAARoomPreview:'ห้องในรอบนี้ · ตัวอย่างเท่านั้น' }, + 'zh-CN': { monsterAaRoundTitle:'选择 AA 轮次', monsterAaRoundDescription:'选择游戏重新加载 AA 数据时显示的轮次。具体房间仍在游戏内选择;下方条目仅供预览。', monsterAaRoundPending:'已选择轮次:{round}(组 {id})。请重新打开游戏内的异相仲裁以显示。', monsterAaRoundSelectLabel:'Anomaly Arbitration 轮次', monsterAaRoundDefaultOption:'使用 Client 默认轮次', monsterAaRoundDefault:'未覆盖轮次,将显示 Client 默认轮次。', monsterAaRoundUpdating:'正在更新 AA 轮次…', monsterAaRoundLoading:'正在加载已保存的 AA 轮次…', monsterAaRoundConnect:'连接 GaC 后才能选择游戏中显示的 AA 轮次。', monsterAaRoundLoadError:'无法加载 AA 轮次:{error}', monsterAaRoundSavedToast:'已选择 AA 轮次。请重新打开游戏内的异相仲裁以显示。', monsterAaRoundClearedToast:'已清除 AA 轮次覆盖,将显示 Client 默认轮次。', monsterAaRoundSaveError:'无法更新 AA 轮次:{error}', monsterAaRoundGroupId:'轮次 {id}', monsterSettingsAARoomPreview:'本轮房间 · 仅预览' }, + 'zh-TW': { monsterAaRoundTitle:'選擇 AA 輪次', monsterAaRoundDescription:'選擇遊戲重新載入 AA 資料時要顯示的輪次。房間仍在遊戲內選擇;下方項目僅供預覽。', monsterAaRoundPending:'已選擇輪次:{round}(組別 {id})。請重新開啟遊戲內的 Anomaly Arbitration 以顯示。', monsterAaRoundSelectLabel:'Anomaly Arbitration 輪次', monsterAaRoundDefaultOption:'使用 Client 預設輪次', monsterAaRoundDefault:'尚未覆寫輪次,將顯示 Client 預設輪次。', monsterAaRoundUpdating:'正在更新 AA 輪次…', monsterAaRoundLoading:'正在載入已儲存的 AA 輪次…', monsterAaRoundConnect:'連線 GaC 後才能選擇遊戲中顯示的 AA 輪次。', monsterAaRoundLoadError:'無法載入 AA 輪次:{error}', monsterAaRoundSavedToast:'已選擇 AA 輪次。請重新開啟遊戲內的 Anomaly Arbitration 以顯示。', monsterAaRoundClearedToast:'已清除 AA 輪次覆寫,將顯示 Client 預設輪次。', monsterAaRoundSaveError:'無法更新 AA 輪次:{error}', monsterAaRoundGroupId:'輪次 {id}', monsterSettingsAARoomPreview:'本輪房間 · 僅供預覽' }, + ja: { monsterAaRoundTitle:'AAのラウンドを選択', monsterAaRoundDescription:'ゲームがAAデータを再読み込みしたときに表示するラウンドを選びます。部屋はゲーム内で選択してください。下の項目はプレビューのみです。', monsterAaRoundPending:'選択中のラウンド:{round}(グループ {id})。表示するにはゲーム内でAnomaly Arbitrationを開き直してください。', monsterAaRoundSelectLabel:'Anomaly Arbitrationのラウンド', monsterAaRoundDefaultOption:'Clientのデフォルトを使用', monsterAaRoundDefault:'ラウンドは上書きされていません。Clientのデフォルトを表示します。', monsterAaRoundUpdating:'AAラウンドを更新中…', monsterAaRoundLoading:'保存済みAAラウンドを読み込み中…', monsterAaRoundConnect:'ゲームに表示するAAラウンドを選ぶにはGaCに接続してください。', monsterAaRoundLoadError:'AAラウンドを読み込めませんでした:{error}', monsterAaRoundSavedToast:'AAラウンドを選択しました。表示するにはゲーム内のAnomaly Arbitrationを開き直してください。', monsterAaRoundClearedToast:'AAラウンド設定を解除しました。Clientのデフォルトを表示します。', monsterAaRoundSaveError:'AAラウンドを更新できませんでした:{error}', monsterAaRoundGroupId:'ラウンド {id}', monsterSettingsAARoomPreview:'このラウンドの部屋 · プレビューのみ' }, + ko: { monsterAaRoundTitle:'AA 라운드 선택', monsterAaRoundDescription:'게임에서 AA 데이터를 다시 불러올 때 표시할 라운드를 선택하세요. 방은 게임 안에서 선택하며 아래 항목은 미리보기 전용입니다.', monsterAaRoundPending:'선택한 라운드: {round} (그룹 {id}). 게임에서 Anomaly Arbitration을 다시 열어 표시하세요.', monsterAaRoundSelectLabel:'Anomaly Arbitration 라운드', monsterAaRoundDefaultOption:'Client 기본 라운드 사용', monsterAaRoundDefault:'라운드 재정의가 없습니다. Client 기본 라운드를 표시합니다.', monsterAaRoundUpdating:'AA 라운드 업데이트 중…', monsterAaRoundLoading:'저장된 AA 라운드 불러오는 중…', monsterAaRoundConnect:'게임에 표시할 AA 라운드를 선택하려면 GaC에 연결하세요.', monsterAaRoundLoadError:'AA 라운드를 불러오지 못했습니다: {error}', monsterAaRoundSavedToast:'AA 라운드를 선택했습니다. 게임에서 Anomaly Arbitration을 다시 열어 표시하세요.', monsterAaRoundClearedToast:'AA 라운드 재정의를 지웠습니다. Client 기본 라운드를 표시합니다.', monsterAaRoundSaveError:'AA 라운드를 업데이트하지 못했습니다: {error}', monsterAaRoundGroupId:'라운드 {id}', monsterSettingsAARoomPreview:'이 라운드의 방 · 미리보기만' }, + vi: { monsterAaRoundTitle:'Chọn vòng AA', monsterAaRoundDescription:'Chọn vòng sẽ hiển thị khi game tải lại dữ liệu AA. Hãy chọn phòng trong game; các mục bên dưới chỉ để xem trước.', monsterAaRoundPending:'Đã chọn vòng: {round} (Nhóm {id}). Mở lại Anomaly Arbitration trong game để hiển thị.', monsterAaRoundSelectLabel:'Vòng Anomaly Arbitration', monsterAaRoundDefaultOption:'Dùng vòng mặc định của Client', monsterAaRoundDefault:'Không ghi đè vòng. Sẽ hiển thị vòng mặc định của Client.', monsterAaRoundUpdating:'Đang cập nhật vòng AA…', monsterAaRoundLoading:'Đang tải vòng AA đã lưu…', monsterAaRoundConnect:'Kết nối GaC để chọn vòng AA hiển thị trong game.', monsterAaRoundLoadError:'Không thể tải vòng AA: {error}', monsterAaRoundSavedToast:'Đã chọn vòng AA. Mở lại Anomaly Arbitration trong game để hiển thị.', monsterAaRoundClearedToast:'Đã xóa ghi đè vòng AA. Sẽ hiển thị vòng mặc định của Client.', monsterAaRoundSaveError:'Không thể cập nhật vòng AA: {error}', monsterAaRoundGroupId:'Vòng {id}', monsterSettingsAARoomPreview:'Phòng trong vòng này · chỉ xem trước' }, + id: { monsterAaRoundTitle:'Pilih ronde AA', monsterAaRoundDescription:'Pilih ronde yang ditampilkan saat game memuat ulang data AA. Pilih ruang dari dalam game; entri di bawah hanya pratinjau.', monsterAaRoundPending:'Ronde terpilih: {round} (Grup {id}). Buka kembali Anomaly Arbitration di game untuk menampilkannya.', monsterAaRoundSelectLabel:'Ronde Anomaly Arbitration', monsterAaRoundDefaultOption:'Gunakan ronde default Client', monsterAaRoundDefault:'Tidak ada ronde yang ditimpa. Ronde default Client akan ditampilkan.', monsterAaRoundUpdating:'Memperbarui ronde AA…', monsterAaRoundLoading:'Memuat ronde AA tersimpan…', monsterAaRoundConnect:'Hubungkan ke GaC untuk memilih ronde AA yang ditampilkan di game.', monsterAaRoundLoadError:'Tidak dapat memuat ronde AA: {error}', monsterAaRoundSavedToast:'Ronde AA dipilih. Buka kembali Anomaly Arbitration di game untuk menampilkannya.', monsterAaRoundClearedToast:'Penggantian ronde AA dihapus. Ronde default Client akan ditampilkan.', monsterAaRoundSaveError:'Tidak dapat memperbarui ronde AA: {error}', monsterAaRoundGroupId:'Ronde {id}', monsterSettingsAARoomPreview:'Ruang di ronde ini · pratinjau saja' }, + es: { monsterAaRoundTitle:'Elegir ronda de AA', monsterAaRoundDescription:'Elige la ronda que mostrará el juego al volver a cargar los datos de AA. Elige la sala dentro del juego; las entradas de abajo son solo una vista previa.', monsterAaRoundPending:'Ronda elegida: {round} (grupo {id}). Vuelve a abrir Anomaly Arbitration en el juego para mostrarla.', monsterAaRoundSelectLabel:'Ronda de Anomaly Arbitration', monsterAaRoundDefaultOption:'Usar la ronda predeterminada del cliente', monsterAaRoundDefault:'No hay una ronda sobrescrita. Se mostrará la predeterminada del cliente.', monsterAaRoundUpdating:'Actualizando ronda de AA…', monsterAaRoundLoading:'Cargando ronda de AA guardada…', monsterAaRoundConnect:'Conéctate a GaC para elegir la ronda de AA que se muestra en el juego.', monsterAaRoundLoadError:'No se pudo cargar la ronda de AA: {error}', monsterAaRoundSavedToast:'Ronda de AA elegida. Vuelve a abrir Anomaly Arbitration en el juego para mostrarla.', monsterAaRoundClearedToast:'Se borró la sobrescritura de AA. Se mostrará la ronda predeterminada del cliente.', monsterAaRoundSaveError:'No se pudo actualizar la ronda de AA: {error}', monsterAaRoundGroupId:'Ronda {id}', monsterSettingsAARoomPreview:'Salas de esta ronda · solo vista previa' }, + fr: { monsterAaRoundTitle:'Choisir le tour AA', monsterAaRoundDescription:'Choisissez le tour que le jeu affichera au prochain chargement des données AA. Choisissez la salle dans le jeu ; les entrées ci-dessous sont des aperçus.', monsterAaRoundPending:'Tour sélectionné : {round} (groupe {id}). Rouvrez Anomaly Arbitration dans le jeu pour l’afficher.', monsterAaRoundSelectLabel:'Tour Anomaly Arbitration', monsterAaRoundDefaultOption:'Utiliser le tour par défaut du client', monsterAaRoundDefault:'Aucun tour forcé. Le tour par défaut du client sera affiché.', monsterAaRoundUpdating:'Mise à jour du tour AA…', monsterAaRoundLoading:'Chargement du tour AA enregistré…', monsterAaRoundConnect:'Connectez-vous à GaC pour choisir le tour AA affiché dans le jeu.', monsterAaRoundLoadError:'Impossible de charger le tour AA : {error}', monsterAaRoundSavedToast:'Tour AA sélectionné. Rouvrez Anomaly Arbitration dans le jeu pour l’afficher.', monsterAaRoundClearedToast:'Remplacement du tour AA effacé. Le tour par défaut du client sera affiché.', monsterAaRoundSaveError:'Impossible de modifier le tour AA : {error}', monsterAaRoundGroupId:'Tour {id}', monsterSettingsAARoomPreview:'Salles de ce tour · aperçu uniquement' }, + de: { monsterAaRoundTitle:'AA-Runde auswählen', monsterAaRoundDescription:'Wähle die Runde, die das Spiel beim erneuten Laden der AA-Daten anzeigen soll. Den Raum wählst du im Spiel; die Einträge unten sind nur Vorschauen.', monsterAaRoundPending:'Ausgewählte Runde: {round} (Gruppe {id}). Öffne Anomaly Arbitration im Spiel erneut, damit sie angezeigt wird.', monsterAaRoundSelectLabel:'Anomaly-Arbitration-Runde', monsterAaRoundDefaultOption:'Client-Standardrunde verwenden', monsterAaRoundDefault:'Keine Runde überschrieben. Die Client-Standardrunde wird angezeigt.', monsterAaRoundUpdating:'AA-Runde wird aktualisiert…', monsterAaRoundLoading:'Gespeicherte AA-Runde wird geladen…', monsterAaRoundConnect:'Mit GaC verbinden, um die im Spiel angezeigte AA-Runde auszuwählen.', monsterAaRoundLoadError:'AA-Runde konnte nicht geladen werden: {error}', monsterAaRoundSavedToast:'AA-Runde ausgewählt. Öffne Anomaly Arbitration im Spiel erneut.', monsterAaRoundClearedToast:'AA-Rundenüberschreibung entfernt. Die Client-Standardrunde wird angezeigt.', monsterAaRoundSaveError:'AA-Runde konnte nicht aktualisiert werden: {error}', monsterAaRoundGroupId:'Runde {id}', monsterSettingsAARoomPreview:'Räume dieser Runde · nur Vorschau' }, + ru: { monsterAaRoundTitle:'Выбрать раунд AA', monsterAaRoundDescription:'Выберите раунд, который игра покажет при повторной загрузке данных AA. Комнату выбирайте в игре; записи ниже — только предварительный просмотр.', monsterAaRoundPending:'Выбран раунд: {round} (группа {id}). Откройте Anomaly Arbitration в игре повторно, чтобы увидеть его.', monsterAaRoundSelectLabel:'Раунд Anomaly Arbitration', monsterAaRoundDefaultOption:'Использовать раунд клиента по умолчанию', monsterAaRoundDefault:'Раунд не переопределён. Будет показан раунд клиента по умолчанию.', monsterAaRoundUpdating:'Обновление раунда AA…', monsterAaRoundLoading:'Загрузка сохранённого раунда AA…', monsterAaRoundConnect:'Подключитесь к GaC, чтобы выбрать отображаемый в игре раунд AA.', monsterAaRoundLoadError:'Не удалось загрузить раунд AA: {error}', monsterAaRoundSavedToast:'Раунд AA выбран. Повторно откройте Anomaly Arbitration в игре.', monsterAaRoundClearedToast:'Переопределение раунда AA очищено. Будет показан раунд клиента по умолчанию.', monsterAaRoundSaveError:'Не удалось изменить раунд AA: {error}', monsterAaRoundGroupId:'Раунд {id}', monsterSettingsAARoomPreview:'Комнаты этого раунда · только просмотр' }, + pt: { monsterAaRoundTitle:'Escolher rodada de AA', monsterAaRoundDescription:'Escolha a rodada que o jogo exibirá ao carregar os dados de AA novamente. Escolha a sala no jogo; as entradas abaixo são apenas prévias.', monsterAaRoundPending:'Rodada selecionada: {round} (grupo {id}). Reabra Anomaly Arbitration no jogo para exibi-la.', monsterAaRoundSelectLabel:'Rodada de Anomaly Arbitration', monsterAaRoundDefaultOption:'Usar rodada padrão do Client', monsterAaRoundDefault:'Nenhuma rodada substituída. A rodada padrão do Client será exibida.', monsterAaRoundUpdating:'Atualizando rodada de AA…', monsterAaRoundLoading:'Carregando rodada de AA salva…', monsterAaRoundConnect:'Conecte-se ao GaC para escolher a rodada de AA exibida no jogo.', monsterAaRoundLoadError:'Não foi possível carregar a rodada de AA: {error}', monsterAaRoundSavedToast:'Rodada de AA selecionada. Reabra Anomaly Arbitration no jogo para exibi-la.', monsterAaRoundClearedToast:'Substituição da rodada de AA removida. A rodada padrão do Client será exibida.', monsterAaRoundSaveError:'Não foi possível atualizar a rodada de AA: {error}', monsterAaRoundGroupId:'Rodada {id}', monsterSettingsAARoomPreview:'Salas desta rodada · apenas prévia' } +}; + +for (const [language, additions] of Object.entries(AA_ROUND_SELECTION_TRANSLATIONS)) { + Object.assign(TRANSLATIONS[language], additions); +} + const MONSTER_PICKER_TRANSLATIONS = { en: 'Search monsters by name or ID', th: 'ค้นหามอนสเตอร์ด้วยชื่อหรือ ID',