From 74153c7814fc4cef988066ef04e38731a943bc12 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 9 Jul 2026 04:03:00 -0400 Subject: feat(admin): nested condition/action buttons to manage complex YAML --- internal/admin/static/objecteditor.js | 190 ++++++++++++---------------------- 1 file changed, 69 insertions(+), 121 deletions(-) (limited to 'internal/admin/static/objecteditor.js') diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index ecb3ced..9c5a279 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -86,28 +86,29 @@ var SECTIONS = [ ] }, { - id: 'on_use', label: 'On Use', labelHint: '(what happens when you use items on this)', path: 'on_use', isArray: true, fullWidth: true, interactionStyle: true, + id: 'on_use', label: 'On Use', labelHint: '(what happens when you use items on this)', path: 'on_use', isArray: true, fullWidth: true, interactionStyle: true, interScope: 'inline', detect: function(d) { return d.on_use && Array.isArray(d.on_use); }, fields: [ ['item_id', 'Item ID', 'search', 'e.g. keycard (empty = bare use)', '', null, 'items'], - ['condition', 'Condition', 'json', 'e.g. {"flag":"my_flag"} or {"player_flag":"x","value":1} or {"all_of":[{"flag":"a"},{"has_item":"key"}]}'], - ['action', 'Action', 'json', 'e.g. {"set_global_flags":{"my_flag":true},"message":"You activate it.","give_item":"gem","teleport":5,"heal":10,"credits":100,"aps_node":true,"spawn_mob":"rat","despawn_mob":"rat","broadcast":"%p activates it.","broadcast_global":"%p did it!","delay":3}'], - ['message', 'Message', 'text', 'You use the object...'], + ['condition', 'Condition', 'inter_cond'], + ['action', 'Action', 'inter_act'], ] }, { - id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look ")', path: 'on_look', isArray: true, fullWidth: true, interactionStyle: true, + id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look ")', path: 'on_look', isArray: true, fullWidth: true, interactionStyle: true, interScope: 'inline', detect: function(d) { return d.on_look && Array.isArray(d.on_look); }, fields: [ ['item_id', 'Item ID', 'search', '(optional — only fires if you carry this item)', '', null, 'items'], - ['condition', 'Condition', 'json', 'e.g. {"player_flag":"read_sign","value":true}'], - ['action', 'Action', 'json', 'e.g. {"set_player_flags":{"read_sign":true},"give_item":"gem"}'], - ['message', 'Message', 'text', 'Shown to the player after the description...'], + ['condition', 'Condition', 'inter_cond'], + ['action', 'Action', 'inter_act'], ] }, ]; function initObjectEditor() { + window._ieRenderCurrent = renderCurrent; + window._ieRenderOnly = function() { if (objectID && objectData) renderObjectEditor(objectID, objectData); }; + window._ieSyncAll = function(ed) { if (ed) ie_syncAllInteractions(ed, SECTIONS); }; editorType = 'objects'; editorFields = []; loadList(); @@ -123,7 +124,7 @@ function loadObject(id) { API.get('/api/objects/' + encodeURIComponent(id)).then(function(data) { objectData = data; window._editorData = objectData; - window._useInterVis = {}; + window._interVis = {}; window._useInterHelpCollapsed = undefined; window._coreVis = {}; window._stealVis = {}; @@ -509,104 +510,30 @@ function renderArraySection(sec, data) { if (isInter) { window._interVis = window._interVis || {}; if (!window._interVis[sec.id]) window._interVis[sec.id] = {}; + return ie_renderArraySection({ + sec: sec, + data: data, + visMap: window._interVis[sec.id], + showRemove: function(idx) { return 'removeArrayItem(\'' + sec.id + '\',' + idx + ')'; }, + showField: function(secId, idx, key) { return 'showInterField(\'' + secId + '\',' + idx + ',\'' + key + '\')'; }, + hideField: function(secId, idx, key) { return 'hideInterField(\'' + secId + '\',' + idx + ',\'' + key + '\')'; }, + addCondition: function(secId, idx) { return 'addInterRootCondition(\'' + secId + '\',' + idx + ')'; }, + addEffect: function(secId, idx) { return 'addInterAction(\'' + secId + '\',' + idx + ')'; }, + renderField: function(sec, f, data, idx, optStyle) { return renderField(sec, f, data, idx, null, optStyle); }, + onAdd: function() { return 'addArrayItem(\'' + sec.id + '\')'; }, + }); } arr.forEach(function(item, idx) { - if (isInter) { - var visMap = window._interVis[sec.id]; - var vis = visMap[idx] || {}; - visMap[idx] = vis; - var showItem = vis.item_id || (item.item_id && item.item_id !== ''); - var showCond = vis.condition || (item.condition && item.condition !== ''); - var showAct = vis.action || (item.action && item.action !== ''); - var showMsg = vis.message || (item.message && item.message !== ''); - - h += '
'; - - h += ''; - - h += '
'; - if (!showItem) { - h += ''; - h += '
'; - } - if (!showCond) { - h += ''; - } - if (!showAct) { - h += ''; - } - if (!showMsg) { - h += ''; - } - h += '
'; - - h += '
'; - h += renderField(sec, sec.fields[0], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - - h += '
'; - h += renderField(sec, sec.fields[1], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - - h += '
'; - h += renderField(sec, sec.fields[2], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - - h += '
'; - h += renderField(sec, sec.fields[3], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - - h += '
'; - } else { - h += '
'; - h += '
'; - sec.fields.forEach(function(f) { - h += renderField(sec, f, data, idx); - }); - h += '
'; - h += ''; - h += '
'; - } - }); - h += ''; - if (isInter) { - var helpKey = sec.id + '_helpCollapsed'; - var helpCollapsed = window[helpKey] === undefined ? true : window[helpKey]; - h += '
'; - h += '
'; - h += '' + (helpCollapsed ? '▶' : '▼') + ''; - h += 'How Conditions & Actions Work'; + h += '
'; + h += '
'; + sec.fields.forEach(function(f) { + h += renderField(sec, f, data, idx); + }); h += '
'; - if (!helpCollapsed) { - h += '
'; - h += 'Entries checked top-to-bottom, first match wins (one fires per use/look/kill).

'; - h += 'Item ID: on_use requires use <item> on <object>; empty = bare use <object>. on_look = only fires if you carry this item. on_kill = only fires if you wield this weapon when the mob is defeated.
'; - h += 'Condition: JSON. Available gates:
'; - h += '  {"global_flag":"gate_open","not":true} — world flag is NOT set
'; - h += '  {"player_flag":"has_key","value":1} — player flag matches value
'; - h += '  {"has_item":"silver_bar"} — player carries item
'; - h += '  {"min_credits":50} — minimum credits
'; - h += '  {"all_of":[{"global_flag":"a"},{"has_item":"b"}]} — ALL
'; - h += '  {"any_of":[...]} — ANY
'; - h += '  Omit for an unconditional interaction.
'; - h += 'Action: JSON. Effects that fire when the interaction runs:
'; - h += '  {"set_global_flags":{"gate_open":true}} — set world flags
'; - h += '  {"set_player_flags":{"spoke":true}} — set player flags
'; - h += '  {"give_item":"keycard","take_item":"bomb"} — give/take item
'; - h += '  {"teleport":5,"heal":10,"credits":100} — move, heal, give credits
'; - h += '  {"aps_node":true} — mark current room as APS node
'; - h += '  {"spawn_mob":"rat","despawn_mob":"rat"} — spawn/despawn transient mob
'; - h += '  {"broadcast":"%p activates it.","broadcast_global":"%p did it!"} — announce (%p = player, %v = flag value)
'; - h += '  {"message":"You feel energized.","delay":3} — only meaningful inside on_enter steps / triggers (delay in ticks)
'; - h += 'Message: Sent to the player when the interaction fires (one-tick action).'; - h += '
'; - } + h += ''; h += '
'; - } + }); + h += ''; return h; } @@ -973,6 +900,7 @@ function validateDropRow(idx) { function renderCurrent() { ced_syncDOMToData(objectData); + ie_syncAllInteractions(objectData, SECTIONS); syncGatherToolsFromDOM(); if (!objectData || !objectID) return; renderObjectEditor(objectID, objectData); @@ -980,20 +908,35 @@ function renderCurrent() { function showInterField(secId, idx, key) { window._interVis = window._interVis || {}; - if (!window._interVis[secId]) window._interVis[secId] = {}; - if (!window._interVis[secId][idx]) window._interVis[secId][idx] = {}; - window._interVis[secId][idx][key] = true; + ie_showField(window._interVis, secId, idx, key); renderCurrent(); } +function addInterRootCondition(secId, idx) { + var sec = SECTIONS.find(function(s) { return s.id === secId; }); + if (!sec) return; + ie_addRootConditionGroup(sec.path, idx); +} + +function addInterAction(secId, idx) { + var sec = SECTIONS.find(function(s) { return s.id === secId; }); + if (!sec) return; + ie_addEffectToInteraction(sec.path, idx); +} + function hideInterField(secId, idx, key) { window._interVis = window._interVis || {}; - if (!window._interVis[secId]) window._interVis[secId] = {}; - if (!window._interVis[secId][idx]) window._interVis[secId][idx] = {}; - window._interVis[secId][idx][key] = false; + ie_hideField(window._interVis, secId, idx, key); + var sec = SECTIONS.find(function(s) { return s.id === secId; }); + var secPath = sec ? sec.path : secId; ced_syncDOMToData(objectData); - if (objectData[secId] && objectData[secId][idx]) { - objectData[secId][idx][key] = ''; + ie_syncAllInteractions(objectData, SECTIONS); + if (objectData[secPath] && objectData[secPath][idx]) { + if (key === 'condition' || key === 'action') { + delete objectData[secPath][idx][key]; + } else { + objectData[secPath][idx][key] = ''; + } } renderObjectEditor(objectID, objectData); } @@ -1032,16 +975,6 @@ function hideGuardMob() { renderObjectEditor(objectID, objectData); } -function toggleInterHelp(secId) { - var key = secId + '_helpCollapsed'; - if (window[key] === undefined) { - window[key] = false; - } else { - window[key] = !window[key]; - } - renderCurrent(); -} - function toggleGatherConditionals() { var skillEl = document.getElementById('f_gather_skill'); var skill = skillEl ? skillEl.value : ''; @@ -1072,6 +1005,8 @@ function collectFieldValue(el, type) { return ced_collectFieldValue(el, type); } function saveObject() { if (!validateFields()) return; + ie_syncAllInteractions(objectData, SECTIONS); + var out = {}; SECTIONS.forEach(function(sec) { @@ -1083,10 +1018,22 @@ function saveObject() { rows.forEach(function(row, idx) { var item = {}; sec.fields.forEach(function(f) { + if (f[2] === 'inter_cond' || f[2] === 'inter_act') return; var fid = fieldID(sec, f[0], idx); var el = document.getElementById(fid); if (el) item[f[0]] = collectFieldValue(el, f[2]); }); + if (sec.interactionStyle) { + var dataArr = objectData[sec.path] || []; + var dataItem = dataArr[idx] || {}; + if (dataItem.condition && typeof dataItem.condition === 'object') { + var cleanedCond = ie_cleanCondition(dataItem.condition); + if (cleanedCond) item.condition = cleanedCond; + } + if (dataItem.action && typeof dataItem.action === 'object') { + item.action = dataItem.action; + } + } if (Object.keys(item).length > 0) arr.push(item); }); if (arr.length > 0) out[sec.path] = arr; @@ -1182,6 +1129,7 @@ function saveObject() { } }); + ced_cleanOutput(out); out.id = objectID; API.put('/api/objects/' + encodeURIComponent(objectID), out).then(function(resp) { -- cgit v1.2.3