aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/mobeditor.js
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-09 04:03:00 -0400
committerhistoria <[not public]>2026-07-09 04:03:00 -0400
commit74153c7814fc4cef988066ef04e38731a943bc12 (patch)
treedebd8229a1fd02daeb3059e9396008bb525ba4b9 /internal/admin/static/mobeditor.js
parent09d325dcf5e779eab5550d3fd3377bde50101428 (diff)
downloadthehouseoficarus-74153c7814fc4cef988066ef04e38731a943bc12.tar.gz
feat(admin): nested condition/action buttons to manage complex YAML
Diffstat (limited to 'internal/admin/static/mobeditor.js')
-rw-r--r--internal/admin/static/mobeditor.js150
1 files changed, 59 insertions, 91 deletions
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 += '<div class="obj-sub-row" data-idx="' + idx + '" style="flex-direction:column;align-items:stretch;position:relative;gap:6px">';
- h += '<button class="btn btn-sm btn-danger" style="position:absolute;top:0;right:0;white-space:nowrap;z-index:1" onclick="mobRemoveArrayItem(\'' + sec.id + '\',' + idx + ')">Remove Interaction</button>';
- h += '<div style="display:flex;gap:4px;align-items:flex-end;flex-wrap:wrap;padding-right:135px">';
- if (!showItem) {
- h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="mobShowInterField(\'' + sec.id + '\',' + idx + ',\'item_id\')">+ Add Required Item</button>';
- h += '<div style="width:1px;background:var(--border);align-self:stretch;margin:2px 0;flex-shrink:0"></div>';
- }
- if (!showCond) {
- h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="mobShowInterField(\'' + sec.id + '\',' + idx + ',\'condition\')">+ Add Condition</button>';
- }
- if (!showAct) {
- h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="mobShowInterField(\'' + sec.id + '\',' + idx + ',\'action\')">+ Add Action</button>';
- }
- if (!showMsg) {
- h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="mobShowInterField(\'' + sec.id + '\',' + idx + ',\'message\')">+ Add Message</button>';
- }
- h += '</div>';
- h += '<div style="display:' + (showItem ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">';
- h += renderMobField(sec, sec.fields[0], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="mobHideInterField(\'' + sec.id + '\',' + idx + ',\'item_id\')" style="margin-top:14px">X</button>';
- h += '</div>';
- h += '<div style="display:' + (showCond ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">';
- h += renderMobField(sec, sec.fields[1], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="mobHideInterField(\'' + sec.id + '\',' + idx + ',\'condition\')" style="margin-top:14px">X</button>';
- h += '</div>';
- h += '<div style="display:' + (showAct ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">';
- h += renderMobField(sec, sec.fields[2], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="mobHideInterField(\'' + sec.id + '\',' + idx + ',\'action\')" style="margin-top:14px">X</button>';
- h += '</div>';
- h += '<div style="display:' + (showMsg ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">';
- h += renderMobField(sec, sec.fields[3], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="mobHideInterField(\'' + sec.id + '\',' + idx + ',\'message\')" style="margin-top:14px">X</button>';
- h += '</div>';
- h += '</div>';
- });
- h += '<button class="btn btn-sm obj-sub-add" onclick="mobAddArrayItem(\'' + sec.id + '\')">+ Add ' + esc(sec.label) + '</button>';
- if (isInter) {
- var helpKey = 'mob_' + sec.id + '_helpCollapsed';
- var helpCollapsed = window[helpKey] === undefined ? true : window[helpKey];
- h += '<div style="margin-top:8px;border:1px solid rgba(100,160,255,.15);border-radius:4px;overflow:hidden">';
- h += '<div style="padding:6px 10px;background:rgba(100,160,255,.08);cursor:pointer;font-size:10px;color:#6af;font-weight:bold;text-transform:uppercase;letter-spacing:.5px" onclick="mobToggleInterHelp(\'' + sec.id + '\')">';
- h += '<span style="font-size:8px;margin-right:4px">' + (helpCollapsed ? '&#9654;' : '&#9660;') + '</span>';
- h += 'How Conditions &amp; Actions Work';
- h += '</div>';
- if (!helpCollapsed) {
- h += '<div style="padding:8px;background:rgba(100,160,255,.05);font-size:10px;color:#aaa;line-height:1.6">';
- h += 'Entries checked top-to-bottom, first match wins (one fires per kill; additive to standard loot).<br><br>';
- h += '<b>Item ID</b>: only fires if you wield this weapon (in main_hand or off_hand) when the mob is defeated. Leave empty to fire unconditionally.<br>';
- h += '<b>Condition</b>: JSON gate. <code>{"global_flag":"boss_quest"}</code>, <code>{"player_flag":"step","value":2}</code>, <code>{"has_item":"trophy"}</code>, <code>{"all_of":[...]}</code>, <code>{"any_of":[...]}</code>.<br>';
- h += '<b>Action</b>: JSON effects. <code>{"set_global_flags":{...}}</code>, <code>{"set_player_flags":{...}}</code>, <code>{"give_item":"gem"}</code>, <code>{"take_item":"key"}</code>, <code>{"teleport":5}</code>, <code>{"heal":10}</code>, <code>{"credits":100}</code>, <code>{"aps_node":true}</code>, <code>{"spawn_mob":"mini_boss"}</code>, <code>{"despawn_mob":"boss"}</code>, <code>{"broadcast":"%p vanquished the boss!"}</code>, <code>{"broadcast_global":"..."}</code>.<br>';
- h += '<b>Message</b>: shown to the killer when this interaction fires.';
- h += '</div>';
- }
- h += '</div>';
- }
- 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;