diff options
29 files changed, 124 insertions, 49 deletions
diff --git a/data/.admin_history.json b/data/.admin_history.json index 4639022..8212f5e 100644 --- a/data/.admin_history.json +++ b/data/.admin_history.json @@ -1 +1 @@ -{"history":[{"time":"2026-07-04T23:03:14-04:00","description":"move objects teak_tree to test/","file_path":"data/objects/teak_tree.yaml","new_file_path":"data/objects/test/teak_tree.yaml","old_content":"color: \"B3\"\ngather:\n deplete_timer: 50\n drops:\n - depletes: false\n item_id: teak_logs\n level: 35\n success_message: You get some {B3}teak logs{/}.\n weight: 100\n xp: 85\n fail_message: You swing but get no logs.\n gather_message: You swing your axe at the teak tree...\n nest_chance: 256\n respawn_broadcast: A {name} grows back.\n respawn_timer: 15\n skill: woodcutting\n success:\n base: 0.32\n cap: 0.82\n per_level: 0.01\n tools:\n - axe\nname: teak tree\n","new_content":"color: \"B3\"\ngather:\n deplete_timer: 50\n drops:\n - depletes: false\n item_id: teak_logs\n level: 35\n success_message: You get some {B3}teak logs{/}.\n weight: 100\n xp: 85\n fail_message: You swing but get no logs.\n gather_message: You swing your axe at the teak tree...\n nest_chance: 256\n respawn_broadcast: A {name} grows back.\n respawn_timer: 15\n skill: woodcutting\n success:\n base: 0.32\n cap: 0.82\n per_level: 0.01\n tools:\n - axe\nname: teak tree\n","is_delete":false,"is_create":false},{"time":"2026-07-04T23:03:19-04:00","description":"move objects teak_tree to root","file_path":"data/objects/test/teak_tree.yaml","new_file_path":"data/objects/teak_tree.yaml","old_content":"color: \"B3\"\ngather:\n deplete_timer: 50\n drops:\n - depletes: false\n item_id: teak_logs\n level: 35\n success_message: You get some {B3}teak logs{/}.\n weight: 100\n xp: 85\n fail_message: You swing but get no logs.\n gather_message: You swing your axe at the teak tree...\n nest_chance: 256\n respawn_broadcast: A {name} grows back.\n respawn_timer: 15\n skill: woodcutting\n success:\n base: 0.32\n cap: 0.82\n per_level: 0.01\n tools:\n - axe\nname: teak tree\n","new_content":"color: \"B3\"\ngather:\n deplete_timer: 50\n drops:\n - depletes: false\n item_id: teak_logs\n level: 35\n success_message: You get some {B3}teak logs{/}.\n weight: 100\n xp: 85\n fail_message: You swing but get no logs.\n gather_message: You swing your axe at the teak tree...\n nest_chance: 256\n respawn_broadcast: A {name} grows back.\n respawn_timer: 15\n skill: woodcutting\n success:\n base: 0.32\n cap: 0.82\n per_level: 0.01\n tools:\n - axe\nname: teak tree\n","is_delete":false,"is_create":false},{"time":"2026-07-04T23:03:22-04:00","description":"delete directory objects/test","file_path":"data/objects/test","old_content":"","new_content":"","is_delete":true,"is_create":false}],"redo":null}
\ No newline at end of file +{"history":[{"time":"2026-07-05T00:28:41-04:00","description":"Update item adamant_arrow","file_path":"data/items/ammo/adamant_arrow.yaml","old_content":"color: \"78\"\ncraft:\n - end_message: \"\"\n fail: \"\"\n ingredients:\n - items:\n - headless_arrow\n quantity: 15\n - items:\n - adamant_arrowtips\n quantity: 15\n level: 60\n output_qty: 15\n start_message: \"\"\n station: []\n subtype: \"\"\n success_message: \"\"\n ticks_per_cycle: 2\n tool: \"\"\n type: fletching\n xp: 10\ndescription: An adamant-tipped arrow.\nequipment:\n other:\n ranged_strength: 31\n slot: ammo\n type: ammo\nid: adamant_arrow\nmax_quality: 0\nname: adamant arrow\nquality: 0\nrecoverable: true\nstackable: true\nvalue: 20\n","new_content":"color: \"78\"\ncraft:\n - end_message: \"\"\n fail: \"\"\n ingredients:\n - items:\n - headless_arrow\n quantity: 15\n - items:\n - adamant_arrowtips\n quantity: 15\n level: 60\n output_qty: 15\n start_message: \"\"\n station: []\n subtype: \"\"\n success_message: \"\"\n ticks_per_cycle: 2\n tool: \"\"\n type: fletching\n xp: 10\ndescription: An adamant-tipped arrow.\nequipment:\n other:\n ranged_strength: 31\n slot: ammo\n type: ammo\nid: adamant_arrow\nmax_quality: 0\nname: adamant arrow\nquality: 0\nrecoverable: true\nstackable: true\nvalue: 20\n","is_delete":false,"is_create":false}],"redo":null}
\ No newline at end of file diff --git a/data/items/ammo/adamant_arrow.yaml b/data/items/ammo/adamant_arrow.yaml index 9cacf2f..4c732ce 100644 --- a/data/items/ammo/adamant_arrow.yaml +++ b/data/items/ammo/adamant_arrow.yaml @@ -22,13 +22,12 @@ craft: description: An adamant-tipped arrow. equipment: other: - ranged: 31 + ranged_strength: 31 slot: ammo type: ammo id: adamant_arrow max_quality: 0 name: adamant arrow -quality: 0 recoverable: true stackable: true value: 20 diff --git a/data/items/ammo/adamant_bolts.yaml b/data/items/ammo/adamant_bolts.yaml index 90476ad..587825d 100644 --- a/data/items/ammo/adamant_bolts.yaml +++ b/data/items/ammo/adamant_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Adamant crossbow bolts. equipment: other: - ranged: 43 + ranged_strength: 43 slot: ammo type: ammo name: adamant bolts diff --git a/data/items/ammo/bronze_arrow.yaml b/data/items/ammo/bronze_arrow.yaml index 855b141..16b109a 100644 --- a/data/items/ammo/bronze_arrow.yaml +++ b/data/items/ammo/bronze_arrow.yaml @@ -15,7 +15,7 @@ craft: description: A bronze-tipped arrow. equipment: other: - ranged: 7 + ranged_strength: 7 slot: ammo type: ammo name: bronze arrow diff --git a/data/items/ammo/bronze_bolts.yaml b/data/items/ammo/bronze_bolts.yaml index ab04b3e..6d293b5 100644 --- a/data/items/ammo/bronze_bolts.yaml +++ b/data/items/ammo/bronze_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Bronze crossbow bolts. equipment: other: - ranged: 10 + ranged_strength: 10 slot: ammo type: ammo name: bronze bolts diff --git a/data/items/ammo/diamond_bolts.yaml b/data/items/ammo/diamond_bolts.yaml index bff23ac..5c8e2a3 100644 --- a/data/items/ammo/diamond_bolts.yaml +++ b/data/items/ammo/diamond_bolts.yaml @@ -14,8 +14,8 @@ craft: xp: 7 description: Bolts tipped with diamond. Can be enchanted via science. equipment: - attack: - ranged: 9 + other: + ranged_strength: 9 slot: ammo type: ammo name: diamond bolts diff --git a/data/items/ammo/diamond_bolts_e.yaml b/data/items/ammo/diamond_bolts_e.yaml index 86209f2..42e4493 100644 --- a/data/items/ammo/diamond_bolts_e.yaml +++ b/data/items/ammo/diamond_bolts_e.yaml @@ -1,8 +1,8 @@ color: FF bold description: Enchanted diamond-tipped bolts. Have a chance to ignore the target's defense. equipment: - attack: - ranged: 10 + other: + ranged_strength: 10 slot: ammo type: ammo name: diamond bolts e diff --git a/data/items/ammo/emerald_bolts.yaml b/data/items/ammo/emerald_bolts.yaml index 4f1a123..a07a5be 100644 --- a/data/items/ammo/emerald_bolts.yaml +++ b/data/items/ammo/emerald_bolts.yaml @@ -14,8 +14,8 @@ craft: xp: 5 description: Bolts tipped with emerald. Can be enchanted via science. equipment: - attack: - ranged: 5 + other: + ranged_strength: 5 slot: ammo type: ammo name: emerald bolts diff --git a/data/items/ammo/emerald_bolts_e.yaml b/data/items/ammo/emerald_bolts_e.yaml index 59f3883..5440c25 100644 --- a/data/items/ammo/emerald_bolts_e.yaml +++ b/data/items/ammo/emerald_bolts_e.yaml @@ -1,8 +1,8 @@ color: 22 bold description: Enchanted emerald-tipped bolts. Have a chance to poison the target. equipment: - attack: - ranged: 6 + other: + ranged_strength: 6 slot: ammo type: ammo name: emerald bolts e diff --git a/data/items/ammo/iron_arrow.yaml b/data/items/ammo/iron_arrow.yaml index baaa64e..f739521 100644 --- a/data/items/ammo/iron_arrow.yaml +++ b/data/items/ammo/iron_arrow.yaml @@ -15,7 +15,7 @@ craft: description: An iron-tipped arrow. equipment: other: - ranged: 10 + ranged_strength: 10 slot: ammo type: ammo name: iron arrow diff --git a/data/items/ammo/iron_bolts.yaml b/data/items/ammo/iron_bolts.yaml index 2b93900..f939c80 100644 --- a/data/items/ammo/iron_bolts.yaml +++ b/data/items/ammo/iron_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Iron crossbow bolts. equipment: other: - ranged: 17 + ranged_strength: 17 slot: ammo type: ammo name: iron bolts diff --git a/data/items/ammo/mithril_arrow.yaml b/data/items/ammo/mithril_arrow.yaml index 8e37103..9dd438a 100644 --- a/data/items/ammo/mithril_arrow.yaml +++ b/data/items/ammo/mithril_arrow.yaml @@ -15,7 +15,7 @@ craft: description: A mithril-tipped arrow. equipment: other: - ranged: 22 + ranged_strength: 22 slot: ammo type: ammo name: mithril arrow diff --git a/data/items/ammo/mithril_bolts.yaml b/data/items/ammo/mithril_bolts.yaml index d21d769..b443ba5 100644 --- a/data/items/ammo/mithril_bolts.yaml +++ b/data/items/ammo/mithril_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Mithril crossbow bolts. equipment: other: - ranged: 32 + ranged_strength: 32 slot: ammo type: ammo name: mithril bolts diff --git a/data/items/ammo/ruby_bolts.yaml b/data/items/ammo/ruby_bolts.yaml index f539b4f..8862503 100644 --- a/data/items/ammo/ruby_bolts.yaml +++ b/data/items/ammo/ruby_bolts.yaml @@ -14,8 +14,8 @@ craft: xp: 6 description: Bolts tipped with ruby. Can be enchanted via science. equipment: - attack: - ranged: 7 + other: + ranged_strength: 7 slot: ammo type: ammo name: ruby bolts diff --git a/data/items/ammo/ruby_bolts_e.yaml b/data/items/ammo/ruby_bolts_e.yaml index 618cf62..b66b0b2 100644 --- a/data/items/ammo/ruby_bolts_e.yaml +++ b/data/items/ammo/ruby_bolts_e.yaml @@ -1,8 +1,8 @@ color: C4 bold description: Enchanted ruby-tipped bolts. Have a chance to deal extra damage based on the target's remaining HP. equipment: - attack: - ranged: 8 + other: + ranged_strength: 8 slot: ammo type: ammo name: ruby bolts e diff --git a/data/items/ammo/rune_arrow.yaml b/data/items/ammo/rune_arrow.yaml index 3db9665..bd663f6 100644 --- a/data/items/ammo/rune_arrow.yaml +++ b/data/items/ammo/rune_arrow.yaml @@ -15,7 +15,7 @@ craft: description: A rune-tipped arrow. equipment: other: - ranged: 49 + ranged_strength: 49 slot: ammo type: ammo name: rune arrow diff --git a/data/items/ammo/rune_bolts.yaml b/data/items/ammo/rune_bolts.yaml index 5875f05..5022d80 100644 --- a/data/items/ammo/rune_bolts.yaml +++ b/data/items/ammo/rune_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Rune crossbow bolts. equipment: other: - ranged: 55 + ranged_strength: 55 slot: ammo type: ammo name: rune bolts diff --git a/data/items/ammo/sapphire_bolts.yaml b/data/items/ammo/sapphire_bolts.yaml index 893ddc3..5b6b698 100644 --- a/data/items/ammo/sapphire_bolts.yaml +++ b/data/items/ammo/sapphire_bolts.yaml @@ -14,8 +14,8 @@ craft: xp: 4 description: Bolts tipped with sapphire. Can be enchanted via science. equipment: - attack: - ranged: 3 + other: + ranged_strength: 3 slot: ammo type: ammo name: sapphire bolts diff --git a/data/items/ammo/sapphire_bolts_e.yaml b/data/items/ammo/sapphire_bolts_e.yaml index 07694ea..aa28211 100644 --- a/data/items/ammo/sapphire_bolts_e.yaml +++ b/data/items/ammo/sapphire_bolts_e.yaml @@ -1,8 +1,8 @@ color: 27 bold description: Enchanted sapphire-tipped bolts. Have a chance to drain the target's Science level. equipment: - attack: - ranged: 4 + other: + ranged_strength: 4 slot: ammo type: ammo name: sapphire bolts e diff --git a/data/items/ammo/steel_arrow.yaml b/data/items/ammo/steel_arrow.yaml index d1094ad..8a6063d 100644 --- a/data/items/ammo/steel_arrow.yaml +++ b/data/items/ammo/steel_arrow.yaml @@ -15,7 +15,7 @@ craft: description: A steel-tipped arrow. equipment: other: - ranged: 16 + ranged_strength: 16 slot: ammo type: ammo name: steel arrow diff --git a/data/items/ammo/steel_bolts.yaml b/data/items/ammo/steel_bolts.yaml index ab42ad7..2d0bc5c 100644 --- a/data/items/ammo/steel_bolts.yaml +++ b/data/items/ammo/steel_bolts.yaml @@ -15,7 +15,7 @@ craft: description: Steel crossbow bolts. equipment: other: - ranged: 25 + ranged_strength: 25 slot: ammo type: ammo name: steel bolts diff --git a/data/items/consumables/bread_dough.yaml b/data/items/consumables/bread_dough.yaml index 9909c58..8b9405e 100644 --- a/data/items/consumables/bread_dough.yaml +++ b/data/items/consumables/bread_dough.yaml @@ -29,6 +29,5 @@ description: A ball of uncooked bread dough. It needs to be baked. id: bread_dough max_quality: 0 name: bread dough -quality: 0 stackable: false value: 3 diff --git a/data/items/tools/lighter.yaml b/data/items/tools/lighter.yaml index 9f47a45..ee73da3 100644 --- a/data/items/tools/lighter.yaml +++ b/data/items/tools/lighter.yaml @@ -5,7 +5,6 @@ equipment: type: tool max_quality: 100 name: lighter -quality: 100 stackable: false tool: speed: 4 diff --git a/internal/admin/api_tools.go b/internal/admin/api_tools.go new file mode 100644 index 0000000..9ed7ec3 --- /dev/null +++ b/internal/admin/api_tools.go @@ -0,0 +1,31 @@ +package admin + +import ( + "net/http" +) + +func (s *AdminServer) handleTools(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + + items, err := s.itemStore.LoadAll() + if err != nil { + http.Error(w, "failed to load items", http.StatusInternalServerError) + return + } + + seen := map[string]bool{} + var tools []string + for _, def := range items { + if def.Tool != nil && def.Tool.Type != "" { + if !seen[def.Tool.Type] { + seen[def.Tool.Type] = true + tools = append(tools, def.Tool.Type) + } + } + } + + writeJSON(w, tools) +} diff --git a/internal/admin/server.go b/internal/admin/server.go index 04e68a6..53dd5c8 100644 --- a/internal/admin/server.go +++ b/internal/admin/server.go @@ -148,6 +148,7 @@ func NewServer(cfg *config.Config, useTLS bool, accountStore *player.AccountStor apiMux.HandleFunc("/api/players", s.handlePlayers) apiMux.HandleFunc("/api/dashboard", s.handleDashboard) apiMux.HandleFunc("/api/flags", s.handleFlags) + apiMux.HandleFunc("/api/tools", s.handleTools) apiMux.HandleFunc("/api/search", s.handleSearch) apiMux.HandleFunc("/api/undo/state", s.handleUndoState) apiMux.HandleFunc("/api/undo/undo", s.doUndo) diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js index 84090a1..f1450ab 100644 --- a/internal/admin/static/itemeditor.js +++ b/internal/admin/static/itemeditor.js @@ -12,8 +12,7 @@ var SECTIONS = [ ['color', 'Color', 'color', 'B2'], ['value', 'Value', 'number', '0'], ['stackable', 'Stackable', 'checkbox'], - ['quality', 'Quality', 'number', ''], - ['max_quality', 'Max Quality', 'number', ''], + ['max_quality', 'Quality', 'number', ''], ['aliases', 'Aliases', 'text', 'comma separated'], ['description', 'Description', 'textarea', 'A chunk of copper ore.'], ] @@ -57,9 +56,9 @@ var SECTIONS = [ ['station', 'Station', 'select', '|anvil|cooking_range|fire|furnace|pottery_oven|pottery_wheel|spinning_wheel|workbench'], ['tool', 'Tool', 'select', '|pickaxe|axe|fire|chisel|hammer|knife|needle|rake|saw|shears|spade|watering_can|fishing_rod|fly_rod|harpoon|small_net|big_net|lobster_pot'], ['fail', 'Fail Item', 'search', '', '', null, 'items'], - ['success_base', 'Base', 'number', '', 'narrow'], - ['success_per_level', 'Per Lvl', 'number', '', 'narrow'], - ['success_cap', 'Cap', 'number', '', 'narrow'], + ['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'; }], @@ -297,7 +296,7 @@ function renderCoreCard(data) { var h = '<div class="obj-core-row">'; h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1'); h += renderField(sec, sec.fields[1], data, -1); - h += renderField(sec, sec.fields[6], data, -1, null, 'flex:1'); + h += renderField(sec, sec.fields[5], data, -1, null, 'flex:1'); h += '</div>'; h += '<div class="obj-core-row" style="align-items:center">'; h += renderField(sec, sec.fields[2], data, -1); @@ -306,7 +305,6 @@ function renderCoreCard(data) { h += '</div>'; h += '<div style="display:flex;gap:8px;margin-left:auto">'; h += renderField(sec, sec.fields[4], data, -1); - h += renderField(sec, sec.fields[5], data, -1); h += '</div>'; h += '</div>'; var descFid = fieldID(sec, 'description', -1); @@ -337,8 +335,8 @@ var EQUIP_STAT_FIELDS = { ], other: [ ['strength', 'Strength', 'number', '', 'narrow'], - ['ranged', 'Ranged', 'number', '', 'narrow'], - ['science', 'Science', 'number', '', 'narrow'], + ['ranged_strength', 'Rng Str.', 'number', '', 'narrow'], + ['science', 'Sci +%', 'number', '', 'narrow'], ['technology', 'Tech', 'number', '', 'narrow'], ], }; @@ -487,7 +485,8 @@ function renderField(sec, f, data, idx, subPath, optStyle) { var hiddenStyle = ''; if (showIf) { - var sectionData = itemData[cardPath(sec)] || {}; + var p = cardPath(sec); + var sectionData = p ? (itemData[p] || {}) : itemData; if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } @@ -616,6 +615,10 @@ function renderSubtable(sec, st, data) { if (!f) return; if (f[5] && !f[5](item)) return; var val = item[f[0]]; + if (val === undefined && item.success && (f[0] === 'success_base' || f[0] === 'success_per_level' || f[0] === 'success_cap')) { + var nestedKey = f[0] === 'success_base' ? 'base' : (f[0] === 'success_per_level' ? 'per_level' : 'cap'); + val = item.success[nestedKey]; + } h += renderSubFieldHTML(sec, st.key, idx, f, val); }); } @@ -626,12 +629,12 @@ function renderSubtable(sec, st, data) { h += '<div class="obj-field-note output-qty-note" style="margin:2px 0;padding:2px 8px;font-size:11px;color:#888;font-style:italic;line-height:1.3">' + esc(group.note) + '</div>'; } else if (group.label) { if (group.label === 'Success') { - var hasSuccess = !!(item.fail || (item.success && (item.success.base || item.success.per_level || item.success.cap)) || item.success_base || item.success_per_level || item.success_cap); + var hasSuccess = !!(item.success_base || item.success_per_level || item.success_cap || (item.success && (item.success.base || item.success.per_level || item.success.cap))); var gid = 'sg_' + sec.id + '_' + st.key + '_' + idx; h += '<div class="obj-inline-group success-group" id="' + gid + '" style="margin:2px 0;padding:4px 8px' + (hasSuccess ? '' : ';display:none') + '">'; h += '<div style="display:flex;align-items:center;margin-bottom:2px">'; h += '<span class="obj-inline-label" style="margin-bottom:0;flex:1">Success</span>'; - h += '<button class="btn btn-sm btn-danger" style="padding:2px 6px;font-size:10px;min-width:auto" onclick="var g=document.getElementById(\'' + gid + '\');g.style.display=\'none\';var b=document.getElementById(\'' + gid + '_btn\');b.style.display=\'\'">X</button>'; + h += '<button class="btn btn-sm btn-danger" style="padding:2px 6px;font-size:10px;min-width:auto" onclick="var g=document.getElementById(\'' + gid + '\');g.style.display=\'none\';var b=document.getElementById(\'' + gid + '_btn\');b.style.display=\'\';g.querySelectorAll(\'input\').forEach(function(inp){inp.value=\'\'})">X</button>'; h += '</div>'; h += '<div class="obj-sub-row-line">'; renderGroup(group.fields); @@ -1353,6 +1356,8 @@ function saveItem() { delete item.success_cap; if (sBase || sPer || sCap) { item.success = {base: sBase, per_level: sPer, cap: sCap}; + } else { + delete item.success; } } diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index 3f2a875..5153458 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -2,6 +2,7 @@ var objectData = null; var objectID = null; var expandedCards = {}; var cardWidths = {}; +var toolTypes = null; var SECTIONS = [ { @@ -48,7 +49,7 @@ var SECTIONS = [ ['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'; }], ]}, - {label:'Tools', key:'tools', type:'tags'}, + {label:'Tools', key:'tools', type:'tools_checkbox'}, ], subtables: [ {label:'Drops', key:'drops', fields:[ @@ -133,6 +134,7 @@ function initObjectEditor() { editorType = 'objects'; editorFields = []; loadList(); + fetchToolTypes(); if (window.location.hash) loadObject(window.location.hash.substring(1)); } @@ -266,6 +268,10 @@ function renderCard(sec, data) { h += '<div class="obj-inline-group" style="flex:1">'; h += renderTags(sec, il, data); h += '</div>'; + } else if (il.type === 'tools_checkbox') { + h += '<div class="obj-inline-group" style="flex:1">'; + h += renderToolsCheckbox(sec, il, data); + h += '</div>'; } else if (il.type === 'kv') { h += '<div class="obj-inline-group" style="flex:1">'; h += renderKV(sec, il, data); @@ -286,6 +292,8 @@ function renderCard(sec, data) { noRow.forEach(function(il) { if (il.type === 'tags') { h += renderTags(sec, il, data); + } else if (il.type === 'tools_checkbox') { + h += renderToolsCheckbox(sec, il, data); } else if (il.type === 'kv') { h += renderKV(sec, il, data); } else { @@ -518,6 +526,35 @@ function renderKV(sec, il, data) { return h; } +function renderToolsCheckbox(sec, il, data) { + var sectionRoot = data[cardPath(sec)] || {}; + var arr = sectionRoot[il.key] || []; + var choices = toolTypes || []; + var h = '<div class="obj-inline-group">'; + h += '<span class="obj-inline-label">' + esc(il.label) + '</span>'; + h += '<div class="obj-tools-checkboxes" style="display:flex;flex-wrap:wrap;gap:4px 12px;margin-top:4px">'; + choices.forEach(function(t) { + var checked = arr.indexOf(t) >= 0 ? ' checked' : ''; + h += '<label class="obj-field obj-check" style="margin:0;padding:2px 0"><input type="checkbox" class="obj-tool-chk" data-tool="' + escAttr(t) + '"' + checked + '> <span style="font-size:12px">' + esc(t) + '</span></label>'; + }); + h += '</div>'; + if (choices.length === 0) { + h += '<span style="color:#888;font-size:11px">Loading tool types...</span>'; + } + h += '</div>'; + return h; +} + +function fetchToolTypes() { + API.get('/api/tools').then(function(tools) { + toolTypes = tools || []; + renderCurrent(); + }).catch(function() { + toolTypes = []; + renderCurrent(); + }); +} + function toggleCard(id) { expandedCards[id] = expandedCards[id] === false ? true : false; renderCurrent(); @@ -742,6 +779,11 @@ function saveObject() { sectionObj[il.key] = el.value.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s; }); hasValues = true; } + } else if (il.type === 'tools_checkbox') { + var chks = document.querySelectorAll('.obj-tool-chk:checked'); + var tools = []; + chks.forEach(function(c) { tools.push(c.getAttribute('data-tool')); }); + if (tools.length > 0) { sectionObj[il.key] = tools; hasValues = true; } } else if (il.type === 'kv') { var kvEl = document.querySelector('.obj-kv-list[data-sec="' + sec.id + '"][data-key="' + il.key + '"]'); if (kvEl) { diff --git a/internal/game/core_utils.go b/internal/game/core_utils.go index 71d0acb..a07e789 100644 --- a/internal/game/core_utils.go +++ b/internal/game/core_utils.go @@ -144,7 +144,7 @@ func (g *Game) broadcastAction(sess *net.Session, format string, args ...interfa func (g *Game) newInventorySlot(itemID string, qty int) *player.InventorySlot { slot := &player.InventorySlot{ItemID: itemID, Quantity: qty} if def, err := g.ItemStore.Load(itemID); err == nil && def.MaxQuality > 0 { - slot.Quality = def.Quality + slot.Quality = def.MaxQuality slot.MaxQuality = def.MaxQuality } return slot diff --git a/internal/item/item.go b/internal/item/item.go index 77f462a..93f736b 100644 --- a/internal/item/item.go +++ b/internal/item/item.go @@ -57,7 +57,7 @@ type DefenseBonuses struct { type OtherBonuses struct { Strength int `yaml:"strength,omitempty"` - Ranged int `yaml:"ranged,omitempty"` + RangedStrength int `yaml:"ranged_strength,omitempty"` Science int `yaml:"science,omitempty"` Technology int `yaml:"technology,omitempty"` } @@ -91,7 +91,6 @@ type ItemDef struct { Tool *ToolDef `yaml:"tool,omitempty"` Requirements map[string]int `yaml:"requirements,omitempty"` - Quality int `yaml:"quality"` MaxQuality int `yaml:"max_quality"` Craft CraftList `yaml:"craft,omitempty"` @@ -207,7 +206,7 @@ func (d *ItemDef) Stats() ItemStats { } if d.Equipment.Other != nil { s.StrengthBonus = d.Equipment.Other.Strength - s.RangedStrength = d.Equipment.Other.Ranged + s.RangedStrength = d.Equipment.Other.RangedStrength s.ScienceDamage = d.Equipment.Other.Science s.TechnologyBonus = d.Equipment.Other.Technology } |
