aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/objecteditor.js
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin/static/objecteditor.js')
-rw-r--r--internal/admin/static/objecteditor.js134
1 files changed, 121 insertions, 13 deletions
diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js
index fdaefd2..29ad66b 100644
--- a/internal/admin/static/objecteditor.js
+++ b/internal/admin/static/objecteditor.js
@@ -45,9 +45,10 @@ var SECTIONS = [
['base', 'Base', 'number', '0.5', 'narrow'], ['per_level', 'Per Lvl', 'number', '0.01', 'narrow'], ['cap', 'Cap', 'number', '0.95', 'narrow']
]},
{label:'Other', row:0, fields:[
- ['respawn_timer', 'Respawn', 'number', '50', 'narrow'],
+ ['respawn_timer', 'Respawn', 'number', '50', 'narrow', function(d) { return d.skill !== 'fishing'; }],
['deplete_timer', 'Deplete', 'number', '45', 'narrow', function(d) { return d.skill === 'woodcutting'; }],
['nest_chance', 'Nest 1/n', 'number', '256', 'narrow', function(d) { return d.skill === 'woodcutting'; }],
+ ['no_tool_speed', 'Bare Speed', 'number', '3', 'narrow', function(d) { return !d.tools || d.tools.length === 0; }],
]},
{label:'Tools', key:'tools', type:'tools_checkbox', hint:'any one is sufficient (OR)'},
],
@@ -55,7 +56,7 @@ var SECTIONS = [
{label:'Drops', key:'drops', fields:[
['item_id', 'Item ID', 'search', 'copper_ore', '', 'items'], ['table', 'Table', 'search', 'gem_table', '', 'drops'],
['weight', 'Weight', 'number', '90', 'narrow'], ['level', 'Level', 'number', '1', 'narrow'], ['xp', 'XP', 'number', '17', 'narrow'],
- ['quantity', 'Quantity', 'number', '1', 'narrow'], ['depletes', 'Depletes node', 'checkbox'],
+ ['quantity', 'Quantity', 'number', '1', 'narrow'], ['tool', 'Tool', 'select'], ['depletes', 'Depletes node', 'checkbox'],
['message', 'Message', 'text', 'You manage to mine some copper ore.'],
]}
]
@@ -74,7 +75,7 @@ var SECTIONS = [
['respawn_on_hide', 'Respawn on hide', 'checkbox'],
],
subtables: [
- {label:'Levels of Decay', key:'levels', fields:[
+ {label:'Levels of Decay (lower is more decayed)', key:'levels', vertical: true, fields:[
['message', 'Look Message', 'text'], ['degrade_message', 'Degrade Message', 'text']
]}
]
@@ -93,7 +94,7 @@ var SECTIONS = [
id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look <this>")', path: 'on_look',
detect: function(d) { return d.on_look; },
fields: [
- ['set_flags', 'Set Flags', 'json', '{"flag":"value"}'],
+ ['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'],
@@ -233,6 +234,28 @@ function renderCard(sec, data) {
} else {
h += '<div class="obj-card-grid">';
sec.fields.forEach(function(f) {
+ if (sec.id === 'gather' && f[0] === 'bait') {
+ window._gatherVis = window._gatherVis || {};
+ var gd = objectData.gather || {};
+ var isFishing = gd.skill === 'fishing';
+ var showBait = isFishing && (window._gatherVis.bait || (gd.bait && gd.bait !== ''));
+ if (isFishing && showBait) {
+ var bv = gd.bait || '';
+ h += '<label class="obj-field obj-search">';
+ h += '<span>Bait Item</span>';
+ h += '<div style="display:flex;gap:4px;align-items:flex-start">';
+ h += '<div style="flex:1;position:relative">';
+ h += '<input id="f_gather_bait" value="' + escAttr(bv) + '" class="search-input" data-search-type="items" data-field-path="gather.bait" placeholder="fishing_bait">';
+ h += '<div class="search-results" style="display:none"></div>';
+ h += '</div>';
+ h += '<button class="btn btn-sm btn-danger" onclick="hideBaitField()" style="flex-shrink:0;padding:3px 7px;font-size:10px;margin-top:0">X</button>';
+ h += '</div>';
+ h += '</label>';
+ } else if (isFishing) {
+ h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap;width:100%;padding:4px 6px;font-size:11px;font-family:monospace" onclick="showBaitField()">+ Add Required Bait</button>';
+ }
+ return;
+ }
h += renderField(sec, f, data, -1);
});
h += '</div>';
@@ -497,6 +520,27 @@ function renderOnLookCard(data) {
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;
}
@@ -602,10 +646,15 @@ function renderSubtable(sec, st, data) {
arr = sectionRoot[st.key] || [];
}
var isDrops = sec.id === 'gather' && st.key === 'drops';
+ var isVertical = st.vertical;
+ var gatherTools = [];
+ if (isDrops) {
+ gatherTools = (sectionRoot.tools || []).filter(function(t) { return t && t !== ''; });
+ }
var h = '<div class="obj-subtable">';
h += '<div class="obj-subtable-header">' + esc(st.label) + '</div>';
arr.forEach(function(item, idx) {
- h += '<div class="obj-sub-row"' + (isDrops ? ' style="flex-direction:column;align-items:stretch;position:relative"' : '') + '>';
+ h += '<div class="obj-sub-row"' + (isDrops || isVertical ? ' style="flex-direction:column;align-items:stretch;position:relative"' : '') + '>';
if (isDrops) {
if (!st.single) {
h += '<button class="btn btn-sm btn-danger" style="position:absolute;top:4px;right:6px" onclick="removeSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ')">X</button>';
@@ -628,7 +677,21 @@ function renderSubtable(sec, st, data) {
var dp = cardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0];
h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>';
} else if (f[2] === 'checkbox') {
- h += '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>';
+ var hideStyle = '';
+ if (isDrops && f[0] === 'depletes' && sectionRoot.skill === 'fishing') {
+ hideStyle = ' style="display:none"';
+ }
+ h += '<label class="obj-field obj-check' + extraCls + '"' + hideStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>';
+ } else if (f[2] === 'select' && f[0] === 'tool') {
+ if (gatherTools.length >= 2) {
+ var dpTool = cardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0];
+ h += '<label class="obj-field obj-narrow"><span>' + esc(f[1]) + '</span>' +
+ '<select id="' + fid + '" data-field-path="' + escAttr(dpTool) + '">';
+ gatherTools.forEach(function(t) {
+ h += '<option value="' + escAttr(t) + '"' + (String(val) === t ? ' selected' : '') + '>' + esc(t) + '</option>';
+ });
+ h += '</select></label>';
+ }
} else {
h += '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '" placeholder="' + escAttr(f[3] || '') + '"></label>';
}
@@ -641,6 +704,9 @@ function renderSubtable(sec, st, data) {
h += '<label class="obj-field obj-grow"><span>' + esc(msgField ? msgField[1] : 'Message') + '</span><input id="' + fidMsg + '" value="' + escAttr(String(msgVal)) + '" placeholder="' + escAttr(msgField ? msgField[3] : '') + '"></label>';
h += '</div>';
} else {
+ if (isVertical && !st.single) {
+ h += '<button class="btn btn-sm btn-danger" style="position:absolute;top:4px;right:6px" onclick="removeSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ')">X</button>';
+ }
st.fields.forEach(function(f) {
var fid = fieldIDSub(sec, st.key, idx, f[0]);
var val = item[f[0]];
@@ -659,7 +725,7 @@ function renderSubtable(sec, st, data) {
h += '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"></label>';
}
});
- if (!st.single) {
+ if (!st.single && !isVertical) {
h += '<button class="btn btn-sm btn-danger obj-sub-remove" onclick="removeSubRow(\'' + sec.id + '\',\'' + st.key + '\',' + idx + ')">X</button>';
}
}
@@ -718,7 +784,7 @@ function renderToolsCheckbox(sec, il, data) {
h += '<div style="display:flex;flex-direction:column;gap:3px;margin-top:4px">';
arr.forEach(function(t, idx) {
h += '<div style="display:flex;gap:4px">';
- h += '<select class="obj-tool-select" style="flex:1;font-size:12px;padding:2px 4px;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:3px">';
+ h += '<select class="obj-tool-select" onchange="renderCurrent()" style="flex:1;font-size:12px;padding:2px 4px;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:3px">';
choices.forEach(function(c) {
var sel = c === t ? ' selected' : '';
h += '<option' + sel + '>' + esc(c) + '</option>';
@@ -738,14 +804,18 @@ function addToolRow(secID, key) {
if (!sec) return;
var p = cardPath(sec);
if (!objectData[p]) objectData[p] = {};
- objectData[p][key] = (objectData[p][key] || []).concat(['']);
- renderCurrent();
+ ced_syncDOMToData(objectData);
+ syncGatherToolsFromDOM();
+ var val = (toolTypes && toolTypes.length > 0) ? toolTypes[0] : '';
+ objectData[p][key] = (objectData[p][key] || []).concat([val]);
+ renderObjectEditor(objectID, objectData);
}
function removeToolRow(secID, key, idx) {
var sec = SECTIONS.find(function(s) { return s.id === secID; });
if (!sec) return;
ced_syncDOMToData(objectData);
+ syncGatherToolsFromDOM();
var p = cardPath(sec);
var arr = objectData[p] && objectData[p][key];
if (!arr) return;
@@ -764,6 +834,19 @@ function fetchToolTypes() {
});
}
+function syncGatherToolsFromDOM() {
+ if (!objectData || !objectData.gather) return;
+ var selects = document.querySelectorAll('.obj-tool-select');
+ if (selects.length === 0) return;
+ var tools = [];
+ selects.forEach(function(s) { if (s.value) tools.push(s.value); });
+ if (tools.length > 0) {
+ objectData.gather.tools = tools;
+ } else if (objectData.gather.tools && objectData.gather.tools.length > 0) {
+ delete objectData.gather.tools;
+ }
+}
+
function toggleCard(id) {
expandedCards[id] = expandedCards[id] === false ? true : false;
renderCurrent();
@@ -774,6 +857,20 @@ function toggleCardWidth(id) {
renderCurrent();
}
+function showBaitField() {
+ window._gatherVis = window._gatherVis || {};
+ window._gatherVis.bait = true;
+ renderCurrent();
+}
+
+function hideBaitField() {
+ window._gatherVis = window._gatherVis || {};
+ window._gatherVis.bait = false;
+ ced_syncDOMToData(objectData);
+ if (objectData.gather) objectData.gather.bait = '';
+ renderObjectEditor(objectID, objectData);
+}
+
function toggleSafespotHelp() {
if (window._safespotHelpCollapsed === undefined) {
window._safespotHelpCollapsed = false;
@@ -884,7 +981,7 @@ function addDropRow(cardID, subKey, dropType) {
if (!sec) return;
var sectionRoot = objectData[sec.path || ''] || objectData;
var arr = sectionRoot[subKey] || [];
- var empty = { item_id: '', table: '', weight: 0, level: 0, xp: 0, quantity: 0, depletes: false, message: '', _type: (dropType === 'table' ? 't' : 'i') };
+ var empty = { item_id: '', table: '', weight: 0, level: 0, xp: 0, quantity: 0, depletes: false, tool: '', message: '', _type: (dropType === 'table' ? 't' : 'i') };
arr.push(empty);
sectionRoot[subKey] = arr;
renderCurrent();
@@ -896,6 +993,7 @@ function validateDropRow(idx) {
function renderCurrent() {
ced_syncDOMToData(objectData);
+ syncGatherToolsFromDOM();
if (!objectData || !objectID) return;
renderObjectEditor(objectID, objectData);
}
@@ -961,13 +1059,22 @@ function toggleUseInterHelp() {
renderCurrent();
}
+function toggleOnLookHelp() {
+ if (window._onLookHelpCollapsed === undefined) {
+ window._onLookHelpCollapsed = false;
+ } else {
+ window._onLookHelpCollapsed = !window._onLookHelpCollapsed;
+ }
+ renderCurrent();
+}
+
function toggleGatherConditionals() {
var skillEl = document.getElementById('f_gather_skill');
var skill = skillEl ? skillEl.value : '';
var fishing = skill === 'fishing';
var wood = skill === 'woodcutting';
- var els = ['bait', 'exhausted_message', 'depleted_message', 'deplete_timer', 'nest_chance', 'respawn_broadcast'];
- var vis = {bait: fishing, exhausted_message: wood, depleted_message: !fishing, deplete_timer: wood, nest_chance: wood, respawn_broadcast: !fishing};
+ var els = ['exhausted_message', 'depleted_message', 'deplete_timer', 'nest_chance', 'respawn_broadcast', 'respawn_timer'];
+ var vis = {exhausted_message: wood, depleted_message: !fishing, deplete_timer: wood, nest_chance: wood, respawn_broadcast: !fishing, respawn_timer: !fishing};
els.forEach(function(key) {
var el = document.getElementById('f_gather_' + key);
if (el) el.closest('.obj-field').style.display = vis[key] ? '' : 'none';
@@ -975,6 +1082,7 @@ function toggleGatherConditionals() {
document.querySelectorAll('[id^="f_gather_drops_"][id$="_depletes"]').forEach(function(el) {
el.closest('.obj-field').style.display = fishing ? 'none' : '';
});
+ renderCurrent();
}
function setupSearchFields() { ced_setupSearchFields(); }