diff options
| author | historia <[not public]> | 2026-07-05 16:42:01 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-05 16:42:01 -0400 |
| commit | 9b54188970c51070d86f4c87e909ea7836c86f07 (patch) | |
| tree | 9dea17fee340bb92dee8bb4632001dbf219d2462 /internal | |
| parent | 21c1997a3fd0f5ec5dcff1add45e4dfaf7a338ca (diff) | |
| download | thehouseoficarus-9b54188970c51070d86f4c87e909ea7836c86f07.tar.gz | |
fix: restructure steal yaml, update admin web gui
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/admin/static/cardeditor.js | 78 | ||||
| -rw-r--r-- | internal/admin/static/dropeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/hazardeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/itemeditor.js | 54 | ||||
| -rw-r--r-- | internal/admin/static/mobeditor.js | 15 | ||||
| -rw-r--r-- | internal/admin/static/moduleeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/objecteditor.js | 177 | ||||
| -rw-r--r-- | internal/admin/static/techeditor.js | 1 | ||||
| -rw-r--r-- | internal/game/act_steal.go | 30 | ||||
| -rw-r--r-- | internal/game/cmd_sneak.go | 4 | ||||
| -rw-r--r-- | internal/game/cmd_verbs.go | 2 | ||||
| -rw-r--r-- | internal/object/object.go | 14 | ||||
| -rw-r--r-- | internal/validate/checks.go | 37 | ||||
| -rw-r--r-- | internal/world/room.go | 12 |
14 files changed, 288 insertions, 139 deletions
diff --git a/internal/admin/static/cardeditor.js b/internal/admin/static/cardeditor.js index f2316e1..8ff5eaf 100644 --- a/internal/admin/static/cardeditor.js +++ b/internal/admin/static/cardeditor.js @@ -175,7 +175,7 @@ function ced_selectSearchResult(el) { function ced_persistSearchToData(input, value) { var ed = window._editorData; - var dataPath = input.getAttribute('data-data-path'); + var dataPath = input.getAttribute('data-field-path'); if (!ed || !dataPath) return; var m = dataPath.match(/^(.+)\[(\d+)\]\.(.+)$/); if (m) { @@ -195,6 +195,48 @@ function ced_persistSearchToData(input, value) { } } +function ced_setPathInData(ed, dataPath, value) { + if (!ed || !dataPath) return; + var m = dataPath.match(/^(.+)\[(\d+)\]\.(.+)$/); + if (m) { + if (!ed[m[1]]) ed[m[1]] = []; + if (!ed[m[1]][m[2]]) ed[m[1]][m[2]] = {}; + ed[m[1]][m[2]][m[3]] = value; + } else { + var m2 = dataPath.match(/^(.+)\.(.+)$/); + if (m2) { + if (!ed[m2[1]]) ed[m2[1]] = {}; + ed[m2[1]][m2[2]] = value; + } else { + ed[dataPath] = value; + } + } +} + +function ced_syncDOMToData(ed) { + if (!ed) return; + var inputs = document.querySelectorAll('[data-field-path]'); + for (var i = 0; i < inputs.length; i++) { + var el = inputs[i]; + var dataPath = el.getAttribute('data-field-path'); + if (!dataPath) continue; + var val; + if (el.tagName === 'TEXTAREA') { + val = el.value; + } else if (el.type === 'checkbox') { + val = el.checked; + } else if (el.type === 'number') { + var raw = el.value.trim(); + val = raw === '' ? '' : (parseFloat(raw) || 0); + } else if (el.tagName === 'SELECT') { + val = el.value; + } else { + val = el.value; + } + ced_setPathInData(ed, dataPath, val); + } +} + function ced_resolveDataPath(obj, path) { return path.split('.').reduce(function(o, k) { if (o === undefined || o === null) return undefined; @@ -236,6 +278,17 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPathFn(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -244,19 +297,19 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -265,24 +318,15 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPathFn(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</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>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } // ---- Subtable row add/remove ---- diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js index cbd51c7..8e463ec 100644 --- a/internal/admin/static/dropeditor.js +++ b/internal/admin/static/dropeditor.js @@ -84,6 +84,7 @@ function renderDropEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(dropData); if (!dropData || !dropID) return; renderDropEditor(dropID, dropData); } diff --git a/internal/admin/static/hazardeditor.js b/internal/admin/static/hazardeditor.js index ac35e78..a0610dc 100644 --- a/internal/admin/static/hazardeditor.js +++ b/internal/admin/static/hazardeditor.js @@ -108,6 +108,7 @@ function renderHazardEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(hazardData); if (!hazardData || !hazardID) return; renderHazardEditor(hazardID, hazardData); } diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js index 5a2b371..5ffb0cb 100644 --- a/internal/admin/static/itemeditor.js +++ b/internal/admin/static/itemeditor.js @@ -224,6 +224,7 @@ function renderItemEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(itemData); if (!itemData || !itemID) return; renderItemEditor(itemID, itemData); } @@ -349,6 +350,8 @@ function equipFieldID(key) { function renderEquipField(key, label, type, hint, val, wide, searchEntity) { var fid = equipFieldID(key); val = val === undefined || val === null ? '' : val; + var dp = 'equipment.' + key; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; @@ -358,7 +361,7 @@ function renderEquipField(key, label, type, hint, val, wide, searchEntity) { if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -368,10 +371,10 @@ function renderEquipField(key, label, type, hint, val, wide, searchEntity) { if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</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>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } function renderEquipmentCard(data) { @@ -491,6 +494,17 @@ function renderField(sec, f, data, idx, subPath, optStyle) { if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPath(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -499,19 +513,19 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -520,24 +534,15 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPath(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</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>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } // ========================================================================= @@ -575,25 +580,26 @@ function renderSubFieldHTML(sec, stKey, idx, f, val) { if (f[4] === 'wide') extraStyle = ' style="flex:1 1 100%"'; var defaultPh = ''; if (f[3]) defaultPh = ' placeholder="' + escAttr(f[3]) + '"'; + var dp = cardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; if (f[2] === 'search') { var entity = f[6] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; - var dp = cardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; - return '<label class="obj-field obj-search' + extraCls + '"' + extraStyle + '><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-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + return '<label class="obj-field obj-search' + extraCls + '"' + extraStyle + '><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>'; } if (f[2] === 'checkbox') { - return '<label class="obj-field obj-check' + extraCls + '"' + extraStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; + return '<label class="obj-field obj-check' + extraCls + '"' + extraStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(f[1]) + '</span></label>'; } if (f[2] === 'select') { var opts = f[3] ? f[3].split('|') : []; - var sel = '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><select id="' + fid + '">'; + var sel = '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { sel += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o || '(none)') + '</option>'; }); sel += '</select></label>'; return sel; } - return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + '></label>'; + return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + dpAttr + '></label>'; } function renderSubtable(sec, st, data) { diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js index c564088..73dd16b 100644 --- a/internal/admin/static/mobeditor.js +++ b/internal/admin/static/mobeditor.js @@ -202,6 +202,7 @@ function renderMobEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(mobData); if (!mobData || !mobID) return; renderMobEditor(mobID, mobData); } @@ -425,16 +426,17 @@ function renderMobSubField(sec, stKey, idx, f, val) { else if (f[4] === 'grow') extraCls = ' obj-grow'; var defaultPh = ''; if (f[3]) defaultPh = ' placeholder="' + escAttr(f[3]) + '"'; + var dp = mobCardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; if (f[2] === 'search') { var entity = f[6] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; - var dp = mobCardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; - return '<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-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + return '<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>'; } if (f[2] === 'checkbox') { - return '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; + return '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(f[1]) + '</span></label>'; } - return '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + '></label>'; + return '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + dpAttr + '></label>'; } function renderMobSubtable(sec, st, data) { @@ -529,11 +531,12 @@ function renderMobTableSubtable(sec, st, arr) { st.fields.forEach(function(f) { var fid = mobFieldIDSub(sec, st.key, idx, f[0]); var val = item[f[0]]; + var dp = mobCardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0]; if (f[2] === 'search') { var entity = f[6] || 'items'; var sval = (val === undefined || val === null) ? '' : String(val); h += '<div class="ingredients-col ingredients-col-items"><div class="obj-field obj-search" style="width:100%"><div style="position:relative;width:100%">' + - '<input id="' + fid + '" value="' + escAttr(sval) + '" class="search-input" data-search-type="' + entity + '" placeholder="Search ' + entity + '...">' + + '<input id="' + fid + '" value="' + escAttr(sval) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '" placeholder="Search ' + entity + '...">' + '<div class="search-results" style="display:none"></div></div></div></div>'; } else { var hasPh = !!f[3]; @@ -543,7 +546,7 @@ function renderMobTableSubtable(sec, st, arr) { else nval = String(val); var ph = hasPh ? ' placeholder="' + escAttr(f[3]) + '"' : ''; h += '<div class="ingredients-col" style="width:' + mobTableColWidth(f) + 'px">' + - '<label class="obj-field"><input id="' + fid + '" value="' + escAttr(nval) + '"' + ph + '></label>' + + '<label class="obj-field"><input id="' + fid + '" value="' + escAttr(nval) + '"' + ph + ' data-field-path="' + escAttr(dp) + '"></label>' + '</div>'; } }); diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js index 6d6a3e0..f1fb0fd 100644 --- a/internal/admin/static/moduleeditor.js +++ b/internal/admin/static/moduleeditor.js @@ -109,6 +109,7 @@ function renderModuleEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(moduleData); if (!moduleData || !moduleID) return; renderModuleEditor(moduleID, moduleData); } diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index c80cb57..f8ef18a 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -18,14 +18,14 @@ var SECTIONS = [ ] }, { - id: 'steal', label: 'Steal', - detect: function(d) { return d.steal_table !== undefined || d.steal_level !== undefined || d.steal_speed !== undefined || d.steal_xp !== undefined || d.guard_mob !== undefined; }, + id: 'steal', label: 'Steal', path: 'steal', + detect: function(d) { return d.steal !== undefined; }, fields: [ - ['steal_table', 'Table ID', 'text'], - ['steal_level', 'Level', 'number', '', 'narrow'], - ['steal_xp', 'XP', 'number', '', 'narrow'], - ['steal_speed', 'Speed', 'number', '', 'narrow'], - ['guard_mob', 'Guard Mob', 'text'], + ['table', 'Table ID', 'search', '', '', null, 'drops'], + ['level', 'Level', 'number', '1', 'narrow'], + ['xp', 'XP', 'number', '5', 'narrow'], + ['speed', 'Speed', 'number', '4', 'narrow'], + ['guard_mob', 'Guard Mob', 'search', '', '', null, 'mobs'], ] }, { @@ -148,6 +148,8 @@ function loadObject(id) { window._editorData = objectData; window._useInterVis = {}; window._useInterHelpCollapsed = undefined; + window._coreVis = {}; + window._stealVis = {}; renderObjectEditor(id, data); }).catch(function(e) { $('#editorMain').innerHTML = '<p style="color:var(--danger)">Failed to load: ' + esc(e.message) + '</p>'; @@ -250,6 +252,8 @@ function renderCard(sec, data) { h += renderArraySection(sec, data); } else if (sec.id === 'core') { h += renderCoreCard(data); + } else if (sec.id === 'steal') { + h += renderStealCard(data); } else { h += '<div class="obj-card-grid">'; sec.fields.forEach(function(f) { @@ -351,6 +355,17 @@ function renderField(sec, f, data, idx, subPath, optStyle) { if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPath(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -359,19 +374,19 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '" onchange="toggleGatherConditionals()">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '" onchange="toggleGatherConditionals()"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + (o === '' ? 'All' : esc(o)) + '</option>'; }); @@ -380,39 +395,93 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPath(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</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>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } function renderCoreCard(data) { var sec = SECTIONS[0]; + window._coreVis = window._coreVis || {}; + var vis = window._coreVis; + var showAliases = vis.aliases || (data.aliases && data.aliases.length > 0); + var showInRoom = vis.inroom_description || (data.inroom_description && data.inroom_description !== ''); + var showRemoval = vis.removal_item || (data.removal_item && data.removal_item !== ''); + var h = '<div class="obj-core-row">'; h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1'); // name - h += renderField(sec, sec.fields[2], data, -1, null, 'flex:2'); // aliases h += renderField(sec, sec.fields[1], data, -1); // color h += renderField(sec, sec.fields[3], data, -1); // hidden h += '</div>'; h += '<div style="display:flex;flex-direction:column;gap:8px">'; h += renderField(sec, sec.fields[5], data, -1); // description - h += renderField(sec, sec.fields[4], data, -1); // in-room description - h += renderField(sec, sec.fields[6], data, -1); // removal item + + if (showAliases) { + h += '<div style="display:flex;gap:6px;align-items:flex-start">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[2], data, -1, null, 'flex:2') + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'aliases\')" style="margin-top:2px">X</button>'; + h += '</div>'; + } + 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 += '<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 += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'removal_item\')" style="margin-top:2px">X</button>'; + h += '</div>'; + } h += '</div>'; + + h += '<div style="display:flex;gap:8px;margin-top:4px">'; + if (!showAliases) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'aliases\')">+ Add Aliases</button>'; + } + if (!showInRoom) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'inroom_description\')">+ Add In-Room Desc</button>'; + } + if (!showRemoval) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'removal_item\')">+ Add Removal Item</button>'; + } + h += '</div>'; + + return h; +} + +function renderStealCard(data) { + var sec = SECTIONS[1]; + window._stealVis = window._stealVis || {}; + var vis = window._stealVis; + var stealData = data.steal || {}; + var showGuard = vis.guard_mob || (stealData.guard_mob && stealData.guard_mob !== ''); + + var h = '<div style="display:flex;gap:10px">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[0], data, -1) + '</div>'; + h += renderField(sec, sec.fields[1], data, -1); + h += renderField(sec, sec.fields[2], data, -1); + h += renderField(sec, sec.fields[3], data, -1); + h += '</div>'; + + if (showGuard) { + h += '<div style="display:flex;gap:6px;align-items:flex-start;margin-top:8px">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[4], data, -1) + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideGuardMob()" style="margin-top:2px">X</button>'; + h += '</div>'; + } else { + h += '<div style="margin-top:8px">'; + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap;width:100%" onclick="showGuardMob()">+ Guard Mob</button>'; + h += '</div>'; + } + return h; } @@ -433,7 +502,9 @@ function renderArraySection(sec, data) { h += '<div class="obj-sub-row" data-idx="' + idx + '" style="flex-direction:column;align-items:stretch;position:relative;gap:6px">'; - h += '<div style="display:flex;gap:6px;align-items:flex-end;flex-wrap:wrap">'; + 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 (!showCond) { @@ -445,7 +516,6 @@ function renderArraySection(sec, data) { 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 btn-danger" style="margin-left:auto;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">Remove Interaction</button>'; h += '</div>'; h += '<div style="display:' + (showCond ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">'; @@ -541,7 +611,7 @@ function renderSubtable(sec, st, data) { var entity = f[5] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; 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-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + 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>'; } else { @@ -567,7 +637,7 @@ function renderSubtable(sec, st, data) { var entity = f[5] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; 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-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + 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>'; } else { @@ -693,11 +763,7 @@ function removeSection(id) { if (sec.path) { delete objectData[sec.path]; } else if (id === 'steal') { - delete objectData.steal_table; - delete objectData.steal_level; - delete objectData.steal_xp; - delete objectData.steal_speed; - delete objectData.guard_mob; + delete objectData.steal; } renderCurrent(); } @@ -724,11 +790,7 @@ function addSection() { objectData[sec.path] = {}; } } else if (id === 'steal') { - objectData.steal_table = ''; - objectData.steal_level = 0; - objectData.steal_xp = 0; - objectData.steal_speed = 0; - objectData.guard_mob = ''; + objectData.steal = {table: ''}; } expandedCards[id] = true; renderCurrent(); @@ -806,6 +868,7 @@ function validateDropRow(idx) { } function renderCurrent() { + ced_syncDOMToData(objectData); if (!objectData || !objectID) return; renderObjectEditor(objectID, objectData); } @@ -827,6 +890,38 @@ function hideUseInterField(idx, key) { renderCurrent(); } +function showCoreField(key) { + window._coreVis = window._coreVis || {}; + window._coreVis[key] = true; + renderCurrent(); +} + +function hideCoreField(key) { + window._coreVis = window._coreVis || {}; + window._coreVis[key] = false; + if (key === 'aliases') { + objectData.aliases = []; + } else if (key === 'inroom_description') { + objectData.inroom_description = ''; + } else if (key === 'removal_item') { + objectData.removal_item = ''; + } + renderCurrent(); +} + +function showGuardMob() { + window._stealVis = window._stealVis || {}; + window._stealVis.guard_mob = true; + renderCurrent(); +} + +function hideGuardMob() { + window._stealVis = window._stealVis || {}; + window._stealVis.guard_mob = false; + if (objectData.steal) objectData.steal.guard_mob = ''; + renderCurrent(); +} + function toggleUseInterHelp() { if (window._useInterHelpCollapsed === undefined) { window._useInterHelpCollapsed = false; @@ -967,6 +1062,8 @@ function saveObject() { } } else if (hasValues && sec.path) { out[sec.path] = sectionObj; + } else if (hasValues) { + Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; }); } }); diff --git a/internal/admin/static/techeditor.js b/internal/admin/static/techeditor.js index cd4f50f..70f881e 100644 --- a/internal/admin/static/techeditor.js +++ b/internal/admin/static/techeditor.js @@ -98,6 +98,7 @@ function renderTechEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(techData); if (!techData || !techID) return; renderTechEditor(techID, techData); } diff --git a/internal/game/act_steal.go b/internal/game/act_steal.go index a64bc96..b5c1210 100644 --- a/internal/game/act_steal.go +++ b/internal/game/act_steal.go @@ -102,7 +102,7 @@ func (g *Game) resolveStealTarget(sess *net.Session, p *player.Player, input str var stealObjs []*object.ObjectDef for _, st := range roomObjs { def, err := g.ObjectStore.Load(st.DefID) - if err != nil || def.StealTable == "" { + if err != nil || def.Steal == nil || def.Steal.Table == "" { continue } stealObjs = append(stealObjs, def) @@ -224,14 +224,14 @@ func (g *Game) startSteal(sess *net.Session, p *player.Player, targetType string return } } else { - stealTable = objDef.StealTable - stealLevel = objDef.StealLevel - stealXP = objDef.StealXP - stealSpeed = objDef.StealSpeed + stealTable = objDef.Steal.Table + stealLevel = objDef.Steal.Level + stealXP = objDef.Steal.XP + stealSpeed = objDef.Steal.Speed targetName = objDef.Name targetID = objDef.ID objDefID = objDef.ID - guardMob = objDef.GuardMob + guardMob = objDef.Steal.GuardMob if stealTable == "" { sess.WriteLine(fmt.Sprintf("You can't steal from the %s.", targetName)) @@ -328,15 +328,15 @@ func (g *Game) advanceSteal(sess *net.Session, p *player.Player) { return } objDef, err := g.ObjectStore.Load(objDefID) - if err == nil { - d.StealLevel = objDef.StealLevel - d.StealXP = objDef.StealXP - d.StealSpeed = objDef.StealSpeed - stealLevel = objDef.StealLevel - stealXP = objDef.StealXP - stealSpeed = objDef.StealSpeed - d.GuardMob = objDef.GuardMob - guardMob = objDef.GuardMob + if err == nil && objDef.Steal != nil { + d.StealLevel = objDef.Steal.Level + d.StealXP = objDef.Steal.XP + d.StealSpeed = objDef.Steal.Speed + stealLevel = objDef.Steal.Level + stealXP = objDef.Steal.XP + stealSpeed = objDef.Steal.Speed + d.GuardMob = objDef.Steal.GuardMob + guardMob = objDef.Steal.GuardMob if guardMob != "" { guard := g.findGuardInRoom(p.RoomID, guardMob) diff --git a/internal/game/cmd_sneak.go b/internal/game/cmd_sneak.go index 325a36b..5ae60e7 100644 --- a/internal/game/cmd_sneak.go +++ b/internal/game/cmd_sneak.go @@ -40,11 +40,11 @@ func (g *Game) SneakTick() { objs := g.World.AllObjInstances(p.RoomID) for _, st := range objs { objDef, err := g.ObjectStore.Load(st.DefID) - if err != nil || objDef.GuardMob == "" { + if err != nil || objDef.Steal == nil || objDef.Steal.GuardMob == "" { continue } - guard := g.findGuardInRoom(p.RoomID, objDef.GuardMob) + guard := g.findGuardInRoom(p.RoomID, objDef.Steal.GuardMob) if guard == nil { continue } diff --git a/internal/game/cmd_verbs.go b/internal/game/cmd_verbs.go index cad90c5..57db0a6 100644 --- a/internal/game/cmd_verbs.go +++ b/internal/game/cmd_verbs.go @@ -118,7 +118,7 @@ func (g *Game) executeVerbs(sess *net.Session, args []string, rawInput string) { } } - if def.StealTable != "" { + if def.Steal != nil && def.Steal.Table != "" { addUnique(§ionObjs, &seen, "steal "+name) } diff --git a/internal/object/object.go b/internal/object/object.go index 2dcc4bf..5b45a91 100644 --- a/internal/object/object.go +++ b/internal/object/object.go @@ -9,6 +9,14 @@ type UseInteraction struct { Action *behavior.NodeAction `yaml:"action"` } +type ObjectSteal struct { + Table string `yaml:"table"` + Level int `yaml:"level"` + XP int `yaml:"xp"` + Speed float64 `yaml:"speed"` + GuardMob string `yaml:"guard_mob,omitempty"` +} + type ObjectDef struct { ID string `yaml:"id"` Name string `yaml:"name"` @@ -19,11 +27,7 @@ type ObjectDef struct { RemovalItem string `yaml:"removal_item"` Description behavior.DescList `yaml:"description"` UseInteractions []UseInteraction `yaml:"use_interactions"` - StealTable string `yaml:"steal_table"` - StealLevel int `yaml:"steal_level"` - StealXP int `yaml:"steal_xp"` - StealSpeed float64 `yaml:"steal_speed"` - GuardMob string `yaml:"guard_mob"` + Steal *ObjectSteal `yaml:"steal,omitempty"` Gather *behavior.GatherConfig `yaml:"gather,omitempty"` Talk *behavior.TalkConfig `yaml:"talk,omitempty"` diff --git a/internal/validate/checks.go b/internal/validate/checks.go index 974998a..0fc5b8f 100644 --- a/internal/validate/checks.go +++ b/internal/validate/checks.go @@ -198,12 +198,9 @@ func validateLocalObject(roomID int, robj world.RoomObject, itemIDs map[string]b if len(def.UseInteractions) > 0 { bad = append(bad, "use_interactions") } - if def.StealTable != "" || def.StealLevel != 0 || def.StealXP != 0 || def.StealSpeed != 0 { + if def.Steal != nil { bad = append(bad, "steal") } - if def.GuardMob != "" { - bad = append(bad, "guard_mob") - } if def.RemovalItem != "" { bad = append(bad, "removal_item") } @@ -548,22 +545,24 @@ func validateObjects(s Source) []Issue { }) } - if obj.StealTable != "" && !dropIDs[obj.StealTable] { - issues = append(issues, Issue{ - Level: "ERROR", - Type: "reference", - Message: fmt.Sprintf("Object %q: steal_table %q does not exist", - id, obj.StealTable), - }) - } + if obj.Steal != nil { + if obj.Steal.Table != "" && !dropIDs[obj.Steal.Table] { + issues = append(issues, Issue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("Object %q: steal.table %q does not exist", + id, obj.Steal.Table), + }) + } - if obj.GuardMob != "" && !mobIDs[obj.GuardMob] { - issues = append(issues, Issue{ - Level: "ERROR", - Type: "reference", - Message: fmt.Sprintf("Object %q: guard_mob %q does not exist", - id, obj.GuardMob), - }) + if obj.Steal.GuardMob != "" && !mobIDs[obj.Steal.GuardMob] { + issues = append(issues, Issue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("Object %q: steal.guard_mob %q does not exist", + id, obj.Steal.GuardMob), + }) + } } for _, ui := range obj.UseInteractions { diff --git a/internal/world/room.go b/internal/world/room.go index 9c2e52f..f66ce1d 100644 --- a/internal/world/room.go +++ b/internal/world/room.go @@ -210,11 +210,7 @@ func (ro RoomObject) MarshalYAML() (interface{}, error) { RemovalItem string `yaml:"removal_item,omitempty"` Description behavior.DescList `yaml:"description,omitempty"` UseInteractions []object.UseInteraction `yaml:"use_interactions,omitempty"` - StealTable string `yaml:"steal_table,omitempty"` - StealLevel int `yaml:"steal_level,omitempty"` - StealXP int `yaml:"steal_xp,omitempty"` - StealSpeed float64 `yaml:"steal_speed,omitempty"` - GuardMob string `yaml:"guard_mob,omitempty"` + Steal *object.ObjectSteal `yaml:"steal,omitempty"` Gather *behavior.GatherConfig `yaml:"gather,omitempty"` Talk *behavior.TalkConfig `yaml:"talk,omitempty"` Use *behavior.UseConfig `yaml:"use,omitempty"` @@ -231,11 +227,7 @@ func (ro RoomObject) MarshalYAML() (interface{}, error) { RemovalItem: def.RemovalItem, Description: def.Description, UseInteractions: def.UseInteractions, - StealTable: def.StealTable, - StealLevel: def.StealLevel, - StealXP: def.StealXP, - StealSpeed: def.StealSpeed, - GuardMob: def.GuardMob, + Steal: def.Steal, Gather: def.Gather, Talk: def.Talk, Use: def.Use, |
