Files
rgoue/game/types.go
sneak 2eff377a73 Un-overload Object fields and pre-parse damage dice (refactor step 3)
Object.Arm carried four meanings in C (o_arm/o_charges/o_goldval plus
ring bonuses); it is now four fields: ArmorClass, Charges, GoldValue,
and Bonus. Stats.Arm becomes Stats.ArmorClass. The Charges()/GoldVal()
accessor pair is gone.

Damage dice strings ("1x4/1x2") are parsed once into DiceSpec — at
table definition for the bestiary and weapon tables, at creation for
items — instead of re-parsed with atoi on every swing as fight.c
roll_em did. ParseDice preserves the C parse semantics exactly,
including the junk-tolerant "%%%x0" bestiary placeholder and the
"000x0" flytrap reset (regression-tested in dice_test.go); the
flytrap's growing grip becomes DiceSpec{{VfHit, 1}}.

Save format bumps to 5.4.4-go3 (field renames and retypes would
silently zero under gob's match-by-name decoding).

No behavior change; full suite green.
2026-07-06 23:07:57 +02:00

444 lines
12 KiB
Go

// Package game is a Go port of Rogue 5.4.4 ("Exploring the Dungeons of
// Doom", Michael Toy, Ken Arnold and Glenn Wichman, 1980-1999).
//
// The port is function-by-function faithful to the C sources kept in the
// repository root; ARCHITECTURE.md documents both the original program and
// the design of this port. All formerly-global state lives on the RogueGame
// type.
package game
// Maximum number of different things (rogue.h)
const (
MaxRooms = 9
MaxThings = 9
MaxObj = 9
MaxPack = 23
MaxTraps = 10
AmuletLevel = 26
NumThings = 7 // number of types of things
MaxPass = 13 // upper limit on number of passages
NumLines = 24
NumCols = 80
StatLine = NumLines - 1
BoreLevel = 50
MaxStr = 1024 // maximum length of strings
MaxLines = 32 // maximum number of screen lines used
MaxCols = 80 // maximum number of screen columns used
)
// Return values for get functions (rogue.h)
const (
Norm = 0 // normal exit
Quit = 1 // quit option setting
Minus = 2 // back up one option
)
// Inventory types (rogue.h)
const (
InvOver = 0
InvSlow = 1
InvClear = 2
)
// Things that appear on the screens (rogue.h). These byte values serve both
// as map characters and as Object.Type discriminators, exactly as in C.
const (
Passage byte = '#'
Door byte = '+'
Floor byte = '.'
PlayerCh byte = '@'
Trap byte = '^'
Stairs byte = '%'
Gold byte = '*'
Potion byte = '!'
Scroll byte = '?'
Magic byte = '$'
Food byte = ':'
Weapon byte = ')'
Armor byte = ']'
Amulet byte = ','
Ring byte = '='
Stick byte = '/'
)
// Various constants (rogue.h)
const (
HealTime = 30
HuhDuration = 20
SeeDuration = 850
HungerTime = 1300
MoreTime = 150
StomachSize = 2000
StarveTime = 850
Escape = 27
Left = 0
Right = 1
BoltLength = 6
LampDist = 3
MaxDaemons = 20
MaxInp = 50 // options.c: max string entered by the user
MaxNameLen = 40 // init.c MAXNAME: max chars in a generated scroll name
)
// Save against things (rogue.h)
const (
VsPoison = 0
VsParalyzation = 0
VsDeath = 0
VsBreath = 2
VsMagic = 3
)
// Flags for rooms (rogue.h)
type RoomFlags int16
const (
Dark RoomFlags = 1 << iota // room is dark
Gone // room is gone (a corridor)
Maze // room is a maze
)
func (f RoomFlags) Has(b RoomFlags) bool { return f&b != 0 }
func (f *RoomFlags) Set(b RoomFlags) { *f |= b }
func (f *RoomFlags) Clear(b RoomFlags) { *f &^= b }
// Flags for objects (rogue.h)
type ObjFlags int32
const (
Cursed ObjFlags = 1 << iota // ISCURSED: object is cursed
Known // ISKNOW: player knows details about the object
Missile // ISMISL: object is a missile type
Stackable // ISMANY: object comes in groups
WasFound // ISFOUND (objects): object has been seen (ISFOUND shares the bit with creatures)
Protected // ISPROT: armor is permanently protected
)
func (f ObjFlags) Has(b ObjFlags) bool { return f&b != 0 }
func (f *ObjFlags) Set(b ObjFlags) { *f |= b }
func (f *ObjFlags) Clear(b ObjFlags) { *f &^= b }
// Flags for creatures (rogue.h). The C bit collisions are deliberate and
// preserved: one name of each pair applies to monsters, the other to the
// hero, and they never coexist on one creature.
type CreatureFlags int32
const (
CanConfuse CreatureFlags = 0o000001 // CANHUH: creature can confuse
CanSeeInvisible CreatureFlags = 0o000002 // CANSEE: creature can see invisible creatures
Blind CreatureFlags = 0o000004 // ISBLIND: creature is blind
Cancelled CreatureFlags = 0o000010 // ISCANC: creature has special qualities cancelled
Levitating CreatureFlags = 0o000010 // ISLEVIT: hero is levitating
Found CreatureFlags = 0o000020 // ISFOUND: creature has been seen
Greedy CreatureFlags = 0o000040 // ISGREED: creature runs to protect gold
Hasted CreatureFlags = 0o000100 // ISHASTE: creature has been hastened
Targeted CreatureFlags = 0o000200 // ISTARGET: creature is the target of an 'f' command
Held CreatureFlags = 0o000400 // ISHELD: creature has been held
Confused CreatureFlags = 0o001000 // ISHUH: creature is confused
Invisible CreatureFlags = 0o002000 // ISINVIS: creature is invisible
Mean CreatureFlags = 0o004000 // ISMEAN: creature can wake when player enters room
Hallucinating CreatureFlags = 0o004000 // ISHALU: hero is on acid trip
Regenerates CreatureFlags = 0o010000 // ISREGEN: creature can regenerate
Awake CreatureFlags = 0o020000 // ISRUN: creature is running at the player
SenseMonsters CreatureFlags = 0o040000 // SEEMONST: hero can detect unseen monsters
Flying CreatureFlags = 0o040000 // ISFLY: creature can fly
Slowed CreatureFlags = 0o100000 // ISSLOW: creature has been slowed
)
func (f CreatureFlags) Has(b CreatureFlags) bool { return f&b != 0 }
func (f *CreatureFlags) Set(b CreatureFlags) { *f |= b }
func (f *CreatureFlags) Clear(b CreatureFlags) { *f &^= b }
// Flags for the level map (rogue.h)
type PlaceFlags uint8
const (
FPassage PlaceFlags = 0x80 // F_PASS: is a passageway
FSeen PlaceFlags = 0x40 // have seen this spot before
FDropped PlaceFlags = 0x20 // object was dropped here
FLocked PlaceFlags = 0x20 // door is locked
FReal PlaceFlags = 0x10 // what you see is what you get
FPassNum PlaceFlags = 0x0f // F_PNUM: passage number mask
FTrapMask PlaceFlags = 0x07 // F_TMASK: trap number mask
)
func (f PlaceFlags) Has(b PlaceFlags) bool { return f&b != 0 }
func (f *PlaceFlags) Set(b PlaceFlags) { *f |= b }
func (f *PlaceFlags) Clear(b PlaceFlags) { *f &^= b }
// TrapKind identifies a trap (rogue.h trap types). The kind is stored in
// the low bits of a map cell's PlaceFlags (FTrapMask).
type TrapKind int
const (
TrapDoor TrapKind = 0
TrapArrow TrapKind = 1
TrapSleep TrapKind = 2
TrapBear TrapKind = 3
TrapTeleport TrapKind = 4
TrapDart TrapKind = 5
TrapRust TrapKind = 6
TrapMystery TrapKind = 7
NumTrapTypes = 8
)
// String returns the trap's display name, article included, as the C
// tr_name table had it.
func (t TrapKind) String() string {
if t < 0 || t >= NumTrapTypes {
return "a bizarre trap"
}
return trName[t]
}
// PotionKind identifies a potion (rogue.h potion types).
type PotionKind int
// Potion kinds (rogue.h)
const (
PotionConfusion PotionKind = iota
PotionLSD
PotionPoison
PotionGainStrength
PotionSeeInvisible
PotionHealing
PotionDetectMonsters
PotionDetectMagic
PotionRaiseLevel
PotionExtraHealing
PotionHaste
PotionRestoreStrength
PotionBlindness
PotionLevitation
NumPotionTypes
)
// String returns the potion's true name ("healing", "haste self", ...).
func (p PotionKind) String() string {
if p < 0 || p >= NumPotionTypes {
return "strange potion"
}
return basePotInfo[p].Name
}
// ScrollKind identifies a scroll (rogue.h scroll types).
type ScrollKind int
// Scroll kinds (rogue.h)
const (
ScrollMonsterConfusion ScrollKind = iota
ScrollMagicMapping
ScrollHoldMonster
ScrollSleep
ScrollEnchantArmor
ScrollIdentifyPotion
ScrollIdentifyScroll
ScrollIdentifyWeapon
ScrollIdentifyArmor
ScrollIdentifyRingOrStick
ScrollScareMonster
ScrollFoodDetection
ScrollTeleportation
ScrollEnchantWeapon
ScrollCreateMonster
ScrollRemoveCurse
ScrollAggravateMonsters
ScrollProtectArmor
NumScrollTypes
)
// String returns the scroll's true name ("magic mapping", ...).
func (s ScrollKind) String() string {
if s < 0 || s >= NumScrollTypes {
return "strange scroll"
}
return baseScrInfo[s].Name
}
// WeaponKind identifies a weapon (rogue.h weapon types).
type WeaponKind int
// Weapon kinds (rogue.h)
const (
WeaponMace WeaponKind = iota
WeaponLongSword
WeaponBow
WeaponArrow
WeaponDagger
WeaponTwoHandedSword
WeaponDart
WeaponShuriken
WeaponSpear
WeaponFlame // fake entry for dragon breath (ick)
NumWeaponTypes = WeaponFlame
)
// String returns the weapon's name ("mace", "two handed sword", ...).
func (w WeaponKind) String() string {
if w < 0 || w > WeaponFlame {
return "strange weapon"
}
return baseWeapInfo[w].Name
}
// ArmorKind identifies a suit of armor (rogue.h armor types).
type ArmorKind int
// Armor kinds (rogue.h)
const (
ArmorLeather ArmorKind = iota
ArmorRingMail
ArmorStuddedLeather
ArmorScaleMail
ArmorChainMail
ArmorSplintMail
ArmorBandedMail
ArmorPlateMail
NumArmorTypes
)
// String returns the armor's name ("ring mail", "plate mail", ...).
func (a ArmorKind) String() string {
if a < 0 || a >= NumArmorTypes {
return "strange armor"
}
return baseArmInfo[a].Name
}
// RingKind identifies a ring (rogue.h ring types).
type RingKind int
// Ring kinds (rogue.h)
const (
RingProtection RingKind = iota
RingAddStrength
RingSustainStrength
RingSearching
RingSeeInvisible
RingAdornment
RingAggravateMonsters
RingDexterity
RingIncreaseDamage
RingRegeneration
RingSlowDigestion
RingTeleportation
RingStealth
RingMaintainArmor
NumRingTypes
)
// String returns the ring's true name ("add strength", "stealth", ...).
func (r RingKind) String() string {
if r < 0 || r >= NumRingTypes {
return "strange ring"
}
return baseRingInfo[r].Name
}
// WandKind identifies a wand or staff (rogue.h rod/wand/staff types).
type WandKind int
// Wand kinds (rogue.h)
const (
WandLight WandKind = iota
WandInvisibility
WandLightning
WandFire
WandCold
WandPolymorph
WandMagicMissile
WandHasteMonster
WandSlowMonster
WandDrainLife
WandNothing
WandTeleportAway
WandTeleportTo
WandCancellation
NumWandTypes
)
// String returns the wand/staff's true name ("lightning", ...).
func (w WandKind) String() string {
if w < 0 || w >= NumWandTypes {
return "strange stick"
}
return baseWsInfo[w].Name
}
// Coord is a position on the level (rogue.h coord). A value type: the C
// ce(a,b) macro is plain == here.
type Coord struct {
X, Y int
}
// Stats describes a fighting being (rogue.h struct stats).
type Stats struct {
Str int // strength (s_str; 3..31)
Exp int // experience
Lvl int // level of mastery
ArmorClass int // armor class (s_arm)
HP int // hit points (s_hpt)
Dmg DiceSpec // damage dice, e.g. "1x4/1x2"
MaxHP int
}
// Room describes a room or passage network (rogue.h struct room).
type Room struct {
Pos Coord // upper left corner
Max Coord // size of room
Gold Coord // where the gold is
GoldVal int // how much the gold is worth
Flags RoomFlags
Exits []Coord // where the exits are (r_exit[12]/r_nexits)
}
// MonsterKind is a bestiary entry (rogue.h struct monster).
type MonsterKind struct {
Name string // what to call the monster
Carry int // probability of carrying something
Flags CreatureFlags
Stats Stats // initial stats
}
// ObjInfo describes one object class: its real name, generation
// probability, score worth, and per-game identification state
// (rogue.h struct obj_info).
type ObjInfo struct {
Name string
Prob int
Worth int
Guess string
Know bool
}
// Stone is a ring stone with its worth (rogue.h STONE).
type Stone struct {
Name string
Value int
}
// CTRL maps a letter to its control character, as the C CTRL() macro.
func CTRL(c byte) byte { return c & 0o37 }
// distance returns the squared distance between two points (chase.c dist).
func distance(y1, x1, y2, x2 int) int {
dx := x2 - x1
dy := y2 - y1
return dx*dx + dy*dy
}
// distCp is dist_cp from chase.c.
func distCp(c1, c2 Coord) int { return distance(c1.Y, c1.X, c2.Y, c2.X) }
// sign is misc.c sign(): -1, 0 or 1.
func sign(nm int) int {
switch {
case nm < 0:
return -1
case nm > 0:
return 1
}
return 0
}