From ecba7f726f70b37126d852c38c7e3eec7b04d730 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 9 Jul 2026 22:15:54 -0400 Subject: feat: old standalone trigger systems completely unified into trigger->condition->action system --- internal/admin/static/map.js | 229 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) (limited to 'internal/admin/static/map.js') diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js index 00230a4..ab20a86 100644 --- a/internal/admin/static/map.js +++ b/internal/admin/static/map.js @@ -575,6 +575,8 @@ function renderSidePanel(data) { html += ''; html += ''; html += ''; + html += ''; + html += ''; html += ''; html += '
'; @@ -595,6 +597,8 @@ function renderSidePanel(data) { html += renderCourseTab(); } } + else if (currentPanelTab === 8) html += renderEnterExitTab(r); + else if (currentPanelTab === 9) html += renderTriggersTab(r); html += '
'; panel.innerHTML = html; @@ -1401,6 +1405,8 @@ function switchPanelTab(idx) { html = renderCourseTab(); } } + 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; var tabs = document.querySelectorAll('.panel-tab'); @@ -2096,6 +2102,10 @@ async function doSavePanel() { r.mobs = currentRoomData.room.mobs; r.exits = currentRoomData.room.exits; 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; } @@ -3350,3 +3360,222 @@ document.addEventListener('DOMContentLoaded', function() { loadRoomDirs(function window.addEventListener('keydown', function(e) { if (isCtrlKey(e)) { ctrlHeld = true; if (dragging) refreshDragMode(true); } }); window.addEventListener('keyup', function(e) { if (isCtrlKey(e)) { ctrlHeld = false; if (dragging) refreshDragMode(false); } }); }); + +// ── Enter/Exit tab ── +// Renders on_enter and on_exit trigger lists, wiring the shared ie_* helpers +// from intereditor.js so the same condition/step/message editors used by the +// object/mob editors are reused here. Edits mutate currentRoomData.room.* +// and the debounced autoSave() persists them. + +function renderEnterExitTab(r) { + var onEnter = Array.isArray(r.on_enter) ? r.on_enter : []; + var onExit = Array.isArray(r.on_exit) ? r.on_exit : []; + window._editorData = { on_enter: onEnter, on_exit: onExit }; + + window._ieRenderCurrent = function() { + if (window._ieSyncAll) window._ieSyncAll(window._editorData, [ + {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'} + ]); + if (currentRoomData && currentRoomData.room) { + currentRoomData.room.on_enter = window._editorData.on_enter; + currentRoomData.room.on_exit = window._editorData.on_exit; + } + switchPanelTab(8); + autoSave(); + }; + window._ieRenderOnly = window._ieRenderCurrent; + window._ieSyncAll = ie_syncAllInteractions; + + var h = '

On-Enter Triggers

'; + h += '

Fires when a player enters this room. First matching entry wins.

'; + h += renderTriggerListHTML(r.on_enter, 'on_enter', 'on_enter', false); + h += '

On-Exit Triggers

'; + h += '

Fires when a player leaves this room. First matching entry wins.

'; + 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 = '

On-Flag-Change Triggers

'; + h += '

Fires when a player flag changes. Use the condition\'s "In Room" gate to scope to this room (delete it to make global).

'; + h += renderFlagTriggerListHTML(r.on_flag_change, 'on_flag_change', 'on_flag_change', 'player'); + h += '

On-Global-Flag-Change Triggers

'; + h += '

Fires when a global flag changes (no player involved).

'; + 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. +function renderTriggerListHTML(arr, secId, secPath, allowItem) { + var visMap = {}; + arr = Array.isArray(arr) ? arr : []; + var sec = { + id: secId, + path: secPath, + isArray: true, + interactionStyle: true, + itemIDAllowed: allowItem, + label: 'Trigger', + fields: [['item_id', 'Item ID', 'search', 'e.g. keycard', '', null, 'items']] + }; + var ctx = { + sec: sec, + data: window._editorData, + visMap: visMap, + showField: function(sid, idx, key) { + ie_showField(visMap, sid, idx, key); + return 'void(0)'; + }, + hideField: function(sid, idx, key) { + ie_hideField(visMap, sid, idx, key); + window._ieRenderCurrent(); + return 'void(0)'; + }, + addCondition: function(sid, idx) { + return 'ie_addRootConditionGroup(\'' + sid + '\',' + idx + ',\'entry\')'; + }, + showRemove: function(idx) { + return '_mapRemoveTrigger(\'' + secPath + '\',' + idx + ')'; + }, + renderField: function(s, field, data, idx, style) { + var val = (data[secPath] && data[secPath][idx] && data[secPath][idx].item_id) || ''; + return ''; + }, + onAdd: function() { + 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 += '
'; + h += '
'; + h += ''; + if (idx > 0) h += ''; + if (idx < arr.length - 1) h += ''; + h += '' + esc(kind === 'player' ? 'Player Flag' : 'Global Flag') + ''; + h += ''; + h += '='; + h += ''; + h += ''; + if (!condExists) { + h += ''; + } + h += ''; + h += '
'; + h += '
'; + h += '
' + ie_renderCond(item.condition || null, secId, idx, secPath, 'entry') + '
'; + h += '
'; + h += '
'; + h += ie_renderStepList(steps, secId, idx, secPath); + h += '
'; + h += '
'; + }); + h += ''; + return h; +} + +// _mapAddTrigger / _mapRemoveTrigger / _mapAddFlagTrigger: mutation helpers +// called from the inline onclick handlers. They sync the DOM, splice the +// _editorData array, and re-render. +function _mapAddTrigger(secPath) { + if (!window._editorData) return; + if (window._ieSyncAll) window._ieSyncAll(window._editorData, []); + if (!window._editorData[secPath]) window._editorData[secPath] = []; + window._editorData[secPath].push({steps: []}); + if (currentRoomData && currentRoomData.room) { + currentRoomData.room[secPath] = window._editorData[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); + if (currentRoomData && currentRoomData.room) { + currentRoomData.room[secPath] = window._editorData[secPath]; + } + 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(); +} -- cgit v1.2.3