// Command rogue is the Go port of Rogue 5.4.4: Exploring the Dungeons of // Doom. It is a faithful function-by-function port of the classic C game; // see ARCHITECTURE.md at the repository root. package main import ( "flag" "fmt" "os" "os/signal" "os/user" "strconv" "syscall" "time" "git.eeqj.de/sneak/rgoue/game" "git.eeqj.de/sneak/rgoue/term" ) func main() { os.Exit(run()) } // run carries the real main so that deferred terminal restoration runs // before the process exits (os.Exit skips defers). func run() int { scores := flag.Bool("s", false, "print the scoreboard and exit") deathDemo := flag.Bool("d", false, "die a random death (demo)") flag.Parse() cfg := loadConfig() if *scores { game.NewGame(cfg).ShowScores() return 0 } t, err := term.New() if err != nil { fmt.Fprintln(os.Stderr, err) return 1 } defer t.Fini() cfg.Term = t var g *game.RogueGame if args := flag.Args(); len(args) == 1 && !*deathDemo { // restore a saved game g, err = game.Restore(args[0], cfg) if err != nil { t.Fini() fmt.Fprintln(os.Stderr, err) return 1 } } else { g = game.NewGame(cfg) } if *deathDemo { g.DeathDemo() return 0 } installAutosave(g, t) runErr := g.Run() if runErr != nil { t.Fini() fmt.Fprintln(os.Stderr, runErr) return 1 } return 0 } // loadConfig gathers the game configuration from the environment: home // directory, ROGUEOPTS, user name, wizard mode, and the dungeon seed // (main.c's startup). func loadConfig() game.Config { home, _ := os.UserHomeDir() name := "" u, userErr := user.Current() if userErr == nil { name = u.Username } wizard := os.Getenv("ROGUE_WIZARD") != "" return game.Config{ Seed: chooseSeed(wizard), Name: name, RogueOpts: os.Getenv("ROGUEOPTS"), Home: home, ScorePath: home + "/.rogue.scores", Wizard: wizard, } } // installAutosave saves the game and exits on SIGHUP/SIGTERM (save.c // auto_save). func installAutosave(g *game.RogueGame, t *term.Tcell) { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGHUP, syscall.SIGTERM) go func() { <-sig g.AutoSave() t.Fini() os.Exit(0) }() } // chooseSeed picks the dungeon number: SEED for reproducible dungeons // (wizard mode, as in the C game), else time+pid (main.c). func chooseSeed(wizard bool) int32 { if env := os.Getenv("SEED"); env != "" && wizard { n, err := strconv.ParseInt(env, 10, 32) if err == nil { return int32(n) } } // The C game computed `lowtime + getpid()` in int; the truncation to // 32 bits is the same wraparound the C int arithmetic performed. return int32(time.Now().Unix()&0x7fffffff) + //nolint:gosec // G115: deliberate wrap int32(os.Getpid()&0x7fffffff) //nolint:gosec // G115: deliberate wrap }