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

@@ -1,20 +1,111 @@
package game
// ObjectKind is the category of an item. In C this was the o_type byte,
// which doubled as the character drawn on the map; the port separates the
// category (ObjectKind) from its display character (Glyph).
type ObjectKind int
const (
KindNone ObjectKind = iota
KindPotion
KindScroll
KindFood
KindWeapon
KindArmor
KindRing
KindWand // a "stick": wand or staff
KindAmulet
KindGold
)
// Prompt-filter pseudo-kinds for item selection (rogue.h CALLABLE and
// R_OR_S): they never appear on an Object, only as getItem filters.
const (
KindCallable ObjectKind = -1
KindRingOrStick ObjectKind = -2
)
// kindGlyphs maps each kind to the character Rogue draws for it.
var kindGlyphs = [...]byte{
KindNone: ' ',
KindPotion: Potion,
KindScroll: Scroll,
KindFood: Food,
KindWeapon: Weapon,
KindArmor: Armor,
KindRing: Ring,
KindWand: Stick,
KindAmulet: Amulet,
KindGold: Gold,
}
// Glyph returns the map/display character for this kind of object.
func (k ObjectKind) Glyph() byte {
if k < 0 || int(k) >= len(kindGlyphs) {
return ' '
}
return kindGlyphs[k]
}
// String names the category the way the C type_name() did.
func (k ObjectKind) String() string {
switch k {
case KindPotion:
return "potion"
case KindScroll:
return "scroll"
case KindFood:
return "food"
case KindWeapon:
return "weapon"
case KindArmor:
return "suit of armor"
case KindRing:
return "ring"
case KindWand:
return "wand or staff"
case KindAmulet:
return "amulet"
case KindGold:
return "gold"
case KindRingOrStick:
return "ring, wand or staff"
}
return "bizarre thing"
}
// objectKindForGlyph is the reverse of Glyph: what category of item does a
// map character denote. Returns KindNone for non-item characters.
func objectKindForGlyph(ch byte) ObjectKind {
for k, g := range kindGlyphs {
if g == ch && ObjectKind(k) != KindNone {
return ObjectKind(k)
}
}
return KindNone
}
// MergesInPack reports whether picking up another of this kind merges into
// an existing pack entry's count (rogue.h ISMULT).
func (k ObjectKind) MergesInPack() bool {
return k == KindPotion || k == KindScroll || k == KindFood
}
// Object is the _o arm of the C THING union: anything that can lie on the
// floor or ride in a pack.
type Object struct {
Type byte // what kind of object it is (Potion, Scroll, Weapon, ...)
Pos Coord // where it lives on the screen
Text string // what it says if you read it
Launch int // what you need to launch it (weapon index, -1 none)
PackCh byte // what character it is in the pack
Damage string // damage if used like sword
HurlDmg string // damage if thrown
Count int // count for plural objects
Which int // which object of a type it is
HPlus int // plusses to hit
DPlus int // plusses to damage
Arm int // armor protection — overloaded as in C (see Charges/GoldVal)
Kind ObjectKind // what kind of object it is (o_type)
Pos Coord // where it lives on the screen
Text string // what it says if you read it
Launch WeaponKind // what you need to launch it (noWeapon if none)
PackCh byte // what character it is in the pack
Damage string // damage if used like sword
HurlDmg string // damage if thrown
Count int // count for plural objects
Which int // which object of a type it is (index for the Kind's table)
HPlus int // plusses to hit
DPlus int // plusses to damage
Arm int // armor protection — overloaded as in C (see Charges/GoldVal)
Flags ObjFlags
Group int // group number for this object
Label string // label for object
@@ -23,7 +114,7 @@ type Object struct {
// newObject is list.c new_item() for objects: a zeroed Object with the
// same non-zero defaults the C code relies on.
func newObject() *Object {
return &Object{Launch: -1}
return &Object{Launch: noWeapon}
}
// Charges is the o_charges alias for Arm on wands and staffs.
@@ -38,6 +129,24 @@ func (o *Object) GoldVal() int { return o.Arm }
// SetGoldVal stores a gold value in the overloaded Arm field.
func (o *Object) SetGoldVal(n int) { o.Arm = n }
// PotionKind returns Which as a potion kind; valid only for KindPotion.
func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) }
// ScrollKind returns Which as a scroll kind; valid only for KindScroll.
func (o *Object) ScrollKind() ScrollKind { return ScrollKind(o.Which) }
// RingKind returns Which as a ring kind; valid only for KindRing.
func (o *Object) RingKind() RingKind { return RingKind(o.Which) }
// WandKind returns Which as a wand kind; valid only for KindWand.
func (o *Object) WandKind() WandKind { return WandKind(o.Which) }
// WeaponKind returns Which as a weapon kind; valid only for KindWeapon.
func (o *Object) WeaponKind() WeaponKind { return WeaponKind(o.Which) }
// ArmorKind returns Which as an armor kind; valid only for KindArmor.
func (o *Object) ArmorKind() ArmorKind { return ArmorKind(o.Which) }
// attachObj is list.c attach(): push item onto the front of a list.
func attachObj(list *[]*Object, item *Object) {
*list = append([]*Object{item}, *list...)