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

@@ -9,9 +9,9 @@ package game
func (g *RogueGame) createObj() {
obj := newObject()
g.msg("type of item: ")
obj.Type = g.readchar()
obj.Kind = objectKindForGlyph(g.readchar())
g.Msgs.Mpos = 0
g.msg("which %c do you want? (0-f)", obj.Type)
g.msg("which %c do you want? (0-f)", obj.Kind.Glyph())
ch := g.readchar()
if isDigit(ch) {
obj.Which = int(ch - '0')
@@ -22,15 +22,15 @@ func (g *RogueGame) createObj() {
obj.Count = 1
g.Msgs.Mpos = 0
switch {
case obj.Type == Weapon || obj.Type == Armor:
case obj.Kind == KindWeapon || obj.Kind == KindArmor:
g.msg("blessing? (+,-,n)")
bless := g.readchar()
g.Msgs.Mpos = 0
if bless == '-' {
obj.Flags.Set(Cursed)
}
if obj.Type == Weapon {
g.initWeapon(obj, obj.Which)
if obj.Kind == KindWeapon {
g.initWeapon(obj, WeaponKind(obj.Which))
if bless == '-' {
obj.HPlus -= g.rnd(3) + 1
}
@@ -46,8 +46,8 @@ func (g *RogueGame) createObj() {
obj.Arm -= g.rnd(3) + 1
}
}
case obj.Type == Ring:
switch obj.Which {
case obj.Kind == KindRing:
switch obj.RingKind() {
case RingProtection, RingAddStrength, RingDexterity, RingIncreaseDamage:
g.msg("blessing? (+,-,n)")
bless := g.readchar()
@@ -61,9 +61,9 @@ func (g *RogueGame) createObj() {
case RingAggravateMonsters, RingTeleportation:
obj.Flags.Set(Cursed)
}
case obj.Type == Stick:
case obj.Kind == KindWand:
g.fixStick(obj)
case obj.Type == Gold:
case obj.Kind == KindGold:
g.msg("how much?")
buf := ""
if g.getStr(&buf, g.scr.Std) == Norm {
@@ -93,7 +93,7 @@ func (g *RogueGame) showMap() {
}
// whatis identifies what a certain object is (wizard.c whatis).
func (g *RogueGame) whatis(insist bool, typ int) {
func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
p := &g.Player
if len(p.Pack) == 0 {
g.msg("you don't have anything in your pack to identify")
@@ -102,7 +102,7 @@ func (g *RogueGame) whatis(insist bool, typ int) {
var obj *Object
for {
obj = g.getItem("identify", typ)
obj = g.getItem("identify", kind)
if !insist {
break
}
@@ -111,9 +111,10 @@ func (g *RogueGame) whatis(insist bool, typ int) {
}
if obj == nil {
g.msg("you must identify something")
} else if typ != 0 && int(obj.Type) != typ &&
!(typ == RorS && (obj.Type == Ring || obj.Type == Stick)) {
g.msg("you must identify a %s", typeName(typ))
} else if kind != KindNone && obj.Kind != kind &&
!(kind == KindRingOrStick &&
(obj.Kind == KindRing || obj.Kind == KindWand)) {
g.msg("you must identify a %s", kind)
} else {
break
}
@@ -123,16 +124,16 @@ func (g *RogueGame) whatis(insist bool, typ int) {
return
}
switch obj.Type {
case Scroll:
switch obj.Kind {
case KindScroll:
setKnow(obj, g.Items.Scrolls[:])
case Potion:
case KindPotion:
setKnow(obj, g.Items.Potions[:])
case Stick:
case KindWand:
setKnow(obj, g.Items.Sticks[:])
case Weapon, Armor:
case KindWeapon, KindArmor:
obj.Flags.Set(Known)
case Ring:
case KindRing:
setKnow(obj, g.Items.Rings[:])
}
g.msg("%s", g.invName(obj, false))
@@ -146,30 +147,8 @@ func setKnow(obj *Object, info []ObjInfo) {
info[obj.Which].Guess = ""
}
// typeNameTable is the wizard.c type_name static tlist.
var typeNameTable = []struct {
ch int
desc string
}{
{int(Potion), "potion"},
{int(Scroll), "scroll"},
{int(Food), "food"},
{RorS, "ring, wand or staff"},
{int(Ring), "ring"},
{int(Stick), "wand or staff"},
{int(Weapon), "weapon"},
{int(Armor), "suit of armor"},
}
// typeName returns the name of an object type (wizard.c type_name).
func typeName(typ int) string {
for _, hp := range typeNameTable {
if typ == hp.ch {
return hp.desc
}
}
return ""
}
// The C type_name()/tlist table is gone: ObjectKind.String() carries the
// same vocabulary.
// teleport bamfs the hero someplace else (wizard.c teleport).
func (g *RogueGame) teleport() {