diff options
Diffstat (limited to 'internal/admin/static/map.js')
| -rw-r--r-- | internal/admin/static/map.js | 184 |
1 files changed, 57 insertions, 127 deletions
diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js index ab20a86..7c47b4e 100644 --- a/internal/admin/static/map.js +++ b/internal/admin/static/map.js @@ -26,6 +26,7 @@ var oppositeDir = { up:'down', down:'up' }; var saveTimer = null; +var _triggersDirty = false; var upTarget = {}, downTarget = {}; var _mapMouseCleanup = false; @@ -530,6 +531,7 @@ function updateView() { function selectRoom(id) { if (saveTimer) { clearTimeout(saveTimer); saveTimer = null; } + _triggersDirty = false; var prev = selectedRoom; selectedRoom = id; multiSelectedIds.clear(); @@ -575,8 +577,7 @@ function renderSidePanel(data) { html += '<button class="panel-tab' + (currentPanelTab === 5 ? ' active' : '') + '" onclick="switchPanelTab(5)">Items</button>'; html += '<button class="panel-tab' + (currentPanelTab === 6 ? ' active' : '') + '" onclick="switchPanelTab(6)">Hazard</button>'; html += '<button class="panel-tab' + (currentPanelTab === 7 ? ' active' : '') + '" onclick="switchPanelTab(7)">Agility</button>'; - html += '<button class="panel-tab' + (currentPanelTab === 8 ? ' active' : '') + '" onclick="switchPanelTab(8)">Enter/Exit</button>'; - html += '<button class="panel-tab' + (currentPanelTab === 9 ? ' active' : '') + '" onclick="switchPanelTab(9)">Triggers</button>'; + html += '<button class="panel-tab' + (currentPanelTab === 8 ? ' active' : '') + '" onclick="switchPanelTab(8)">Triggers</button>'; html += '</div>'; html += '<div class="panel-tab-content">'; @@ -598,11 +599,14 @@ function renderSidePanel(data) { } } else if (currentPanelTab === 8) html += renderEnterExitTab(r); - else if (currentPanelTab === 9) html += renderTriggersTab(r); html += '</div>'; panel.innerHTML = html; setTimeout(function() { + if (currentPanelTab >= 8) { + var content = panel.querySelector('.panel-tab-content'); + if (content) { bindTriggerInputSave(content); ced_setupSearchFields(content); } + } var cf = document.querySelector('#panelContent .color-field'); if (cf) { bindColorField(cf); @@ -1406,9 +1410,9 @@ function switchPanelTab(idx) { } } else if (idx === 8) html = renderEnterExitTab(r); - else if (idx === 9) html = renderTriggersTab(r); var content = document.querySelector('.panel-tab-content'); if (content) content.innerHTML = html; + if (idx >= 8) { setTimeout(function() { bindTriggerInputSave(content); ced_setupSearchFields(content); }, 10); } var tabs = document.querySelectorAll('.panel-tab'); tabs.forEach(function(t, i) { t.classList.toggle('active', i === idx); }); if (idx === 0) { @@ -2076,6 +2080,43 @@ function autoSave() { saveTimer = setTimeout(function() { doSavePanel(); }, 600); } +function bindTriggerInputSave(container) { + var inputs = container.querySelectorAll('input:not([type="checkbox"]):not([type="radio"]), textarea'); + inputs.forEach(function(el) { + el.removeEventListener('input', _triggerInputHandler); + el.addEventListener('input', _triggerInputHandler); + }); + var checks = container.querySelectorAll('input[type="checkbox"], input[type="radio"]'); + checks.forEach(function(el) { + el.removeEventListener('change', _triggerInputHandler); + el.addEventListener('change', _triggerInputHandler); + }); +} + +function _syncTriggersToData() { + if (!window._editorData || !window._panelTriggerSecs) return; + ced_syncDOMToData(window._editorData); + if (typeof ie_syncAllInteractions === 'function') { + ie_syncAllInteractions(window._editorData, window._panelTriggerSecs); + } + if (currentRoomData && currentRoomData.room) { + window._panelTriggerSecs.forEach(function(sec) { + currentRoomData.room[sec.path] = window._editorData[sec.path]; + }); + } +} + +function _triggerInputHandler() { + _syncTriggersToData(); + _triggersDirty = true; +} + +function saveTriggers() { + _syncTriggersToData(); + _triggersDirty = false; + saveNow(); +} + function saveNow() { if (window._colorPickerActive) return; if (saveTimer) clearTimeout(saveTimer); @@ -2104,8 +2145,6 @@ async function doSavePanel() { r.hazard = currentRoomData.room.hazard || ''; r.on_enter = currentRoomData.room.on_enter; r.on_exit = currentRoomData.room.on_exit; - r.on_flag_change = currentRoomData.room.on_flag_change; - r.on_global_flag_change = currentRoomData.room.on_global_flag_change; if (r.block_transport === undefined) r.block_transport = currentRoomData.room.block_transport; } @@ -2126,6 +2165,7 @@ async function doSavePanel() { await API.put('/api/rooms/' + id, r); notify('Room #' + id + ' saved', 'success'); + _triggersDirty = false; updateUndoBar(); await loadMap(); selectRoom(id); @@ -3382,51 +3422,26 @@ function renderEnterExitTab(r) { currentRoomData.room.on_exit = window._editorData.on_exit; } switchPanelTab(8); - autoSave(); + _triggersDirty = true; }; window._ieRenderOnly = window._ieRenderCurrent; window._ieSyncAll = ie_syncAllInteractions; + window._panelTriggerSecs = [ + {id: 'on_enter', path: 'on_enter', isArray: true, interactionStyle: true, itemIDAllowed: false, label: 'Trigger'}, + {id: 'on_exit', path: 'on_exit', isArray: true, interactionStyle: true, itemIDAllowed: false, label: 'Trigger'} + ]; - var h = '<h3 style="margin:8px 0 4px">On-Enter Triggers</h3>'; + var h = '<div style="margin:8px 0"><button class="btn btn-primary" onclick="saveTriggers()">Save Triggers</button></div>'; + h += '<h3 style="margin:8px 0 4px">On-Enter Triggers</h3>'; h += '<p style="color:#888;font-size:11px;margin:0 0 6px">Fires when a player enters this room. First matching entry wins.</p>'; h += renderTriggerListHTML(r.on_enter, 'on_enter', 'on_enter', false); - h += '<h3 style="margin:16px 0 4px">On-Exit Triggers</h3>'; + h += '<hr style="border:none;border-top:1px solid var(--border);margin:12px 0">'; + h += '<h3 style="margin:8px 0 4px">On-Exit Triggers</h3>'; h += '<p style="color:#888;font-size:11px;margin:0 0 6px">Fires when a player leaves this room. First matching entry wins.</p>'; h += renderTriggerListHTML(r.on_exit, 'on_exit', 'on_exit', false); return h; } -// ── Triggers tab (on_flag_change / on_global_flag_change) ── - -function renderTriggersTab(r) { - var fc = Array.isArray(r.on_flag_change) ? r.on_flag_change : []; - var gfc = Array.isArray(r.on_global_flag_change) ? r.on_global_flag_change : []; - window._editorData = { on_flag_change: fc, on_global_flag_change: gfc }; - - window._ieRenderCurrent = function() { - if (window._ieSyncAll) window._ieSyncAll(window._editorData, [ - {id: 'on_flag_change', path: 'on_flag_change', isArray: true, interactionStyle: true, itemIDAllowed: false, label: 'Trigger'}, - {id: 'on_global_flag_change', path: 'on_global_flag_change', isArray: true, interactionStyle: true, itemIDAllowed: false, label: 'Trigger'} - ]); - if (currentRoomData && currentRoomData.room) { - currentRoomData.room.on_flag_change = window._editorData.on_flag_change; - currentRoomData.room.on_global_flag_change = window._editorData.on_global_flag_change; - } - switchPanelTab(9); - autoSave(); - }; - window._ieRenderOnly = window._ieRenderCurrent; - window._ieSyncAll = ie_syncAllInteractions; - - var h = '<h3 style="margin:8px 0 4px">On-Flag-Change Triggers</h3>'; - h += '<p style="color:#888;font-size:11px;margin:0 0 6px">Fires when a player flag changes. Use the condition\'s "In Room" gate to scope to this room (delete it to make global).</p>'; - h += renderFlagTriggerListHTML(r.on_flag_change, 'on_flag_change', 'on_flag_change', 'player'); - h += '<h3 style="margin:16px 0 4px">On-Global-Flag-Change Triggers</h3>'; - h += '<p style="color:#888;font-size:11px;margin:0 0 6px">Fires when a global flag changes (no player involved).</p>'; - h += renderFlagTriggerListHTML(r.on_global_flag_change, 'on_global_flag_change', 'on_global_flag_change', 'global'); - return h; -} - // renderTriggerListHTML emits the HTML for a []Trigger block using the shared // ie_renderArraySection. It's a thin shim that builds the ctx the card // editors construct. @@ -3469,61 +3484,10 @@ function renderTriggerListHTML(arr, secId, secPath, allowItem) { return '_mapAddTrigger(\'' + secPath + '\')'; } }; - return ie_renderArraySection(ctx); -} - -// renderFlagTriggerListHTML is like renderTriggerListHTML but adds the -// on_player_flag / on_global_flag subscription header per entry. -function renderFlagTriggerListHTML(arr, secId, secPath, kind) { - var visMap = {}; - arr = Array.isArray(arr) ? arr : []; - var sec = { - id: secId, - path: secPath, - isArray: true, - interactionStyle: true, - itemIDAllowed: false, - label: 'Trigger', - fields: [] - }; - var flagKey = kind === 'player' ? 'on_player_flag' : 'on_global_flag'; - - var h = ''; - arr.forEach(function(item, idx) { - visMap[idx] = visMap[idx] || {}; - var condExists = !!(item.condition && typeof item.condition === 'object'); - var lockVal = !!item.lock; - var steps = item.steps || []; - var flagName = item[flagKey] || ''; - - h += '<div class="obj-sub-row ie-inter-row" data-idx="' + idx + '">'; - h += '<div class="ie-inter-header">'; - h += '<label class="ie-lock-lbl"><input type="checkbox" class="ie-lock-cb"' + (lockVal ? ' checked' : '') + '> Lock Player</label>'; - if (idx > 0) h += '<button class="btn btn-sm ie-trig-up" onclick="ie_moveRow(\'' + escAttr(secPath) + '\',' + idx + ',' + (idx - 1) + ')" title="Move up">▲</button>'; - if (idx < arr.length - 1) h += '<button class="btn btn-sm ie-trig-dn" onclick="ie_moveRow(\'' + escAttr(secPath) + '\',' + idx + ',' + (idx + 1) + ')" title="Move down">▼</button>'; - h += '<span class="ie-cond-leaf-label">' + esc(kind === 'player' ? 'Player Flag' : 'Global Flag') + '</span>'; - h += '<input class="ie-flag-name" data-ie-flag-key="' + escAttr(flagKey) + '" data-idx="' + idx + '" value="' + escAttr(flagName) + '" placeholder="' + escAttr(kind === 'player' ? 'player flag name' : 'global flag name') + '" style="flex:1;min-width:80px" oninput="_mapSyncFlag(\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(flagKey) + '\')">'; - h += '<span class="ie-cond-eq">=</span>'; - h += '<input class="ie-flag-value" data-idx="' + idx + '" value="' + escAttr(item.value !== undefined ? String(item.value) : '') + '" placeholder="exists" style="width:60px" oninput="_mapSyncFlag(\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(flagKey) + '\')">'; - h += '<span class="ie-inter-header-spacer"></span>'; - if (!condExists) { - h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addRootConditionGroup(\'' + escAttr(secPath) + '\',' + idx + ',\'entry\')">+ Add Condition</button>'; - } - h += '<button class="btn btn-sm btn-danger ie-inter-remove" onclick="_mapRemoveTrigger(\'' + escAttr(secPath) + '\',' + idx + ')">Remove Trigger</button>'; - h += '</div>'; - h += '<div class="ie-inter-panel" style="display:' + (condExists ? 'block' : 'none') + '">'; - h += '<div class="ie-inter-panel-body">' + ie_renderCond(item.condition || null, secId, idx, secPath, 'entry') + '</div>'; - h += '</div>'; - h += '<div class="ie-inter-steps">'; - h += ie_renderStepList(steps, secId, idx, secPath); - h += '</div>'; - h += '</div>'; - }); - h += '<button class="btn btn-sm obj-sub-add" onclick="_mapAddFlagTrigger(\'' + escAttr(secPath) + '\',\'' + escAttr(flagKey) + '\')">+ Add Trigger</button>'; - return h; + return '<div class="obj-card" data-card="' + escAttr(secId) + '" style="max-width:100%">' + ie_renderArraySection(ctx) + '</div>'; } -// _mapAddTrigger / _mapRemoveTrigger / _mapAddFlagTrigger: mutation helpers +// _mapAddTrigger / _mapRemoveTrigger: mutation helpers // called from the inline onclick handlers. They sync the DOM, splice the // _editorData array, and re-render. function _mapAddTrigger(secPath) { @@ -3537,18 +3501,6 @@ function _mapAddTrigger(secPath) { window._ieRenderCurrent(); } -function _mapAddFlagTrigger(secPath, flagKey) { - if (!window._editorData) return; - if (!window._editorData[secPath]) window._editorData[secPath] = []; - var entry = {steps: []}; - entry[flagKey] = ''; - window._editorData[secPath].push(entry); - if (currentRoomData && currentRoomData.room) { - currentRoomData.room[secPath] = window._editorData[secPath]; - } - window._ieRenderCurrent(); -} - function _mapRemoveTrigger(secPath, idx) { if (!window._editorData || !window._editorData[secPath]) return; window._editorData[secPath].splice(idx, 1); @@ -3557,25 +3509,3 @@ function _mapRemoveTrigger(secPath, idx) { } window._ieRenderCurrent(); } - -function _mapSyncFlag(secPath, idx, flagKey) { - if (!window._editorData || !window._editorData[secPath]) return; - var row = document.querySelector('.ie-inter-row[data-idx="' + idx + '"]'); - if (!row) return; - var nameEl = row.querySelector('.ie-flag-name'); - var valEl = row.querySelector('.ie-flag-value'); - if (nameEl) { - window._editorData[secPath][idx][flagKey] = nameEl.value.trim(); - } - if (valEl) { - var v = valEl.value.trim(); - if (v === '') delete window._editorData[secPath][idx].value; - else { - try { window._editorData[secPath][idx].value = JSON.parse(v); } catch(e) { window._editorData[secPath][idx].value = v; } - } - } - if (currentRoomData && currentRoomData.room) { - currentRoomData.room[secPath] = window._editorData[secPath]; - } - autoSave(); -} |
