.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.
483 lines
13 KiB
Go
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
|
|
}
|