aboutsummaryrefslogtreecommitdiff
path: root/internal/admin
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-13 16:10:40 -0400
committerhistoria <[not public]>2026-07-13 16:10:40 -0400
commit3274b700a6f655b8ecc3505e814ac5d782ae6ffe (patch)
treee669b47726f8cb93b75c7df85997cc8556009f20 /internal/admin
parent93a412aed2b11406399504c81d6d19ed1341908a (diff)
downloadthehouseoficarus-3274b700a6f655b8ecc3505e814ac5d782ae6ffe.tar.gz
feat(admin): reminder text for color tags and item variables, improve placeholder text
Diffstat (limited to 'internal/admin')
-rw-r--r--internal/admin/static/cardeditor.js37
-rw-r--r--internal/admin/static/dropeditor.js4
-rw-r--r--internal/admin/static/hazardeditor.js5
-rw-r--r--internal/admin/static/itemeditor.js23
-rw-r--r--internal/admin/static/map.js6
-rw-r--r--internal/admin/static/mobeditor.js11
-rw-r--r--internal/admin/static/moduleeditor.js2
-rw-r--r--internal/admin/static/objecteditor.js33
-rw-r--r--internal/admin/static/talktree.js2
9 files changed, 84 insertions, 39 deletions
diff --git a/internal/admin/static/cardeditor.js b/internal/admin/static/cardeditor.js
index f32ea7e..6a8cda7 100644
--- a/internal/admin/static/cardeditor.js
+++ b/internal/admin/static/cardeditor.js
@@ -42,7 +42,7 @@ function ced_collectFieldValue(el, type) {
if (type === 'checkbox') return el.checked;
if (type === 'select') return el.value;
if (type === 'search') return el.value;
- if (type === 'number') { var n = parseFloat(el.value); return isNaN(n) ? 0 : n; }
+ if (type === 'number') { var n = parseFloat(el.value); return isNaN(n) ? '' : n; }
if (type === 'json') {
var raw = el.value.trim();
if (!raw) return null;
@@ -86,7 +86,7 @@ function ced_addKVRow(cardID, subKey) {
if (!el) return;
var row = document.createElement('div');
row.className = 'obj-kv-row';
- row.innerHTML = '<input value="" placeholder="key" class="obj-kv-key"><input value="0" placeholder="val" class="obj-kv-val"><button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button>';
+ row.innerHTML = '<input value="" placeholder="key" class="obj-kv-key"><input value="" placeholder="val" class="obj-kv-val"><button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button>';
el.appendChild(row);
}
@@ -125,7 +125,7 @@ function ced_addKVSearchRow(cardID, subKey, entity) {
if (!el) return;
var row = document.createElement('div');
row.className = 'obj-kv-row';
- row.innerHTML = '<div class="obj-kv-key-wrap obj-search" style="flex:1;position:relative"><div style="position:relative;width:100%"><input value="" class="search-input obj-kv-key" data-search-type="' + entity + '" placeholder="Search ' + entity + '..."><div class="search-results" style="display:none"></div></div></div><input value="0" placeholder="val" class="obj-kv-val"><button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button>';
+ row.innerHTML = '<div class="obj-kv-key-wrap obj-search" style="flex:1;position:relative"><div style="position:relative;width:100%"><input value="" class="search-input obj-kv-key" data-search-type="' + entity + '" placeholder="Search ' + entity + '..."><div class="search-results" style="display:none"></div></div></div><input value="" placeholder="val" class="obj-kv-val"><button class="btn btn-sm btn-danger" onclick="this.parentElement.remove()">X</button>';
el.appendChild(row);
ced_setupSearchFields(row);
}
@@ -398,7 +398,7 @@ function ced_addSubRow(sec, subKey, dataRef, cardPathFn) {
var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
if (st) {
st.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
arr.push(empty);
@@ -579,8 +579,18 @@ function ced_cleanOutput(obj) {
} else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
ced_cleanOutput(val);
if (Object.keys(val).length === 0) delete obj[key];
- } else if (Array.isArray(val) && val.length === 0) {
- delete obj[key];
+ } else if (Array.isArray(val)) {
+ if (val.length === 0) {
+ delete obj[key];
+ } else {
+ for (var i = val.length - 1; i >= 0; i--) {
+ if (typeof val[i] === 'object' && val[i] !== null) {
+ ced_cleanOutput(val[i]);
+ if (Object.keys(val[i]).length === 0) val.splice(i, 1);
+ }
+ }
+ if (val.length === 0) delete obj[key];
+ }
}
}
}
@@ -658,3 +668,18 @@ function ced_collectAtkType(widgetID) {
if (sci && sci.checked) out.push('science');
return out;
}
+
+function ced_renderTagHelp(type) {
+ var h = '<div style="margin-top:8px;font-size:10px;color:#aaa;line-height:1.5">';
+ if (type === 'color') {
+ h += 'Name is displayed without an article (e.g. "John Smith" instead of "a John Smith")<br>';
+ h += 'Cannot be attacked by players. Combat level hidden<br>';
+ h += 'Text fields support color tags: <code style="color:#aaa;font-family:monospace">{NN}text{/}</code> where <code style="color:#aaa;font-family:monospace">NN</code> is a hex color index (00–FF).';
+ } else if (type === 'crafting') {
+ h += 'Message fields support template variables:<br>%i1 = first ingredient name<br>%i2 = second ingredient name<br>%n = output item name.<br>Color tags are also supported: {NN}text{/} where NN is a hex color index (00–FF).';
+ } else if (type === 'gather') {
+ h += 'Message fields support template variables: {name} = object name.<br>Color tags are also supported: {NN}text{/} where NN is a hex color index (00–FF).';
+ }
+ h += '</div>';
+ return h;
+}
diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js
index ac1df97..5900930 100644
--- a/internal/admin/static/dropeditor.js
+++ b/internal/admin/static/dropeditor.js
@@ -7,8 +7,8 @@ var DROP_SECTIONS = [
{
id: 'drops', label: 'Drops', always: true, path: 'drops', isArray: true,
fields: [
- ['item_id', 'Item ID', 'search', 'e.g. copper_ore', '', null, 'items'],
- ['table', 'Table', 'search', 'e.g. gem_table', '', null, 'drops'],
+ ['item_id', 'Item ID', 'search', 'copper_ore', '', null, 'items'],
+ ['table', 'Table', 'search', 'gem_table', '', null, 'drops'],
['weight', 'Weight', 'number', '10', 'narrow'],
['quantity', 'Quantity', 'number', '1', 'narrow'],
]
diff --git a/internal/admin/static/hazardeditor.js b/internal/admin/static/hazardeditor.js
index 89586ac..1f5fdf3 100644
--- a/internal/admin/static/hazardeditor.js
+++ b/internal/admin/static/hazardeditor.js
@@ -13,7 +13,7 @@ var HAZARD_SECTIONS = [
['warn_message', 'Warn Message', 'text', 'The air grows thick with toxic fumes...', 'wide'],
['hit_message', 'Hit Message', 'text', 'You cough and choke on the toxic gas!'],
['miss_message', 'Miss Message', 'text', 'You hold your breath and avoid the fumes.'],
- ['required_item', 'Required Item', 'search', 'e.g. gas_mask', '', null, 'items'],
+ ['required_item', 'Required Item', 'search', 'gas_mask', '', null, 'items'],
]
},
{
@@ -146,6 +146,9 @@ function renderCard(sec, data) {
h += renderField(sec, f, data, -1);
});
h += '</div>';
+ if (sec.id === 'core') {
+ h += ced_renderTagHelp('color');
+ }
}
h += '</div>';
}
diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js
index d48d70e..6375c16 100644
--- a/internal/admin/static/itemeditor.js
+++ b/internal/admin/static/itemeditor.js
@@ -55,14 +55,14 @@ var SECTIONS = [
['output_qty', 'Output Qty', 'number', '', 'narrow'],
['station', 'Station', 'select', 'dyn:stations'],
['tool', 'Tool', 'select', 'dyn:tools'],
- ['fail', 'Fail Item', 'search', 'e.g. burnt_fish', '', null, 'items'],
+ ['fail', 'Fail Item', 'search', 'burnt_fish', '', null, 'items'],
['success_base', 'Base', 'number', '0.5', 'narrow'],
['success_per_level', 'Per Lvl', 'number', '0.01', 'narrow'],
['success_cap', 'Cap', 'number', '0.95', 'narrow'],
- ['start_message', 'Start Message', 'text', 'e.g. You begin smithing %i1.', 'wide'],
- ['success_message', 'Success Message', 'text', 'e.g. You smith a %n.', 'wide'],
- ['fail_message', 'Fail Message', 'text', 'e.g. You accidentally burn the %i1.', 'wide', function(item) { return item.type === 'cooking' || item.subtype === 'smelt'; }],
- ['end_message', 'End Message', 'text', 'e.g. You\'ve finished smithing.', 'wide'],
+ ['start_message', 'Start Message', 'text', 'You begin smithing %i1.', 'wide'],
+ ['success_message', 'Success Message', 'text', 'You smith a %n.', 'wide'],
+ ['fail_message', 'Fail Message', 'text', 'You accidentally burn the %i1.', 'wide', function(item) { return item.type === 'cooking' || item.subtype === 'smelt'; }],
+ ['end_message', 'End Message', 'text', 'You\'ve finished smithing.', 'wide'],
],
fieldGroups: [
['type', 'subtype', 'level', 'xp', 'ticks_per_cycle', 'output_qty'],
@@ -101,7 +101,7 @@ var SECTIONS = [
detect: function(d) { return d.farm_patch_type; },
fields: [
['farm_patch_type', 'Patch Type', 'select', 'herb|allotment|flower|bush|tree'],
- ['farm_product', 'Product', 'search', 'e.g. tomato', '', null, 'items'],
+ ['farm_product', 'Product', 'search', 'tomato', '', null, 'items'],
['farm_plant_xp', 'Plant XP', 'number', '5', 'narrow'],
['farm_harvest_xp', 'Harvest XP', 'number', '10', 'narrow'],
['farm_level', 'Farm Level', 'number', '1', 'narrow'],
@@ -307,6 +307,10 @@ function renderCard(sec, data) {
h += renderSubtable(sec, st, data);
});
}
+
+ if (sec.id === 'crafting') {
+ h += ced_renderTagHelp('crafting');
+ }
}
h += '</div>';
}
@@ -334,6 +338,7 @@ function renderCoreCard(data) {
var descVal = getVal(itemData, sec, 'description');
var dstr = typeof descVal === 'object' ? (Array.isArray(descVal) ? descVal.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(descVal)) : String(descVal);
h += '<label class="obj-field obj-wide"><span>Description</span><textarea id="' + descFid + '" rows="6" style="min-height:56px">' + esc(dstr) + '</textarea></label>';
+ h += ced_renderTagHelp('color');
return h;
}
@@ -1009,7 +1014,7 @@ function addArrayItem(cardID) {
} else {
var empty = {};
sec.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
arr.push(empty);
}
@@ -1061,7 +1066,7 @@ function addSubRow(cardID, subKey) {
var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
if (st) {
st.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
arr.push(empty);
@@ -1110,7 +1115,7 @@ function addSubSubRow(cardID, subKey, rowIdx, subSubKey) {
var sst = st.subSubtables.find(function(ss) { return ss.key === subSubKey; });
if (sst) {
sst.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
}
diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js
index 6d56097..390043e 100644
--- a/internal/admin/static/map.js
+++ b/internal/admin/static/map.js
@@ -1260,8 +1260,8 @@ function courseShowNew() {
menu.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);min-width:520px;max-width:90vw;max-height:85vh;overflow-y:auto;overflow-x:hidden;padding:14px;z-index:201';
var h = '<h3 style="margin-bottom:8px;font-size:13px">New Course (room #' + roomID + ' = step 1)</h3>';
- h += '<div class="form-group"><label>Course ID (filename)</label><input id="newCourseID" placeholder="e.g. vent_shaft" ></div>';
- h += '<div class="form-group"><label>Course Name</label><input id="newCourseName" placeholder="e.g. Ventilation Shaft Course"></div>';
+ h += '<div class="form-group"><label>Course ID (filename)</label><input id="newCourseID" placeholder="vent_shaft" ></div>';
+ h += '<div class="form-group"><label>Course Name</label><input id="newCourseName" placeholder="Ventilation Shaft Course"></div>';
h += '<div class="form-group"><label>Required Level</label><input id="newCourseReqLevel" type="number" value="1"></div>';
h += '<div class="form-group"><label>Start Room</label><input id="newCourseStartRoom" type="number" value="' + roomID + '"></div>';
h += '<div class="form-group"><label>Completion XP</label><input id="newCourseCompletionXP" type="number" value="0"></div>';
@@ -3382,7 +3382,7 @@ function renderTriggerListHTML(arr, secId, secPath, allowItem) {
interactionStyle: true,
itemIDAllowed: allowItem,
label: 'Trigger',
- fields: [['item_id', 'Item ID', 'search', 'e.g. keycard', '', null, 'items']]
+ fields: [['item_id', 'Item ID', 'search', 'keycard', '', null, 'items']]
};
var ctx = {
sec: sec,
diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js
index 10b256e..4b136a3 100644
--- a/internal/admin/static/mobeditor.js
+++ b/internal/admin/static/mobeditor.js
@@ -48,8 +48,8 @@ var MOB_SECTIONS = [
['stats.defenses.weakness', 'Weakness', 'select', '(none)|bio|eco|hydro|solar|chaos'],
['stats.defenses.weakness_percent', 'Weakness %', 'number', '50', 'narrow'],
['assassin_level', 'Assassin Lvl', 'number', '1', 'narrow'],
- ['finishing_blow', 'Finishing Blow', 'search', 'e.g. stake', '', null, 'items'],
- ['damage_without', 'Damage Without', 'search', 'e.g. holy_symbol', '', null, 'items'],
+ ['finishing_blow', 'Finishing Blow', 'search', 'stake', '', null, 'items'],
+ ['damage_without', 'Damage Without', 'search', 'holy_symbol', '', null, 'items'],
['size', 'Size', 'text', 'small'],
],
inline: [
@@ -301,9 +301,14 @@ function renderMobCoreCard(data) {
h += ced_renderTags(sec, il, data);
});
h += '<div class="obj-card-grid" style="margin-top:6px">';
+ h += '<div style="display:flex;flex-direction:column">';
h += renderMobField(sec, byKey['unique'], data, -1);
+ h += '</div>';
+ h += '<div style="display:flex;flex-direction:column">';
h += renderMobField(sec, byKey['protected'], data, -1);
h += '</div>';
+ h += '</div>';
+ h += ced_renderTagHelp('color');
return h;
}
@@ -507,7 +512,7 @@ function mobAddArrayItem(cardID) {
var arr = mobData[sec.path] || [];
var empty = {};
sec.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
arr.push(empty);
mobData[sec.path] = arr;
diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js
index fe71f88..b2eb79f 100644
--- a/internal/admin/static/moduleeditor.js
+++ b/internal/admin/static/moduleeditor.js
@@ -221,7 +221,7 @@ function addArrayItem(cardID) {
var arr = moduleData[sec.path] || [];
var empty = {};
sec.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
arr.push(empty);
moduleData[sec.path] = arr;
diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js
index 88bc5b7..b4d8282 100644
--- a/internal/admin/static/objecteditor.js
+++ b/internal/admin/static/objecteditor.js
@@ -14,7 +14,7 @@ var SECTIONS = [
['hidden', 'Hidden', 'checkbox'],
['inroom_description', 'In-Room Description', 'text', 'A copper rock juts from the ground.'],
['description', 'Description', 'textarea', 'A vein of copper runs through this rock.'],
- ['removal_item', 'Removal Item ID', 'search', 'e.g. ashes', '', null, 'items'],
+ ['removal_item', 'Removal Item ID', 'search', 'ashes', '', null, 'items'],
]
},
{
@@ -73,10 +73,10 @@ var SECTIONS = [
['tier', 'Tier', 'number', '1', 'narrow'],
['max_block_size', 'Max Block Size', 'select', '|small|medium|large|massive'],
['max_occupants', 'Max Occupants', 'number', '1', 'narrow'],
- ['unsafe_chance', 'Unsafe Chance', 'number', '0 = never, e.g. 0.05', 'narrow'],
- ['decay_ticks', 'Decay Ticks', 'number', '0 = never, e.g. 100', 'narrow'],
- ['decay_chance', 'Decay Chance', 'number', '0 = never, e.g. 0.01', 'narrow'],
- ['respawn_ticks', 'Respawn Ticks', 'number', '0 = no respawn, e.g. 50', 'narrow'],
+ ['unsafe_chance', 'Unsafe Chance', 'number', '0.05', 'narrow'],
+ ['decay_ticks', 'Decay Ticks', 'number', '100', 'narrow'],
+ ['decay_chance', 'Decay Chance', 'number', '0.01', 'narrow'],
+ ['respawn_ticks', 'Respawn Ticks', 'number', '50', 'narrow'],
['respawn_on_hide', 'Respawn on hide', 'checkbox'],
],
subtables: [
@@ -89,7 +89,7 @@ var SECTIONS = [
id: 'on_use', label: '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 (empty = bare use)', '', null, 'items'],
+ ['item_id', 'Item ID', 'search', 'keycard', '', null, 'items'],
]
},
{
@@ -340,6 +340,10 @@ function renderCard(sec, data) {
}
h += '</div>';
}
+
+ if (sec.id === 'gather') {
+ h += ced_renderTagHelp('gather');
+ }
}
h += '</div>';
}
@@ -447,13 +451,15 @@ function renderCoreCard(data) {
}
if (showInRoom) {
h += '<div style="display:flex;gap:6px;align-items:flex-start">';
- h += '<div style="flex:1">' + renderField(sec, sec.fields[4], data, -1) + '</div>';
+ h += '<div style="flex:1;display:flex;flex-direction:column">' + renderField(sec, sec.fields[4], data, -1);
+ h += '<div style="font-size:10px;color:#888;font-style:italic;margin-top:1px">Alternative way this object can appear with the \'look\' command</div></div>';
h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'inroom_description\')" style="margin-top:2px">X</button>';
h += '</div>';
}
if (showRemoval) {
h += '<div style="display:flex;gap:6px;align-items:flex-start">';
- h += '<div style="flex:1">' + renderField(sec, sec.fields[6], data, -1) + '</div>';
+ h += '<div style="flex:1;display:flex;flex-direction:column">' + renderField(sec, sec.fields[6], data, -1);
+ h += '<div style="font-size:10px;color:#888;font-style:italic;margin-top:1px">Item left behind when this object is removed</div></div>';
h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'removal_item\')" style="margin-top:2px">X</button>';
h += '</div>';
}
@@ -471,6 +477,8 @@ function renderCoreCard(data) {
}
h += '</div>';
+ h += ced_renderTagHelp('color');
+
return h;
}
@@ -672,8 +680,7 @@ function renderToolsCheckbox(sec, il, data) {
return '<span style="color:#888;font-size:11px">Loading tool types...</span>';
}
if (arr.length === 0) {
- var hint = il.hint ? '<div style="font-size:10px;color:#888;margin-bottom:4px;font-style:italic">' + esc(il.hint) + '</div>' : '';
- return hint + '<button class="btn btn-sm obj-sub-add" onclick="addToolRow(\'' + sec.id + '\',\'' + il.key + '\')">+ Add Tools</button>';
+ return '<button class="btn btn-sm obj-sub-add" onclick="addToolRow(\'' + sec.id + '\',\'' + il.key + '\')">+ Add Tools</button>';
}
var h = '<div class="obj-inline-group">';
h += '<span class="obj-inline-label">' + esc(il.label) + (il.hint ? ' <span style="color:#666;font-size:10px;font-weight:normal;font-style:italic">(' + esc(il.hint) + ')</span>' : '') + '</span>';
@@ -822,7 +829,7 @@ function addArrayItem(cardID) {
} else {
empty = {};
sec.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
arr.push(empty);
@@ -848,7 +855,7 @@ function addSubRow(cardID, subKey) {
var st = (sec.subtables || []).find(function(s) { return s.key === subKey; });
if (st) {
st.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
arr.push(empty);
@@ -884,7 +891,7 @@ function addDropRow(cardID, subKey, dropType) {
var empty = { _type: (dropType === 'table' ? 't' : 'i') };
if (st) {
st.fields.forEach(function(f) {
- empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ empty[f[0]] = f[2] === 'checkbox' ? false : '';
});
}
empty.weight = 10;
diff --git a/internal/admin/static/talktree.js b/internal/admin/static/talktree.js
index 8e35404..ff44ff1 100644
--- a/internal/admin/static/talktree.js
+++ b/internal/admin/static/talktree.js
@@ -204,7 +204,7 @@
if (tag === 'INPUT' || tag === 'TEXTAREA') {
var val;
if (type === 'checkbox') { val = el.checked; }
- else if (type === 'number') { var n = parseFloat(el.value); val = isNaN(n) ? 0 : n; }
+ else if (type === 'number') { var n = parseFloat(el.value); val = isNaN(n) ? '' : n; }
else { val = el.value; }
talkSyncPath(path, val);
if (path.endsWith('.goto') && e.type === 'change') renderTalkTree(window._talkData);