package game // ItemLore is the per-game item identity state: the randomized appearance // names and the seven mutable ObjInfo tables (extern.c/init.c). type ItemLore struct { PotColors [NumPotionTypes]string // p_colors: colors of the potions ScrNames [NumScrollTypes]string // s_names: names of the scrolls RingStones [NumRingTypes]string // r_stones: stone settings of the rings WandMade [NumWandTypes]string // ws_made: what sticks are made of WandType [NumWandTypes]string // ws_type: "wand" or "staff" Things [NumThings]ObjInfo Potions [NumPotionTypes]ObjInfo Scrolls [NumScrollTypes]ObjInfo Rings [NumRingTypes]ObjInfo Sticks [NumWandTypes]ObjInfo Weapons [NumWeaponTypes + 1]ObjInfo Armors [NumArmorTypes]ObjInfo Group int // group number for the next stack of missiles (weapons.c `group`) } // Options are the user-settable game options (options.c optlist). type Options struct { Terse bool // terse: shorter messages FightFlush bool // flush: flush typeahead during battle Jump bool // jump: show position only at end of run SeeFloor bool // seefloor: show the lamp-illuminated floor PassGo bool // passgo: follow turnings in passageways Tombstone bool // tombstone: print tombstone when killed InvType int // inven: inventory style (InvOver/InvSlow/InvClear) } // Config carries everything needed to construct a game. type Config struct { Seed int32 // dungeon number; the caller derives it (time+pid or SEED env) Name string // player name (overridden by ROGUEOPTS name=) RogueOpts string // the ROGUEOPTS environment string Home string // home directory (save file default location) ScorePath string // scoreboard file; empty disables scoring Wizard bool // enable debug commands (implies NoScore) Term Terminal // the display/input device; nil runs headless } // RogueGame is one complete game of Rogue: every piece of state that was a // global (or file-scope static) in the C sources, plus the terminal it is // played on. Construct with NewGame, then call Run. // // The struct grows with the port; fields appear in the phase that ports the // code owning them. type RogueGame struct { // identity / RNG Rng *Rng Dnum int // dungeon number Whoami string // name of player Fruit string // favorite fruit Home string // user's home directory FileName string // save file name Wizard bool // true if allows wizard commands NoScore bool // was a wizard sometime // the world Player Player Level Level Depth int // C `level`: what level she is on MaxDepth int // C `max_level`: deepest player has gone HasAmulet bool // C `amulet`: he found the amulet SeenStairs bool // have seen the stairs (for lsd) // turn/command engine Playing bool // true until he quits After bool // true if we want after daemons Again bool // repeating the last command Count int // number of times to repeat command NoCommand int // number of turns asleep NoMove int // number of turns held in place Quiet int // number of quiet turns Running bool // true if player is running RunCh byte // direction player is running DoorStop bool // stop running when we pass a door Firstmove bool // first move after setting door_stop MoveOn bool // next move shouldn't pick up items ToDeath bool // fighting is to the death! Kamikaze bool // to_death really to DEATH HasHit bool // has a "hit" message pending in msg MaxHit int // max damage done to her in to_death Take byte // thing she is taking Delta Coord DirCh byte LastComm byte LastDir byte LLastComm byte LLastDir byte LastPick *Object LLastPick *Object lastDelt Coord // misc.c get_dir static last_delt // command.c statics countCh byte direction byte newCount bool // dungeon generation working state maze mazeState // rooms.c maze statics pnum int // passages.c passnum statics newpnum bool chRet Coord // chase.c static ch_ret: where chasing takes you // daemons/fuses Daemons DaemonList // screen / messages scr *Screen Msgs MsgLine statusCache statusCache invPage invPage // things.c discovery-list pagination statics // UI state StatMsg bool // should status() print as a msg() InvDescribe bool // say which way items are being used QComm bool // are we executing a 'Q' command? InShell bool // true if executing a shell Oldpos Coord // position before last look() call Oldrp *Room NObjs int // # items listed in inventory() call // options and item identity Options Options Items ItemLore // Monsters is the per-game copy of the bestiary: the C code mutates // the venus flytrap's damage string during play, and the table is // part of the save state. Monsters [26]MonsterKind // scores LastScore int AllScore bool ScorePath string rogueOpts string // the ROGUEOPTS string, re-parsed by playit as in C restored bool // game came from a save file; Run skips setup } // NewGame builds a game from cfg, seeds the RNG, and randomizes the item // appearance tables (the front half of main.c main(); the player roll-up // and first level arrive with later porting phases). func NewGame(cfg Config) *RogueGame { g := &RogueGame{ Rng: &Rng{Seed: cfg.Seed}, Dnum: int(cfg.Seed), Whoami: cfg.Name, Fruit: "slime-mold", Home: cfg.Home, Wizard: cfg.Wizard, NoScore: cfg.Wizard, Playing: true, Depth: 1, ScorePath: cfg.ScorePath, LastScore: -1, } g.Options = Options{ SeeFloor: true, Tombstone: true, InvType: InvOver, } g.InvDescribe = true g.Msgs.SaveMsg = true g.scr = NewScreen(cfg.Term) g.FileName = cfg.Home + "/rogue.save" g.rogueOpts = cfg.RogueOpts if cfg.Wizard { g.Player.Flags.Set(SenseMonsters) } if cfg.RogueOpts != "" { g.ParseOpts(cfg.RogueOpts) } g.Monsters = monsterTable g.Items.Group = 2 // weapons.c: int group = 2 for i := range g.Level.Passages { g.Level.Passages[i].Flags = Gone | Dark } g.initProbs() // set up prob tables for objects g.initPlayer() // set up initial player stats g.initNames() // set up names of scrolls g.initColors() // set up colors of potions g.initStones() // set up stone settings of rings g.initMaterials() // set up materials of wands return g } // Run plays the game to its end: the back half of main.c main() plus // playit(). It returns after death, victory, quitting, or saving. func (g *RogueGame) Run() error { // A gameEnd panic is the port's my_exit(): recovering it here makes // Run return normally (zero values), restoring the terminal via the // caller's defers. defer func() { if r := recover(); r != nil { if _, ok := r.(gameEnd); ok { return // normal game over / save exit } panic(r) } }() if !g.restored { g.NewLevel() // draw current level // Start up daemons and fuses g.StartDaemon(DRunners, 0, After) g.StartDaemon(DDoctor, 0, After) g.Fuse(DSwander, 0, wanderTime(g), After) g.StartDaemon(DStomach, 0, After) } g.playit() return nil } // playit is the main loop of the program (main.c playit). func (g *RogueGame) playit() { // set up defaults for modern terminals: curses' md_hasclreol() is // always true, so the C default inventory style applies if !g.restored { g.Options.InvType = InvClear } // parse environment declaration of options (C parses ROGUEOPTS again // here, letting it override the terminal defaults) if g.rogueOpts != "" { g.ParseOpts(g.rogueOpts) } g.Oldpos = g.Player.Pos g.Oldrp = g.roomin(g.Player.Pos) for g.Playing { g.command() // command execution } g.endit() } // endit exits the game (main.c endit). func (g *RogueGame) endit() { g.fatal("Okay, bye bye!\n") } // fatal prints a message and leaves (main.c fatal). func (g *RogueGame) fatal(s string) { g.mvaddstr(NumLines-2, 0, s) g.refresh() g.myExit() } // quit has the player make certain, then exits (main.c quit). The final // scoring display arrives with the endgame phase. func (g *RogueGame) quit(int) { // Reset the message position in case we got here via an interrupt if !g.QComm { g.Msgs.Mpos = 0 } oy, ox := g.scr.Std.GetYX() g.msg("really quit?") if g.readchar() == 'y' { g.clear() g.scr.Std.MvPrintwf(NumLines-2, 0, "You quit with %d gold pieces", g.Player.Purse) g.move(NumLines-1, 0) g.refresh() g.score(g.Player.Purse, 1, 0) g.myExit() return } g.move(0, 0) g.clrtoeol() g.status() g.move(oy, ox) g.refresh() g.Msgs.Mpos = 0 g.Count = 0 g.ToDeath = false }