Give item categories and subtypes real types (refactor step 2)

ObjectKind separates what an item is from how it draws: Object.Type
(a byte that doubled as the map character) becomes Kind ObjectKind,
with Glyph() producing the display character and objectKindForGlyph
converting back at the map boundary. KindWand covers the C 'stick'
category. The magic-missile bolt uses KindGold, matching C's literal
o_type='*' trick, with a comment.

PotionKind, ScrollKind, RingKind, WandKind, WeaponKind, ArmorKind and
TrapKind are now iota enums with Stringer (names come from the base
info tables); Object gains typed accessors (obj.RingKind(), ...) and
Launch is typed WeaponKind. beTrapped returns TrapKind. The
getItem/inventory/whatis prompt filters take ObjectKind, with
KindCallable/KindRingOrStick replacing the C CALLABLE/R_OR_S
sentinels; wizard.c's type_name table is subsumed by
ObjectKind.String(). IsRing/IsWearing/initWeapon/doPot signatures are
typed accordingly.

The save format version becomes 5.4.4-go2: the gob field rename would
otherwise silently zero Kind when reading old saves.

No behavior change; full suite green.
This commit is contained in:
2026-07-06 22:00:43 +02:00
parent 626f8c5a7d
commit b940cfc41f
27 changed files with 426 additions and 259 deletions

View File

@@ -15,7 +15,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
}
// Check for and deal with scare monster scrolls
if obj.Type == Scroll && obj.Which == ScrollScareMonster && obj.Flags.Has(WasFound) {
if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) {
detachObj(&g.Level.Objects, obj)
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(Gone) {
@@ -39,12 +39,12 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
lp := -1
merged := false
for i := 0; i < len(p.Pack); i++ {
if p.Pack[i].Type != obj.Type {
if p.Pack[i].Kind != obj.Kind {
lp = i
continue
}
// found the group of our type: scan for matching subtype
for p.Pack[i].Type == obj.Type && p.Pack[i].Which != obj.Which {
for p.Pack[i].Kind == obj.Kind && p.Pack[i].Which != obj.Which {
lp = i
if i+1 >= len(p.Pack) {
break
@@ -52,8 +52,8 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
i++
}
op := p.Pack[i]
if op.Type == obj.Type && op.Which == obj.Which {
if IsMult(op.Type) {
if op.Kind == obj.Kind && op.Which == obj.Which {
if op.Kind.MergesInPack() {
if !g.packRoom(fromFloor, obj) {
return
}
@@ -63,7 +63,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
merged = true
} else if obj.Group != 0 {
lp = i
for p.Pack[i].Type == obj.Type &&
for p.Pack[i].Kind == obj.Kind &&
p.Pack[i].Which == obj.Which &&
p.Pack[i].Group != obj.Group {
lp = i
@@ -73,7 +73,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
i++
}
op = p.Pack[i]
if op.Type == obj.Type && op.Which == obj.Which &&
if op.Kind == obj.Kind && op.Which == obj.Which &&
op.Group == obj.Group {
op.Count += obj.Count
p.Inpack--
@@ -111,7 +111,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
}
}
if obj.Type == Amulet {
if obj.Kind == KindAmulet {
g.HasAmulet = true
}
// Notify the user
@@ -193,12 +193,14 @@ func (g *RogueGame) packChar() byte {
// inventory lists what is in the pack; returns true if there is something
// of the given type (pack.c inventory).
func (g *RogueGame) inventory(list []*Object, typ int) bool {
func (g *RogueGame) inventory(list []*Object, kind ObjectKind) bool {
g.NObjs = 0
for _, item := range list {
if typ != 0 && typ != int(item.Type) &&
!(typ == Callable && item.Type != Food && item.Type != Amulet) &&
!(typ == RorS && (item.Type == Ring || item.Type == Stick)) {
if kind != KindNone && kind != item.Kind &&
!(kind == KindCallable && item.Kind != KindFood &&
item.Kind != KindAmulet) &&
!(kind == KindRingOrStick &&
(item.Kind == KindRing || item.Kind == KindWand)) {
continue
}
g.NObjs++
@@ -213,13 +215,13 @@ func (g *RogueGame) inventory(list []*Object, typ int) bool {
}
if g.NObjs == 0 {
if g.Options.Terse {
if typ == 0 {
if kind == KindNone {
g.msg("empty handed")
} else {
g.msg("nothing appropriate")
}
} else {
if typ == 0 {
if kind == KindNone {
g.msg("you are empty handed")
} else {
g.msg("you don't have anything appropriate")
@@ -294,7 +296,7 @@ func (g *RogueGame) pickyInven() {
}
// getItem picks something out of a pack for a purpose (pack.c get_item).
func (g *RogueGame) getItem(purpose string, typ int) *Object {
func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
p := &g.Player
if len(p.Pack) == 0 {
g.msg("you aren't carrying anything")
@@ -328,7 +330,7 @@ func (g *RogueGame) getItem(purpose string, typ int) *Object {
g.NObjs = 1 // normal case: person types one char
if ch == '*' {
g.Msgs.Mpos = 0
if !g.inventory(p.Pack, typ) {
if !g.inventory(p.Pack, kind) {
g.After = false
return nil
}