aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/objecteditor.js
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-08 19:59:14 -0400
committerhistoria <[not public]>2026-07-08 19:59:14 -0400
commit09d325dcf5e779eab5550d3fd3377bde50101428 (patch)
treea433be903aabbf1d2eadce2aacdac12a9eb8dde0 /internal/admin/static/objecteditor.js
parent9184377301c2604e003f36426788c032fb0ca524 (diff)
downloadthehouseoficarus-09d325dcf5e779eab5550d3fd3377bde50101428.tar.gz
feat: unify on use, on look, and on kill. all support same conditions/actions now.
Diffstat (limited to 'internal/admin/static/objecteditor.js')
-rw-r--r--internal/admin/static/objecteditor.js183
1 files changed, 73 insertions, 110 deletions
diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js
index 6abec3e..ecb3ced 100644
--- a/internal/admin/static/objecteditor.js
+++ b/internal/admin/static/objecteditor.js
@@ -86,26 +86,23 @@ var SECTIONS = [
]
},
{
- id: 'use_interactions', label: 'Use Interactions', labelHint: '(what happens when you use items on this)', path: 'use_interactions', isArray: true, fullWidth: true,
- detect: function(d) { return d.use_interactions && Array.isArray(d.use_interactions); },
+ id: 'on_use', label: 'On Use', labelHint: '(what happens when you use items on this)', path: 'on_use', isArray: true, fullWidth: true, interactionStyle: true,
+ detect: function(d) { return d.on_use && Array.isArray(d.on_use); },
fields: [
- ['item_id', 'Item ID', 'search', 'e.g. keycard', '', null, 'items'],
+ ['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_flags":{"my_flag":true},"message":"You activate it.","give_item":"gem"}'],
+ ['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...'],
]
},
{
- id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look <this>")', path: 'on_look',
- detect: function(d) { return d.on_look; },
+ id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look <this>")', path: 'on_look', isArray: true, fullWidth: true, interactionStyle: true,
+ detect: function(d) { return d.on_look && Array.isArray(d.on_look); },
fields: [
- ['set_flags', 'Set Global Flags', 'json', '{"flag":"value"}'],
- ['set_player_flags', 'Set Player Flags', 'json', '{"pflag":"value"}'],
- ['give_item', 'Give Item', 'search', 'e.g. keycard', '', null, 'items'],
- ['take_item', 'Take Item', 'search', 'e.g. bomb', '', null, 'items'],
- ['teleport', 'Teleport', 'number', '5', 'narrow'],
- ['heal', 'Heal', 'number', '10', 'narrow'],
- ['credits', 'Credits', 'number', '100', 'narrow'],
+ ['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...'],
]
},
];
@@ -191,7 +188,7 @@ function cardPath(sec) {
function getVal(data, sec, key) {
var p = cardPath(sec);
- if (p === 'on_look' || sec.isArray) {
+ if (sec.isArray) {
return data[p] && data[p][key] !== undefined ? data[p][key] : '';
}
var fp = p ? p + '.' + key : key;
@@ -234,8 +231,6 @@ function renderCard(sec, data) {
h += renderCoreCard(data);
} else if (sec.id === 'steal') {
h += renderStealCard(data);
- } else if (sec.id === 'on_look') {
- h += renderOnLookCard(data);
} else {
h += '<div class="obj-card-grid">';
sec.fields.forEach(function(f) {
@@ -507,56 +502,20 @@ function renderStealCard(data) {
return h;
}
-function renderOnLookCard(data) {
- var sec = SECTIONS.find(function(s) { return s.id === 'on_look'; });
- var h = '<div style="display:flex;gap:10px">';
- h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1');
- h += renderField(sec, sec.fields[1], data, -1, null, 'flex:1');
- h += '</div>';
- h += '<div style="display:flex;gap:10px;margin-top:8px">';
- h += renderField(sec, sec.fields[2], data, -1, null, 'flex:1');
- h += renderField(sec, sec.fields[3], data, -1, null, 'flex:1');
- h += '</div>';
- h += '<div style="display:flex;gap:10px;margin-top:8px">';
- h += renderField(sec, sec.fields[4], data, -1);
- h += renderField(sec, sec.fields[5], data, -1);
- h += renderField(sec, sec.fields[6], data, -1);
- h += '</div>';
- var helpCollapsed = window._onLookHelpCollapsed === undefined ? true : window._onLookHelpCollapsed;
- 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="toggleOnLookHelp()">';
- h += '<span style="font-size:8px;margin-right:4px">' + (helpCollapsed ? '&#9654;' : '&#9660;') + '</span>';
- h += 'How Set Global Flags &amp; Set Player Flags 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 += 'When a player looks at this object, these fields apply effects.<br><br>';
- h += '<b>Set Global Flags</b> &mdash; Sets world flags shared by all players. JSON object mapping flag names to values:<br>';
- h += '&nbsp;&nbsp;<code>{"gate_open":true,"boss_summoned":false}</code><br>';
- h += '&nbsp;&nbsp;Flags persist until changed by another on_look, room script, or interact action.<br><br>';
- h += '<b>Set Player Flags</b> &mdash; Sets per-character flags saved to the player\'s YAML. JSON object mapping flag names to values:<br>';
- h += '&nbsp;&nbsp;<code>{"has_seen_cave":true,"dialog_spoke":1}</code><br>';
- h += '&nbsp;&nbsp;These flags can be used in conditions (<code>player_flag</code>) to track player progress.<br><br>';
- h += '<b>Other Fields</b> &mdash; <code>Give Item</code> grants an item, <code>Take Item</code> removes one, ';
- h += '<code>Teleport</code> sends player to a room, <code>Heal</code> restores HP, <code>Credits</code> adds/removes credits (use negative for cost).<br><br>';
- h += 'All effects fire simultaneously when the player looks at this object.';
- h += '</div>';
- }
- h += '</div>';
- return h;
-}
-
function renderArraySection(sec, data) {
var arr = data[cardPath(sec)] || [];
var h = '';
- var isUseInter = sec.id === 'use_interactions';
- if (isUseInter) {
- window._useInterVis = window._useInterVis || {};
+ var isInter = !!sec.interactionStyle;
+ if (isInter) {
+ window._interVis = window._interVis || {};
+ if (!window._interVis[sec.id]) window._interVis[sec.id] = {};
}
arr.forEach(function(item, idx) {
- if (isUseInter) {
- var vis = window._useInterVis[idx] || {};
- window._useInterVis[idx] = vis;
+ 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 !== '');
@@ -566,32 +525,39 @@ function renderArraySection(sec, data) {
h += '<button class="btn btn-sm btn-danger" style="position:absolute;top:0;right:0;white-space:nowrap;z-index:1" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">Remove Interaction</button>';
h += '<div style="display:flex;gap:4px;align-items:flex-end;flex-wrap:wrap;padding-right:125px">';
- h += renderField(sec, sec.fields[0], data, idx, null, 'max-width:260px');
- h += '<div style="width:1px;background:var(--border);align-self:stretch;margin:2px 0;flex-shrink:0"></div>';
+ 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="showInterField(\'' + 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="showUseInterField(' + idx + ',\'condition\')">+ Add Condition</button>';
+ 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="showInterField(\'' + 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="showUseInterField(' + idx + ',\'action\')">+ Add Action</button>';
+ 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="showInterField(\'' + 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="showUseInterField(' + idx + ',\'message\')">+ Add Message</button>';
+ 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="showInterField(\'' + sec.id + '\',' + idx + ',\'message\')">+ Add Message</button>';
}
h += '</div>';
+ h += '<div style="display:' + (showItem ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">';
+ h += renderField(sec, sec.fields[0], data, idx, null, 'flex:1;min-width:0');
+ h += '<button class="btn btn-sm btn-danger" onclick="hideInterField(\'' + 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 += renderField(sec, sec.fields[1], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="hideUseInterField(' + idx + ',\'condition\')" style="margin-top:14px">X</button>';
+ h += '<button class="btn btn-sm btn-danger" onclick="hideInterField(\'' + 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 += renderField(sec, sec.fields[2], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="hideUseInterField(' + idx + ',\'action\')" style="margin-top:14px">X</button>';
+ h += '<button class="btn btn-sm btn-danger" onclick="hideInterField(\'' + 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 += renderField(sec, sec.fields[3], data, idx, null, 'flex:1;min-width:0');
- h += '<button class="btn btn-sm btn-danger" onclick="hideUseInterField(' + idx + ',\'message\')" style="margin-top:14px">X</button>';
+ h += '<button class="btn btn-sm btn-danger" onclick="hideInterField(\'' + sec.id + '\',' + idx + ',\'message\')" style="margin-top:14px">X</button>';
h += '</div>';
h += '</div>';
@@ -606,32 +572,37 @@ function renderArraySection(sec, data) {
h += '</div>';
}
});
- h += '<button class="btn btn-sm obj-sub-add" onclick="addArrayItem(\'' + sec.id + '\')">+ Add ' + esc(sec.label).slice(0,-1) + '</button>';
- if (isUseInter) {
- var helpCollapsed = window._useInterHelpCollapsed === undefined ? true : window._useInterHelpCollapsed;
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addArrayItem(\'' + sec.id + '\')">+ Add ' + esc(sec.label) + '</button>';
+ if (isInter) {
+ var helpKey = 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="toggleUseInterHelp()">';
+ 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="toggleInterHelp(\'' + 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.<br><br><b>Item ID</b>: required for <code>use &lt;item&gt; on &lt;object&gt;</code>; omit for plain <code>use &lt;object&gt;</code>.<br>';
- h += '<b>Condition</b>: JSON. When this interaction is available:<br>';
- h += '&nbsp;&nbsp;<code>{"flag":"gate_open","not":true}</code> — world flag is NOT set<br>';
+ h += 'Entries checked top-to-bottom, first match wins (one fires per use/look/kill).<br><br>';
+ h += '<b>Item ID</b>: <b>on_use</b> requires <code>use &lt;item&gt; on &lt;object&gt;</code>; empty = bare <code>use &lt;object&gt;</code>. <b>on_look</b> = only fires if you carry this item. <b>on_kill</b> = only fires if you wield this weapon when the mob is defeated.<br>';
+ h += '<b>Condition</b>: JSON. Available gates:<br>';
+ h += '&nbsp;&nbsp;<code>{"global_flag":"gate_open","not":true}</code> — world flag is NOT set<br>';
h += '&nbsp;&nbsp;<code>{"player_flag":"has_key","value":1}</code> — player flag matches value<br>';
h += '&nbsp;&nbsp;<code>{"has_item":"silver_bar"}</code> — player carries item<br>';
- h += '&nbsp;&nbsp;<code>{"all_of":[{"flag":"a"},{"has_item":"b"}]}</code> — ALL sub-conditions<br>';
- h += '&nbsp;&nbsp;<code>{"any_of":[...]}</code> — ANY sub-condition<br>';
+ h += '&nbsp;&nbsp;<code>{"min_credits":50}</code> — minimum credits<br>';
+ h += '&nbsp;&nbsp;<code>{"all_of":[{"global_flag":"a"},{"has_item":"b"}]}</code> — ALL<br>';
+ h += '&nbsp;&nbsp;<code>{"any_of":[...]}</code> — ANY<br>';
h += '&nbsp;&nbsp;Omit for an unconditional interaction.<br>';
- h += '<b>Action</b>: JSON. What happens when the interaction fires:<br>';
- h += '&nbsp;&nbsp;<code>{"set_flags":{"gate_open":true}}</code> — set world flags<br>';
+ h += '<b>Action</b>: JSON. Effects that fire when the interaction runs:<br>';
+ h += '&nbsp;&nbsp;<code>{"set_global_flags":{"gate_open":true}}</code> — set world flags<br>';
h += '&nbsp;&nbsp;<code>{"set_player_flags":{"spoke":true}}</code> — set player flags<br>';
- h += '&nbsp;&nbsp;<code>{"give_item":"keycard","teleport":5}</code> — give item + teleport<br>';
- h += '&nbsp;&nbsp;<code>{"take_item":"bomb","heal":10}</code> — take item + heal<br>';
- h += '&nbsp;&nbsp;<code>{"credits":-50}</code> — deduct credits<br>';
- h += '&nbsp;&nbsp;<code>{"reputation_cost":1,"aps_node":true}</code> — rep cost + mark APS<br>';
- h += '<b>Message</b>: Sent to the player when the interaction fires (1-tick action).';
+ h += '&nbsp;&nbsp;<code>{"give_item":"keycard","take_item":"bomb"}</code> — give/take item<br>';
+ h += '&nbsp;&nbsp;<code>{"teleport":5,"heal":10,"credits":100}</code> — move, heal, give credits<br>';
+ h += '&nbsp;&nbsp;<code>{"aps_node":true}</code> — mark current room as APS node<br>';
+ h += '&nbsp;&nbsp;<code>{"spawn_mob":"rat","despawn_mob":"rat"}</code> — spawn/despawn transient mob<br>';
+ h += '&nbsp;&nbsp;<code>{"broadcast":"%p activates it.","broadcast_global":"%p did it!"}</code> — announce (%p = player, %v = flag value)<br>';
+ h += '&nbsp;&nbsp;<code>{"message":"You feel energized.","delay":3}</code> — only meaningful inside on_enter steps / triggers (delay in ticks)<br>';
+ h += '<b>Message</b>: Sent to the player when the interaction fires (one-tick action).';
h += '</div>';
}
h += '</div>';
@@ -906,8 +877,6 @@ function addSection() {
if (sec.path) {
if (sec.isArray) {
objectData[sec.path] = [];
- } else if (sec.id === 'on_look') {
- objectData[sec.path] = {};
} else if (sec.id === 'gather') {
objectData[sec.path] = {skill:'', tools:[], success:{base:0, per_level:0, cap:1}, drops:[], gather_message:'', fail_message:'', respawn_timer:0, respawn_broadcast:'', deplete_timer:0, nest_chance:0};
} else if (sec.id === 'safespot') {
@@ -1009,20 +978,22 @@ function renderCurrent() {
renderObjectEditor(objectID, objectData);
}
-function showUseInterField(idx, key) {
- window._useInterVis = window._useInterVis || {};
- if (!window._useInterVis[idx]) window._useInterVis[idx] = {};
- window._useInterVis[idx][key] = true;
+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;
renderCurrent();
}
-function hideUseInterField(idx, key) {
- window._useInterVis = window._useInterVis || {};
- if (!window._useInterVis[idx]) window._useInterVis[idx] = {};
- window._useInterVis[idx][key] = false;
+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;
ced_syncDOMToData(objectData);
- if (objectData.use_interactions && objectData.use_interactions[idx]) {
- objectData.use_interactions[idx][key] = '';
+ if (objectData[secId] && objectData[secId][idx]) {
+ objectData[secId][idx][key] = '';
}
renderObjectEditor(objectID, objectData);
}
@@ -1061,20 +1032,12 @@ function hideGuardMob() {
renderObjectEditor(objectID, objectData);
}
-function toggleUseInterHelp() {
- if (window._useInterHelpCollapsed === undefined) {
- window._useInterHelpCollapsed = false;
- } else {
- window._useInterHelpCollapsed = !window._useInterHelpCollapsed;
- }
- renderCurrent();
-}
-
-function toggleOnLookHelp() {
- if (window._onLookHelpCollapsed === undefined) {
- window._onLookHelpCollapsed = false;
+function toggleInterHelp(secId) {
+ var key = secId + '_helpCollapsed';
+ if (window[key] === undefined) {
+ window[key] = false;
} else {
- window._onLookHelpCollapsed = !window._onLookHelpCollapsed;
+ window[key] = !window[key];
}
renderCurrent();
}