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/mobeditor.js | 150 +++++++++++++++---------------------- 1 file changed, 59 insertions(+), 91 deletions(-) (limited to 'internal/admin/static/mobeditor.js') diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js index 7f8c0e3..c2364c8 100644 --- a/internal/admin/static/mobeditor.js +++ b/internal/admin/static/mobeditor.js @@ -121,13 +121,12 @@ var MOB_SECTIONS = [ ] }, { - id: 'on_kill', label: 'On Kill', labelHint: '(fired when this mob is defeated or its task completes — additive over drops)', path: 'on_kill', isArray: true, fullWidth: true, interactionStyle: true, + id: 'on_kill', label: 'On Kill', labelHint: '(fired when this mob is defeated or its task completes — additive over drops)', path: 'on_kill', isArray: true, fullWidth: true, interactionStyle: true, interScope: 'inline', detect: function(d) { return d.on_kill && Array.isArray(d.on_kill); }, fields: [ ['item_id', 'Item ID', 'search', '(optional — only fires if you wield this weapon when the mob is defeated)', '', null, 'items'], - ['condition', 'Condition', 'json', 'e.g. {"global_flag":"quest_active"}'], - ['action', 'Action', 'json', 'e.g. {"set_global_flags":{"boss_killed":true},"broadcast":"%p has slain the boss!","spawn_mob":"mini_boss"}'], - ['message', 'Message', 'text', 'Shown to the killer when this fires...'], + ['condition', 'Condition', 'inter_cond'], + ['action', 'Action', 'inter_act'], ] }, ]; @@ -138,6 +137,9 @@ function mobFieldID(sec, key, idx) { return ced_fieldID(sec, key, idx); } function mobFieldIDSub(sec, subKey, idx, fieldKey) { return ced_fieldIDSub(sec, subKey, idx, fieldKey); } function initMobEditor() { + window._ieRenderCurrent = renderCurrent; + window._ieRenderOnly = function() { if (mobID && mobData) renderMobEditor(mobID, mobData); }; + window._ieSyncAll = function(ed) { if (ed) ie_syncAllInteractions(ed, MOB_SECTIONS); }; editorType = 'mobs'; editorFields = []; loadList(); @@ -156,6 +158,7 @@ function loadMob(id) { API.get('/api/mobs/' + encodeURIComponent(id)).then(function(data) { mobData = data; window._editorData = mobData; + window._mobInterVis = {}; if (mobData.shop && mobData.shop.buys_anything === undefined) { mobData.shop.buys_anything = true; } @@ -220,6 +223,7 @@ function renderMobEditor(id, data) { function renderCurrent() { ced_syncDOMToData(mobData); + ie_syncAllInteractions(mobData, MOB_SECTIONS); if (!mobData || !mobID) return; renderMobEditor(mobID, mobData); } @@ -438,78 +442,23 @@ function renderMobField(sec, f, data, idx, subPath, optStyle) { // ---- Interaction-style array rendering (On Kill) ---- function renderMobArraySection(sec, data) { - var arr = data[mobCardPath(sec)] || []; - var h = ''; - var isInter = !!sec.interactionStyle; - if (isInter) { + if (sec.interactionStyle) { window._mobInterVis = window._mobInterVis || {}; if (!window._mobInterVis[sec.id]) window._mobInterVis[sec.id] = {}; + return ie_renderArraySection({ + sec: sec, + data: data, + visMap: window._mobInterVis[sec.id], + showRemove: function(idx) { return 'mobRemoveArrayItem(\'' + sec.id + '\',' + idx + ')'; }, + showField: function(secId, idx, key) { return 'mobShowInterField(\'' + secId + '\',' + idx + ',\'' + key + '\')'; }, + hideField: function(secId, idx, key) { return 'mobHideInterField(\'' + secId + '\',' + idx + ',\'' + key + '\')'; }, + addCondition: function(secId, idx) { return 'mobAddInterRootCondition(\'' + secId + '\',' + idx + ')'; }, + addEffect: function(secId, idx) { return 'mobAddInterAction(\'' + secId + '\',' + idx + ')'; }, + renderField: function(sec, f, data, idx, optStyle) { return renderMobField(sec, f, data, idx, null, optStyle); }, + onAdd: function() { return 'mobAddArrayItem(\'' + sec.id + '\')'; }, + }); } - arr.forEach(function(item, idx) { - var visMap = window._mobInterVis[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 += renderMobField(sec, sec.fields[0], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - h += '
'; - h += renderMobField(sec, sec.fields[1], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - h += '
'; - h += renderMobField(sec, sec.fields[2], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - h += '
'; - h += renderMobField(sec, sec.fields[3], data, idx, null, 'flex:1;min-width:0'); - h += ''; - h += '
'; - h += '
'; - }); - h += ''; - if (isInter) { - var helpKey = 'mob_' + sec.id + '_helpCollapsed'; - var helpCollapsed = window[helpKey] === undefined ? true : window[helpKey]; - h += '
'; - h += '
'; - h += '' + (helpCollapsed ? '▶' : '▼') + ''; - h += 'How Conditions & Actions Work'; - h += '
'; - if (!helpCollapsed) { - h += '
'; - h += 'Entries checked top-to-bottom, first match wins (one fires per kill; additive to standard loot).

'; - h += 'Item ID: only fires if you wield this weapon (in main_hand or off_hand) when the mob is defeated. Leave empty to fire unconditionally.
'; - h += 'Condition: JSON gate. {"global_flag":"boss_quest"}, {"player_flag":"step","value":2}, {"has_item":"trophy"}, {"all_of":[...]}, {"any_of":[...]}.
'; - h += 'Action: JSON effects. {"set_global_flags":{...}}, {"set_player_flags":{...}}, {"give_item":"gem"}, {"take_item":"key"}, {"teleport":5}, {"heal":10}, {"credits":100}, {"aps_node":true}, {"spawn_mob":"mini_boss"}, {"despawn_mob":"boss"}, {"broadcast":"%p vanquished the boss!"}, {"broadcast_global":"..."}.
'; - h += 'Message: shown to the killer when this interaction fires.'; - h += '
'; - } - h += '
'; - } - return h; + return ''; } function mobAddArrayItem(cardID) { @@ -536,34 +485,39 @@ function mobRemoveArrayItem(cardID, idx) { function mobShowInterField(secId, idx, key) { window._mobInterVis = window._mobInterVis || {}; - if (!window._mobInterVis[secId]) window._mobInterVis[secId] = {}; - if (!window._mobInterVis[secId][idx]) window._mobInterVis[secId][idx] = {}; - window._mobInterVis[secId][idx][key] = true; + ie_showField(window._mobInterVis, secId, idx, key); renderCurrent(); } +function mobAddInterRootCondition(secId, idx) { + var sec = MOB_SECTIONS.find(function(s) { return s.id === secId; }); + if (!sec) return; + ie_addRootConditionGroup(sec.path, idx); +} + +function mobAddInterAction(secId, idx) { + var sec = MOB_SECTIONS.find(function(s) { return s.id === secId; }); + if (!sec) return; + ie_addEffectToInteraction(sec.path, idx); +} + function mobHideInterField(secId, idx, key) { window._mobInterVis = window._mobInterVis || {}; - if (!window._mobInterVis[secId]) window._mobInterVis[secId] = {}; - if (!window._mobInterVis[secId][idx]) window._mobInterVis[secId][idx] = {}; - window._mobInterVis[secId][idx][key] = false; + ie_hideField(window._mobInterVis, secId, idx, key); + var sec = MOB_SECTIONS.find(function(s) { return s.id === secId; }); + var secPath = sec ? sec.path : secId; ced_syncDOMToData(mobData); - if (mobData[secId] && mobData[secId][idx]) { - mobData[secId][idx][key] = ''; + ie_syncAllInteractions(mobData, MOB_SECTIONS); + if (mobData[secPath] && mobData[secPath][idx]) { + if (key === 'condition' || key === 'action') { + delete mobData[secPath][idx][key]; + } else { + mobData[secPath][idx][key] = ''; + } } renderMobEditor(mobID, mobData); } -function mobToggleInterHelp(secId) { - var key = 'mob_' + secId + '_helpCollapsed'; - if (window[key] === undefined) { - window[key] = false; - } else { - window[key] = !window[key]; - } - renderCurrent(); -} - // ---- Subtable rendering ---- function renderMobSubField(sec, stKey, idx, f, val) { @@ -829,6 +783,8 @@ function unflattenObj(obj) { function saveMob() { if (!ced_validateFields(MOB_SECTIONS, mobFieldID, mobFieldIDSub)) return; + ie_syncAllInteractions(mobData, MOB_SECTIONS); + var out = {}; MOB_SECTIONS.forEach(function(sec) { @@ -845,10 +801,22 @@ function saveMob() { rows.forEach(function(row, idx) { var item = {}; sec.fields.forEach(function(f) { + if (f[2] === 'inter_cond' || f[2] === 'inter_act') return; var fid = mobFieldID(sec, f[0], idx); var el = document.getElementById(fid); if (el) item[f[0]] = ced_collectFieldValue(el, f[2]); }); + if (sec.interactionStyle) { + var dataArr = mobData[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; -- cgit v1.2.3