The Go cutover omitted get_character_inventory; the React InventoryWindow
fetches GET /inventory/{name} and got 404 -> empty. Port the endpoint:
per-character items with placement (current_wielded_location/container_id/
items_capacity), mana (current_mana/max_mana from original_json IntValues),
icon overlays, and join-table combat/req/enh/rating stats; material-prefixed
name. Returns {character_name,item_count,items}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
92 lines
3.6 KiB
Go
92 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// GET /inventory/{character_name} — full per-character inventory for the React
|
|
// Inventory window. Port of inventory-service get_character_inventory +
|
|
// enrich_db_item (main.py:2622 / 2338). The Go cutover omitted this endpoint
|
|
// (it was assumed unused), but the React InventoryWindow fetches it, so its
|
|
// absence (404) made the live inventory render empty.
|
|
//
|
|
// Returns {character_name, item_count, items:[...]} with the snake_case fields
|
|
// the frontend normalizeItem consumes: placement via current_wielded_location /
|
|
// container_id / items_capacity, the mana panel via current_mana / max_mana,
|
|
// icon overlays, plus tooltip combat/requirement/enhancement/rating stats. Mana
|
|
// and icon overlays are pulled straight from original_json IntValues (same keys
|
|
// the plugin/search path use); the rest come from the normalized join tables.
|
|
func (s *Server) handleCharacterInventory(w http.ResponseWriter, r *http.Request) {
|
|
name := r.PathValue("character_name")
|
|
limit := clampInt(qIntDefault(r.URL.Query(), "limit", 1000), 1, 5000)
|
|
offset := qIntDefault(r.URL.Query(), "offset", 0)
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
const q = `
|
|
SELECT
|
|
i.item_id, i.name, i.icon, i.object_class, i.value, i.burden,
|
|
i.current_wielded_location, i.container_id, i.items_capacity, i.stack_size,
|
|
cs.max_damage, cs.armor_level, cs.damage_bonus, cs.attack_bonus,
|
|
cs.melee_defense_bonus, cs.magic_defense_bonus,
|
|
r.wield_level, r.skill_level, r.equip_skill, r.lore_requirement,
|
|
e.material, e.imbue, e.item_set, e.tinks, e.workmanship,
|
|
rt.damage_rating, rt.crit_rating, rt.crit_damage_rating, rt.heal_boost_rating,
|
|
NULLIF((rd.original_json->'IntValues'->>'218103815')::int, 0) AS current_mana,
|
|
NULLIF((rd.original_json->'IntValues'->>'218103814')::int, 0) AS max_mana,
|
|
NULLIF((rd.original_json->'IntValues'->>'218103849')::int, 0) AS icon_overlay_id,
|
|
NULLIF((rd.original_json->'IntValues'->>'218103850')::int, 0) AS icon_underlay_id
|
|
FROM items i
|
|
LEFT JOIN item_combat_stats cs ON i.id = cs.item_id
|
|
LEFT JOIN item_requirements r ON i.id = r.item_id
|
|
LEFT JOIN item_enhancements e ON i.id = e.item_id
|
|
LEFT JOIN item_ratings rt ON i.id = rt.item_id
|
|
LEFT JOIN item_raw_data rd ON i.id = rd.item_id
|
|
WHERE i.character_name = $1
|
|
ORDER BY i.name
|
|
LIMIT $2 OFFSET $3`
|
|
|
|
rows, err := queryRowsAsMaps(r.Context(), s.pool, q, name, limit, offset)
|
|
if err != nil {
|
|
s.dbErr(w, "inventory/"+name, err)
|
|
return
|
|
}
|
|
|
|
items := make([]map[string]any, 0, len(rows))
|
|
for _, row := range rows {
|
|
items = append(items, enrichInventoryRow(row))
|
|
}
|
|
|
|
// Unlike the Python endpoint (404 on no rows), always return 200 with a
|
|
// possibly-empty list — the window treats both as empty, and 200 avoids the
|
|
// frontend's catch-all error path.
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"character_name": name,
|
|
"item_count": len(items),
|
|
"items": items,
|
|
})
|
|
}
|
|
|
|
// enrichInventoryRow flattens a joined inventory row into the frontend item
|
|
// shape: drops NULL columns and applies the material-name prefix to the item
|
|
// name (enrich_db_item parity, e.g. "Pyreal" + "Chiran Helm" -> "Pyreal Chiran
|
|
// Helm"), preserving the un-prefixed name in original_name.
|
|
func enrichInventoryRow(row map[string]any) map[string]any {
|
|
out := make(map[string]any, len(row)+2)
|
|
for k, v := range row {
|
|
if v != nil {
|
|
out[k] = v
|
|
}
|
|
}
|
|
if mat, ok := out["material"].(string); ok && mat != "" {
|
|
out["material_name"] = mat
|
|
if name, ok := out["name"].(string); ok && name != "" &&
|
|
!strings.HasPrefix(strings.ToLower(name), strings.ToLower(mat)) {
|
|
out["name"] = mat + " " + name
|
|
out["original_name"] = name
|
|
}
|
|
}
|
|
return out
|
|
}
|