go: port command loop, save/restore, tcell terminal, and the binary

- command.c in full: the command dispatcher with repeat counts, run
  prefixes, ctrl-run door-stop mode, fight-to-death targeting, and all
  wizard debug commands; search, help, identify, d_level/u_level, call,
  current
- main.c completed: Run()/playit() with the C double ROGUEOPTS parse;
  exit()-anywhere becomes a gameEnd panic recovered in Run
- save.c + state.c: gob SaveState snapshot with explicit pointer-to-
  index fixups for equipment, monster rooms, and chase targets (the
  rs_fix_thing role); save file deleted on restore as in C; SIGHUP/
  SIGTERM autosave hook
- wizard.c completed (create_obj, show_map), passages.c add_pass
- term/: tcell/v2 Terminal — the one third-party dependency — replacing
  curses and mdport's 900-line key decoder; shell escape via suspend
- cmd/rogue: flags -s/-d, SEED (wizard), ROGUEOPTS, ROGUE_WIZARD

Tests: full scripted sessions through Run (quit, descend stairs,
3200-move crash sweep, save-command round trip). Notable fixes found
by tests: gob silently drops embedded fields of unexported types, and
--More-- prompts swallow space-free input scripts.
This commit is contained in:
2026-07-06 20:15:47 +02:00
parent cdf9bf73a9
commit 41fc1042fc
14 changed files with 2067 additions and 20 deletions

View File

@@ -216,6 +216,26 @@ func (g *RogueGame) killname(monst byte, doart bool) string {
return sp
}
// DeathDemo implements the -d command line option (main.c): burn some
// random numbers to break patterns, then die a random death.
func (g *RogueGame) DeathDemo() {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(gameEnd); ok {
return
}
panic(r)
}
}()
dnum := g.rnd(100)
for dnum--; dnum > 0; dnum-- {
g.rnd(100)
}
g.Player.Purse = g.rnd(100) + 1
g.Depth = g.rnd(100) + 1
g.death(g.deathMonst())
}
// deathMonst returns a monster appropriate for a random death (rip.c
// death_monst).
func (g *RogueGame) deathMonst() byte {