diff options
| author | historia <[not public]> | 2026-07-13 16:10:40 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-13 16:10:40 -0400 |
| commit | 3274b700a6f655b8ecc3505e814ac5d782ae6ffe (patch) | |
| tree | e669b47726f8cb93b75c7df85997cc8556009f20 /internal | |
| parent | 93a412aed2b11406399504c81d6d19ed1341908a (diff) | |
| download | thehouseoficarus-3274b700a6f655b8ecc3505e814ac5d782ae6ffe.tar.gz | |
feat(admin): reminder text for color tags and item variables, improve placeholder text
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/admin/static/cardeditor.js | 37 | ||||
| -rw-r--r-- | internal/admin/static/dropeditor.js | 4 | ||||
| -rw-r--r-- | internal/admin/static/hazardeditor.js | 5 | ||||
| -rw-r--r-- | internal/admin/static/itemeditor.js | 23 | ||||
| -rw-r--r-- | internal/admin/static/map.js | 6 | ||||
| -rw-r--r-- | internal/admin/static/mobeditor.js | 11 | ||||
| -rw-r--r-- | internal/admin/static/moduleeditor.js | 2 | ||||
| -rw-r--r-- | internal/admin/static/objecteditor.js | 33 | ||||
| -rw-r--r-- | internal/admin/static/talktree.js | 2 |
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); |
