Files
rgoue/game/types.go
sneak 5ba9fe8f66 Adopt house golangci-lint config; fix all approved-linter findings
.golangci.yml is the prompts-repo standard verbatim plus one approved
exception (paralleltest, sneak 2026-07-06). Roughly 1,500 findings
fixed:

- autofix sweep (wsl_v5/nlreturn/intrange/modernize formatting), with
  the misspell autofix REVERTED where it rewrote authentic C game text
  ("missle vanishes" stays, with nolint and a comment)
- error handling: errcheck sites get real handling — saveFile now
  closes explicitly and removes corrupt saves, scoreboard writes are
  documented best-effort, err113 sentinel errors (ErrSaveOutOfDate,
  ErrScreenTooSmall); panics allowed for unrecoverable states per
  MEMORY.md policy
- gosec: real fixes (ParseInt for SEED, 0600 scorefile) and justified
  per-line nolints for provably-bounded conversions (randomMonsterLetter
  helper collapses eight rnd(26)+'A' sites)
- API tidying from linters: pointer receivers on flag types
  (recvcheck), msg helpers renamed addmsgf/doaddf/Printwf/MvPrintwf
  (goprintffuncname), myExit()/findFloor(monst)/mkGameInput(t) drop
  always-constant params (unparam), gameEnd carries no status
- gocritic/staticcheck: if-else chains to switches, the pack.c
  inventory filter untangled into matchesFilter, main.go split so
  defers run before exit (exitAfterDefer, funlen)
- revive doc comments on all exported flag types/consts/methods

Remaining findings are confined to linters pending sneak's exception
decision (mnd, gochecknoglobals, cyclop, nestif, gocognit, exhaustive,
goconst, testpackage); TODO.md records the state. MEMORY.md added at
repo root as the project's agent working notes.
2026-07-07 00:03:45 +02:00

483 lines
13 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
)
// RoomFlags are the room state bits (rogue.h room flags).
type RoomFlags int16
// Room state bits (rogue.h ISDARK/ISGONE/ISMAZE).
const (
Dark RoomFlags = 1 << iota // room is dark
Gone // room is gone (a corridor)
Maze // room is a maze
)
// Has reports whether any of the given bits are set.
func (f *RoomFlags) Has(b RoomFlags) bool { return *f&b != 0 }
// Set turns the given bits on.
func (f *RoomFlags) Set(b RoomFlags) { *f |= b }
// Clear turns the given bits off.
func (f *RoomFlags) Clear(b RoomFlags) { *f &^= b }
// ObjFlags are the object state bits (rogue.h object flags).
type ObjFlags int32
// Object state bits (rogue.h).
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
)
// Has reports whether any of the given bits are set.
func (f *ObjFlags) Has(b ObjFlags) bool { return *f&b != 0 }
// Set turns the given bits on.
func (f *ObjFlags) Set(b ObjFlags) { *f |= b }
// Clear turns the given bits off.
func (f *ObjFlags) Clear(b ObjFlags) { *f &^= b }
// CreatureFlags are the creature state bits (rogue.h creature flags). 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
// Creature state bits (rogue.h).
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
)
// Has reports whether any of the given bits are set.
func (f *CreatureFlags) Has(b CreatureFlags) bool { return *f&b != 0 }
// Set turns the given bits on.
func (f *CreatureFlags) Set(b CreatureFlags) { *f |= b }
// Clear turns the given bits off.
func (f *CreatureFlags) Clear(b CreatureFlags) { *f &^= b }
// PlaceFlags are the per-map-cell bits (rogue.h level map flags). The low
// bits double as the passage number (FPassNum) or trap kind (FTrapMask).
type PlaceFlags uint8
// Map cell bits (rogue.h).
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
)
// Has reports whether any of the given bits are set.
func (f *PlaceFlags) Has(b PlaceFlags) bool { return *f&b != 0 }
// Set turns the given bits on.
func (f *PlaceFlags) Set(b PlaceFlags) { *f |= b }
// Clear turns the given bits off.
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
// Trap kinds (rogue.h T_* constants).
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 counts the real weapons; the flame pseudo-weapon sits
// just past them in the tables (C's MAXWEAPONS == FLAME).
const 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
}