From 81f3e3bae7d82f0e0ad72d89ea5a5fdae8804712 Mon Sep 17 00:00:00 2001
From: historia <[not public]>
Date: Sun, 5 Jul 2026 00:41:11 -0400
Subject: fix: remove redundant 'quality' on items, fix ranged str on ammo,
item admin ui updates
---
internal/admin/api_tools.go | 31 ++++++++++++++++++++++++
internal/admin/server.go | 1 +
internal/admin/static/itemeditor.js | 29 +++++++++++++----------
internal/admin/static/objecteditor.js | 44 ++++++++++++++++++++++++++++++++++-
internal/game/core_utils.go | 2 +-
internal/item/item.go | 5 ++--
6 files changed, 95 insertions(+), 17 deletions(-)
create mode 100644 internal/admin/api_tools.go
(limited to 'internal')
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 = '
';
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 += '
';
h += '';
h += renderField(sec, sec.fields[2], data, -1);
@@ -306,7 +305,6 @@ function renderCoreCard(data) {
h += '
';
h += '';
h += renderField(sec, sec.fields[4], data, -1);
- h += renderField(sec, sec.fields[5], data, -1);
h += '
';
h += '';
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 += '' + esc(group.note) + '
';
} 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 += '';
h += '
';
h += 'Success ';
- h += 'X ';
+ h += 'X ';
h += '
';
h += '
';
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 += '
';
h += renderTags(sec, il, data);
h += '
';
+ } else if (il.type === 'tools_checkbox') {
+ h += '
';
+ h += renderToolsCheckbox(sec, il, data);
+ h += '
';
} else if (il.type === 'kv') {
h += '
';
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 = '
';
+ h += '
' + esc(il.label) + ' ';
+ h += '
';
+ choices.forEach(function(t) {
+ var checked = arr.indexOf(t) >= 0 ? ' checked' : '';
+ h += ' ' + esc(t) + ' ';
+ });
+ h += '
';
+ if (choices.length === 0) {
+ h += '
Loading tool types... ';
+ }
+ h += '
';
+ 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
}
--
cgit v1.2.3