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

@@ -7,11 +7,11 @@ import "fmt"
// doZap performs a zap with a wand (sticks.c do_zap).
func (g *RogueGame) doZap() {
p := &g.Player
obj := g.getItem("zap with", int(Stick))
obj := g.getItem("zap with", KindWand)
if obj == nil {
return
}
if obj.Type != Stick {
if obj.Kind != KindWand {
g.After = false
g.msg("you can't zap with that!")
return
@@ -20,7 +20,7 @@ func (g *RogueGame) doZap() {
g.msg("nothing happens")
return
}
switch obj.Which {
switch obj.WandKind() {
case WandLight:
// Reddy Kilowatt wand. Light up the room
g.Items.Sticks[WandLight].Know = true
@@ -57,7 +57,7 @@ func (g *RogueGame) doZap() {
if monster == 'F' {
p.Flags.Clear(Held)
}
switch obj.Which {
switch obj.WandKind() {
case WandInvisibility:
tp.Flags.Set(Invisible)
if g.cansee(y, x) {
@@ -91,7 +91,7 @@ func (g *RogueGame) doZap() {
}
case WandTeleportAway, WandTeleportTo:
var newPos Coord
if obj.Which == WandTeleportAway {
if obj.WandKind() == WandTeleportAway {
for {
newPos, _ = g.findFloor(nil, 0, true)
if newPos != p.Pos {
@@ -110,13 +110,13 @@ func (g *RogueGame) doZap() {
case WandMagicMissile:
g.Items.Sticks[WandMagicMissile].Know = true
bolt := newObject()
bolt.Type = '*'
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
bolt.HurlDmg = "1x4"
bolt.HPlus = 100
bolt.DPlus = 1
bolt.Flags = Missile
if p.CurWeapon != nil {
bolt.Launch = p.CurWeapon.Which
bolt.Launch = WeaponKind(p.CurWeapon.Which)
}
g.doMotion(bolt, g.Delta.Y, g.Delta.X)
if tp := g.Level.MonsterAt(bolt.Pos.Y, bolt.Pos.X); tp != nil &&
@@ -135,7 +135,7 @@ func (g *RogueGame) doZap() {
x += g.Delta.X
}
if tp := g.Level.MonsterAt(y, x); tp != nil {
if obj.Which == WandHasteMonster {
if obj.WandKind() == WandHasteMonster {
if tp.On(Slowed) {
tp.Flags.Clear(Slowed)
} else {
@@ -155,7 +155,7 @@ func (g *RogueGame) doZap() {
}
case WandLightning, WandFire, WandCold:
var name string
switch obj.Which {
switch obj.WandKind() {
case WandLightning:
name = "bolt"
case WandFire:
@@ -211,8 +211,8 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
fromHero := start == p.Pos
bolt := newObject()
bolt.Type = Weapon
bolt.Which = WeaponFlame
bolt.Kind = KindWeapon
bolt.Which = int(WeaponFlame)
bolt.HurlDmg = "6x6"
bolt.HPlus = 100
bolt.DPlus = 0
@@ -328,7 +328,7 @@ func (g *RogueGame) fixStick(cur *Object) {
}
cur.HurlDmg = "1x1"
switch cur.Which {
switch cur.WandKind() {
case WandLight:
cur.SetCharges(g.rnd(10) + 10)
default: