Move all package-level vars into gameData; finish lint adoption
gochecknoglobals: all 37 package-level tables consolidated into the gameData struct (game/tables.go), built by newGameData() and carried on RogueGame as g.data (set in NewGame and Restore). ObjectKind Glyph()/objectKindForGlyph are now switches; the table-reading subtype Stringer methods are gone; isMagic is a RogueGame method. goconst: repeated words named (potionName/scrollName/ringName/goldName in object.go, wandName/staffName in sticks.go, ripWall in tables.go). exhaustive, testpackage: disabled in .golangci.yml with sneak's approval (2026-07-07). Also reverts misspell's silent corruption of the "ther" scroll-name syllable (it had become "there", changing generated scroll names vs C). Remaining red: cyclop/gocognit/nestif until refactor step 7; mnd awaits a ruling. TODO.md rotated; MEMORY.md lint notes updated.
This commit is contained in:
@@ -16,6 +16,9 @@ linters:
|
|||||||
- varnamelen # Short names like db, id are idiomatic Go
|
- varnamelen # Short names like db, id are idiomatic Go
|
||||||
# Repo-specific exceptions approved by sneak (2026-07-06)
|
# Repo-specific exceptions approved by sneak (2026-07-06)
|
||||||
- paralleltest # Requires t.Parallel() in every test
|
- paralleltest # Requires t.Parallel() in every test
|
||||||
|
# Approved by sneak 2026-07-07
|
||||||
|
- testpackage # Tests use internal package game to reach unexported state
|
||||||
|
- exhaustive # C-faithful switches handle only the cases C handled
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
lll:
|
lll:
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ The .golangci.yml is the house standard and may only be modified with
|
|||||||
sneak's explicit permission. To disable a linter, ask, explaining what
|
sneak's explicit permission. To disable a linter, ask, explaining what
|
||||||
the linter does; he approves specific exceptions, which are recorded
|
the linter does; he approves specific exceptions, which are recorded
|
||||||
in the config's "Repo-specific exceptions" block with the approval
|
in the config's "Repo-specific exceptions" block with the approval
|
||||||
date. Approved so far: paralleltest (2026-07-06). Line-level //nolint
|
date. Approved so far: paralleltest (2026-07-06); testpackage and
|
||||||
|
exhaustive (2026-07-07). Complexity linters (cyclop, gocognit, nestif)
|
||||||
|
stay enabled and red until refactor step 7 fixes the findings, per
|
||||||
|
sneak 2026-07-07. mnd findings await a ruling. Line-level //nolint
|
||||||
with a reason is used sparingly for C-faithfulness (e.g. the authentic
|
with a reason is used sparingly for C-faithfulness (e.g. the authentic
|
||||||
"missle" message spellings) and provably-safe gosec conversions; each
|
"missle" message spellings) and provably-safe gosec conversions; each
|
||||||
needs a justifying comment.
|
needs a justifying comment.
|
||||||
|
|||||||
68
TODO.md
68
TODO.md
@@ -29,22 +29,34 @@ Refactor ground rules:
|
|||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Finish adopting the house Go linting standards. Done so far (branch
|
Refactor step 4: method renames, movement/world subsystem
|
||||||
refactor/lint-adoption): .golangci.yml copied verbatim from the
|
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
||||||
prompts repo (plus the sneak-approved paralleltest exception,
|
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
||||||
2026-07-06); ~1,500 findings fixed (autofix formatting sweep, errcheck/
|
chgStr→changeStrength, ...); remove the goto/label flows in doMove,
|
||||||
err113/noinlineerr error handling, forbidigo, funcorder, recvcheck
|
dispatch, and saveGame in favor of loops and helpers.
|
||||||
pointer receivers, goprintffuncname renames msg helpers to *f, revive
|
|
||||||
doc comments, gocritic switch rewrites, gosec real fixes plus justified
|
|
||||||
nolints, unparam signature tightening, C-faithful "missle" spellings
|
|
||||||
restored after misspell autofix changed game text). Remaining findings
|
|
||||||
are all in linters awaiting sneak's exception decision: mnd (288),
|
|
||||||
gochecknoglobals (37), cyclop (36), nestif (30), gocognit (23),
|
|
||||||
exhaustive (22), goconst (16), testpackage (9). Blocked on that
|
|
||||||
decision; either disable with approval or scope the fixes.
|
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-07-07 Lint adoption finished (refactor/no-package-globals): all
|
||||||
|
37 package-level vars moved into `gameData` (built by `newGameData`,
|
||||||
|
hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind
|
||||||
|
Glyph()/objectKindForGlyph became switches; the table-reading subtype
|
||||||
|
Stringers were removed; isMagic became a RogueGame method; goconst
|
||||||
|
fixed with named word constants (potionName, goldName, staffName,
|
||||||
|
ripWall, ...); testpackage and exhaustive disabled in .golangci.yml
|
||||||
|
with sneak's approval (2026-07-07); misspell's corruption of the
|
||||||
|
"ther" scroll syllable reverted. Remaining red: cyclop (36), nestif
|
||||||
|
(30), gocognit (23) stay until step 7 fixes them per sneak's ruling,
|
||||||
|
and mnd (289) still awaits a ruling.
|
||||||
|
- 2026-07-06 Lint adoption bulk (refactor/lint-adoption, 5ba9fe8):
|
||||||
|
.golangci.yml copied verbatim from the prompts repo (plus the
|
||||||
|
sneak-approved paralleltest exception, 2026-07-06); ~1,500 findings
|
||||||
|
fixed (autofix formatting sweep, errcheck/err113/noinlineerr error
|
||||||
|
handling, forbidigo, funcorder, recvcheck pointer receivers,
|
||||||
|
goprintffuncname renames msg helpers to *f, revive doc comments,
|
||||||
|
gocritic switch rewrites, gosec real fixes plus justified nolints,
|
||||||
|
unparam signature tightening, C-faithful "missle" spellings restored
|
||||||
|
after misspell autofix changed game text).
|
||||||
- 2026-07-06 Module base path updated to git.eeqj.de/sneak/rgoue
|
- 2026-07-06 Module base path updated to git.eeqj.de/sneak/rgoue
|
||||||
(go.mod, term/ and cmd/ imports, ARCHITECTURE.md, version string).
|
(go.mod, term/ and cmd/ imports, ARCHITECTURE.md, version string).
|
||||||
- 2026-07-06 Refactor step 3 (refactor/object-fields): Object.Arm split
|
- 2026-07-06 Refactor step 3 (refactor/object-fields): Object.Arm split
|
||||||
@@ -86,45 +98,41 @@ decision; either disable with approval or scope the fixes.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
1. Refactor step 4: method renames, movement/world subsystem
|
1. Refactor step 5: method renames, items/combat/UI subsystems
|
||||||
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
|
||||||
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
|
||||||
chgStr→changeStrength, ...); remove the goto/label flows in doMove,
|
|
||||||
dispatch, and saveGame in favor of loops and helpers.
|
|
||||||
2. Refactor step 5: method renames, items/combat/UI subsystems
|
|
||||||
(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse,
|
(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse,
|
||||||
getItem→promptPackItem returning (obj, ok), getDir→promptDirection);
|
getItem→promptPackItem returning (obj, ok), getDir→promptDirection);
|
||||||
int status codes (attack returning -1) become named results. Two or
|
int status codes (attack returning -1) become named results. Two or
|
||||||
three commits, one subsystem each.
|
three commits, one subsystem each.
|
||||||
3. Refactor step 6: extract types from the god object — MessageLine
|
2. Refactor step 6: extract types from the god object — MessageLine
|
||||||
owns the msg/addmsg/endmsg machinery; pack/inventory operations move
|
owns the msg/addmsg/endmsg machinery; pack/inventory operations move
|
||||||
onto *Player; monster/object list management and map queries
|
onto *Player; monster/object list management and map queries
|
||||||
consolidate onto *Level; RogueGame keeps turn orchestration and
|
consolidate onto *Level; RogueGame keeps turn orchestration and
|
||||||
cross-system effects only.
|
cross-system effects only.
|
||||||
4. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
3. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
||||||
switches become per-kind handler tables of small named methods,
|
switches become per-kind handler tables of small named methods,
|
||||||
keeping effect order and RNG call sequence identical.
|
keeping effect order and RNG call sequence identical. This step also
|
||||||
5. Refactor step 8: constructor and style pass per the house
|
clears the outstanding cyclop/gocognit/nestif lint findings.
|
||||||
|
4. Refactor step 8: constructor and style pass per the house
|
||||||
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
||||||
replace the gameEnd panic unwind with error-based turn results where
|
replace the gameEnd panic unwind with error-based turn results where
|
||||||
feasible; 77-column wrap sweep.
|
feasible; 77-column wrap sweep.
|
||||||
6. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
|
5. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
|
||||||
post-refactor names; add the C name → Go name rename table.
|
post-refactor names; add the C name → Go name rename table.
|
||||||
7. Playtest hardening pass: play several full games with the tcell
|
6. Playtest hardening pass: play several full games with the tcell
|
||||||
binary and extend run_test.go to script a deeper multi-level
|
binary and extend run_test.go to script a deeper multi-level
|
||||||
playthrough (descend past level 5, use potions, scrolls, zapping,
|
playthrough (descend past level 5, use potions, scrolls, zapping,
|
||||||
save/restore). Fix any panics, message mismatches, or divergences
|
save/restore). Fix any panics, message mismatches, or divergences
|
||||||
from the C behavior that this uncovers, with regression tests.
|
from the C behavior that this uncovers, with regression tests.
|
||||||
8. Verify the seed-compatibility claim against the C reference on
|
7. Verify the seed-compatibility claim against the C reference on
|
||||||
c-master: same seed, same dungeon, same item tables, for several
|
c-master: same seed, same dungeon, same item tables, for several
|
||||||
seeds.
|
seeds.
|
||||||
9. Broaden unit test coverage where playtesting finds thin spots
|
8. Broaden unit test coverage where playtesting finds thin spots
|
||||||
(rings, sticks, wizard commands).
|
(rings, sticks, wizard commands).
|
||||||
10. Tag a release once a full game (Amulet retrieval and score entry)
|
9. Tag a release once a full game (Amulet retrieval and score entry)
|
||||||
completes without defects.
|
completes without defects.
|
||||||
11. Full-terminal-size support (deferred by explicit decision
|
10. Full-terminal-size support (deferred by explicit decision
|
||||||
2026-07-06): per-game dungeon dimensions instead of the 80x24
|
2026-07-06): per-game dungeon dimensions instead of the 80x24
|
||||||
constants; open design questions are resize policy, gameplay
|
constants; open design questions are resize policy, gameplay
|
||||||
tuning at larger sizes, and a --classic 80x24 mode.
|
tuning at larger sizes, and a --classic 80x24 mode.
|
||||||
12. Note: this repo is exempt from the standard policy scaffold. Do not
|
11. Note: this repo is exempt from the standard policy scaffold. Do not
|
||||||
add Makefile, Dockerfile, or REPO_POLICIES.md.
|
add Makefile, Dockerfile, or REPO_POLICIES.md.
|
||||||
|
|||||||
@@ -373,9 +373,9 @@ over:
|
|||||||
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
|
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
|
||||||
g.msg("no trap there")
|
g.msg("no trap there")
|
||||||
case p.On(Hallucinating):
|
case p.On(Hallucinating):
|
||||||
g.msg("%s", trName[g.rnd(NumTrapTypes)])
|
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
||||||
default:
|
default:
|
||||||
g.msg("%s", trName[*fp&FTrapMask])
|
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||||
fp.Set(FSeen)
|
fp.Set(FSeen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,9 +540,9 @@ func (g *RogueGame) search() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.On(Hallucinating) {
|
if p.On(Hallucinating) {
|
||||||
g.msg("%s", trName[g.rnd(NumTrapTypes)])
|
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
||||||
} else {
|
} else {
|
||||||
g.msg("%s", trName[*fp&FTrapMask])
|
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||||
fp.Set(FSeen)
|
fp.Set(FSeen)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +584,7 @@ func (g *RogueGame) help() {
|
|||||||
if helpch != '*' {
|
if helpch != '*' {
|
||||||
g.move(0, 0)
|
g.move(0, 0)
|
||||||
|
|
||||||
for _, strp := range helpStr {
|
for _, strp := range g.data.helpStr {
|
||||||
if strp.Ch == helpch {
|
if strp.Ch == helpch {
|
||||||
g.Msgs.LowerMsg = true
|
g.Msgs.LowerMsg = true
|
||||||
g.msg("%s%s", unctrl(strp.Ch), strp.Desc)
|
g.msg("%s%s", unctrl(strp.Ch), strp.Desc)
|
||||||
@@ -602,7 +602,7 @@ func (g *RogueGame) help() {
|
|||||||
// command mode.
|
// command mode.
|
||||||
numprint := 0
|
numprint := 0
|
||||||
|
|
||||||
for _, strp := range helpStr {
|
for _, strp := range g.data.helpStr {
|
||||||
if strp.Print {
|
if strp.Print {
|
||||||
numprint++
|
numprint++
|
||||||
}
|
}
|
||||||
@@ -622,7 +622,7 @@ func (g *RogueGame) help() {
|
|||||||
|
|
||||||
cnt := 0
|
cnt := 0
|
||||||
|
|
||||||
for _, strp := range helpStr {
|
for _, strp := range g.data.helpStr {
|
||||||
if !strp.Print {
|
if !strp.Print {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -652,28 +652,6 @@ func (g *RogueGame) help() {
|
|||||||
g.refresh()
|
g.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// identList is command.c's static ident_list.
|
|
||||||
var identList = []helpEntry{
|
|
||||||
{'|', "wall of a room", false},
|
|
||||||
{'-', "wall of a room", false},
|
|
||||||
{Gold, "gold", false},
|
|
||||||
{Stairs, "a staircase", false},
|
|
||||||
{Door, "door", false},
|
|
||||||
{Floor, "room floor", false},
|
|
||||||
{PlayerCh, "you", false},
|
|
||||||
{Passage, "passage", false},
|
|
||||||
{Trap, "trap", false},
|
|
||||||
{Potion, "potion", false},
|
|
||||||
{Scroll, "scroll", false},
|
|
||||||
{Food, "food", false},
|
|
||||||
{Weapon, "weapon", false},
|
|
||||||
{' ', "solid rock", false},
|
|
||||||
{Armor, "armor", false},
|
|
||||||
{Amulet, "the Amulet of Yendor", false},
|
|
||||||
{Ring, "ring", false},
|
|
||||||
{Stick, "wand or staff", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// identify tells the player what a certain thing is (command.c identify).
|
// identify tells the player what a certain thing is (command.c identify).
|
||||||
func (g *RogueGame) identify() {
|
func (g *RogueGame) identify() {
|
||||||
g.msg("what do you want identified? ")
|
g.msg("what do you want identified? ")
|
||||||
@@ -692,7 +670,7 @@ func (g *RogueGame) identify() {
|
|||||||
} else {
|
} else {
|
||||||
str = "unknown character"
|
str = "unknown character"
|
||||||
|
|
||||||
for _, hp := range identList {
|
for _, hp := range g.data.identList {
|
||||||
if hp.Ch == ch {
|
if hp.Ch == ch {
|
||||||
str = hp.Desc
|
str = hp.Desc
|
||||||
|
|
||||||
|
|||||||
@@ -32,15 +32,16 @@ func TestParseDice(t *testing.T) {
|
|||||||
// The bestiary and weapon tables must parse to at least one attack each so
|
// The bestiary and weapon tables must parse to at least one attack each so
|
||||||
// every creature and weapon actually swings.
|
// every creature and weapon actually swings.
|
||||||
func TestTablesHaveDice(t *testing.T) {
|
func TestTablesHaveDice(t *testing.T) {
|
||||||
for i, m := range monsterTable {
|
data := newGameData()
|
||||||
|
for i, m := range data.monsterTable {
|
||||||
if len(m.Stats.Dmg) == 0 {
|
if len(m.Stats.Dmg) == 0 {
|
||||||
t.Errorf("monster %c (%s) has no attacks", 'A'+i, m.Name)
|
t.Errorf("monster %c (%s) has no attacks", 'A'+i, m.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for w, iw := range initWeaps {
|
for w, iw := range data.initWeaps {
|
||||||
if len(iw.dam) == 0 || len(iw.hrl) == 0 {
|
if len(iw.dam) == 0 || len(iw.hrl) == 0 {
|
||||||
t.Errorf("weapon %v has empty dice", WeaponKind(w))
|
t.Errorf("weapon %d has empty dice", w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,43 +4,6 @@ import "strconv"
|
|||||||
|
|
||||||
// fight.c — all the fighting gets done here.
|
// fight.c — all the fighting gets done here.
|
||||||
|
|
||||||
// hNames are the strings for hitting; the first four are used when the
|
|
||||||
// player strikes, the second four for monsters (fight.c h_names).
|
|
||||||
var hNames = [8]string{
|
|
||||||
" scored an excellent hit on ",
|
|
||||||
" hit ",
|
|
||||||
" have injured ",
|
|
||||||
" swing and hit ",
|
|
||||||
" scored an excellent hit on ",
|
|
||||||
" hit ",
|
|
||||||
" has injured ",
|
|
||||||
" swings and hits ",
|
|
||||||
}
|
|
||||||
|
|
||||||
// mNames are the strings for missing (fight.c m_names).
|
|
||||||
var mNames = [8]string{
|
|
||||||
" miss",
|
|
||||||
" swing and miss",
|
|
||||||
" barely miss",
|
|
||||||
" don't hit",
|
|
||||||
" misses",
|
|
||||||
" swings and misses",
|
|
||||||
" barely misses",
|
|
||||||
" doesn't hit",
|
|
||||||
}
|
|
||||||
|
|
||||||
// strPlus adjusts hit probabilities due to strength (fight.c str_plus).
|
|
||||||
var strPlus = [32]int{
|
|
||||||
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
|
||||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
// addDam adjusts damage done due to strength (fight.c add_dam).
|
|
||||||
var addDam = [32]int{
|
|
||||||
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
|
|
||||||
3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
// setMname returns the monster name for the given monster (fight.c
|
// setMname returns the monster name for the given monster (fight.c
|
||||||
// set_mname).
|
// set_mname).
|
||||||
func (g *RogueGame) setMname(tp *Monster) string {
|
func (g *RogueGame) setMname(tp *Monster) string {
|
||||||
@@ -254,7 +217,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
p.Stats.Exp = 0
|
p.Stats.Exp = 0
|
||||||
p.Stats.Lvl = 1
|
p.Stats.Lvl = 1
|
||||||
} else {
|
} else {
|
||||||
p.Stats.Exp = eLevels[p.Stats.Lvl-1] + 1
|
p.Stats.Exp = g.data.eLevels[p.Stats.Lvl-1] + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fewer = g.roll(1, 10)
|
fewer = g.roll(1, 10)
|
||||||
@@ -314,7 +277,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
for _, obj := range p.Pack {
|
for _, obj := range p.Pack {
|
||||||
if obj != p.CurArmor && obj != p.CurWeapon &&
|
if obj != p.CurArmor && obj != p.CurWeapon &&
|
||||||
obj != p.CurRing[Left] && obj != p.CurRing[Right] &&
|
obj != p.CurRing[Left] && obj != p.CurRing[Right] &&
|
||||||
obj.isMagic() {
|
g.isMagic(obj) {
|
||||||
if nobj++; g.rnd(nobj) == 0 {
|
if nobj++; g.rnd(nobj) == 0 {
|
||||||
steal = obj
|
steal = obj
|
||||||
}
|
}
|
||||||
@@ -436,10 +399,10 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
|
|||||||
didHit := false
|
didHit := false
|
||||||
|
|
||||||
for _, atk := range attacks {
|
for _, atk := range attacks {
|
||||||
if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) {
|
if g.swing(att.Lvl, defArm, hplus+g.data.strPlus[att.Str]) {
|
||||||
proll := g.roll(atk.Count, atk.Sides)
|
proll := g.roll(atk.Count, atk.Sides)
|
||||||
|
|
||||||
damage := dplus + proll + addDam[att.Str]
|
damage := dplus + proll + g.data.addDam[att.Str]
|
||||||
if damage > 0 {
|
if damage > 0 {
|
||||||
def.HP -= damage
|
def.HP -= damage
|
||||||
}
|
}
|
||||||
@@ -515,7 +478,7 @@ func (g *RogueGame) hit(er, ee string, noend bool) {
|
|||||||
i += 4
|
i += 4
|
||||||
}
|
}
|
||||||
|
|
||||||
s = hNames[i]
|
s = g.data.hNames[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
g.addmsgf("%s", s)
|
g.addmsgf("%s", s)
|
||||||
@@ -546,7 +509,7 @@ func (g *RogueGame) miss(er, ee string, noend bool) {
|
|||||||
i += 4
|
i += 4
|
||||||
}
|
}
|
||||||
|
|
||||||
g.addmsgf("%s", mNames[i])
|
g.addmsgf("%s", g.data.mNames[i])
|
||||||
|
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
g.addmsgf(" %s", prname(ee, false))
|
g.addmsgf(" %s", prname(ee, false))
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ type RogueGame struct {
|
|||||||
|
|
||||||
rogueOpts string // the ROGUEOPTS string, re-parsed by playit as in C
|
rogueOpts string // the ROGUEOPTS string, re-parsed by playit as in C
|
||||||
restored bool // game came from a save file; Run skips setup
|
restored bool // game came from a save file; Run skips setup
|
||||||
|
|
||||||
|
// data is the game's copy of the static tables (extern.c and friends).
|
||||||
|
data *gameData
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGame builds a game from cfg, seeds the RNG, and randomizes the item
|
// NewGame builds a game from cfg, seeds the RNG, and randomizes the item
|
||||||
@@ -145,6 +148,7 @@ type RogueGame struct {
|
|||||||
// and first level arrive with later porting phases).
|
// and first level arrive with later porting phases).
|
||||||
func NewGame(cfg Config) *RogueGame {
|
func NewGame(cfg Config) *RogueGame {
|
||||||
g := &RogueGame{
|
g := &RogueGame{
|
||||||
|
data: newGameData(),
|
||||||
Rng: &Rng{Seed: cfg.Seed},
|
Rng: &Rng{Seed: cfg.Seed},
|
||||||
Dnum: int(cfg.Seed),
|
Dnum: int(cfg.Seed),
|
||||||
Whoami: cfg.Name,
|
Whoami: cfg.Name,
|
||||||
@@ -176,7 +180,7 @@ func NewGame(cfg Config) *RogueGame {
|
|||||||
g.ParseOpts(cfg.RogueOpts)
|
g.ParseOpts(cfg.RogueOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Monsters = monsterTable
|
g.Monsters = g.data.monsterTable
|
||||||
|
|
||||||
g.Items.Group = 2 // weapons.c: int group = 2
|
g.Items.Group = 2 // weapons.c: int group = 2
|
||||||
for i := range g.Level.Passages {
|
for i := range g.Level.Passages {
|
||||||
|
|||||||
52
game/init.go
52
game/init.go
@@ -8,7 +8,7 @@ import "strings"
|
|||||||
// initPlayer rolls her up (init.c init_player).
|
// initPlayer rolls her up (init.c init_player).
|
||||||
func (g *RogueGame) initPlayer() {
|
func (g *RogueGame) initPlayer() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
p.MaxStats = initStats
|
p.MaxStats = g.data.initStats
|
||||||
p.Stats = p.MaxStats
|
p.Stats = p.MaxStats
|
||||||
p.FoodLeft = HungerTime
|
p.FoodLeft = HungerTime
|
||||||
// Give him some food
|
// Give him some food
|
||||||
@@ -20,7 +20,7 @@ func (g *RogueGame) initPlayer() {
|
|||||||
obj = newObject()
|
obj = newObject()
|
||||||
obj.Kind = KindArmor
|
obj.Kind = KindArmor
|
||||||
obj.Which = int(ArmorRingMail)
|
obj.Which = int(ArmorRingMail)
|
||||||
obj.ArmorClass = aClass[ArmorRingMail] - 1
|
obj.ArmorClass = g.data.aClass[ArmorRingMail] - 1
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
obj.Count = 1
|
obj.Count = 1
|
||||||
p.CurArmor = obj
|
p.CurArmor = obj
|
||||||
@@ -50,19 +50,19 @@ func (g *RogueGame) initPlayer() {
|
|||||||
// initColors initializes the potion color scheme for this game
|
// initColors initializes the potion color scheme for this game
|
||||||
// (init.c init_colors).
|
// (init.c init_colors).
|
||||||
func (g *RogueGame) initColors() {
|
func (g *RogueGame) initColors() {
|
||||||
used := make([]bool, len(rainbow))
|
used := make([]bool, len(g.data.rainbow))
|
||||||
|
|
||||||
for i := range NumPotionTypes {
|
for i := range NumPotionTypes {
|
||||||
var j int
|
var j int
|
||||||
for {
|
for {
|
||||||
j = g.rnd(len(rainbow))
|
j = g.rnd(len(g.data.rainbow))
|
||||||
if !used[j] {
|
if !used[j] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
used[j] = true
|
used[j] = true
|
||||||
g.Items.PotColors[i] = rainbow[j]
|
g.Items.PotColors[i] = g.data.rainbow[j]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ func (g *RogueGame) initNames() {
|
|||||||
for ; nwords > 0; nwords-- {
|
for ; nwords > 0; nwords-- {
|
||||||
nsyl := g.rnd(3) + 1
|
nsyl := g.rnd(3) + 1
|
||||||
for ; nsyl > 0; nsyl-- {
|
for ; nsyl > 0; nsyl-- {
|
||||||
sp := sylls[g.rnd(len(sylls))]
|
sp := g.data.sylls[g.rnd(len(g.data.sylls))]
|
||||||
if cp.Len()+len(sp) > MaxNameLen {
|
if cp.Len()+len(sp) > MaxNameLen {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -93,47 +93,47 @@ func (g *RogueGame) initNames() {
|
|||||||
// initStones initializes the ring stone setting scheme for this game
|
// initStones initializes the ring stone setting scheme for this game
|
||||||
// (init.c init_stones).
|
// (init.c init_stones).
|
||||||
func (g *RogueGame) initStones() {
|
func (g *RogueGame) initStones() {
|
||||||
used := make([]bool, len(stoneTable))
|
used := make([]bool, len(g.data.stoneTable))
|
||||||
|
|
||||||
for i := range NumRingTypes {
|
for i := range NumRingTypes {
|
||||||
var j int
|
var j int
|
||||||
for {
|
for {
|
||||||
j = g.rnd(len(stoneTable))
|
j = g.rnd(len(g.data.stoneTable))
|
||||||
if !used[j] {
|
if !used[j] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
used[j] = true
|
used[j] = true
|
||||||
g.Items.RingStones[i] = stoneTable[j].Name
|
g.Items.RingStones[i] = g.data.stoneTable[j].Name
|
||||||
g.Items.Rings[i].Worth += stoneTable[j].Value
|
g.Items.Rings[i].Worth += g.data.stoneTable[j].Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initMaterials initializes the construction materials for wands and staffs
|
// initMaterials initializes the construction materials for wands and staffs
|
||||||
// (init.c init_materials).
|
// (init.c init_materials).
|
||||||
func (g *RogueGame) initMaterials() {
|
func (g *RogueGame) initMaterials() {
|
||||||
used := make([]bool, len(woods))
|
used := make([]bool, len(g.data.woods))
|
||||||
metused := make([]bool, len(metals))
|
metused := make([]bool, len(g.data.metals))
|
||||||
|
|
||||||
for i := range NumWandTypes {
|
for i := range NumWandTypes {
|
||||||
var str string
|
var str string
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if g.rnd(2) == 0 {
|
if g.rnd(2) == 0 {
|
||||||
j := g.rnd(len(metals))
|
j := g.rnd(len(g.data.metals))
|
||||||
if !metused[j] {
|
if !metused[j] {
|
||||||
g.Items.WandType[i] = "wand"
|
g.Items.WandType[i] = wandName
|
||||||
str = metals[j]
|
str = g.data.metals[j]
|
||||||
metused[j] = true
|
metused[j] = true
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
j := g.rnd(len(woods))
|
j := g.rnd(len(g.data.woods))
|
||||||
if !used[j] {
|
if !used[j] {
|
||||||
g.Items.WandType[i] = "staff"
|
g.Items.WandType[i] = staffName
|
||||||
str = woods[j]
|
str = g.data.woods[j]
|
||||||
used[j] = true
|
used[j] = true
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -156,13 +156,13 @@ func sumProbs(info []ObjInfo) {
|
|||||||
// initProbs copies the base tables into the game and initializes the
|
// initProbs copies the base tables into the game and initializes the
|
||||||
// probabilities for the various items (init.c init_probs).
|
// probabilities for the various items (init.c init_probs).
|
||||||
func (g *RogueGame) initProbs() {
|
func (g *RogueGame) initProbs() {
|
||||||
g.Items.Things = baseThings
|
g.Items.Things = g.data.baseThings
|
||||||
g.Items.Potions = basePotInfo
|
g.Items.Potions = g.data.basePotInfo
|
||||||
g.Items.Scrolls = baseScrInfo
|
g.Items.Scrolls = g.data.baseScrInfo
|
||||||
g.Items.Rings = baseRingInfo
|
g.Items.Rings = g.data.baseRingInfo
|
||||||
g.Items.Sticks = baseWsInfo
|
g.Items.Sticks = g.data.baseWsInfo
|
||||||
g.Items.Weapons = baseWeapInfo
|
g.Items.Weapons = g.data.baseWeapInfo
|
||||||
g.Items.Armors = baseArmInfo
|
g.Items.Armors = g.data.baseArmInfo
|
||||||
|
|
||||||
sumProbs(g.Items.Things[:])
|
sumProbs(g.Items.Things[:])
|
||||||
sumProbs(g.Items.Potions[:])
|
sumProbs(g.Items.Potions[:])
|
||||||
@@ -177,7 +177,7 @@ func (g *RogueGame) initProbs() {
|
|||||||
// hallucinating (init.c pick_color).
|
// hallucinating (init.c pick_color).
|
||||||
func (g *RogueGame) pickColor(col string) string {
|
func (g *RogueGame) pickColor(col string) string {
|
||||||
if g.Player.On(Hallucinating) {
|
if g.Player.On(Hallucinating) {
|
||||||
return rainbow[g.rnd(len(rainbow))]
|
return g.data.rainbow[g.rnd(len(g.data.rainbow))]
|
||||||
}
|
}
|
||||||
|
|
||||||
return col
|
return col
|
||||||
|
|||||||
@@ -146,8 +146,6 @@ type statusCache struct {
|
|||||||
init bool
|
init bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var hungerStateName = [...]string{"", "Hungry", "Weak", "Faint"}
|
|
||||||
|
|
||||||
// status displays the important stats line, keeping the cursor where it was
|
// status displays the important stats line, keeping the cursor where it was
|
||||||
// (io.c status).
|
// (io.c status).
|
||||||
func (g *RogueGame) status() {
|
func (g *RogueGame) status() {
|
||||||
@@ -192,7 +190,7 @@ func (g *RogueGame) status() {
|
|||||||
"Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%d %s",
|
"Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%d %s",
|
||||||
g.Depth, p.Purse, s.hpwidth, p.Stats.HP, s.hpwidth, p.Stats.MaxHP,
|
g.Depth, p.Purse, s.hpwidth, p.Stats.HP, s.hpwidth, p.Stats.MaxHP,
|
||||||
p.Stats.Str, p.MaxStats.Str, 10-s.arm, p.Stats.Lvl, p.Stats.Exp,
|
p.Stats.Str, p.MaxStats.Str, 10-s.arm, p.Stats.Lvl, p.Stats.Exp,
|
||||||
hungerStateName[p.HungryState])
|
g.data.hungerStateName[p.HungryState])
|
||||||
if g.StatMsg {
|
if g.StatMsg {
|
||||||
g.move(0, 0)
|
g.move(0, 0)
|
||||||
g.msg("%s", line)
|
g.msg("%s", line)
|
||||||
|
|||||||
15
game/misc.go
15
game/misc.go
@@ -286,8 +286,8 @@ func (g *RogueGame) checkLevel() {
|
|||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for i = 0; eLevels[i] != 0; i++ {
|
for i = 0; g.data.eLevels[i] != 0; i++ {
|
||||||
if eLevels[i] > p.Stats.Exp {
|
if g.data.eLevels[i] > p.Stats.Exp {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,22 +493,17 @@ func (g *RogueGame) callIt(info *ObjInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// thingList is misc.c rnd_thing()'s static table.
|
|
||||||
var thingList = []byte{
|
|
||||||
Potion, Scroll, Ring, Stick, Food, Weapon, Armor, Stairs, Gold, Amulet,
|
|
||||||
}
|
|
||||||
|
|
||||||
// rndThing picks a random thing appropriate for this level (misc.c
|
// rndThing picks a random thing appropriate for this level (misc.c
|
||||||
// rnd_thing).
|
// rnd_thing).
|
||||||
func (g *RogueGame) rndThing() byte {
|
func (g *RogueGame) rndThing() byte {
|
||||||
var i int
|
var i int
|
||||||
if g.Depth >= AmuletLevel {
|
if g.Depth >= AmuletLevel {
|
||||||
i = g.rnd(len(thingList))
|
i = g.rnd(len(g.data.thingList))
|
||||||
} else {
|
} else {
|
||||||
i = g.rnd(len(thingList) - 1)
|
i = g.rnd(len(g.data.thingList) - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return thingList[i]
|
return g.data.thingList[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// chooseStr picks the first or second string depending on whether the
|
// chooseStr picks the first or second string depending on whether the
|
||||||
|
|||||||
@@ -2,24 +2,12 @@ package game
|
|||||||
|
|
||||||
// monsters.c — monster creation and saving throws.
|
// monsters.c — monster creation and saving throws.
|
||||||
|
|
||||||
// lvlMons and wandMons list monsters in rough order of vorpalness; zero
|
|
||||||
// entries in wandMons never wander (monsters.c).
|
|
||||||
var lvlMons = [26]byte{
|
|
||||||
'K', 'E', 'B', 'S', 'H', 'I', 'R', 'O', 'Z', 'L', 'C', 'Q', 'A',
|
|
||||||
'N', 'Y', 'F', 'T', 'W', 'P', 'X', 'U', 'M', 'V', 'G', 'J', 'D',
|
|
||||||
}
|
|
||||||
|
|
||||||
var wandMons = [26]byte{
|
|
||||||
'K', 'E', 'B', 'S', 'H', 0, 'R', 'O', 'Z', 0, 'C', 'Q', 'A',
|
|
||||||
0, 'Y', 0, 'T', 'W', 'P', 0, 'U', 'M', 'V', 'G', 'J', 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// randMonster picks a monster to show up; the lower the level, the meaner
|
// randMonster picks a monster to show up; the lower the level, the meaner
|
||||||
// the monster (monsters.c randmonster).
|
// the monster (monsters.c randmonster).
|
||||||
func (g *RogueGame) randMonster(wander bool) byte {
|
func (g *RogueGame) randMonster(wander bool) byte {
|
||||||
mons := &lvlMons
|
mons := &g.data.lvlMons
|
||||||
if wander {
|
if wander {
|
||||||
mons = &wandMons
|
mons = &g.data.wandMons
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -255,17 +255,17 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
case 0:
|
case 0:
|
||||||
g.msg("you are suddenly in a parallel dimension")
|
g.msg("you are suddenly in a parallel dimension")
|
||||||
case 1:
|
case 1:
|
||||||
g.msg("the light in here suddenly seems %s", rainbow[g.rnd(len(rainbow))])
|
g.msg("the light in here suddenly seems %s", g.data.rainbow[g.rnd(len(g.data.rainbow))])
|
||||||
case 2:
|
case 2:
|
||||||
g.msg("you feel a sting in the side of your neck")
|
g.msg("you feel a sting in the side of your neck")
|
||||||
case 3:
|
case 3:
|
||||||
g.msg("multi-colored lines swirl around you, then fade")
|
g.msg("multi-colored lines swirl around you, then fade")
|
||||||
case 4:
|
case 4:
|
||||||
g.msg("a %s light flashes in your eyes", rainbow[g.rnd(len(rainbow))])
|
g.msg("a %s light flashes in your eyes", g.data.rainbow[g.rnd(len(g.data.rainbow))])
|
||||||
case 5:
|
case 5:
|
||||||
g.msg("a spike shoots past your ear!")
|
g.msg("a spike shoots past your ear!")
|
||||||
case 6:
|
case 6:
|
||||||
g.msg("%s sparks dance across your armor", rainbow[g.rnd(len(rainbow))])
|
g.msg("%s sparks dance across your armor", g.data.rainbow[g.rnd(len(g.data.rainbow))])
|
||||||
case 7:
|
case 7:
|
||||||
g.msg("you suddenly feel very thirsty")
|
g.msg("you suddenly feel very thirsty")
|
||||||
case 8:
|
case 8:
|
||||||
@@ -273,7 +273,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
case 9:
|
case 9:
|
||||||
g.msg("time now seems to be going slower")
|
g.msg("time now seems to be going slower")
|
||||||
case 10:
|
case 10:
|
||||||
g.msg("you pack turns %s!", rainbow[g.rnd(len(rainbow))])
|
g.msg("you pack turns %s!", g.data.rainbow[g.rnd(len(g.data.rainbow))])
|
||||||
}
|
}
|
||||||
case TrapSleep:
|
case TrapSleep:
|
||||||
g.NoCommand += g.spread(5) // SLEEPTIME
|
g.NoCommand += g.spread(5) // SLEEPTIME
|
||||||
|
|||||||
@@ -26,36 +26,49 @@ const (
|
|||||||
KindRingOrStick ObjectKind = -2
|
KindRingOrStick ObjectKind = -2
|
||||||
)
|
)
|
||||||
|
|
||||||
// kindGlyphs maps each kind to the character Rogue draws for it.
|
// Category words shared by ObjectKind.String, the discovery list, and the
|
||||||
var kindGlyphs = [...]byte{
|
// ident table. (The bare identifiers Potion, Scroll, Ring, Gold are the
|
||||||
KindNone: ' ',
|
// glyph byte constants.)
|
||||||
KindPotion: Potion,
|
const (
|
||||||
KindScroll: Scroll,
|
potionName = "potion"
|
||||||
KindFood: Food,
|
scrollName = "scroll"
|
||||||
KindWeapon: Weapon,
|
ringName = "ring"
|
||||||
KindArmor: Armor,
|
goldName = "gold"
|
||||||
KindRing: Ring,
|
)
|
||||||
KindWand: Stick,
|
|
||||||
KindAmulet: Amulet,
|
|
||||||
KindGold: Gold,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Glyph returns the map/display character for this kind of object.
|
// Glyph returns the map/display character for this kind of object.
|
||||||
func (k ObjectKind) Glyph() byte {
|
func (k ObjectKind) Glyph() byte {
|
||||||
if k < 0 || int(k) >= len(kindGlyphs) {
|
switch k {
|
||||||
return ' '
|
case KindPotion:
|
||||||
|
return Potion
|
||||||
|
case KindScroll:
|
||||||
|
return Scroll
|
||||||
|
case KindFood:
|
||||||
|
return Food
|
||||||
|
case KindWeapon:
|
||||||
|
return Weapon
|
||||||
|
case KindArmor:
|
||||||
|
return Armor
|
||||||
|
case KindRing:
|
||||||
|
return Ring
|
||||||
|
case KindWand:
|
||||||
|
return Stick
|
||||||
|
case KindAmulet:
|
||||||
|
return Amulet
|
||||||
|
case KindGold:
|
||||||
|
return Gold
|
||||||
}
|
}
|
||||||
|
|
||||||
return kindGlyphs[k]
|
return ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
// String names the category the way the C type_name() did.
|
// String names the category the way the C type_name() did.
|
||||||
func (k ObjectKind) String() string {
|
func (k ObjectKind) String() string {
|
||||||
switch k {
|
switch k {
|
||||||
case KindPotion:
|
case KindPotion:
|
||||||
return "potion"
|
return potionName
|
||||||
case KindScroll:
|
case KindScroll:
|
||||||
return "scroll"
|
return scrollName
|
||||||
case KindFood:
|
case KindFood:
|
||||||
return "food"
|
return "food"
|
||||||
case KindWeapon:
|
case KindWeapon:
|
||||||
@@ -63,13 +76,13 @@ func (k ObjectKind) String() string {
|
|||||||
case KindArmor:
|
case KindArmor:
|
||||||
return "suit of armor"
|
return "suit of armor"
|
||||||
case KindRing:
|
case KindRing:
|
||||||
return "ring"
|
return ringName
|
||||||
case KindWand:
|
case KindWand:
|
||||||
return "wand or staff"
|
return "wand or staff"
|
||||||
case KindAmulet:
|
case KindAmulet:
|
||||||
return "amulet"
|
return "amulet"
|
||||||
case KindGold:
|
case KindGold:
|
||||||
return "gold"
|
return goldName
|
||||||
case KindRingOrStick:
|
case KindRingOrStick:
|
||||||
return "ring, wand or staff"
|
return "ring, wand or staff"
|
||||||
}
|
}
|
||||||
@@ -80,10 +93,25 @@ func (k ObjectKind) String() string {
|
|||||||
// objectKindForGlyph is the reverse of Glyph: what category of item does a
|
// objectKindForGlyph is the reverse of Glyph: what category of item does a
|
||||||
// map character denote. Returns KindNone for non-item characters.
|
// map character denote. Returns KindNone for non-item characters.
|
||||||
func objectKindForGlyph(ch byte) ObjectKind {
|
func objectKindForGlyph(ch byte) ObjectKind {
|
||||||
for k, g := range kindGlyphs {
|
switch ch {
|
||||||
if g == ch && ObjectKind(k) != KindNone {
|
case Potion:
|
||||||
return ObjectKind(k)
|
return KindPotion
|
||||||
}
|
case Scroll:
|
||||||
|
return KindScroll
|
||||||
|
case Food:
|
||||||
|
return KindFood
|
||||||
|
case Weapon:
|
||||||
|
return KindWeapon
|
||||||
|
case Armor:
|
||||||
|
return KindArmor
|
||||||
|
case Ring:
|
||||||
|
return KindRing
|
||||||
|
case Stick:
|
||||||
|
return KindWand
|
||||||
|
case Amulet:
|
||||||
|
return KindAmulet
|
||||||
|
case Gold:
|
||||||
|
return KindGold
|
||||||
}
|
}
|
||||||
|
|
||||||
return KindNone
|
return KindNone
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (g *RogueGame) putOpt(op *optDesc) {
|
|||||||
case optBool, optSeeFloor:
|
case optBool, optSeeFloor:
|
||||||
hw.AddStr(boolStr(*op.boolP))
|
hw.AddStr(boolStr(*op.boolP))
|
||||||
case optInvT:
|
case optInvT:
|
||||||
hw.AddStr(invTName[*op.intP])
|
hw.AddStr(g.data.invTName[*op.intP])
|
||||||
case optStr:
|
case optStr:
|
||||||
hw.AddStr(*op.strP)
|
hw.AddStr(*op.strP)
|
||||||
}
|
}
|
||||||
@@ -291,7 +291,7 @@ func displayStr(buf []byte) string {
|
|||||||
func (g *RogueGame) getInvT(ip *int) int {
|
func (g *RogueGame) getInvT(ip *int) int {
|
||||||
win := g.scr.Hw
|
win := g.scr.Hw
|
||||||
oy, ox := win.GetYX()
|
oy, ox := win.GetYX()
|
||||||
win.AddStr(invTName[*ip])
|
win.AddStr(g.data.invTName[*ip])
|
||||||
|
|
||||||
for {
|
for {
|
||||||
win.Move(oy, ox)
|
win.Move(oy, ox)
|
||||||
@@ -319,7 +319,7 @@ func (g *RogueGame) getInvT(ip *int) int {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
win.MvPrintwf(oy, ox, "%s\n", invTName[*ip])
|
win.MvPrintwf(oy, ox, "%s\n", g.data.invTName[*ip])
|
||||||
|
|
||||||
return Norm
|
return Norm
|
||||||
}
|
}
|
||||||
@@ -383,7 +383,7 @@ func (g *RogueGame) ParseOpts(str string) {
|
|||||||
w = string(toUpper(w[0])) + w[1:]
|
w = string(toUpper(w[0])) + w[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
for ti, tn := range invTName {
|
for ti, tn := range g.data.invTName {
|
||||||
if strings.HasPrefix(tn, w) {
|
if strings.HasPrefix(tn, w) {
|
||||||
*op.intP = ti
|
*op.intP = ti
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,6 @@ package game
|
|||||||
|
|
||||||
// passages.c — draw the connecting passages.
|
// passages.c — draw the connecting passages.
|
||||||
|
|
||||||
// rdesConn is the hardcoded 3x3 room adjacency matrix from do_passages.
|
|
||||||
var rdesConn = [MaxRooms][MaxRooms]bool{
|
|
||||||
{false, true, false, true, false, false, false, false, false},
|
|
||||||
{true, false, true, false, true, false, false, false, false},
|
|
||||||
{false, true, false, false, false, true, false, false, false},
|
|
||||||
{true, false, false, false, true, false, true, false, false},
|
|
||||||
{false, true, false, true, false, true, false, true, false},
|
|
||||||
{false, false, true, false, true, false, false, false, true},
|
|
||||||
{false, false, false, true, false, false, false, true, false},
|
|
||||||
{false, false, false, false, true, false, true, false, true},
|
|
||||||
{false, false, false, false, false, true, false, true, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// doPassages draws all the passages on a level (passages.c do_passages).
|
// doPassages draws all the passages on a level (passages.c do_passages).
|
||||||
func (g *RogueGame) doPassages() {
|
func (g *RogueGame) doPassages() {
|
||||||
var (
|
var (
|
||||||
@@ -34,7 +21,7 @@ func (g *RogueGame) doPassages() {
|
|||||||
r2 := -1
|
r2 := -1
|
||||||
|
|
||||||
for i := range MaxRooms {
|
for i := range MaxRooms {
|
||||||
if rdesConn[r1][i] && !ingraph[i] {
|
if g.data.rdesConn[r1][i] && !ingraph[i] {
|
||||||
if j++; g.rnd(j) == 0 {
|
if j++; g.rnd(j) == 0 {
|
||||||
r2 = i
|
r2 = i
|
||||||
}
|
}
|
||||||
@@ -74,7 +61,7 @@ func (g *RogueGame) doPassages() {
|
|||||||
r2 := -1
|
r2 := -1
|
||||||
|
|
||||||
for i := range MaxRooms {
|
for i := range MaxRooms {
|
||||||
if rdesConn[r1][i] && !isconn[r1][i] {
|
if g.data.rdesConn[r1][i] && !isconn[r1][i] {
|
||||||
if j++; g.rnd(j) == 0 {
|
if j++; g.rnd(j) == 0 {
|
||||||
r2 = i
|
r2 = i
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,24 +13,6 @@ type pact struct {
|
|||||||
straight string
|
straight string
|
||||||
}
|
}
|
||||||
|
|
||||||
// pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic
|
|
||||||
// (it names the fruit) and is computed in doPot.
|
|
||||||
var pActions = [NumPotionTypes]pact{
|
|
||||||
PotionConfusion: {Confused, DUnconfuse, HuhDuration,
|
|
||||||
"what a tripy feeling!",
|
|
||||||
"wait, what's going on here. Huh? What? Who?"},
|
|
||||||
PotionLSD: {Hallucinating, DComeDown, SeeDuration,
|
|
||||||
"Oh, wow! Everything seems so cosmic!",
|
|
||||||
"Oh, wow! Everything seems so cosmic!"},
|
|
||||||
PotionSeeInvisible: {CanSeeInvisible, DUnsee, SeeDuration, "", ""},
|
|
||||||
PotionBlindness: {Blind, DSight, SeeDuration,
|
|
||||||
"oh, bummer! Everything is dark! Help!",
|
|
||||||
"a cloak of darkness falls around you"},
|
|
||||||
PotionLevitation: {Levitating, DLand, HealTime,
|
|
||||||
"oh, wow! You're floating in the air!",
|
|
||||||
"you start to float in the air"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// quaff drinks a potion from the pack (potions.c quaff).
|
// quaff drinks a potion from the pack (potions.c quaff).
|
||||||
func (g *RogueGame) quaff() {
|
func (g *RogueGame) quaff() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
@@ -100,7 +82,7 @@ func (g *RogueGame) quaff() {
|
|||||||
g.scr.Hw.Clear()
|
g.scr.Hw.Clear()
|
||||||
|
|
||||||
for _, tp := range g.Level.Objects {
|
for _, tp := range g.Level.Objects {
|
||||||
if tp.isMagic() {
|
if g.isMagic(tp) {
|
||||||
show = true
|
show = true
|
||||||
|
|
||||||
g.scr.Hw.MvAddCh(tp.Pos.Y, tp.Pos.X, Magic)
|
g.scr.Hw.MvAddCh(tp.Pos.Y, tp.Pos.X, Magic)
|
||||||
@@ -110,7 +92,7 @@ func (g *RogueGame) quaff() {
|
|||||||
|
|
||||||
for _, mp := range g.Level.Monsters {
|
for _, mp := range g.Level.Monsters {
|
||||||
for _, tp := range mp.Pack {
|
for _, tp := range mp.Pack {
|
||||||
if tp.isMagic() {
|
if g.isMagic(tp) {
|
||||||
show = true
|
show = true
|
||||||
|
|
||||||
g.scr.Hw.MvAddCh(mp.Pos.Y, mp.Pos.X, Magic)
|
g.scr.Hw.MvAddCh(mp.Pos.Y, mp.Pos.X, Magic)
|
||||||
@@ -208,14 +190,14 @@ func (g *RogueGame) quaff() {
|
|||||||
// raiseLevel: the guy just magically went up a level (potions.c
|
// raiseLevel: the guy just magically went up a level (potions.c
|
||||||
// raise_level).
|
// raise_level).
|
||||||
func (g *RogueGame) raiseLevel() {
|
func (g *RogueGame) raiseLevel() {
|
||||||
g.Player.Stats.Exp = eLevels[g.Player.Stats.Lvl-1] + 1
|
g.Player.Stats.Exp = g.data.eLevels[g.Player.Stats.Lvl-1] + 1
|
||||||
g.checkLevel()
|
g.checkLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// doPot does a potion with standard setup: it uses a fuse and turns on a
|
// doPot does a potion with standard setup: it uses a fuse and turns on a
|
||||||
// flag (potions.c do_pot).
|
// flag (potions.c do_pot).
|
||||||
func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
||||||
pp := &pActions[kind]
|
pp := &g.data.pActions[kind]
|
||||||
if !g.Items.Potions[kind].Know {
|
if !g.Items.Potions[kind].Know {
|
||||||
g.Items.Potions[kind].Know = knowit
|
g.Items.Potions[kind].Know = knowit
|
||||||
}
|
}
|
||||||
@@ -240,10 +222,10 @@ func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isMagic reports whether an object radiates magic (potions.c is_magic).
|
// isMagic reports whether an object radiates magic (potions.c is_magic).
|
||||||
func (o *Object) isMagic() bool {
|
func (g *RogueGame) isMagic(o *Object) bool {
|
||||||
switch o.Kind {
|
switch o.Kind {
|
||||||
case KindArmor:
|
case KindArmor:
|
||||||
return o.Flags.Has(Protected) || o.ArmorClass != aClass[o.Which]
|
return o.Flags.Has(Protected) || o.ArmorClass != g.data.aClass[o.Which]
|
||||||
case KindWeapon:
|
case KindWeapon:
|
||||||
return o.HPlus != 0 || o.DPlus != 0
|
return o.HPlus != 0 || o.DPlus != 0
|
||||||
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
|
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
|
||||||
|
|||||||
@@ -139,25 +139,6 @@ func (g *RogueGame) gethand() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ringUses is the rings.c ring_eat static uses[] table: how much food each
|
|
||||||
// ring type uses up per turn (negative = a 1-in-n chance of 1).
|
|
||||||
var ringUses = [NumRingTypes]int{
|
|
||||||
1, // R_PROTECT
|
|
||||||
1, // R_ADDSTR
|
|
||||||
1, // R_SUSTSTR
|
|
||||||
-3, // R_SEARCH
|
|
||||||
-5, // R_SEEINVIS
|
|
||||||
0, // R_NOP
|
|
||||||
0, // R_AGGR
|
|
||||||
-3, // R_ADDHIT
|
|
||||||
-3, // R_ADDDAM
|
|
||||||
2, // R_REGEN
|
|
||||||
-2, // R_DIGEST
|
|
||||||
0, // R_TELEPORT
|
|
||||||
1, // R_STEALTH
|
|
||||||
1, // R_SUSTARM
|
|
||||||
}
|
|
||||||
|
|
||||||
// ringEat reports how much food the ring on the given hand uses up
|
// ringEat reports how much food the ring on the given hand uses up
|
||||||
// (rings.c ring_eat).
|
// (rings.c ring_eat).
|
||||||
func (g *RogueGame) ringEat(hand int) int {
|
func (g *RogueGame) ringEat(hand int) int {
|
||||||
@@ -166,7 +147,7 @@ func (g *RogueGame) ringEat(hand int) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
eat := ringUses[ring.RingKind()]
|
eat := g.data.ringUses[ring.RingKind()]
|
||||||
if eat < 0 {
|
if eat < 0 {
|
||||||
if g.rnd(-eat) == 0 {
|
if g.rnd(-eat) == 0 {
|
||||||
eat = 1
|
eat = 1
|
||||||
|
|||||||
31
game/rip.go
31
game/rip.go
@@ -21,22 +21,6 @@ func (g *RogueGame) myExit() {
|
|||||||
panic(gameEnd{})
|
panic(gameEnd{})
|
||||||
}
|
}
|
||||||
|
|
||||||
var ripArt = []string{
|
|
||||||
" __________",
|
|
||||||
" / \\",
|
|
||||||
" / REST \\",
|
|
||||||
" / IN \\",
|
|
||||||
" / PEACE \\",
|
|
||||||
" / \\",
|
|
||||||
" | |",
|
|
||||||
" | |",
|
|
||||||
" | killed by a |",
|
|
||||||
" | |",
|
|
||||||
" | 1980 |",
|
|
||||||
" *| * * * | *",
|
|
||||||
" ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______",
|
|
||||||
}
|
|
||||||
|
|
||||||
// death does something really fun when he dies (rip.c death).
|
// death does something really fun when he dies (rip.c death).
|
||||||
func (g *RogueGame) death(monst byte) {
|
func (g *RogueGame) death(monst byte) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
@@ -56,7 +40,7 @@ func (g *RogueGame) death(monst byte) {
|
|||||||
} else {
|
} else {
|
||||||
year := time.Now().Year()
|
year := time.Now().Year()
|
||||||
|
|
||||||
for i, line := range ripArt {
|
for i, line := range g.data.ripArt {
|
||||||
g.scr.Std.MvAddStr(8+i, 0, line)
|
g.scr.Std.MvAddStr(8+i, 0, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +128,7 @@ func (g *RogueGame) totalWinner() {
|
|||||||
case KindArmor:
|
case KindArmor:
|
||||||
worth = it.Armors[obj.Which].Worth
|
worth = it.Armors[obj.Which].Worth
|
||||||
worth += (9 - obj.ArmorClass) * 100
|
worth += (9 - obj.ArmorClass) * 100
|
||||||
worth += 10 * (aClass[obj.Which] - obj.ArmorClass)
|
worth += 10 * (g.data.aClass[obj.Which] - obj.ArmorClass)
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
case KindScroll:
|
case KindScroll:
|
||||||
op := &it.Scrolls[obj.Which]
|
op := &it.Scrolls[obj.Which]
|
||||||
@@ -216,15 +200,6 @@ func (g *RogueGame) totalWinner() {
|
|||||||
g.myExit()
|
g.myExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// killnameTable is the rip.c nlist[]: special death causes.
|
|
||||||
var killnameTable = []helpEntry{
|
|
||||||
{'a', "arrow", true},
|
|
||||||
{'b', "bolt", true},
|
|
||||||
{'d', "dart", true},
|
|
||||||
{'h', "hypothermia", false},
|
|
||||||
{'s', "starvation", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// killname converts a code to a monster name (rip.c killname).
|
// killname converts a code to a monster name (rip.c killname).
|
||||||
func (g *RogueGame) killname(monst byte, doart bool) string {
|
func (g *RogueGame) killname(monst byte, doart bool) string {
|
||||||
var (
|
var (
|
||||||
@@ -239,7 +214,7 @@ func (g *RogueGame) killname(monst byte, doart bool) string {
|
|||||||
sp = "Wally the Wonder Badger"
|
sp = "Wally the Wonder Badger"
|
||||||
article = false
|
article = false
|
||||||
|
|
||||||
for _, hp := range killnameTable {
|
for _, hp := range g.data.killnameTable {
|
||||||
if hp.Ch == monst {
|
if hp.Ch == monst {
|
||||||
sp = hp.Desc
|
sp = hp.Desc
|
||||||
article = hp.Print
|
article = hp.Print
|
||||||
|
|||||||
@@ -606,6 +606,7 @@ func Restore(path string, cfg Config) (*RogueGame, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g := &RogueGame{
|
g := &RogueGame{
|
||||||
|
data: newGameData(),
|
||||||
Rng: &Rng{},
|
Rng: &Rng{},
|
||||||
Playing: true,
|
Playing: true,
|
||||||
ScorePath: cfg.ScorePath,
|
ScorePath: cfg.ScorePath,
|
||||||
|
|||||||
@@ -25,13 +25,6 @@ type ScoreEnt struct {
|
|||||||
Time int64
|
Time int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var scoreReasons = [4]string{
|
|
||||||
"killed",
|
|
||||||
"quit",
|
|
||||||
"A total winner",
|
|
||||||
"killed with Amulet",
|
|
||||||
}
|
|
||||||
|
|
||||||
// rdScore reads the scoreboard file (save.c rd_score).
|
// rdScore reads the scoreboard file (save.c rd_score).
|
||||||
func (g *RogueGame) rdScore() []ScoreEnt {
|
func (g *RogueGame) rdScore() []ScoreEnt {
|
||||||
topTen := make([]ScoreEnt, numScores)
|
topTen := make([]ScoreEnt, numScores)
|
||||||
@@ -187,7 +180,7 @@ func (g *RogueGame) score(amount, flags int, monst byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
line := fmt.Sprintf("%2d %5d %s: %s on level %d", i+1,
|
line := fmt.Sprintf("%2d %5d %s: %s on level %d", i+1,
|
||||||
scp.Score, scp.Name, scoreReasons[scp.Flags], scp.Level)
|
scp.Score, scp.Name, g.data.scoreReasons[scp.Flags], scp.Level)
|
||||||
if scp.Flags == 0 || scp.Flags == 3 {
|
if scp.Flags == 0 || scp.Flags == 3 {
|
||||||
line += " by " + g.killname(scp.Monster, true)
|
line += " by " + g.killname(scp.Monster, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,6 @@ package game
|
|||||||
|
|
||||||
// scrolls.c — read a scroll and let it happen.
|
// scrolls.c — read a scroll and let it happen.
|
||||||
|
|
||||||
// idType maps identify scrolls to the kind of item they identify
|
|
||||||
// (scrolls.c static id_type).
|
|
||||||
var idType = [ScrollIdentifyRingOrStick + 1]ObjectKind{
|
|
||||||
ScrollIdentifyPotion: KindPotion,
|
|
||||||
ScrollIdentifyScroll: KindScroll,
|
|
||||||
ScrollIdentifyWeapon: KindWeapon,
|
|
||||||
ScrollIdentifyArmor: KindArmor,
|
|
||||||
ScrollIdentifyRingOrStick: KindRingOrStick,
|
|
||||||
}
|
|
||||||
|
|
||||||
// readScroll reads a scroll from the pack and does the appropriate thing
|
// readScroll reads a scroll from the pack and does the appropriate thing
|
||||||
// (scrolls.c read_scroll).
|
// (scrolls.c read_scroll).
|
||||||
func (g *RogueGame) readScroll() {
|
func (g *RogueGame) readScroll() {
|
||||||
@@ -136,7 +126,7 @@ func (g *RogueGame) readScroll() {
|
|||||||
// Identify, let him figure something out
|
// Identify, let him figure something out
|
||||||
g.Items.Scrolls[obj.Which].Know = true
|
g.Items.Scrolls[obj.Which].Know = true
|
||||||
g.msg("this scroll is an %s scroll", g.Items.Scrolls[obj.Which].Name)
|
g.msg("this scroll is an %s scroll", g.Items.Scrolls[obj.Which].Name)
|
||||||
g.whatis(true, idType[obj.ScrollKind()])
|
g.whatis(true, g.data.idType[obj.ScrollKind()])
|
||||||
case ScrollMagicMapping:
|
case ScrollMagicMapping:
|
||||||
// Scroll of magic mapping.
|
// Scroll of magic mapping.
|
||||||
g.Items.Scrolls[ScrollMagicMapping].Know = true
|
g.Items.Scrolls[ScrollMagicMapping].Know = true
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ import "fmt"
|
|||||||
|
|
||||||
// sticks.c — zap wands and staffs.
|
// sticks.c — zap wands and staffs.
|
||||||
|
|
||||||
|
// The two ws_type strings a stick can be made as.
|
||||||
|
const (
|
||||||
|
wandName = "wand"
|
||||||
|
staffName = "staff"
|
||||||
|
)
|
||||||
|
|
||||||
// doZap performs a zap with a wand (sticks.c do_zap).
|
// doZap performs a zap with a wand (sticks.c do_zap).
|
||||||
func (g *RogueGame) doZap() {
|
func (g *RogueGame) doZap() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
@@ -378,7 +384,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
|
|||||||
|
|
||||||
// fixStick sets up a new wand or staff (sticks.c fix_stick).
|
// fixStick sets up a new wand or staff (sticks.c fix_stick).
|
||||||
func (g *RogueGame) fixStick(cur *Object) {
|
func (g *RogueGame) fixStick(cur *Object) {
|
||||||
if g.Items.WandType[cur.Which] == "staff" {
|
if g.Items.WandType[cur.Which] == staffName {
|
||||||
cur.Damage = dice("2x3")
|
cur.Damage = dice("2x3")
|
||||||
} else {
|
} else {
|
||||||
cur.Damage = dice("1x1")
|
cur.Damage = dice("1x1")
|
||||||
|
|||||||
397
game/tables.go
397
game/tables.go
@@ -1,15 +1,141 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
// This file is the immutable data from extern.c and init.c: tables that are
|
// tables.go — the static data tables the C game kept as file-scope globals
|
||||||
// never written after program start. Per-game mutable copies (the ObjInfo
|
// (extern.c, init.c, and assorted per-file statics). The port gathers them
|
||||||
// tables, whose probabilities are re-summed and whose Know/Guess fields
|
// into gameData: every RogueGame carries its own copy, so the package holds
|
||||||
// change during play) are cloned into RogueGame.Items by NewGame.
|
// no package-level state. Per-game mutable copies (the ObjInfo tables,
|
||||||
|
// whose probabilities are re-summed and whose Know/Guess fields change
|
||||||
|
// during play) are cloned into RogueGame.Items by NewGame.
|
||||||
|
|
||||||
|
// helpEntry is rogue.h struct h_list.
|
||||||
|
type helpEntry struct {
|
||||||
|
Ch byte
|
||||||
|
Desc string
|
||||||
|
Print bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// gameData is the bundle of static tables, built by newGameData and hung on
|
||||||
|
// RogueGame as g.data. Nothing in it mutates during play: the one table the
|
||||||
|
// C code writes to (the venus flytrap damage hack) operates on the game's
|
||||||
|
// Monsters copy, not on this template.
|
||||||
|
type gameData struct {
|
||||||
// initStats is the C INIT_STATS: the player's starting statistics.
|
// initStats is the C INIT_STATS: the player's starting statistics.
|
||||||
var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, ArmorClass: 10, HP: 12, Dmg: dice("1x4"), MaxHP: 12}
|
initStats Stats
|
||||||
|
|
||||||
// aClass is extern.c a_class[]: armor class for each armor type.
|
// aClass is extern.c a_class[]: armor class for each armor type.
|
||||||
var aClass = [NumArmorTypes]int{
|
aClass [NumArmorTypes]int
|
||||||
|
|
||||||
|
// eLevels is extern.c e_levels[]: experience thresholds per level; the
|
||||||
|
// zero terminates the table as in C.
|
||||||
|
eLevels []int
|
||||||
|
|
||||||
|
// trName is extern.c tr_name[]: names of the traps.
|
||||||
|
trName [NumTrapTypes]string
|
||||||
|
|
||||||
|
// invTName is extern.c inv_t_name[]: the inventory style names.
|
||||||
|
invTName []string
|
||||||
|
|
||||||
|
// monsterTable is extern.c monsters[26]: all monster kinds, indexed by
|
||||||
|
// letter - 'A'. Monster strength (XX in C) is always 10; HP (___ in C)
|
||||||
|
// is rolled from the level at creation time.
|
||||||
|
monsterTable [26]MonsterKind
|
||||||
|
|
||||||
|
// Base ObjInfo tables (extern.c). These are templates: NewGame copies
|
||||||
|
// them into ItemLore before initProbs converts Prob to cumulative form.
|
||||||
|
baseThings [NumThings]ObjInfo
|
||||||
|
baseArmInfo [NumArmorTypes]ObjInfo
|
||||||
|
basePotInfo [NumPotionTypes]ObjInfo
|
||||||
|
baseRingInfo [NumRingTypes]ObjInfo
|
||||||
|
baseScrInfo [NumScrollTypes]ObjInfo
|
||||||
|
baseWeapInfo [NumWeaponTypes + 1]ObjInfo
|
||||||
|
baseWsInfo [NumWandTypes]ObjInfo
|
||||||
|
|
||||||
|
// rainbow is init.c rainbow[]: the possible potion colors.
|
||||||
|
rainbow []string
|
||||||
|
|
||||||
|
// sylls is init.c sylls[]: syllables for generated scroll names.
|
||||||
|
sylls []string
|
||||||
|
|
||||||
|
// stoneTable is init.c stones[]: ring stones and their worth.
|
||||||
|
stoneTable []Stone
|
||||||
|
|
||||||
|
// woods is init.c wood[]: what staffs are made of.
|
||||||
|
woods []string
|
||||||
|
|
||||||
|
// metals is init.c metal[]: what wands are made of.
|
||||||
|
metals []string
|
||||||
|
|
||||||
|
// helpStr is extern.c helpstr[]: the '?' command help text.
|
||||||
|
helpStr []helpEntry
|
||||||
|
|
||||||
|
// hNames are the strings for hitting; the first four are used when the
|
||||||
|
// player strikes, the second four for monsters (fight.c h_names).
|
||||||
|
hNames [8]string
|
||||||
|
|
||||||
|
// mNames are the strings for missing (fight.c m_names).
|
||||||
|
mNames [8]string
|
||||||
|
|
||||||
|
// strPlus adjusts hit probabilities due to strength (fight.c str_plus).
|
||||||
|
strPlus [32]int
|
||||||
|
|
||||||
|
// addDam adjusts damage done due to strength (fight.c add_dam).
|
||||||
|
addDam [32]int
|
||||||
|
|
||||||
|
// lvlMons and wandMons list monsters in rough order of vorpalness;
|
||||||
|
// zero entries in wandMons never wander (monsters.c).
|
||||||
|
lvlMons [26]byte
|
||||||
|
wandMons [26]byte
|
||||||
|
|
||||||
|
// ringUses is the rings.c ring_eat static uses[] table: how much food
|
||||||
|
// each ring type uses up per turn (negative = a 1-in-n chance of 1).
|
||||||
|
ringUses [NumRingTypes]int
|
||||||
|
|
||||||
|
// initWeaps is the weapons.c init_dam[] table.
|
||||||
|
initWeaps [NumWeaponTypes]weaponSetup
|
||||||
|
|
||||||
|
// pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic
|
||||||
|
// (it names the fruit) and is computed in doPot.
|
||||||
|
pActions [NumPotionTypes]pact
|
||||||
|
|
||||||
|
// idType maps identify scrolls to the kind of item they identify
|
||||||
|
// (scrolls.c static id_type).
|
||||||
|
idType [ScrollIdentifyRingOrStick + 1]ObjectKind
|
||||||
|
|
||||||
|
// rdesConn is the hardcoded 3x3 room adjacency matrix from
|
||||||
|
// passages.c do_passages.
|
||||||
|
rdesConn [MaxRooms][MaxRooms]bool
|
||||||
|
|
||||||
|
// thingList is misc.c rnd_thing()'s static table.
|
||||||
|
thingList []byte
|
||||||
|
|
||||||
|
// identList is command.c's static ident_list.
|
||||||
|
identList []helpEntry
|
||||||
|
|
||||||
|
// hungerStateName is io.c state_name[].
|
||||||
|
hungerStateName [4]string
|
||||||
|
|
||||||
|
// ripArt is the rip.c rip[] tombstone art.
|
||||||
|
ripArt []string
|
||||||
|
|
||||||
|
// killnameTable is the rip.c nlist[]: special death causes.
|
||||||
|
killnameTable []helpEntry
|
||||||
|
|
||||||
|
// scoreReasons is the rip.c reason[] scoreboard strings.
|
||||||
|
scoreReasons [4]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ripWall is the repeated blank wall line of the tombstone art.
|
||||||
|
const ripWall = " | |"
|
||||||
|
|
||||||
|
// newGameData builds the static tables. Each game gets a fresh copy, which
|
||||||
|
// keeps the package free of globals.
|
||||||
|
//
|
||||||
|
//nolint:funlen,maintidx // a single composite literal holding every C data table
|
||||||
|
func newGameData() *gameData {
|
||||||
|
return &gameData{
|
||||||
|
initStats: Stats{Str: 16, Exp: 0, Lvl: 1, ArmorClass: 10, HP: 12, Dmg: dice("1x4"), MaxHP: 12},
|
||||||
|
|
||||||
|
aClass: [NumArmorTypes]int{
|
||||||
8, // LEATHER
|
8, // LEATHER
|
||||||
7, // RING_MAIL
|
7, // RING_MAIL
|
||||||
7, // STUDDED_LEATHER
|
7, // STUDDED_LEATHER
|
||||||
@@ -18,17 +144,14 @@ var aClass = [NumArmorTypes]int{
|
|||||||
4, // SPLINT_MAIL
|
4, // SPLINT_MAIL
|
||||||
4, // BANDED_MAIL
|
4, // BANDED_MAIL
|
||||||
3, // PLATE_MAIL
|
3, // PLATE_MAIL
|
||||||
}
|
},
|
||||||
|
|
||||||
// eLevels is extern.c e_levels[]: experience thresholds per level; the
|
eLevels: []int{
|
||||||
// zero terminates the table as in C.
|
|
||||||
var eLevels = []int{
|
|
||||||
10, 20, 40, 80, 160, 320, 640, 1300, 2600, 5200, 13000, 26000,
|
10, 20, 40, 80, 160, 320, 640, 1300, 2600, 5200, 13000, 26000,
|
||||||
50000, 100000, 200000, 400000, 800000, 2000000, 4000000, 8000000, 0,
|
50000, 100000, 200000, 400000, 800000, 2000000, 4000000, 8000000, 0,
|
||||||
}
|
},
|
||||||
|
|
||||||
// trName is extern.c tr_name[]: names of the traps.
|
trName: [NumTrapTypes]string{
|
||||||
var trName = [NumTrapTypes]string{
|
|
||||||
"a trapdoor",
|
"a trapdoor",
|
||||||
"an arrow trap",
|
"an arrow trap",
|
||||||
"a sleeping gas trap",
|
"a sleeping gas trap",
|
||||||
@@ -37,15 +160,11 @@ var trName = [NumTrapTypes]string{
|
|||||||
"a poison dart trap",
|
"a poison dart trap",
|
||||||
"a rust trap",
|
"a rust trap",
|
||||||
"a mysterious trap",
|
"a mysterious trap",
|
||||||
}
|
},
|
||||||
|
|
||||||
// invTName is extern.c inv_t_name[]: the inventory style names.
|
invTName: []string{"Overwrite", "Slow", "Clear"},
|
||||||
var invTName = []string{"Overwrite", "Slow", "Clear"}
|
|
||||||
|
|
||||||
// monsterTable is extern.c monsters[26]: all monster kinds, indexed by
|
monsterTable: [26]MonsterKind{
|
||||||
// letter - 'A'. Monster strength (XX in C) is always 10; HP (___ in C) is
|
|
||||||
// rolled from the level at creation time.
|
|
||||||
var monsterTable = [26]MonsterKind{
|
|
||||||
/* Name CARRY FLAGS str exp lvl arm hp dmg */
|
/* Name CARRY FLAGS str exp lvl arm hp dmg */
|
||||||
{"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}},
|
{"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}},
|
||||||
{"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}},
|
{"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}},
|
||||||
@@ -73,12 +192,9 @@ var monsterTable = [26]MonsterKind{
|
|||||||
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}},
|
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}},
|
||||||
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}},
|
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}},
|
||||||
{"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, dice("1x8"), 0}},
|
{"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, dice("1x8"), 0}},
|
||||||
}
|
},
|
||||||
|
|
||||||
// Base ObjInfo tables (extern.c). These are templates: NewGame copies them
|
baseThings: [NumThings]ObjInfo{
|
||||||
// into ItemLore before initProbs converts Prob to cumulative form.
|
|
||||||
|
|
||||||
var baseThings = [NumThings]ObjInfo{
|
|
||||||
{Prob: 26}, // potion
|
{Prob: 26}, // potion
|
||||||
{Prob: 36}, // scroll
|
{Prob: 36}, // scroll
|
||||||
{Prob: 16}, // food
|
{Prob: 16}, // food
|
||||||
@@ -86,9 +202,9 @@ var baseThings = [NumThings]ObjInfo{
|
|||||||
{Prob: 7}, // armor
|
{Prob: 7}, // armor
|
||||||
{Prob: 4}, // ring
|
{Prob: 4}, // ring
|
||||||
{Prob: 4}, // stick
|
{Prob: 4}, // stick
|
||||||
}
|
},
|
||||||
|
|
||||||
var baseArmInfo = [NumArmorTypes]ObjInfo{
|
baseArmInfo: [NumArmorTypes]ObjInfo{
|
||||||
{Name: "leather armor", Prob: 20, Worth: 20},
|
{Name: "leather armor", Prob: 20, Worth: 20},
|
||||||
{Name: "ring mail", Prob: 15, Worth: 25},
|
{Name: "ring mail", Prob: 15, Worth: 25},
|
||||||
{Name: "studded leather armor", Prob: 15, Worth: 20},
|
{Name: "studded leather armor", Prob: 15, Worth: 20},
|
||||||
@@ -97,9 +213,9 @@ var baseArmInfo = [NumArmorTypes]ObjInfo{
|
|||||||
{Name: "splint mail", Prob: 10, Worth: 80},
|
{Name: "splint mail", Prob: 10, Worth: 80},
|
||||||
{Name: "banded mail", Prob: 10, Worth: 90},
|
{Name: "banded mail", Prob: 10, Worth: 90},
|
||||||
{Name: "plate mail", Prob: 5, Worth: 150},
|
{Name: "plate mail", Prob: 5, Worth: 150},
|
||||||
}
|
},
|
||||||
|
|
||||||
var basePotInfo = [NumPotionTypes]ObjInfo{
|
basePotInfo: [NumPotionTypes]ObjInfo{
|
||||||
{Name: "confusion", Prob: 7, Worth: 5},
|
{Name: "confusion", Prob: 7, Worth: 5},
|
||||||
{Name: "hallucination", Prob: 8, Worth: 5},
|
{Name: "hallucination", Prob: 8, Worth: 5},
|
||||||
{Name: "poison", Prob: 8, Worth: 5},
|
{Name: "poison", Prob: 8, Worth: 5},
|
||||||
@@ -114,9 +230,9 @@ var basePotInfo = [NumPotionTypes]ObjInfo{
|
|||||||
{Name: "restore strength", Prob: 13, Worth: 130},
|
{Name: "restore strength", Prob: 13, Worth: 130},
|
||||||
{Name: "blindness", Prob: 5, Worth: 5},
|
{Name: "blindness", Prob: 5, Worth: 5},
|
||||||
{Name: "levitation", Prob: 6, Worth: 75},
|
{Name: "levitation", Prob: 6, Worth: 75},
|
||||||
}
|
},
|
||||||
|
|
||||||
var baseRingInfo = [NumRingTypes]ObjInfo{
|
baseRingInfo: [NumRingTypes]ObjInfo{
|
||||||
{Name: "protection", Prob: 9, Worth: 400},
|
{Name: "protection", Prob: 9, Worth: 400},
|
||||||
{Name: "add strength", Prob: 9, Worth: 400},
|
{Name: "add strength", Prob: 9, Worth: 400},
|
||||||
{Name: "sustain strength", Prob: 5, Worth: 280},
|
{Name: "sustain strength", Prob: 5, Worth: 280},
|
||||||
@@ -131,9 +247,9 @@ var baseRingInfo = [NumRingTypes]ObjInfo{
|
|||||||
{Name: "teleportation", Prob: 5, Worth: 30},
|
{Name: "teleportation", Prob: 5, Worth: 30},
|
||||||
{Name: "stealth", Prob: 7, Worth: 470},
|
{Name: "stealth", Prob: 7, Worth: 470},
|
||||||
{Name: "maintain armor", Prob: 5, Worth: 380},
|
{Name: "maintain armor", Prob: 5, Worth: 380},
|
||||||
}
|
},
|
||||||
|
|
||||||
var baseScrInfo = [NumScrollTypes]ObjInfo{
|
baseScrInfo: [NumScrollTypes]ObjInfo{
|
||||||
{Name: "monster confusion", Prob: 7, Worth: 140},
|
{Name: "monster confusion", Prob: 7, Worth: 140},
|
||||||
{Name: "magic mapping", Prob: 4, Worth: 150},
|
{Name: "magic mapping", Prob: 4, Worth: 150},
|
||||||
{Name: "hold monster", Prob: 2, Worth: 180},
|
{Name: "hold monster", Prob: 2, Worth: 180},
|
||||||
@@ -152,9 +268,9 @@ var baseScrInfo = [NumScrollTypes]ObjInfo{
|
|||||||
{Name: "remove curse", Prob: 7, Worth: 105},
|
{Name: "remove curse", Prob: 7, Worth: 105},
|
||||||
{Name: "aggravate monsters", Prob: 3, Worth: 20},
|
{Name: "aggravate monsters", Prob: 3, Worth: 20},
|
||||||
{Name: "protect armor", Prob: 2, Worth: 250},
|
{Name: "protect armor", Prob: 2, Worth: 250},
|
||||||
}
|
},
|
||||||
|
|
||||||
var baseWeapInfo = [NumWeaponTypes + 1]ObjInfo{
|
baseWeapInfo: [NumWeaponTypes + 1]ObjInfo{
|
||||||
{Name: "mace", Prob: 11, Worth: 8},
|
{Name: "mace", Prob: 11, Worth: 8},
|
||||||
{Name: "long sword", Prob: 11, Worth: 15},
|
{Name: "long sword", Prob: 11, Worth: 15},
|
||||||
{Name: "short bow", Prob: 12, Worth: 15},
|
{Name: "short bow", Prob: 12, Worth: 15},
|
||||||
@@ -165,9 +281,9 @@ var baseWeapInfo = [NumWeaponTypes + 1]ObjInfo{
|
|||||||
{Name: "shuriken", Prob: 12, Worth: 5},
|
{Name: "shuriken", Prob: 12, Worth: 5},
|
||||||
{Name: "spear", Prob: 12, Worth: 5},
|
{Name: "spear", Prob: 12, Worth: 5},
|
||||||
{}, // DO NOT REMOVE: fake entry for dragon's breath
|
{}, // DO NOT REMOVE: fake entry for dragon's breath
|
||||||
}
|
},
|
||||||
|
|
||||||
var baseWsInfo = [NumWandTypes]ObjInfo{
|
baseWsInfo: [NumWandTypes]ObjInfo{
|
||||||
{Name: "light", Prob: 12, Worth: 250},
|
{Name: "light", Prob: 12, Worth: 250},
|
||||||
{Name: "invisibility", Prob: 6, Worth: 5},
|
{Name: "invisibility", Prob: 6, Worth: 5},
|
||||||
{Name: "lightning", Prob: 3, Worth: 330},
|
{Name: "lightning", Prob: 3, Worth: 330},
|
||||||
@@ -182,18 +298,17 @@ var baseWsInfo = [NumWandTypes]ObjInfo{
|
|||||||
{Name: "teleport away", Prob: 6, Worth: 340},
|
{Name: "teleport away", Prob: 6, Worth: 340},
|
||||||
{Name: "teleport to", Prob: 6, Worth: 50},
|
{Name: "teleport to", Prob: 6, Worth: 50},
|
||||||
{Name: "cancellation", Prob: 5, Worth: 280},
|
{Name: "cancellation", Prob: 5, Worth: 280},
|
||||||
}
|
},
|
||||||
|
|
||||||
// rainbow is init.c rainbow[]: the possible potion colors.
|
rainbow: []string{
|
||||||
var rainbow = []string{
|
|
||||||
"amber", "aquamarine", "black", "blue", "brown", "clear", "crimson",
|
"amber", "aquamarine", "black", "blue", "brown", "clear", "crimson",
|
||||||
"cyan", "ecru", "gold", "green", "grey", "magenta", "orange", "pink",
|
"cyan", "ecru", "gold", "green", "grey", "magenta", "orange", "pink",
|
||||||
"plaid", "purple", "red", "silver", "tan", "tangerine", "topaz",
|
"plaid", "purple", "red", "silver", "tan", "tangerine", "topaz",
|
||||||
"turquoise", "vermilion", "violet", "white", "yellow",
|
"turquoise", "vermilion", "violet", "white", "yellow",
|
||||||
}
|
},
|
||||||
|
|
||||||
// sylls is init.c sylls[]: syllables for generated scroll names.
|
//nolint:misspell // "ther" is a C scroll syllable, kept faithfully
|
||||||
var sylls = []string{
|
sylls: []string{
|
||||||
"a", "ab", "ag", "aks", "ala", "an", "app", "arg", "arze", "ash",
|
"a", "ab", "ag", "aks", "ala", "an", "app", "arg", "arze", "ash",
|
||||||
"bek", "bie", "bit", "bjor", "blu", "bot", "bu", "byt", "comp",
|
"bek", "bie", "bit", "bjor", "blu", "bot", "bu", "byt", "comp",
|
||||||
"con", "cos", "cre", "dalf", "dan", "den", "do", "e", "eep", "el",
|
"con", "cos", "cre", "dalf", "dan", "den", "do", "e", "eep", "el",
|
||||||
@@ -206,14 +321,13 @@ var sylls = []string{
|
|||||||
"prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", "sa",
|
"prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", "sa",
|
||||||
"san", "sat", "sef", "seh", "shu", "ski", "sna", "sne", "snik",
|
"san", "sat", "sef", "seh", "shu", "ski", "sna", "sne", "snik",
|
||||||
"sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", "tem",
|
"sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", "tem",
|
||||||
"there", "ti", "tox", "trol", "tue", "turs", "u", "ulk", "um", "un",
|
"ther", "ti", "tox", "trol", "tue", "turs", "u", "ulk", "um", "un",
|
||||||
"uni", "ur", "val", "viv", "vly", "vom", "wah", "wed", "werg",
|
"uni", "ur", "val", "viv", "vly", "vom", "wah", "wed", "werg",
|
||||||
"wex", "whon", "wun", "xo", "y", "yot", "yu", "zant", "zeb", "zim",
|
"wex", "whon", "wun", "xo", "y", "yot", "yu", "zant", "zeb", "zim",
|
||||||
"zok", "zon", "zum",
|
"zok", "zon", "zum",
|
||||||
}
|
},
|
||||||
|
|
||||||
// stoneTable is init.c stones[]: ring stones and their worth.
|
stoneTable: []Stone{
|
||||||
var stoneTable = []Stone{
|
|
||||||
{"agate", 25}, {"alexandrite", 40}, {"amethyst", 50},
|
{"agate", 25}, {"alexandrite", 40}, {"amethyst", 50},
|
||||||
{"carnelian", 40}, {"diamond", 300}, {"emerald", 300},
|
{"carnelian", 40}, {"diamond", 300}, {"emerald", 300},
|
||||||
{"germanium", 225}, {"granite", 5}, {"garnet", 50},
|
{"germanium", 225}, {"granite", 5}, {"garnet", 50},
|
||||||
@@ -223,35 +337,25 @@ var stoneTable = []Stone{
|
|||||||
{"ruby", 350}, {"sapphire", 285}, {"stibotantalite", 200},
|
{"ruby", 350}, {"sapphire", 285}, {"stibotantalite", 200},
|
||||||
{"tiger eye", 50}, {"topaz", 60}, {"turquoise", 70},
|
{"tiger eye", 50}, {"topaz", 60}, {"turquoise", 70},
|
||||||
{"taaffeite", 300}, {"zircon", 80},
|
{"taaffeite", 300}, {"zircon", 80},
|
||||||
}
|
},
|
||||||
|
|
||||||
// woods is init.c wood[]: what staffs are made of.
|
woods: []string{
|
||||||
var woods = []string{
|
|
||||||
"avocado wood", "balsa", "bamboo", "banyan", "birch", "cedar",
|
"avocado wood", "balsa", "bamboo", "banyan", "birch", "cedar",
|
||||||
"cherry", "cinnibar", "cypress", "dogwood", "driftwood", "ebony",
|
"cherry", "cinnibar", "cypress", "dogwood", "driftwood", "ebony",
|
||||||
"elm", "eucalyptus", "fall", "hemlock", "holly", "ironwood",
|
"elm", "eucalyptus", "fall", "hemlock", "holly", "ironwood",
|
||||||
"kukui wood", "mahogany", "manzanita", "maple", "oaken",
|
"kukui wood", "mahogany", "manzanita", "maple", "oaken",
|
||||||
"persimmon wood", "pecan", "pine", "poplar", "redwood", "rosewood",
|
"persimmon wood", "pecan", "pine", "poplar", "redwood", "rosewood",
|
||||||
"spruce", "teak", "walnut", "zebrawood",
|
"spruce", "teak", "walnut", "zebrawood",
|
||||||
}
|
},
|
||||||
|
|
||||||
// metals is init.c metal[]: what wands are made of.
|
metals: []string{
|
||||||
var metals = []string{
|
|
||||||
"aluminum", "beryllium", "bone", "brass", "bronze", "copper",
|
"aluminum", "beryllium", "bone", "brass", "bronze", "copper",
|
||||||
"electrum", "gold", "iron", "lead", "magnesium", "mercury",
|
"electrum", "gold", "iron", "lead", "magnesium", "mercury",
|
||||||
"nickel", "pewter", "platinum", "steel", "silver", "silicon",
|
"nickel", "pewter", "platinum", "steel", "silver", "silicon",
|
||||||
"tin", "titanium", "tungsten", "zinc",
|
"tin", "titanium", "tungsten", "zinc",
|
||||||
}
|
},
|
||||||
|
|
||||||
// helpEntry is rogue.h struct h_list.
|
helpStr: []helpEntry{
|
||||||
type helpEntry struct {
|
|
||||||
Ch byte
|
|
||||||
Desc string
|
|
||||||
Print bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpStr is extern.c helpstr[]: the '?' command help text.
|
|
||||||
var helpStr = []helpEntry{
|
|
||||||
{'?', " prints help", true},
|
{'?', " prints help", true},
|
||||||
{'/', " identify object", true},
|
{'/', " identify object", true},
|
||||||
{'h', " left", true},
|
{'h', " left", true},
|
||||||
@@ -317,6 +421,173 @@ var helpStr = []helpEntry{
|
|||||||
{'!', " shell escape", true},
|
{'!', " shell escape", true},
|
||||||
{'F', "<dir> fight till either of you dies", true},
|
{'F', "<dir> fight till either of you dies", true},
|
||||||
{'v', " print version number", true},
|
{'v', " print version number", true},
|
||||||
|
},
|
||||||
|
|
||||||
|
hNames: [8]string{
|
||||||
|
" scored an excellent hit on ",
|
||||||
|
" hit ",
|
||||||
|
" have injured ",
|
||||||
|
" swing and hit ",
|
||||||
|
" scored an excellent hit on ",
|
||||||
|
" hit ",
|
||||||
|
" has injured ",
|
||||||
|
" swings and hits ",
|
||||||
|
},
|
||||||
|
|
||||||
|
mNames: [8]string{
|
||||||
|
" miss",
|
||||||
|
" swing and miss",
|
||||||
|
" barely miss",
|
||||||
|
" don't hit",
|
||||||
|
" misses",
|
||||||
|
" swings and misses",
|
||||||
|
" barely misses",
|
||||||
|
" doesn't hit",
|
||||||
|
},
|
||||||
|
|
||||||
|
strPlus: [32]int{
|
||||||
|
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||||
|
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
addDam: [32]int{
|
||||||
|
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
|
||||||
|
3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
lvlMons: [26]byte{
|
||||||
|
'K', 'E', 'B', 'S', 'H', 'I', 'R', 'O', 'Z', 'L', 'C', 'Q', 'A',
|
||||||
|
'N', 'Y', 'F', 'T', 'W', 'P', 'X', 'U', 'M', 'V', 'G', 'J', 'D',
|
||||||
|
},
|
||||||
|
|
||||||
|
wandMons: [26]byte{
|
||||||
|
'K', 'E', 'B', 'S', 'H', 0, 'R', 'O', 'Z', 0, 'C', 'Q', 'A',
|
||||||
|
0, 'Y', 0, 'T', 'W', 'P', 0, 'U', 'M', 'V', 'G', 'J', 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
ringUses: [NumRingTypes]int{
|
||||||
|
1, // R_PROTECT
|
||||||
|
1, // R_ADDSTR
|
||||||
|
1, // R_SUSTSTR
|
||||||
|
-3, // R_SEARCH
|
||||||
|
-5, // R_SEEINVIS
|
||||||
|
0, // R_NOP
|
||||||
|
0, // R_AGGR
|
||||||
|
-3, // R_ADDHIT
|
||||||
|
-3, // R_ADDDAM
|
||||||
|
2, // R_REGEN
|
||||||
|
-2, // R_DIGEST
|
||||||
|
0, // R_TELEPORT
|
||||||
|
1, // R_STEALTH
|
||||||
|
1, // R_SUSTARM
|
||||||
|
},
|
||||||
|
|
||||||
|
initWeaps: [NumWeaponTypes]weaponSetup{
|
||||||
|
{dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace
|
||||||
|
{dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword
|
||||||
|
{dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow
|
||||||
|
{dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow
|
||||||
|
{dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger
|
||||||
|
{dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword
|
||||||
|
{dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart
|
||||||
|
{dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken
|
||||||
|
{dice("2x3"), dice("1x6"), noWeapon, Missile}, // WeaponSpear
|
||||||
|
},
|
||||||
|
|
||||||
|
pActions: [NumPotionTypes]pact{
|
||||||
|
PotionConfusion: {Confused, DUnconfuse, HuhDuration,
|
||||||
|
"what a tripy feeling!",
|
||||||
|
"wait, what's going on here. Huh? What? Who?"},
|
||||||
|
PotionLSD: {Hallucinating, DComeDown, SeeDuration,
|
||||||
|
"Oh, wow! Everything seems so cosmic!",
|
||||||
|
"Oh, wow! Everything seems so cosmic!"},
|
||||||
|
PotionSeeInvisible: {CanSeeInvisible, DUnsee, SeeDuration, "", ""},
|
||||||
|
PotionBlindness: {Blind, DSight, SeeDuration,
|
||||||
|
"oh, bummer! Everything is dark! Help!",
|
||||||
|
"a cloak of darkness falls around you"},
|
||||||
|
PotionLevitation: {Levitating, DLand, HealTime,
|
||||||
|
"oh, wow! You're floating in the air!",
|
||||||
|
"you start to float in the air"},
|
||||||
|
},
|
||||||
|
|
||||||
|
idType: [ScrollIdentifyRingOrStick + 1]ObjectKind{
|
||||||
|
ScrollIdentifyPotion: KindPotion,
|
||||||
|
ScrollIdentifyScroll: KindScroll,
|
||||||
|
ScrollIdentifyWeapon: KindWeapon,
|
||||||
|
ScrollIdentifyArmor: KindArmor,
|
||||||
|
ScrollIdentifyRingOrStick: KindRingOrStick,
|
||||||
|
},
|
||||||
|
|
||||||
|
rdesConn: [MaxRooms][MaxRooms]bool{
|
||||||
|
{false, true, false, true, false, false, false, false, false},
|
||||||
|
{true, false, true, false, true, false, false, false, false},
|
||||||
|
{false, true, false, false, false, true, false, false, false},
|
||||||
|
{true, false, false, false, true, false, true, false, false},
|
||||||
|
{false, true, false, true, false, true, false, true, false},
|
||||||
|
{false, false, true, false, true, false, false, false, true},
|
||||||
|
{false, false, false, true, false, false, false, true, false},
|
||||||
|
{false, false, false, false, true, false, true, false, true},
|
||||||
|
{false, false, false, false, false, true, false, true, false},
|
||||||
|
},
|
||||||
|
|
||||||
|
thingList: []byte{
|
||||||
|
Potion, Scroll, Ring, Stick, Food, Weapon, Armor, Stairs, Gold, Amulet,
|
||||||
|
},
|
||||||
|
|
||||||
|
identList: []helpEntry{
|
||||||
|
{'|', "wall of a room", false},
|
||||||
|
{'-', "wall of a room", false},
|
||||||
|
{Gold, goldName, false},
|
||||||
|
{Stairs, "a staircase", false},
|
||||||
|
{Door, "door", false},
|
||||||
|
{Floor, "room floor", false},
|
||||||
|
{PlayerCh, "you", false},
|
||||||
|
{Passage, "passage", false},
|
||||||
|
{Trap, "trap", false},
|
||||||
|
{Potion, potionName, false},
|
||||||
|
{Scroll, scrollName, false},
|
||||||
|
{Food, "food", false},
|
||||||
|
{Weapon, "weapon", false},
|
||||||
|
{' ', "solid rock", false},
|
||||||
|
{Armor, "armor", false},
|
||||||
|
{Amulet, "the Amulet of Yendor", false},
|
||||||
|
{Ring, ringName, false},
|
||||||
|
{Stick, "wand or staff", false},
|
||||||
|
},
|
||||||
|
|
||||||
|
hungerStateName: [4]string{"", "Hungry", "Weak", "Faint"},
|
||||||
|
|
||||||
|
ripArt: []string{
|
||||||
|
" __________",
|
||||||
|
" / \\",
|
||||||
|
" / REST \\",
|
||||||
|
" / IN \\",
|
||||||
|
" / PEACE \\",
|
||||||
|
" / \\",
|
||||||
|
ripWall,
|
||||||
|
ripWall,
|
||||||
|
" | killed by a |",
|
||||||
|
ripWall,
|
||||||
|
" | 1980 |",
|
||||||
|
" *| * * * | *",
|
||||||
|
" ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______",
|
||||||
|
},
|
||||||
|
|
||||||
|
killnameTable: []helpEntry{
|
||||||
|
{'a', "arrow", true},
|
||||||
|
{'b', "bolt", true},
|
||||||
|
{'d', "dart", true},
|
||||||
|
{'h', "hypothermia", false},
|
||||||
|
{'s', "starvation", false},
|
||||||
|
},
|
||||||
|
|
||||||
|
scoreReasons: [4]string{
|
||||||
|
"killed",
|
||||||
|
"quit",
|
||||||
|
"A total winner",
|
||||||
|
"killed with Amulet",
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version strings (vers.c). The encstr/statlist XOR keys are not ported:
|
// Version strings (vers.c). The encstr/statlist XOR keys are not ported:
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ func TestProbabilitiesSumTo100(t *testing.T) {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data := newGameData()
|
||||||
|
|
||||||
tables := map[string][]ObjInfo{
|
tables := map[string][]ObjInfo{
|
||||||
"things": baseThings[:],
|
"things": data.baseThings[:],
|
||||||
"potions": basePotInfo[:],
|
"potions": data.basePotInfo[:],
|
||||||
"scrolls": baseScrInfo[:],
|
"scrolls": data.baseScrInfo[:],
|
||||||
"rings": baseRingInfo[:],
|
"rings": data.baseRingInfo[:],
|
||||||
"sticks": baseWsInfo[:],
|
"sticks": data.baseWsInfo[:],
|
||||||
"weapons": baseWeapInfo[:NumWeaponTypes], // excludes the flame entry
|
"weapons": data.baseWeapInfo[:NumWeaponTypes], // excludes the flame entry
|
||||||
"armor": baseArmInfo[:],
|
"armor": data.baseArmInfo[:],
|
||||||
}
|
}
|
||||||
for name, tab := range tables {
|
for name, tab := range tables {
|
||||||
if s := sum(tab); s != 100 {
|
if s := sum(tab); s != 100 {
|
||||||
@@ -88,11 +90,12 @@ func TestNewGameRandomizesAppearances(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonsterTable(t *testing.T) {
|
func TestMonsterTable(t *testing.T) {
|
||||||
if monsterTable[0].Name != "aquator" || monsterTable[25].Name != "zombie" {
|
data := newGameData()
|
||||||
|
if data.monsterTable[0].Name != "aquator" || data.monsterTable[25].Name != "zombie" {
|
||||||
t.Error("monster table order broken")
|
t.Error("monster table order broken")
|
||||||
}
|
}
|
||||||
|
|
||||||
if monsterTable['D'-'A'].Name != "dragon" {
|
if data.monsterTable['D'-'A'].Name != "dragon" {
|
||||||
t.Error("letter indexing broken")
|
t.Error("letter indexing broken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
|
|||||||
|
|
||||||
switch obj.Kind {
|
switch obj.Kind {
|
||||||
case KindPotion:
|
case KindPotion:
|
||||||
g.nameit(&pb, obj, "potion", it.PotColors[which], &it.Potions[which], nullstr)
|
g.nameit(&pb, obj, potionName, it.PotColors[which], &it.Potions[which], nullstr)
|
||||||
case KindRing:
|
case KindRing:
|
||||||
g.nameit(&pb, obj, "ring", it.RingStones[which], &it.Rings[which], ringNum)
|
g.nameit(&pb, obj, ringName, it.RingStones[which], &it.Rings[which], ringNum)
|
||||||
case KindWand:
|
case KindWand:
|
||||||
g.nameit(&pb, obj, it.WandType[which], it.WandMade[which], &it.Sticks[which], chargeStr)
|
g.nameit(&pb, obj, it.WandType[which], it.WandMade[which], &it.Sticks[which], chargeStr)
|
||||||
case KindScroll:
|
case KindScroll:
|
||||||
@@ -78,7 +78,7 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
|
|||||||
case KindArmor:
|
case KindArmor:
|
||||||
sp := it.Armors[which].Name
|
sp := it.Armors[which].Name
|
||||||
if obj.Flags.Has(Known) {
|
if obj.Flags.Has(Known) {
|
||||||
fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.ArmorClass, 0, Armor), sp)
|
fmt.Fprintf(&pb, "%s %s [", num(g.data.aClass[which]-obj.ArmorClass, 0, Armor), sp)
|
||||||
|
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
pb.WriteString("protection ")
|
pb.WriteString("protection ")
|
||||||
@@ -257,7 +257,7 @@ func (g *RogueGame) newThing() *Object {
|
|||||||
cur.Kind = KindArmor
|
cur.Kind = KindArmor
|
||||||
cur.Which = pickOne(g, g.Items.Armors[:])
|
cur.Which = pickOne(g, g.Items.Armors[:])
|
||||||
|
|
||||||
cur.ArmorClass = aClass[cur.Which]
|
cur.ArmorClass = g.data.aClass[cur.Which]
|
||||||
if r := g.rnd(100); r < 20 {
|
if r := g.rnd(100); r < 20 {
|
||||||
cur.Flags.Set(Cursed)
|
cur.Flags.Set(Cursed)
|
||||||
cur.ArmorClass += g.rnd(3) + 1
|
cur.ArmorClass += g.rnd(3) + 1
|
||||||
@@ -536,11 +536,11 @@ func (g *RogueGame) nothing(typ byte) string {
|
|||||||
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case Potion:
|
case Potion:
|
||||||
tystr = "potion"
|
tystr = potionName
|
||||||
case Scroll:
|
case Scroll:
|
||||||
tystr = "scroll"
|
tystr = scrollName
|
||||||
case Ring:
|
case Ring:
|
||||||
tystr = "ring"
|
tystr = ringName
|
||||||
case Stick:
|
case Stick:
|
||||||
tystr = "stick"
|
tystr = "stick"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,16 +210,6 @@ const (
|
|||||||
NumTrapTypes = 8
|
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).
|
// PotionKind identifies a potion (rogue.h potion types).
|
||||||
type PotionKind int
|
type PotionKind int
|
||||||
|
|
||||||
@@ -242,15 +232,6 @@ const (
|
|||||||
NumPotionTypes
|
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).
|
// ScrollKind identifies a scroll (rogue.h scroll types).
|
||||||
type ScrollKind int
|
type ScrollKind int
|
||||||
|
|
||||||
@@ -277,15 +258,6 @@ const (
|
|||||||
NumScrollTypes
|
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).
|
// WeaponKind identifies a weapon (rogue.h weapon types).
|
||||||
type WeaponKind int
|
type WeaponKind int
|
||||||
|
|
||||||
@@ -307,15 +279,6 @@ const (
|
|||||||
// just past them in the tables (C's MAXWEAPONS == FLAME).
|
// just past them in the tables (C's MAXWEAPONS == FLAME).
|
||||||
const NumWeaponTypes = WeaponFlame
|
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).
|
// ArmorKind identifies a suit of armor (rogue.h armor types).
|
||||||
type ArmorKind int
|
type ArmorKind int
|
||||||
|
|
||||||
@@ -332,15 +295,6 @@ const (
|
|||||||
NumArmorTypes
|
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).
|
// RingKind identifies a ring (rogue.h ring types).
|
||||||
type RingKind int
|
type RingKind int
|
||||||
|
|
||||||
@@ -363,15 +317,6 @@ const (
|
|||||||
NumRingTypes
|
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).
|
// WandKind identifies a wand or staff (rogue.h rod/wand/staff types).
|
||||||
type WandKind int
|
type WandKind int
|
||||||
|
|
||||||
@@ -394,15 +339,6 @@ const (
|
|||||||
NumWandTypes
|
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
|
// Coord is a position on the level (rogue.h coord). A value type: the C
|
||||||
// ce(a,b) macro is plain == here.
|
// ce(a,b) macro is plain == here.
|
||||||
type Coord struct {
|
type Coord struct {
|
||||||
|
|||||||
@@ -143,27 +143,18 @@ func (g *RogueGame) wield() {
|
|||||||
g.msg("wielding %s (%c)", sp, obj.PackCh)
|
g.msg("wielding %s (%c)", sp, obj.PackCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initWeaps is the weapons.c init_dam[] table.
|
// weaponSetup is one row of the weapons.c init_dam[] table (see
|
||||||
var initWeaps = [NumWeaponTypes]struct {
|
// gameData.initWeaps).
|
||||||
|
type weaponSetup struct {
|
||||||
dam DiceSpec // damage when wielded
|
dam DiceSpec // damage when wielded
|
||||||
hrl DiceSpec // damage when thrown
|
hrl DiceSpec // damage when thrown
|
||||||
launch WeaponKind // launching weapon
|
launch WeaponKind // launching weapon
|
||||||
flags ObjFlags
|
flags ObjFlags
|
||||||
}{
|
|
||||||
{dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace
|
|
||||||
{dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword
|
|
||||||
{dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow
|
|
||||||
{dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow
|
|
||||||
{dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger
|
|
||||||
{dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword
|
|
||||||
{dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart
|
|
||||||
{dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken
|
|
||||||
{dice("2x3"), dice("1x6"), noWeapon, Missile}, // WeaponSpear
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initWeapon sets up a new weapon (weapons.c init_weapon).
|
// initWeapon sets up a new weapon (weapons.c init_weapon).
|
||||||
func (g *RogueGame) initWeapon(weap *Object, which WeaponKind) {
|
func (g *RogueGame) initWeapon(weap *Object, which WeaponKind) {
|
||||||
iwp := &initWeaps[which]
|
iwp := &g.data.initWeaps[which]
|
||||||
weap.Kind = KindWeapon
|
weap.Kind = KindWeapon
|
||||||
weap.Which = int(which)
|
weap.Which = int(which)
|
||||||
weap.Damage = iwp.dam
|
weap.Damage = iwp.dam
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func (g *RogueGame) createObj() {
|
|||||||
obj.HPlus += g.rnd(3) + 1
|
obj.HPlus += g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obj.ArmorClass = aClass[obj.Which]
|
obj.ArmorClass = g.data.aClass[obj.Which]
|
||||||
if bless == '-' {
|
if bless == '-' {
|
||||||
obj.ArmorClass += g.rnd(3) + 1
|
obj.ArmorClass += g.rnd(3) + 1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user