go: port dungeon generation, items base, pack, and monster creation
rooms.c, passages.c, new_level.c ported in full: room/maze layout, corridor spanning tree with extra connections, passage numbering flood fill, trap/stairs/object placement, treasure rooms. Careful RNG-call ordering throughout keeps generation seed-faithful to C. Supporting subsystems this depends on, also ported: - screen.go: curses replaced by in-memory Window cell buffers behind a Terminal interface (tcell arrives with the UI phase; tests run headless) - io.go: msg/addmsg/endmsg message-line protocol, status line, step_ok - pack.c in full (add_pack sorting/stacking walk, get_item, inventory) - things.c in full (inv_name, new_thing, discovery lists, pagination) - monsters.c in full; chase.c navigation half (roomin/cansee/see_monst/ find_dest/runto/set_oldch); rings.c, armor.c in full - weapons.c/sticks.c creation half (init_weapon, fix_stick, num) - misc.c look() display update, eat, level-up, direction input - daemons.c callbacks except stomach (needs death()) and the runners chase driver (combat phase) - init.c init_player with the starting kit Tests: level invariants across seeds, determinism, 30-depth sweep.
This commit is contained in:
73
game/game.go
73
game/game.go
@@ -33,12 +33,13 @@ type Options struct {
|
||||
|
||||
// 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)
|
||||
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
|
||||
@@ -92,11 +93,23 @@ type RogueGame struct {
|
||||
LLastDir byte
|
||||
LastPick *Object
|
||||
LLastPick *Object
|
||||
lastDelt Coord // misc.c get_dir static last_delt
|
||||
|
||||
// dungeon generation working state
|
||||
maze mazeState // rooms.c maze statics
|
||||
pnum int // passages.c passnum statics
|
||||
newpnum bool
|
||||
|
||||
// daemons/fuses
|
||||
Daemons DaemonList
|
||||
|
||||
// UI state (the Screen itself arrives with the UI phase)
|
||||
// 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?
|
||||
@@ -138,18 +151,58 @@ func NewGame(cfg Config) *RogueGame {
|
||||
InvType: InvOver,
|
||||
}
|
||||
g.InvDescribe = true
|
||||
g.Msgs.SaveMsg = true
|
||||
g.scr = NewScreen(cfg.Term)
|
||||
g.FileName = cfg.Home + "/rogue.save"
|
||||
if cfg.Wizard {
|
||||
g.Player.Flags.Set(SeeMonst)
|
||||
}
|
||||
// TODO(port): parse cfg.RogueOpts once options.go lands.
|
||||
|
||||
g.initProbs() // set up prob tables for objects
|
||||
// TODO(port): initPlayer() goes exactly here (RNG order!) once pack.go lands.
|
||||
g.Items.Group = 2 // weapons.c: int group = 2
|
||||
for i := range g.Level.Passages {
|
||||
g.Level.Passages[i].Flags = IsGone | IsDark
|
||||
}
|
||||
|
||||
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
|
||||
// TODO(port): new level + daemon start-up once their phases land.
|
||||
return g
|
||||
}
|
||||
|
||||
// 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.MvPrintw(NumLines-2, 0, "You quit with %d gold pieces", g.Player.Purse)
|
||||
g.move(NumLines-1, 0)
|
||||
g.refresh()
|
||||
// TODO(port): score(purse, 1, 0) once rip.go lands.
|
||||
g.gameOver()
|
||||
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
|
||||
}
|
||||
|
||||
// gameOver ends the game loop; it replaces the C exit() calls so that Run
|
||||
// can unwind normally and restore the terminal.
|
||||
func (g *RogueGame) gameOver() {
|
||||
g.Playing = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user