adventure: show the item, not just its name and price

Renders what gogobee now sends: descriptions, the engine's own effect
summary, slot and skill tags, and a Worn panel with the bond count. One row
template for all three panels — worn, backpack, vault show the same facts and
only the frame differs.

The one distinction worth the wrapper struct: "inert" is a real problem state
— the item is on you doing nothing because all three bonds are spoken for.
The same item in a backpack isn't inert, it's just not worn yet. Rendering
both the same would invent a problem the player doesn't have, on the exact
panel they'd go to to fix it. An ItemView can't tell you which panel it's in,
so itemRow carries it, and TestWhoInertOnlyWhenWorn pins it.

Effect arrives resolved and Pete renders it as given. If it ever disagrees
with what an item does in a fight, that's a gogobee bug — deriving it here
from tier and slot would just be a second opinion that drifts.

No migration: detail rides as a JSON blob.
This commit is contained in:
prosolis
2026-07-17 06:45:06 -07:00
parent cd84f64a22
commit 4ce025a82c
5 changed files with 157 additions and 25 deletions

View File

@@ -60,6 +60,12 @@ type whoPage struct {
Abilities []abilityRow
HasSelf bool
Self storage.PlayerDetail
// The private panels, wrapped so a row knows where it is sitting. Bond state
// only means something on a worn item — see itemRow.
Worn []itemRow
Backpack []itemRow
VaultRows []itemRow
BondsUsed int
// History. Unlike everything above, these are not a gogobee snapshot — they
// are counted from the facts Pete has been keeping since adventure_events
// landed. HasHistory is false for an adventurer who hasn't done anything since
@@ -71,6 +77,28 @@ type whoPage struct {
MoreHistory bool // the trail was capped; there is older history than this
}
// itemRow is one item plus the one thing the item itself can't tell you: which
// panel it's in. It matters for bond state. An attunement item that is worn
// without a bond is *inert* — on you, doing nothing, and worth shouting about.
// The same item in a backpack isn't inert, it's just not worn yet; gogobee only
// tracks bonds on equipped rows, so its Attuned is undefined rather than false.
// Rendering both as "inert" would invent a problem the player doesn't have.
type itemRow struct {
storage.ItemView
Worn bool
}
func itemRows(items []storage.ItemView, worn bool) []itemRow {
if len(items) == 0 {
return nil
}
out := make([]itemRow, 0, len(items))
for _, it := range items {
out = append(out, itemRow{ItemView: it, Worn: worn})
}
return out
}
// timelineEntry is one line of an adventurer's trail — a fact, rendered short,
// pointing at the dispatch that told it.
type timelineEntry struct {
@@ -150,6 +178,14 @@ func (s *Server) handleAdventureWho(w http.ResponseWriter, r *http.Request) {
if self, ok, err := storage.PlayerDetailByOwner(buyerLocalpart(u), token); err == nil && ok {
page.HasSelf = true
page.Self = self
page.Worn = itemRows(self.Equipped, true)
page.Backpack = itemRows(self.Inventory, false)
page.VaultRows = itemRows(self.Vault, false)
for _, it := range self.Equipped {
if it.Attuned {
page.BondsUsed++
}
}
}
}
}