8 Commits

Author SHA1 Message Date
3ed7931676 Merge refactor/object-fields (refactor step 3 + module rename) 2026-07-06 23:10:39 +02:00
43c59b7f82 Update module base path to git.eeqj.de/sneak/rgoue
go.mod, term/ and cmd/ imports, the ARCHITECTURE.md module references,
and the in-game version string. User request; the repo already lives
at this remote.
2026-07-06 23:10:39 +02:00
2eff377a73 Un-overload Object fields and pre-parse damage dice (refactor step 3)
Object.Arm carried four meanings in C (o_arm/o_charges/o_goldval plus
ring bonuses); it is now four fields: ArmorClass, Charges, GoldValue,
and Bonus. Stats.Arm becomes Stats.ArmorClass. The Charges()/GoldVal()
accessor pair is gone.

Damage dice strings ("1x4/1x2") are parsed once into DiceSpec — at
table definition for the bestiary and weapon tables, at creation for
items — instead of re-parsed with atoi on every swing as fight.c
roll_em did. ParseDice preserves the C parse semantics exactly,
including the junk-tolerant "%%%x0" bestiary placeholder and the
"000x0" flytrap reset (regression-tested in dice_test.go); the
flytrap's growing grip becomes DiceSpec{{VfHit, 1}}.

Save format bumps to 5.4.4-go3 (field renames and retypes would
silently zero under gob's match-by-name decoding).

No behavior change; full suite green.
2026-07-06 23:07:57 +02:00
c75af2ec22 Rotate TODO.md for completed refactor step 2
The rotation should have ridden the step-2 commit per the Workflow
section, but the file was edited concurrently and the update was lost;
recording it now. Step 3 (un-overload Object fields) is Next Step.
2026-07-06 22:01:33 +02:00
4b04ac08ea Merge refactor/typed-kinds (refactor step 2) 2026-07-06 22:00:43 +02:00
b940cfc41f Give item categories and subtypes real types (refactor step 2)
ObjectKind separates what an item is from how it draws: Object.Type
(a byte that doubled as the map character) becomes Kind ObjectKind,
with Glyph() producing the display character and objectKindForGlyph
converting back at the map boundary. KindWand covers the C 'stick'
category. The magic-missile bolt uses KindGold, matching C's literal
o_type='*' trick, with a comment.

PotionKind, ScrollKind, RingKind, WandKind, WeaponKind, ArmorKind and
TrapKind are now iota enums with Stringer (names come from the base
info tables); Object gains typed accessors (obj.RingKind(), ...) and
Launch is typed WeaponKind. beTrapped returns TrapKind. The
getItem/inventory/whatis prompt filters take ObjectKind, with
KindCallable/KindRingOrStick replacing the C CALLABLE/R_OR_S
sentinels; wizard.c's type_name table is subsumed by
ObjectKind.String(). IsRing/IsWearing/initWeapon/doPot signatures are
typed accordingly.

The save format version becomes 5.4.4-go2: the gob field rename would
otherwise silently zero Kind when reading old saves.

No behavior change; full suite green.
2026-07-06 22:00:43 +02:00
626f8c5a7d Merge refactor/descriptive-constants (refactor step 1) 2026-07-06 21:29:19 +02:00
e71a2ef3fd Rename constants to descriptive names (refactor step 1)
Pure rename, no behavior change; the full suite (RNG goldens,
generation invariants, scripted sessions, save round trip) is
unchanged and green.

- Creature flags: IsHuh→Confused, IsHalu→Hallucinating,
  CanHuh→CanConfuse, CanSee→CanSeeInvisible, IsRun→Awake,
  SeeMonst→SenseMonsters, IsCanc→Cancelled, IsLevit→Levitating,
  IsBlind/IsGreed/IsHaste/IsTarget/IsHeld/IsInvis/IsMean/IsRegen/
  IsFly/IsSlow → Blind/Greedy/Hasted/Targeted/Held/Invisible/Mean/
  Regenerates/Flying/Slowed, IsFound→Found
- Object flags: IsCursed→Cursed, IsKnow→Known, IsMissl→Missile,
  IsMany→Stackable, ObjIsFound→WasFound, IsProt→Protected
- Room flags: IsDark/IsGone/IsMaze → Dark/Gone/Maze; place flags:
  FPass→FPassage, FPNum→FPassNum, FTMask→FTrapMask
- Trap types: TDoor→TrapDoor ... TMyst→TrapMystery
- Item subtypes: P*→Potion* (PLSD→PotionLSD, PMFind→
  PotionDetectMonsters, ...), S*→Scroll* (SIDRorS→
  ScrollIdentifyRingOrStick, ...), R*→Ring* (RAddHit→RingDexterity,
  RNop→RingAdornment, ...), Ws*→Wand* (WsElect→WandLightning, ...),
  weapons (TwoSword→WeaponTwoHandedSword, Shiraken→WeaponShuriken,
  ...), armor (RingMail→ArmorRingMail, ...)
- Counts: Max{Potions,Scrolls,Rings,Sticks,Weapons,Armors} →
  Num{Potion,Scroll,Ring,Wand,Weapon,Armor}Types; NTraps→NumTrapTypes
- Level.NTraps field → TrapCount (also in the save snapshot)
- Original C constant names preserved as comment breadcrumbs in
  types.go so the lineage stays greppable

TODO.md: step 1 moved to Completed, step 2 (typed kinds) promoted to
Next Step.
2026-07-06 21:28:57 +02:00
41 changed files with 1252 additions and 1004 deletions

View File

@@ -6,7 +6,7 @@ This document has two parts:
codebase (every file, every function, every type, every global) as it exists codebase (every file, every function, every type, every global) as it exists
on the `modern-rogue` branch. on the `modern-rogue` branch.
- **Part 2 — The Go Port Design**: the object-oriented architecture for the Go - **Part 2 — The Go Port Design**: the object-oriented architecture for the Go
port (`sneak.berlin/go/rogue`), including how every C construct maps onto Go port (`git.eeqj.de/sneak/rgoue`), including how every C construct maps onto Go
types and methods. types and methods.
--- ---
@@ -887,7 +887,7 @@ Declares `rd_score(SCORE*)` / `wr_score(SCORE*)` (implemented in save.c).
# Part 2 — The Go Port Design # Part 2 — The Go Port Design
Module: **`sneak.berlin/go/rogue`** Module: **`git.eeqj.de/sneak/rgoue`**
## 1. Goals and principles ## 1. Goals and principles
@@ -929,7 +929,7 @@ branches; this branch is Go-only. The tcell wrapper ended up in its own
`term` package so that `game` itself has no third-party imports: `term` package so that `game` itself has no third-party imports:
``` ```
go.mod module sneak.berlin/go/rogue go.mod module git.eeqj.de/sneak/rgoue
cmd/rogue/main.go package main — flag parsing, constructs RogueGame, calls Run cmd/rogue/main.go package main — flag parsing, constructs RogueGame, calls Run
game/ package game — the port, one .go file per .c file game/ package game — the port, one .go file per .c file
game.go RogueGame struct, NewGame, Run (main.c) game.go RogueGame struct, NewGame, Run (main.c)

70
TODO.md
View File

@@ -29,19 +29,32 @@ Refactor ground rules:
# Next Step # Next Step
Refactor step 1 (branch refactor/descriptive-constants): rename Adopt the house Go linting standards: copy .golangci.yml from the
constants to descriptive, less C-like names. Creature/object/room/place prompts repo and bring game/, term/, and cmd/ lint-clean (the port is
flag bits (IsHuh→Confused, IsHalu→Hallucinating, CanHuh→CanConfuse, greenfield code, so no exemptions apply).
SeeMonst→SenseMonsters, ObjIsFound→WasFound, IsCanc→Cancelled, ...),
trap types (TMyst→TrapMystery), item subtype constants
(PLSD→PotionLSD, SIDRorS→ScrollIdentifyRingOrStick,
RAddHit→RingDexterity, WsHasteM→WandHasteMonster,
TwoSword→WeaponTwoHandedSword, ...), Max* count constants →
Num*Types, and the Level.NTraps field → TrapCount. Pure rename; no
behavior change; tests unchanged.
# Completed Steps # Completed Steps
- 2026-07-06 Module base path updated to git.eeqj.de/sneak/rgoue
(go.mod, term/ and cmd/ imports, ARCHITECTURE.md, version string).
- 2026-07-06 Refactor step 3 (refactor/object-fields): Object.Arm split
into ArmorClass/Charges/GoldValue/Bonus (rings); Stats.Arm →
ArmorClass; damage strings parsed once into DiceSpec at table
definition (ParseDice keeps C roll_em parse semantics, incl. "%%%x0"
and "000x0" edge cases, regression-tested); save format 5.4.4-go3.
- 2026-07-06 Refactor step 2 (refactor/typed-kinds, b940cfc):
ObjectKind separates item category from map glyph (Object.Type byte
→ Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/
WandKind/WeaponKind/ArmorKind/TrapKind typed iota enums with
Stringer; typed accessors on Object; getItem/inventory/whatis
filters take ObjectKind (KindCallable/KindRingOrStick replace
CALLABLE/R_OR_S); save format bumped to 5.4.4-go2. Suite green.
- 2026-07-06 Refactor step 1 (refactor/descriptive-constants): renamed
all flag bits, trap types, item subtype constants, and Max* counts to
descriptive names (IsHuh→Confused, SeeMonst→SenseMonsters,
WsHasteM→WandHasteMonster, MaxSticks→NumWandTypes, ...);
Level.NTraps→TrapCount; C names kept as comment breadcrumbs. Pure
rename, suite green.
- 2026-07-06 Made the rgoue branch Go-only: removed C sources and the - 2026-07-06 Made the rgoue branch Go-only: removed C sources and the
autoconf/VS build system (they remain on master and modern-rogue), autoconf/VS build system (they remain on master and modern-rogue),
ported the last wizard command (item-probability listing), rewrote ported the last wizard command (item-probability listing), rewrote
@@ -63,58 +76,45 @@ behavior change; tests unchanged.
# Future Steps # Future Steps
1. Refactor step 2: typed kind enums — PotionKind, ScrollKind, 1. Refactor step 4: method renames, movement/world subsystem
RingKind, WandKind, WeaponKind, ArmorKind, TrapKind as iota enums
with fmt.Stringer; Object.Which and the ObjInfo tables move to the
typed kinds; the object category glyph bytes ('!', '?', ...) become
an ObjectKind with a Glyph() method so item category and map
character stop sharing one byte namespace.
2. Refactor step 3: un-overload Object fields — split Object.Arm into
ArmorClass/Charges/GoldValue; parse damage dice ("1x4/1x2") once
into a DiceSpec type at table definition instead of per swing;
save-format update with round-trip test migration.
3. Adopt the house Go linting standards: copy .golangci.yml from the
prompts repo and bring game/, term/, and cmd/ lint-clean (the port
is greenfield code, so no exemptions apply).
4. Refactor step 4: method renames, movement/world subsystem
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, (doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze, doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
chgStr→changeStrength, ...); remove the goto/label flows in doMove, chgStr→changeStrength, ...); remove the goto/label flows in doMove,
dispatch, and saveGame in favor of loops and helpers. dispatch, and saveGame in favor of loops and helpers.
5. Refactor step 5: method renames, items/combat/UI subsystems 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.
6. Refactor step 6: extract types from the god object — MessageLine 3. 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.
7. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap 4. 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.
8. Refactor step 8: constructor and style pass per the house 5. 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.
9. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the 6. 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.
10. Playtest hardening pass: play several full games with the tcell 7. 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.
11. Verify the seed-compatibility claim against the C reference on 8. 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.
12. Broaden unit test coverage where playtesting finds thin spots 9. Broaden unit test coverage where playtesting finds thin spots
(rings, sticks, wizard commands). (rings, sticks, wizard commands).
13. Tag a release once a full game (Amulet retrieval and score entry) 10. Tag a release once a full game (Amulet retrieval and score entry)
completes without defects. completes without defects.
14. Full-terminal-size support (deferred by explicit decision 11. 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.
15. Note: this repo is exempt from the standard policy scaffold. Do not 12. 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.

View File

@@ -13,8 +13,8 @@ import (
"syscall" "syscall"
"time" "time"
"sneak.berlin/go/rogue/game" "git.eeqj.de/sneak/rgoue/game"
"sneak.berlin/go/rogue/term" "git.eeqj.de/sneak/rgoue/term"
) )
func main() { func main() {

View File

@@ -5,7 +5,7 @@ package game
// wear lets the player put armor on (armor.c wear). // wear lets the player put armor on (armor.c wear).
func (g *RogueGame) wear() { func (g *RogueGame) wear() {
p := &g.Player p := &g.Player
obj := g.getItem("wear", int(Armor)) obj := g.getItem("wear", KindArmor)
if obj == nil { if obj == nil {
return return
} }
@@ -18,12 +18,12 @@ func (g *RogueGame) wear() {
g.After = false g.After = false
return return
} }
if obj.Type != Armor { if obj.Kind != KindArmor {
g.msg("you can't wear that") g.msg("you can't wear that")
return return
} }
g.wasteTime() g.wasteTime()
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
sp := g.invName(obj, true) sp := g.invName(obj, true)
p.CurArmor = obj p.CurArmor = obj
if !g.Options.Terse { if !g.Options.Terse {

View File

@@ -9,19 +9,19 @@ const dragonShot = 5
func (g *RogueGame) runners(int) { func (g *RogueGame) runners(int) {
list := append([]*Monster(nil), g.Level.Monsters...) list := append([]*Monster(nil), g.Level.Monsters...)
for _, tp := range list { for _, tp := range list {
if !tp.On(IsHeld) && tp.On(IsRun) { if !tp.On(Held) && tp.On(Awake) {
origPos := tp.Pos origPos := tp.Pos
wastarget := tp.On(IsTarget) wastarget := tp.On(Targeted)
if g.moveMonst(tp) == -1 { if g.moveMonst(tp) == -1 {
continue continue
} }
if tp.On(IsFly) && distCp(g.Player.Pos, tp.Pos) >= 3 { if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 {
if g.moveMonst(tp) == -1 { if g.moveMonst(tp) == -1 {
continue continue
} }
} }
if wastarget && origPos != tp.Pos { if wastarget && origPos != tp.Pos {
tp.Flags.Clear(IsTarget) tp.Flags.Clear(Targeted)
g.ToDeath = false g.ToDeath = false
} }
} }
@@ -35,12 +35,12 @@ func (g *RogueGame) runners(int) {
// moveMonst executes a single turn of running for a monster (chase.c // moveMonst executes a single turn of running for a monster (chase.c
// move_monst). Returns -1 if the monster died or left the level. // move_monst). Returns -1 if the monster died or left the level.
func (g *RogueGame) moveMonst(tp *Monster) int { func (g *RogueGame) moveMonst(tp *Monster) int {
if !tp.On(IsSlow) || tp.Turn { if !tp.On(Slowed) || tp.Turn {
if g.doChase(tp) == -1 { if g.doChase(tp) == -1 {
return -1 return -1
} }
} }
if tp.On(IsHaste) { if tp.On(Hasted) {
if g.doChase(tp) == -1 { if g.doChase(tp) == -1 {
return -1 return -1
} }
@@ -68,7 +68,7 @@ func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
g.move(newLoc.Y, newLoc.X) g.move(newLoc.Y, newLoc.X)
if g.seeMonst(th) { if g.seeMonst(th) {
g.addch(th.Disguise) g.addch(th.Disguise)
} else if g.Player.On(SeeMonst) { } else if g.Player.On(SenseMonsters) {
g.standout() g.standout()
g.addch(th.Type) g.addch(th.Type)
g.standend() g.standend()
@@ -83,7 +83,7 @@ func (g *RogueGame) doChase(th *Monster) int {
mindist := 32767 mindist := 32767
rer := th.Room // find room of chaser rer := th.Room // find room of chaser
if th.On(IsGreed) && rer.GoldVal == 0 { if th.On(Greedy) && rer.GoldVal == 0 {
th.Dest = &p.Pos // if gold has been taken, run after hero th.Dest = &p.Pos // if gold has been taken, run after hero
} }
var ree *Room // find room of chasee var ree *Room // find room of chasee
@@ -108,7 +108,7 @@ over:
} }
} }
if door { if door {
rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPNum] rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum]
door = false door = false
goto over goto over
} }
@@ -120,7 +120,7 @@ over:
if th.Type == 'D' && (th.Pos.Y == p.Pos.Y || th.Pos.X == p.Pos.X || if th.Type == 'D' && (th.Pos.Y == p.Pos.Y || th.Pos.X == p.Pos.X ||
abs(th.Pos.Y-p.Pos.Y) == abs(th.Pos.X-p.Pos.X)) && abs(th.Pos.Y-p.Pos.Y) == abs(th.Pos.X-p.Pos.X)) &&
distCp(th.Pos, p.Pos) <= BoltLength*BoltLength && distCp(th.Pos, p.Pos) <= BoltLength*BoltLength &&
!th.On(IsCanc) && g.rnd(dragonShot) == 0 { !th.On(Cancelled) && g.rnd(dragonShot) == 0 {
g.Delta.Y = sign(p.Pos.Y - th.Pos.Y) g.Delta.Y = sign(p.Pos.Y - th.Pos.Y)
g.Delta.X = sign(p.Pos.X - th.Pos.X) g.Delta.X = sign(p.Pos.X - th.Pos.X)
if g.HasHit { if g.HasHit {
@@ -130,7 +130,7 @@ over:
g.Running = false g.Running = false
g.Count = 0 g.Count = 0
g.Quiet = 0 g.Quiet = 0
if g.ToDeath && !th.On(IsTarget) { if g.ToDeath && !th.On(Targeted) {
g.ToDeath = false g.ToDeath = false
g.Kamikaze = false g.Kamikaze = false
} }
@@ -147,7 +147,7 @@ over:
if th.Dest == &obj.Pos { if th.Dest == &obj.Pos {
detachObj(&g.Level.Objects, obj) detachObj(&g.Level.Objects, obj)
attachObj(&th.Pack, obj) attachObj(&th.Pack, obj)
if th.Room.Flags.Has(IsGone) { if th.Room.Flags.Has(Gone) {
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Passage) g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Passage)
} else { } else {
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Floor) g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Floor)
@@ -168,7 +168,7 @@ over:
g.relocate(th, g.chRet) g.relocate(th, g.chRet)
// And stop running if need be // And stop running if need be
if stoprun && th.Pos == *th.Dest { if stoprun && th.Pos == *th.Dest {
th.Flags.Clear(IsRun) th.Flags.Clear(Awake)
} }
return 0 return 0
} }
@@ -185,14 +185,14 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool {
// If the thing is confused, let it move randomly. Invisible Stalkers // If the thing is confused, let it move randomly. Invisible Stalkers
// are slightly confused all of the time, and bats are quite confused // are slightly confused all of the time, and bats are quite confused
// all the time // all the time
if (tp.On(IsHuh) && g.rnd(5) != 0) || (tp.Type == 'P' && g.rnd(5) == 0) || if (tp.On(Confused) && g.rnd(5) != 0) || (tp.Type == 'P' && g.rnd(5) == 0) ||
(tp.Type == 'B' && g.rnd(2) == 0) { (tp.Type == 'B' && g.rnd(2) == 0) {
// get a valid random move // get a valid random move
g.chRet = g.rndmove(&tp.Creature) g.chRet = g.rndmove(&tp.Creature)
curdist = distCp(g.chRet, ee) curdist = distCp(g.chRet, ee)
// Small chance that it will become un-confused // Small chance that it will become un-confused
if g.rnd(20) == 0 { if g.rnd(20) == 0 {
tp.Flags.Clear(IsHuh) tp.Flags.Clear(Confused)
} }
} else { } else {
// Otherwise, find the empty spot next to the chaser that is // Otherwise, find the empty spot next to the chaser that is
@@ -233,7 +233,7 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool {
break break
} }
} }
if found != nil && found.Which == SScare { if found != nil && found.ScrollKind() == ScrollScareMonster {
continue continue
} }
} }
@@ -268,8 +268,8 @@ func (g *RogueGame) setOldch(tp *Monster, cp Coord) {
} }
sch := tp.OldCh sch := tp.OldCh
tp.OldCh = g.mvinch(cp.Y, cp.X) tp.OldCh = g.mvinch(cp.Y, cp.X)
if !g.Player.On(IsBlind) { if !g.Player.On(Blind) {
if (sch == Floor || tp.OldCh == Floor) && tp.Room.Flags.Has(IsDark) { if (sch == Floor || tp.OldCh == Floor) && tp.Room.Flags.Has(Dark) {
tp.OldCh = ' ' tp.OldCh = ' '
} else if distCp(cp, g.Player.Pos) <= LampDist && g.Options.SeeFloor { } else if distCp(cp, g.Player.Pos) <= LampDist && g.Options.SeeFloor {
tp.OldCh = g.Level.Char(cp.Y, cp.X) tp.OldCh = g.Level.Char(cp.Y, cp.X)
@@ -281,10 +281,10 @@ func (g *RogueGame) setOldch(tp *Monster, cp Coord) {
// see_monst). // see_monst).
func (g *RogueGame) seeMonst(mp *Monster) bool { func (g *RogueGame) seeMonst(mp *Monster) bool {
p := &g.Player p := &g.Player
if p.On(IsBlind) { if p.On(Blind) {
return false return false
} }
if mp.On(IsInvis) && !p.On(CanSee) { if mp.On(Invisible) && !p.On(CanSeeInvisible) {
return false return false
} }
y, x := mp.Pos.Y, mp.Pos.X y, x := mp.Pos.Y, mp.Pos.X
@@ -298,7 +298,7 @@ func (g *RogueGame) seeMonst(mp *Monster) bool {
if mp.Room != p.Room { if mp.Room != p.Room {
return false return false
} }
return !mp.Room.Flags.Has(IsDark) return !mp.Room.Flags.Has(Dark)
} }
// runto sets a monster running after the hero (chase.c runto). // runto sets a monster running after the hero (chase.c runto).
@@ -308,8 +308,8 @@ func (g *RogueGame) runto(runner Coord) {
return return
} }
// Start the beastie running // Start the beastie running
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
tp.Flags.Clear(IsHeld) tp.Flags.Clear(Held)
tp.Dest = g.findDest(tp) tp.Dest = g.findDest(tp)
} }
@@ -317,8 +317,8 @@ func (g *RogueGame) runto(runner Coord) {
// any room (chase.c roomin). // any room (chase.c roomin).
func (g *RogueGame) roomin(cp Coord) *Room { func (g *RogueGame) roomin(cp Coord) *Room {
fp := *g.Level.FlagsAt(cp.Y, cp.X) fp := *g.Level.FlagsAt(cp.Y, cp.X)
if fp.Has(FPass) { if fp.Has(FPassage) {
return &g.Level.Passages[fp&FPNum] return &g.Level.Passages[fp&FPassNum]
} }
for i := range g.Level.Rooms { for i := range g.Level.Rooms {
@@ -348,11 +348,11 @@ func (g *RogueGame) diagOk(sp, ep Coord) bool {
// cansee). // cansee).
func (g *RogueGame) cansee(y, x int) bool { func (g *RogueGame) cansee(y, x int) bool {
p := &g.Player p := &g.Player
if p.On(IsBlind) { if p.On(Blind) {
return false return false
} }
if distance(y, x, p.Pos.Y, p.Pos.X) < LampDist { if distance(y, x, p.Pos.Y, p.Pos.X) < LampDist {
if g.Level.FlagsAt(y, x).Has(FPass) { if g.Level.FlagsAt(y, x).Has(FPassage) {
if y != p.Pos.Y && x != p.Pos.X && if y != p.Pos.Y && x != p.Pos.X &&
!stepOk(g.Level.Char(y, p.Pos.X)) && !stepOk(g.Level.Char(y, p.Pos.X)) &&
!stepOk(g.Level.Char(p.Pos.Y, x)) { !stepOk(g.Level.Char(p.Pos.Y, x)) {
@@ -364,7 +364,7 @@ func (g *RogueGame) cansee(y, x int) bool {
// We can only see if the hero is in the same room as the coordinate // We can only see if the hero is in the same room as the coordinate
// and the room is lit, or if it is close. // and the room is lit, or if it is close.
rer := g.roomin(Coord{X: x, Y: y}) rer := g.roomin(Coord{X: x, Y: y})
return rer == p.Room && !rer.Flags.Has(IsDark) return rer == p.Room && !rer.Flags.Has(Dark)
} }
// findDest finds the proper destination for the monster (chase.c // findDest finds the proper destination for the monster (chase.c
@@ -375,7 +375,7 @@ func (g *RogueGame) findDest(tp *Monster) *Coord {
return &g.Player.Pos return &g.Player.Pos
} }
for _, obj := range g.Level.Objects { for _, obj := range g.Level.Objects {
if obj.Type == Scroll && obj.Which == SScare { if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster {
continue continue
} }
if g.roomin(obj.Pos) == tp.Room && g.rnd(100) < prob { if g.roomin(obj.Pos) == tp.Room && g.rnd(100) < prob {

View File

@@ -7,7 +7,7 @@ package game
func (g *RogueGame) command() { func (g *RogueGame) command() {
p := &g.Player p := &g.Player
ntimes := 1 // number of player moves ntimes := 1 // number of player moves
if p.On(IsHaste) { if p.On(Hasted) {
ntimes++ ntimes++
} }
// Let the daemons start up // Let the daemons start up
@@ -21,7 +21,7 @@ func (g *RogueGame) command() {
} }
// these are illegal things for the player to be, so if any are // these are illegal things for the player to be, so if any are
// set, someone's been poking in memory // set, someone's been poking in memory
if p.On(IsSlow | IsGreed | IsInvis | IsRegen | IsTarget) { if p.On(Slowed | Greedy | Invisible | Regenerates | Targeted) {
panic("player flags corrupted") panic("player flags corrupted")
} }
@@ -59,7 +59,7 @@ func (g *RogueGame) command() {
} }
if g.NoCommand != 0 { if g.NoCommand != 0 {
if g.NoCommand--; g.NoCommand == 0 { if g.NoCommand--; g.NoCommand == 0 {
p.Flags.Set(IsRun) p.Flags.Set(Awake)
g.msg("you can move again") g.msg("you can move again")
} }
} else { } else {
@@ -121,14 +121,14 @@ func (g *RogueGame) command() {
} }
g.DoDaemons(After) g.DoDaemons(After)
g.DoFuses(After) g.DoFuses(After)
if p.IsRing(Left, RSearch) { if p.IsRing(Left, RingSearching) {
g.search() g.search()
} else if p.IsRing(Left, RTeleport) && g.rnd(50) == 0 { } else if p.IsRing(Left, RingTeleportation) && g.rnd(50) == 0 {
g.teleport() g.teleport()
} }
if p.IsRing(Right, RSearch) { if p.IsRing(Right, RingSearching) {
g.search() g.search()
} else if p.IsRing(Right, RTeleport) && g.rnd(50) == 0 { } else if p.IsRing(Right, RingTeleportation) && g.rnd(50) == 0 {
g.teleport() g.teleport()
} }
} }
@@ -149,7 +149,7 @@ over:
} }
if found != nil { if found != nil {
if !g.levitCheck() { if !g.levitCheck() {
g.pickUp(found.Type) g.pickUp(found.Kind.Glyph())
} }
} else { } else {
if !g.Options.Terse { if !g.Options.Terse {
@@ -197,7 +197,7 @@ over:
g.doRun('n') g.doRun('n')
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'), case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'): CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
if !p.On(IsBlind) { if !p.On(Blind) {
g.DoorStop = true g.DoorStop = true
g.Firstmove = true g.Firstmove = true
} }
@@ -219,7 +219,7 @@ over:
g.Delta.Y += p.Pos.Y g.Delta.Y += p.Pos.Y
g.Delta.X += p.Pos.X g.Delta.X += p.Pos.X
mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X) mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X)
if mp == nil || (!g.seeMonst(mp) && !p.On(SeeMonst)) { if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) {
if !g.Options.Terse { if !g.Options.Terse {
g.addmsg("I see ") g.addmsg("I see ")
} }
@@ -228,7 +228,7 @@ over:
} else if g.diagOk(p.Pos, g.Delta) { } else if g.diagOk(p.Pos, g.Delta) {
g.ToDeath = true g.ToDeath = true
g.MaxHit = 0 g.MaxHit = 0
mp.Flags.Set(IsTarget) mp.Flags.Set(Targeted)
g.RunCh = g.DirCh g.RunCh = g.DirCh
ch = g.DirCh ch = g.DirCh
goto over goto over
@@ -333,10 +333,10 @@ over:
} }
if g.Level.Char(g.Delta.Y, g.Delta.X) != Trap { if g.Level.Char(g.Delta.Y, g.Delta.X) != Trap {
g.msg("no trap there") g.msg("no trap there")
} else if p.On(IsHalu) { } else if p.On(Hallucinating) {
g.msg("%s", trName[g.rnd(NTraps)]) g.msg("%s", trName[g.rnd(NumTrapTypes)])
} else { } else {
g.msg("%s", trName[*fp&FTMask]) g.msg("%s", trName[*fp&FTrapMask])
fp.Set(FSeen) fp.Set(FSeen)
} }
} }
@@ -407,10 +407,10 @@ func (g *RogueGame) wizardCommand(ch byte) {
case CTRL('C'): case CTRL('C'):
g.addPass() g.addPass()
case CTRL('X'): case CTRL('X'):
g.turnSee(p.On(SeeMonst)) g.turnSee(p.On(SenseMonsters))
case CTRL('~'): case CTRL('~'):
if item := g.getItem("charge", int(Stick)); item != nil { if item := g.getItem("charge", KindWand); item != nil {
item.SetCharges(10000) item.Charges = 10000
} }
case CTRL('I'): case CTRL('I'):
for range 9 { for range 9 {
@@ -418,17 +418,17 @@ func (g *RogueGame) wizardCommand(ch byte) {
} }
// Give him a sword (+1,+1) // Give him a sword (+1,+1)
obj := newObject() obj := newObject()
g.initWeapon(obj, TwoSword) g.initWeapon(obj, WeaponTwoHandedSword)
obj.HPlus = 1 obj.HPlus = 1
obj.DPlus = 1 obj.DPlus = 1
g.addPack(obj, true) g.addPack(obj, true)
p.CurWeapon = obj p.CurWeapon = obj
// And his suit of armor // And his suit of armor
obj = newObject() obj = newObject()
obj.Type = Armor obj.Kind = KindArmor
obj.Which = PlateMail obj.Which = int(ArmorPlateMail)
obj.Arm = -5 obj.ArmorClass = -5
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
obj.Count = 1 obj.Count = 1
p.CurArmor = obj p.CurArmor = obj
g.addPack(obj, true) g.addPack(obj, true)
@@ -453,10 +453,10 @@ func (g *RogueGame) search() {
ey := p.Pos.Y + 1 ey := p.Pos.Y + 1
ex := p.Pos.X + 1 ex := p.Pos.X + 1
probinc := 0 probinc := 0
if p.On(IsHalu) { if p.On(Hallucinating) {
probinc = 3 probinc = 3
} }
if p.On(IsBlind) { if p.On(Blind) {
probinc += 2 probinc += 2
} }
found := false found := false
@@ -486,10 +486,10 @@ func (g *RogueGame) search() {
if !g.Options.Terse { if !g.Options.Terse {
g.addmsg("you found ") g.addmsg("you found ")
} }
if p.On(IsHalu) { if p.On(Hallucinating) {
g.msg("%s", trName[g.rnd(NTraps)]) g.msg("%s", trName[g.rnd(NumTrapTypes)])
} else { } else {
g.msg("%s", trName[*fp&FTMask]) g.msg("%s", trName[*fp&FTrapMask])
fp.Set(FSeen) fp.Set(FSeen)
} }
foundone = true foundone = true
@@ -661,7 +661,7 @@ func (g *RogueGame) uLevel() {
// levitCheck checks whether she's levitating, and if she is, prints an // levitCheck checks whether she's levitating, and if she is, prints an
// appropriate message (command.c levit_check). // appropriate message (command.c levit_check).
func (g *RogueGame) levitCheck() bool { func (g *RogueGame) levitCheck() bool {
if !g.Player.On(IsLevit) { if !g.Player.On(Levitating) {
return false return false
} }
g.msg("You can't. You're floating off the ground!") g.msg("You can't. You're floating off the ground!")
@@ -671,7 +671,7 @@ func (g *RogueGame) levitCheck() bool {
// call allows a user to call a potion, scroll, or ring something // call allows a user to call a potion, scroll, or ring something
// (command.c call). // (command.c call).
func (g *RogueGame) call() { func (g *RogueGame) call() {
obj := g.getItem("call", Callable) obj := g.getItem("call", KindCallable)
// Make certain that it is something that we want to name // Make certain that it is something that we want to name
if obj == nil { if obj == nil {
return return
@@ -681,20 +681,20 @@ func (g *RogueGame) call() {
var know *bool var know *bool
var guess *string var guess *string
it := &g.Items it := &g.Items
switch obj.Type { switch obj.Kind {
case Ring: case KindRing:
op = &it.Rings[obj.Which] op = &it.Rings[obj.Which]
elsewise = it.RingStones[obj.Which] elsewise = it.RingStones[obj.Which]
case Potion: case KindPotion:
op = &it.Potions[obj.Which] op = &it.Potions[obj.Which]
elsewise = it.PotColors[obj.Which] elsewise = it.PotColors[obj.Which]
case Scroll: case KindScroll:
op = &it.Scrolls[obj.Which] op = &it.Scrolls[obj.Which]
elsewise = it.ScrNames[obj.Which] elsewise = it.ScrNames[obj.Which]
case Stick: case KindWand:
op = &it.Sticks[obj.Which] op = &it.Sticks[obj.Which]
elsewise = it.WandMade[obj.Which] elsewise = it.WandMade[obj.Which]
case Food: case KindFood:
g.msg("you can't call that anything") g.msg("you can't call that anything")
return return
default: default:

View File

@@ -40,12 +40,12 @@ type Player struct {
func (c *Creature) On(f CreatureFlags) bool { return c.Flags.Has(f) } func (c *Creature) On(f CreatureFlags) bool { return c.Flags.Has(f) }
// IsRing is the C ISRING(hand, ring) macro. // IsRing is the C ISRING(hand, ring) macro.
func (p *Player) IsRing(hand, ring int) bool { func (p *Player) IsRing(hand int, ring RingKind) bool {
return p.CurRing[hand] != nil && p.CurRing[hand].Which == ring return p.CurRing[hand] != nil && p.CurRing[hand].RingKind() == ring
} }
// IsWearing is the C ISWEARING(ring) macro: is the ring on either hand. // IsWearing is the C ISWEARING(ring) macro: is the ring on either hand.
func (p *Player) IsWearing(ring int) bool { func (p *Player) IsWearing(ring RingKind) bool {
return p.IsRing(Left, ring) || p.IsRing(Right, ring) return p.IsRing(Left, ring) || p.IsRing(Right, ring)
} }

View File

@@ -53,10 +53,10 @@ func (g *RogueGame) doctor(int) {
} else if g.Quiet >= 3 { } else if g.Quiet >= 3 {
p.Stats.HP += g.rnd(lv-7) + 1 p.Stats.HP += g.rnd(lv-7) + 1
} }
if p.IsRing(Left, RRegen) { if p.IsRing(Left, RingRegeneration) {
p.Stats.HP++ p.Stats.HP++
} }
if p.IsRing(Right, RRegen) { if p.IsRing(Right, RingRegeneration) {
p.Stats.HP++ p.Stats.HP++
} }
if ohp != p.Stats.HP { if ohp != p.Stats.HP {
@@ -92,27 +92,27 @@ func wanderTime(g *RogueGame) int { return g.spread(70) }
// unconfuse releases the poor player from his confusion (daemons.c // unconfuse releases the poor player from his confusion (daemons.c
// unconfuse). // unconfuse).
func (g *RogueGame) unconfuse(int) { func (g *RogueGame) unconfuse(int) {
g.Player.Flags.Clear(IsHuh) g.Player.Flags.Clear(Confused)
g.msg("you feel less %s now", g.chooseStr("trippy", "confused")) g.msg("you feel less %s now", g.chooseStr("trippy", "confused"))
} }
// unsee turns off the ability to see invisible (daemons.c unsee). // unsee turns off the ability to see invisible (daemons.c unsee).
func (g *RogueGame) unsee(int) { func (g *RogueGame) unsee(int) {
for _, th := range g.Level.Monsters { for _, th := range g.Level.Monsters {
if th.On(IsInvis) && g.seeMonst(th) { if th.On(Invisible) && g.seeMonst(th) {
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh) g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
} }
} }
g.Player.Flags.Clear(CanSee) g.Player.Flags.Clear(CanSeeInvisible)
} }
// sight gives the hero his sight back (daemons.c sight). // sight gives the hero his sight back (daemons.c sight).
func (g *RogueGame) sight(int) { func (g *RogueGame) sight(int) {
p := &g.Player p := &g.Player
if p.On(IsBlind) { if p.On(Blind) {
g.Extinguish(DSight) g.Extinguish(DSight)
p.Flags.Clear(IsBlind) p.Flags.Clear(Blind)
if !p.Room.Flags.Has(IsGone) { if !p.Room.Flags.Has(Gone) {
g.enterRoom(p.Pos) g.enterRoom(p.Pos)
} }
g.msg("%s", g.chooseStr("far out! Everything is all cosmic again", g.msg("%s", g.chooseStr("far out! Everything is all cosmic again",
@@ -122,7 +122,7 @@ func (g *RogueGame) sight(int) {
// nohaste ends the hasting (daemons.c nohaste). // nohaste ends the hasting (daemons.c nohaste).
func (g *RogueGame) nohaste(int) { func (g *RogueGame) nohaste(int) {
g.Player.Flags.Clear(IsHaste) g.Player.Flags.Clear(Hasted)
g.msg("you feel yourself slowing down") g.msg("you feel yourself slowing down")
} }
@@ -170,7 +170,7 @@ func (g *RogueGame) stomach(int) {
} }
} }
if p.HungryState != origHungry { if p.HungryState != origHungry {
p.Flags.Clear(IsRun) p.Flags.Clear(Awake)
g.Running = false g.Running = false
g.ToDeath = false g.ToDeath = false
g.Count = 0 g.Count = 0
@@ -180,30 +180,30 @@ func (g *RogueGame) stomach(int) {
// comeDown takes the hero down off her acid trip (daemons.c come_down). // comeDown takes the hero down off her acid trip (daemons.c come_down).
func (g *RogueGame) comeDown(int) { func (g *RogueGame) comeDown(int) {
p := &g.Player p := &g.Player
if !p.On(IsHalu) { if !p.On(Hallucinating) {
return return
} }
g.KillDaemon(DVisuals) g.KillDaemon(DVisuals)
p.Flags.Clear(IsHalu) p.Flags.Clear(Hallucinating)
if p.On(IsBlind) { if p.On(Blind) {
return return
} }
// undo the things // undo the things
for _, tp := range g.Level.Objects { for _, tp := range g.Level.Objects {
if g.cansee(tp.Pos.Y, tp.Pos.X) { if g.cansee(tp.Pos.Y, tp.Pos.X) {
g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Type) g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Kind.Glyph())
} }
} }
// undo the monsters // undo the monsters
seemonst := p.On(SeeMonst) seemonst := p.On(SenseMonsters)
for _, tp := range g.Level.Monsters { for _, tp := range g.Level.Monsters {
g.move(tp.Pos.Y, tp.Pos.X) g.move(tp.Pos.Y, tp.Pos.X)
if g.cansee(tp.Pos.Y, tp.Pos.X) { if g.cansee(tp.Pos.Y, tp.Pos.X) {
if !tp.On(IsInvis) || p.On(CanSee) { if !tp.On(Invisible) || p.On(CanSeeInvisible) {
g.addch(tp.Disguise) g.addch(tp.Disguise)
} else { } else {
g.addch(g.Level.Char(tp.Pos.Y, tp.Pos.X)) g.addch(g.Level.Char(tp.Pos.Y, tp.Pos.X))
@@ -237,7 +237,7 @@ func (g *RogueGame) visuals(int) {
} }
// change the monsters // change the monsters
seemonst := p.On(SeeMonst) seemonst := p.On(SenseMonsters)
for _, tp := range g.Level.Monsters { for _, tp := range g.Level.Monsters {
g.move(tp.Pos.Y, tp.Pos.X) g.move(tp.Pos.Y, tp.Pos.X)
if g.seeMonst(tp) { if g.seeMonst(tp) {
@@ -256,7 +256,7 @@ func (g *RogueGame) visuals(int) {
// land lands the hero from a levitation potion (daemons.c land). // land lands the hero from a levitation potion (daemons.c land).
func (g *RogueGame) land(int) { func (g *RogueGame) land(int) {
g.Player.Flags.Clear(IsLevit) g.Player.Flags.Clear(Levitating)
g.msg("%s", g.chooseStr("bummer! You've hit the ground", g.msg("%s", g.chooseStr("bummer! You've hit the ground",
"you float gently to the ground")) "you float gently to the ground"))
} }

58
game/dice.go Normal file
View File

@@ -0,0 +1,58 @@
package game
import (
"fmt"
"strings"
)
// Rogue expresses damage as strings like "1x4/3x6": one attack rolling
// 1d4 and a second rolling 3d6. The C code re-parsed these with atoi on
// every swing (fight.c roll_em); the port parses them once, at table
// definition or item creation, into a DiceSpec.
// DiceRoll is one attack's dice: Count rolls of a Sides-sided die.
type DiceRoll struct {
Count int
Sides int
}
// DiceSpec is the sequence of attacks a damage string described.
type DiceSpec []DiceRoll
// ParseDice parses a C damage string with the exact semantics of the
// roll_em loop: leading digits (C atoi) before and after each 'x', attacks
// separated by '/'. Junk like the bestiary's "%%%x0" placeholder parses as
// a single 0x0 attack, as it did in C.
func ParseDice(s string) DiceSpec {
var spec DiceSpec
for s != "" {
count := cAtoi(s)
xi := strings.IndexByte(s, 'x')
if xi < 0 {
break
}
s = s[xi+1:]
spec = append(spec, DiceRoll{Count: count, Sides: cAtoi(s)})
si := strings.IndexByte(s, '/')
if si < 0 {
break
}
s = s[si+1:]
}
return spec
}
// dice is the table-definition shorthand for ParseDice.
func dice(s string) DiceSpec { return ParseDice(s) }
// String renders the spec back in the classic "NxM/NxM" form.
func (d DiceSpec) String() string {
var sb strings.Builder
for i, r := range d {
if i > 0 {
sb.WriteByte('/')
}
fmt.Fprintf(&sb, "%dx%d", r.Count, r.Sides)
}
return sb.String()
}

45
game/dice_test.go Normal file
View File

@@ -0,0 +1,45 @@
package game
import "testing"
// ParseDice must keep the exact semantics of the C roll_em parse loop,
// including its junk-tolerant edges: the bestiary placeholder "%%%x0" and
// the flytrap reset "000x0" both mean a single 0x0 attack.
func TestParseDice(t *testing.T) {
cases := []struct {
in string
want string
len int
}{
{"1x4", "1x4", 1},
{"1x2/1x5/1x5", "1x2/1x5/1x5", 3},
{"2x12/2x4", "2x12/2x4", 2},
{"0x0", "0x0", 1},
{"000x0", "0x0", 1},
{"%%%x0", "0x0", 1},
{"", "", 0},
{"3", "", 0}, // no 'x': C parses nothing
}
for _, c := range cases {
got := ParseDice(c.in)
if len(got) != c.len || got.String() != c.want {
t.Errorf("ParseDice(%q) = %v (len %d), want %q (len %d)",
c.in, got, len(got), c.want, c.len)
}
}
}
// The bestiary and weapon tables must parse to at least one attack each so
// every creature and weapon actually swings.
func TestTablesHaveDice(t *testing.T) {
for i, m := range monsterTable {
if len(m.Stats.Dmg) == 0 {
t.Errorf("monster %c (%s) has no attacks", 'A'+i, m.Name)
}
}
for w, iw := range initWeaps {
if len(iw.dam) == 0 || len(iw.hrl) == 0 {
t.Errorf("weapon %v has empty dice", WeaponKind(w))
}
}
}

View File

@@ -22,8 +22,8 @@ func give(g *RogueGame, obj *Object) byte {
func TestQuaffHealingPotion(t *testing.T) { func TestQuaffHealingPotion(t *testing.T) {
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
pot := newObject() pot := newObject()
pot.Type = Potion pot.Kind = KindPotion
pot.Which = PHealing pot.Which = int(PotionHealing)
ch := give(g, pot) ch := give(g, pot)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
@@ -32,7 +32,7 @@ func TestQuaffHealingPotion(t *testing.T) {
if g.Player.Stats.HP <= 1 { if g.Player.Stats.HP <= 1 {
t.Error("healing potion did not heal") t.Error("healing potion did not heal")
} }
if !g.Items.Potions[PHealing].Know { if !g.Items.Potions[PotionHealing].Know {
t.Error("healing potion not identified after drinking") t.Error("healing potion not identified after drinking")
} }
if len(g.Player.Pack) != 5 { if len(g.Player.Pack) != 5 {
@@ -43,13 +43,13 @@ func TestQuaffHealingPotion(t *testing.T) {
func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) { func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) {
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
pot := newObject() pot := newObject()
pot.Type = Potion pot.Kind = KindPotion
pot.Which = PConfuse pot.Which = int(PotionConfusion)
ch := give(g, pot) ch := give(g, pot)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
g.quaff() g.quaff()
if !g.Player.On(IsHuh) { if !g.Player.On(Confused) {
t.Error("confusion potion did not confuse") t.Error("confusion potion did not confuse")
} }
if g.findSlot(DUnconfuse) == nil { if g.findSlot(DUnconfuse) == nil {
@@ -59,7 +59,7 @@ func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) {
for range 30 { for range 30 {
g.DoFuses(After) g.DoFuses(After)
} }
if g.Player.On(IsHuh) { if g.Player.On(Confused) {
t.Error("confusion never wore off") t.Error("confusion never wore off")
} }
} }
@@ -67,16 +67,16 @@ func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) {
func TestReadEnchantArmor(t *testing.T) { func TestReadEnchantArmor(t *testing.T) {
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
scr := newObject() scr := newObject()
scr.Type = Scroll scr.Kind = KindScroll
scr.Which = SArmor scr.Which = int(ScrollEnchantArmor)
ch := give(g, scr) ch := give(g, scr)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
before := g.Player.CurArmor.Arm before := g.Player.CurArmor.ArmorClass
g.readScroll() g.readScroll()
if g.Player.CurArmor.Arm != before-1 { if g.Player.CurArmor.ArmorClass != before-1 {
t.Errorf("enchant armor: AC %d -> %d, want %d", t.Errorf("enchant armor: AC %d -> %d, want %d",
before, g.Player.CurArmor.Arm, before-1) before, g.Player.CurArmor.ArmorClass, before-1)
} }
} }
@@ -88,16 +88,16 @@ func TestReadHoldMonsterFreezesAdjacent(t *testing.T) {
// TestHoldScrollGreedyMonsterQuirk. // TestHoldScrollGreedyMonsterQuirk.
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
tp := spawnAdjacent(g, 'Z') tp := spawnAdjacent(g, 'Z')
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
scr := newObject() scr := newObject()
scr.Type = Scroll scr.Kind = KindScroll
scr.Which = SHold scr.Which = int(ScrollHoldMonster)
ch := give(g, scr) ch := give(g, scr)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
g.readScroll() g.readScroll()
t.Logf("after scroll: flags=%o huh=%q", tp.Flags, g.Msgs.Huh) t.Logf("after scroll: flags=%o huh=%q", tp.Flags, g.Msgs.Huh)
if tp.On(IsRun) || !tp.On(IsHeld) { if tp.On(Awake) || !tp.On(Held) {
t.Error("hold monster scroll did not hold the adjacent monster") t.Error("hold monster scroll did not hold the adjacent monster")
} }
} }
@@ -109,20 +109,20 @@ func TestReadHoldMonsterFreezesAdjacent(t *testing.T) {
func TestHoldScrollGreedyMonsterQuirk(t *testing.T) { func TestHoldScrollGreedyMonsterQuirk(t *testing.T) {
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
tp := spawnAdjacent(g, 'O') tp := spawnAdjacent(g, 'O')
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
scr := newObject() scr := newObject()
scr.Type = Scroll scr.Kind = KindScroll
scr.Which = SHold scr.Which = int(ScrollHoldMonster)
ch := give(g, scr) ch := give(g, scr)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
g.readScroll() g.readScroll()
t.Logf("orc after scroll: flags=%o (IsRun=%v IsHeld=%v)", t.Logf("orc after scroll: flags=%o (Awake=%v Held=%v)",
tp.Flags, tp.On(IsRun), tp.On(IsHeld)) tp.Flags, tp.On(Awake), tp.On(Held))
if !tp.On(IsHeld) { if !tp.On(Held) {
t.Error("orc lost IsHeld entirely") t.Error("orc lost Held entirely")
} }
if !tp.On(IsRun) { if !tp.On(Awake) {
t.Error("quirk changed: greedy monster stayed held; if this is a " + t.Error("quirk changed: greedy monster stayed held; if this is a " +
"deliberate fix, update this test and ARCHITECTURE.md") "deliberate fix, update this test and ARCHITECTURE.md")
} }
@@ -132,19 +132,19 @@ func TestZapSlowMonster(t *testing.T) {
g := mkGameInput(t, 5, "") g := mkGameInput(t, 5, "")
tp := spawnAdjacent(g, 'Z') tp := spawnAdjacent(g, 'Z')
stick := newObject() stick := newObject()
stick.Type = Stick stick.Kind = KindWand
stick.Which = WsSlowM stick.Which = int(WandSlowMonster)
g.fixStick(stick) g.fixStick(stick)
ch := give(g, stick) ch := give(g, stick)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
g.Delta = Coord{X: 1, Y: 0} // aim at the monster g.Delta = Coord{X: 1, Y: 0} // aim at the monster
charges := stick.Charges() charges := stick.Charges
g.doZap() g.doZap()
if !tp.On(IsSlow) { if !tp.On(Slowed) {
t.Error("slow monster wand did not slow") t.Error("slow monster wand did not slow")
} }
if stick.Charges() != charges-1 { if stick.Charges != charges-1 {
t.Error("zap did not use a charge") t.Error("zap did not use a charge")
} }
} }

View File

@@ -1,10 +1,6 @@
package game package game
import ( import "strconv"
"fmt"
"strconv"
"strings"
)
// fight.c — all the fighting gets done here. // fight.c — all the fighting gets done here.
@@ -48,14 +44,14 @@ var addDam = [32]int{
// 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 {
if !g.seeMonst(tp) && !g.Player.On(SeeMonst) { if !g.seeMonst(tp) && !g.Player.On(SenseMonsters) {
if g.Options.Terse { if g.Options.Terse {
return "it" return "it"
} }
return "something" return "something"
} }
var mname string var mname string
if g.Player.On(IsHalu) { if g.Player.On(Hallucinating) {
ch := int(g.mvinch(tp.Pos.Y, tp.Pos.X)) ch := int(g.mvinch(tp.Pos.Y, tp.Pos.X))
if !isUpper(byte(ch)) { if !isUpper(byte(ch)) {
ch = g.rnd(26) ch = g.rnd(26)
@@ -83,9 +79,9 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
g.Quiet = 0 g.Quiet = 0
g.runto(mp) g.runto(mp)
// Let him know it was really a xeroc (if it was one). // Let him know it was really a xeroc (if it was one).
if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(IsBlind) { if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) {
tp.Disguise = 'X' tp.Disguise = 'X'
if p.On(IsHalu) { if p.On(Hallucinating) {
g.mvaddch(tp.Pos.Y, tp.Pos.X, byte(g.rnd(26)+'A')) g.mvaddch(tp.Pos.Y, tp.Pos.X, byte(g.rnd(26)+'A'))
} }
g.msg("%s", g.chooseStr("heavy! That's a nasty critter!", g.msg("%s", g.chooseStr("heavy! That's a nasty critter!",
@@ -104,17 +100,17 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
} else { } else {
g.hit("", mname, g.Options.Terse) g.hit("", mname, g.Options.Terse)
} }
if p.On(CanHuh) { if p.On(CanConfuse) {
didHit = true didHit = true
tp.Flags.Set(IsHuh) tp.Flags.Set(Confused)
p.Flags.Clear(CanHuh) p.Flags.Clear(CanConfuse)
g.endmsg() g.endmsg()
g.HasHit = false g.HasHit = false
g.msg("your hands stop glowing %s", g.pickColor("red")) g.msg("your hands stop glowing %s", g.pickColor("red"))
} }
if tp.Stats.HP <= 0 { if tp.Stats.HP <= 0 {
g.killed(tp, true) g.killed(tp, true)
} else if didHit && !p.On(IsBlind) { } else if didHit && !p.On(Blind) {
g.msg("%s appears confused", mname) g.msg("%s appears confused", mname)
} }
didHit = true didHit = true
@@ -137,13 +133,13 @@ func (g *RogueGame) attack(mp *Monster) int {
g.Running = false g.Running = false
g.Count = 0 g.Count = 0
g.Quiet = 0 g.Quiet = 0
if g.ToDeath && !mp.On(IsTarget) { if g.ToDeath && !mp.On(Targeted) {
g.ToDeath = false g.ToDeath = false
g.Kamikaze = false g.Kamikaze = false
} }
if mp.Type == 'X' && mp.Disguise != 'X' && !p.On(IsBlind) { if mp.Type == 'X' && mp.Disguise != 'X' && !p.On(Blind) {
mp.Disguise = 'X' mp.Disguise = 'X'
if p.On(IsHalu) { if p.On(Hallucinating) {
g.mvaddch(mp.Pos.Y, mp.Pos.X, byte(g.rnd(26)+'A')) g.mvaddch(mp.Pos.Y, mp.Pos.X, byte(g.rnd(26)+'A'))
} }
} }
@@ -171,14 +167,14 @@ func (g *RogueGame) attack(mp *Monster) int {
g.ToDeath = false g.ToDeath = false
} }
} }
if !mp.On(IsCanc) { if !mp.On(Cancelled) {
switch mp.Type { switch mp.Type {
case 'A': case 'A':
// If an aquator hits, you can lose armor class. // If an aquator hits, you can lose armor class.
g.rustArmor(p.CurArmor) g.rustArmor(p.CurArmor)
case 'I': case 'I':
// The ice monster freezes you // The ice monster freezes you
p.Flags.Clear(IsRun) p.Flags.Clear(Awake)
if g.NoCommand == 0 { if g.NoCommand == 0 {
g.addmsg("you are frozen") g.addmsg("you are frozen")
if !g.Options.Terse { if !g.Options.Terse {
@@ -193,7 +189,7 @@ func (g *RogueGame) attack(mp *Monster) int {
case 'R': case 'R':
// Rattlesnakes have poisonous bites // Rattlesnakes have poisonous bites
if !g.save(VsPoison) { if !g.save(VsPoison) {
if !p.IsWearing(RSustStr) { if !p.IsWearing(RingSustainStrength) {
g.chgStr(-1) g.chgStr(-1)
if !g.Options.Terse { if !g.Options.Terse {
g.msg("you feel a bite in your leg and now feel weaker") g.msg("you feel a bite in your leg and now feel weaker")
@@ -243,9 +239,9 @@ func (g *RogueGame) attack(mp *Monster) int {
} }
case 'F': case 'F':
// Venus Flytrap stops the poor guy from moving // Venus Flytrap stops the poor guy from moving
p.Flags.Set(IsHeld) p.Flags.Set(Held)
p.VfHit++ p.VfHit++
g.Monsters['F'-'A'].Stats.Dmg = fmt.Sprintf("%dx1", p.VfHit) g.Monsters['F'-'A'].Stats.Dmg = DiceSpec{{Count: p.VfHit, Sides: 1}}
if p.Stats.HP--; p.Stats.HP <= 0 { if p.Stats.HP--; p.Stats.HP <= 0 {
g.death('F') g.death('F')
} }
@@ -322,76 +318,64 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
p := &g.Player p := &g.Player
att := &thatt.Stats att := &thatt.Stats
def := &thdef.Stats def := &thdef.Stats
var cp string var attacks DiceSpec
var hplus, dplus int var hplus, dplus int
if weap == nil { if weap == nil {
cp = att.Dmg attacks = att.Dmg
} else { } else {
hplus = weap.HPlus hplus = weap.HPlus
dplus = weap.DPlus dplus = weap.DPlus
if weap == p.CurWeapon { if weap == p.CurWeapon {
if p.IsRing(Left, RAddDam) { if p.IsRing(Left, RingIncreaseDamage) {
dplus += p.CurRing[Left].Arm dplus += p.CurRing[Left].Bonus
} else if p.IsRing(Left, RAddHit) { } else if p.IsRing(Left, RingDexterity) {
hplus += p.CurRing[Left].Arm hplus += p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RAddDam) { if p.IsRing(Right, RingIncreaseDamage) {
dplus += p.CurRing[Right].Arm dplus += p.CurRing[Right].Bonus
} else if p.IsRing(Right, RAddHit) { } else if p.IsRing(Right, RingDexterity) {
hplus += p.CurRing[Right].Arm hplus += p.CurRing[Right].Bonus
} }
} }
cp = weap.Damage attacks = weap.Damage
if hurl { if hurl {
if weap.Flags.Has(IsMissl) && p.CurWeapon != nil && if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
p.CurWeapon.Which == weap.Launch { WeaponKind(p.CurWeapon.Which) == weap.Launch {
cp = weap.HurlDmg attacks = weap.HurlDmg
hplus += p.CurWeapon.HPlus hplus += p.CurWeapon.HPlus
dplus += p.CurWeapon.DPlus dplus += p.CurWeapon.DPlus
} else if weap.Launch < 0 { } else if weap.Launch < 0 {
cp = weap.HurlDmg attacks = weap.HurlDmg
} }
} }
} }
// If the creature being attacked is not running (asleep or held) then // If the creature being attacked is not running (asleep or held) then
// the attacker gets a plus four bonus to hit. // the attacker gets a plus four bonus to hit.
if !thdef.Flags.Has(IsRun) { if !thdef.Flags.Has(Awake) {
hplus += 4 hplus += 4
} }
defArm := def.Arm defArm := def.ArmorClass
if def == &p.Stats { if def == &p.Stats {
if p.CurArmor != nil { if p.CurArmor != nil {
defArm = p.CurArmor.Arm defArm = p.CurArmor.ArmorClass
} }
if p.IsRing(Left, RProtect) { if p.IsRing(Left, RingProtection) {
defArm -= p.CurRing[Left].Arm defArm -= p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RProtect) { if p.IsRing(Right, RingProtection) {
defArm -= p.CurRing[Right].Arm defArm -= p.CurRing[Right].Bonus
} }
} }
didHit := false didHit := false
for cp != "" { for _, atk := range attacks {
ndice := cAtoi(cp)
xi := strings.IndexByte(cp, 'x')
if xi < 0 {
break
}
cp = cp[xi+1:]
nsides := cAtoi(cp)
if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) { if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) {
proll := g.roll(ndice, nsides) proll := g.roll(atk.Count, atk.Sides)
damage := dplus + proll + addDam[att.Str] damage := dplus + proll + addDam[att.Str]
if damage > 0 { if damage > 0 {
def.HP -= damage def.HP -= damage
} }
didHit = true didHit = true
} }
si := strings.IndexByte(cp, '/')
if si < 0 {
break
}
cp = cp[si+1:]
} }
return didHit return didHit
} }
@@ -425,7 +409,7 @@ func (g *RogueGame) thunk(weap *Object, mname string, noend bool) {
if g.ToDeath { if g.ToDeath {
return return
} }
if weap.Type == Weapon { if weap.Kind == KindWeapon {
g.addmsg("the %s hits ", g.Items.Weapons[weap.Which].Name) g.addmsg("the %s hits ", g.Items.Weapons[weap.Which].Name)
} else { } else {
g.addmsg("you hit ") g.addmsg("you hit ")
@@ -488,7 +472,7 @@ func (g *RogueGame) bounce(weap *Object, mname string, noend bool) {
if g.ToDeath { if g.ToDeath {
return return
} }
if weap.Type == Weapon { if weap.Kind == KindWeapon {
g.addmsg("the %s misses ", g.Items.Weapons[weap.Which].Name) g.addmsg("the %s misses ", g.Items.Weapons[weap.Which].Name)
} else { } else {
g.addmsg("you missed ") g.addmsg("you missed ")
@@ -512,7 +496,7 @@ func (g *RogueGame) removeMon(mp Coord, tp *Monster, waskill bool) {
g.Level.SetMonsterAt(mp.Y, mp.X, nil) g.Level.SetMonsterAt(mp.Y, mp.X, nil)
g.mvaddch(mp.Y, mp.X, tp.OldCh) g.mvaddch(mp.Y, mp.X, tp.OldCh)
detachMon(&g.Level.Monsters, tp) detachMon(&g.Level.Monsters, tp)
if tp.On(IsTarget) { if tp.On(Targeted) {
g.Kamikaze = false g.Kamikaze = false
g.ToDeath = false g.ToDeath = false
if g.Options.FightFlush { if g.Options.FightFlush {
@@ -529,9 +513,9 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
// If the monster was a venus flytrap, un-hold him // If the monster was a venus flytrap, un-hold him
switch tp.Type { switch tp.Type {
case 'F': case 'F':
p.Flags.Clear(IsHeld) p.Flags.Clear(Held)
p.VfHit = 0 p.VfHit = 0
g.Monsters['F'-'A'].Stats.Dmg = "000x0" g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
case 'L': case 'L':
pos, ok := g.fallpos(tp.Pos) pos, ok := g.fallpos(tp.Pos)
if ok { if ok {
@@ -539,10 +523,10 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
} }
if ok && g.Depth >= g.MaxDepth { if ok && g.Depth >= g.MaxDepth {
gold := newObject() gold := newObject()
gold.Type = Gold gold.Kind = KindGold
gold.SetGoldVal(g.goldCalc()) gold.GoldValue = g.goldCalc()
if g.save(VsMagic) { if g.save(VsMagic) {
gold.Arm += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc() gold.GoldValue += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
} }
attachObj(&tp.Pack, gold) attachObj(&tp.Pack, gold)
} }

View File

@@ -23,9 +23,9 @@ func spawnAdjacent(g *RogueGame, typ byte) *Monster {
func TestRollEmParsesMultiAttackDice(t *testing.T) { func TestRollEmParsesMultiAttackDice(t *testing.T) {
g := mkGame(t, 42) g := mkGame(t, 42)
att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: "1x4/1x4/1x4"}} att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: dice("1x4/1x4/1x4")}}
def := &Creature{Stats: Stats{Arm: 10, HP: 1000}} def := &Creature{Stats: Stats{ArmorClass: 10, HP: 1000}}
def.Flags.Set(IsRun) def.Flags.Set(Awake)
// With attacker level 20 vs armor 10, swing always hits // With attacker level 20 vs armor 10, swing always hits
// (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of // (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of
// 1x4 + str bonus 1 each must deal between 6 and 15 damage. // 1x4 + str bonus 1 each must deal between 6 and 15 damage.
@@ -60,7 +60,7 @@ func TestAttackHurtsPlayer(t *testing.T) {
g := mkGame(t, 9) g := mkGame(t, 9)
tp := spawnAdjacent(g, 'T') // troll: 1x8/1x8/2x6 tp := spawnAdjacent(g, 'T') // troll: 1x8/1x8/2x6
tp.Stats.Lvl = 20 // always hits tp.Stats.Lvl = 20 // always hits
tp.Flags.Clear(IsCanc) tp.Flags.Clear(Cancelled)
hpBefore := g.Player.Stats.HP hpBefore := g.Player.Stats.HP
g.Player.Stats.HP = 500 g.Player.Stats.HP = 500
g.Player.Stats.MaxHP = 500 g.Player.Stats.MaxHP = 500
@@ -96,7 +96,7 @@ func TestRunnersChaseHero(t *testing.T) {
} }
tp := &Monster{} tp := &Monster{}
g.newMonster(tp, 'H', pos) g.newMonster(tp, 'H', pos)
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
tp.Dest = &p.Pos tp.Dest = &p.Pos
d0 := distCp(tp.Pos, p.Pos) d0 := distCp(tp.Pos, p.Pos)
g.runners(0) g.runners(0)

View File

@@ -3,19 +3,19 @@ package game
// ItemLore is the per-game item identity state: the randomized appearance // ItemLore is the per-game item identity state: the randomized appearance
// names and the seven mutable ObjInfo tables (extern.c/init.c). // names and the seven mutable ObjInfo tables (extern.c/init.c).
type ItemLore struct { type ItemLore struct {
PotColors [MaxPotions]string // p_colors: colors of the potions PotColors [NumPotionTypes]string // p_colors: colors of the potions
ScrNames [MaxScrolls]string // s_names: names of the scrolls ScrNames [NumScrollTypes]string // s_names: names of the scrolls
RingStones [MaxRings]string // r_stones: stone settings of the rings RingStones [NumRingTypes]string // r_stones: stone settings of the rings
WandMade [MaxSticks]string // ws_made: what sticks are made of WandMade [NumWandTypes]string // ws_made: what sticks are made of
WandType [MaxSticks]string // ws_type: "wand" or "staff" WandType [NumWandTypes]string // ws_type: "wand" or "staff"
Things [NumThings]ObjInfo Things [NumThings]ObjInfo
Potions [MaxPotions]ObjInfo Potions [NumPotionTypes]ObjInfo
Scrolls [MaxScrolls]ObjInfo Scrolls [NumScrollTypes]ObjInfo
Rings [MaxRings]ObjInfo Rings [NumRingTypes]ObjInfo
Sticks [MaxSticks]ObjInfo Sticks [NumWandTypes]ObjInfo
Weapons [MaxWeapons + 1]ObjInfo Weapons [NumWeaponTypes + 1]ObjInfo
Armors [MaxArmors]ObjInfo Armors [NumArmorTypes]ObjInfo
Group int // group number for the next stack of missiles (weapons.c `group`) Group int // group number for the next stack of missiles (weapons.c `group`)
} }
@@ -168,7 +168,7 @@ func NewGame(cfg Config) *RogueGame {
g.FileName = cfg.Home + "/rogue.save" g.FileName = cfg.Home + "/rogue.save"
g.rogueOpts = cfg.RogueOpts g.rogueOpts = cfg.RogueOpts
if cfg.Wizard { if cfg.Wizard {
g.Player.Flags.Set(SeeMonst) g.Player.Flags.Set(SenseMonsters)
} }
if cfg.RogueOpts != "" { if cfg.RogueOpts != "" {
g.ParseOpts(cfg.RogueOpts) g.ParseOpts(cfg.RogueOpts)
@@ -177,7 +177,7 @@ func NewGame(cfg Config) *RogueGame {
g.Monsters = monsterTable g.Monsters = 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 {
g.Level.Passages[i].Flags = IsGone | IsDark g.Level.Passages[i].Flags = Gone | Dark
} }
g.initProbs() // set up prob tables for objects g.initProbs() // set up prob tables for objects

View File

@@ -13,37 +13,37 @@ func (g *RogueGame) initPlayer() {
p.FoodLeft = HungerTime p.FoodLeft = HungerTime
// Give him some food // Give him some food
obj := newObject() obj := newObject()
obj.Type = Food obj.Kind = KindFood
obj.Count = 1 obj.Count = 1
g.addPack(obj, true) g.addPack(obj, true)
// And his suit of armor // And his suit of armor
obj = newObject() obj = newObject()
obj.Type = Armor obj.Kind = KindArmor
obj.Which = RingMail obj.Which = int(ArmorRingMail)
obj.Arm = aClass[RingMail] - 1 obj.ArmorClass = aClass[ArmorRingMail] - 1
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
obj.Count = 1 obj.Count = 1
p.CurArmor = obj p.CurArmor = obj
g.addPack(obj, true) g.addPack(obj, true)
// Give him his weaponry. First a mace. // Give him his weaponry. First a mace.
obj = newObject() obj = newObject()
g.initWeapon(obj, Mace) g.initWeapon(obj, WeaponMace)
obj.HPlus = 1 obj.HPlus = 1
obj.DPlus = 1 obj.DPlus = 1
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
g.addPack(obj, true) g.addPack(obj, true)
p.CurWeapon = obj p.CurWeapon = obj
// Now a +1 bow // Now a +1 bow
obj = newObject() obj = newObject()
g.initWeapon(obj, Bow) g.initWeapon(obj, WeaponBow)
obj.HPlus = 1 obj.HPlus = 1
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
g.addPack(obj, true) g.addPack(obj, true)
// Now some arrows // Now some arrows
obj = newObject() obj = newObject()
g.initWeapon(obj, Arrow) g.initWeapon(obj, WeaponArrow)
obj.Count = g.rnd(15) + 25 obj.Count = g.rnd(15) + 25
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
g.addPack(obj, true) g.addPack(obj, true)
} }
@@ -51,7 +51,7 @@ func (g *RogueGame) initPlayer() {
// (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(rainbow))
for i := 0; i < MaxPotions; i++ { for i := PotionKind(0); i < NumPotionTypes; i++ {
var j int var j int
for { for {
j = g.rnd(len(rainbow)) j = g.rnd(len(rainbow))
@@ -66,7 +66,7 @@ func (g *RogueGame) initColors() {
// initNames generates the names of the various scrolls (init.c init_names). // initNames generates the names of the various scrolls (init.c init_names).
func (g *RogueGame) initNames() { func (g *RogueGame) initNames() {
for i := 0; i < MaxScrolls; i++ { for i := ScrollKind(0); i < NumScrollTypes; i++ {
var cp strings.Builder var cp strings.Builder
nwords := g.rnd(3) + 2 nwords := g.rnd(3) + 2
for ; nwords > 0; nwords-- { for ; nwords > 0; nwords-- {
@@ -88,7 +88,7 @@ func (g *RogueGame) initNames() {
// (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(stoneTable))
for i := 0; i < MaxRings; i++ { for i := RingKind(0); i < NumRingTypes; i++ {
var j int var j int
for { for {
j = g.rnd(len(stoneTable)) j = g.rnd(len(stoneTable))
@@ -107,7 +107,7 @@ func (g *RogueGame) initStones() {
func (g *RogueGame) initMaterials() { func (g *RogueGame) initMaterials() {
used := make([]bool, len(woods)) used := make([]bool, len(woods))
metused := make([]bool, len(metals)) metused := make([]bool, len(metals))
for i := 0; i < MaxSticks; i++ { for i := WandKind(0); i < NumWandTypes; i++ {
var str string var str string
for { for {
if g.rnd(2) == 0 { if g.rnd(2) == 0 {
@@ -156,14 +156,14 @@ func (g *RogueGame) initProbs() {
sumProbs(g.Items.Scrolls[:]) sumProbs(g.Items.Scrolls[:])
sumProbs(g.Items.Rings[:]) sumProbs(g.Items.Rings[:])
sumProbs(g.Items.Sticks[:]) sumProbs(g.Items.Sticks[:])
sumProbs(g.Items.Weapons[:MaxWeapons]) // C sums MAXWEAPONS, excluding the flame entry sumProbs(g.Items.Weapons[:NumWeaponTypes]) // C sums MAXWEAPONS, excluding the flame entry
sumProbs(g.Items.Armors[:]) sumProbs(g.Items.Armors[:])
} }
// pickColor returns the given color, or a random one if the hero is // pickColor returns the given color, or a random one if the hero is
// 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(IsHalu) { if g.Player.On(Hallucinating) {
return rainbow[g.rnd(len(rainbow))] return rainbow[g.rnd(len(rainbow))]
} }
return col return col

View File

@@ -142,9 +142,9 @@ func (g *RogueGame) status() {
p := &g.Player p := &g.Player
// If nothing has changed since the last status, don't bother. // If nothing has changed since the last status, don't bother.
temp := p.Stats.Arm temp := p.Stats.ArmorClass
if p.CurArmor != nil { if p.CurArmor != nil {
temp = p.CurArmor.Arm temp = p.CurArmor.ArmorClass
} }
if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp && if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp &&
s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str && s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str &&

View File

@@ -10,13 +10,13 @@ type Place struct {
// Level is the current dungeon level: the map and everything on it. It is // Level is the current dungeon level: the map and everything on it. It is
// reset in place by NewLevel, matching the C reuse of the global arrays. // reset in place by NewLevel, matching the C reuse of the global arrays.
type Level struct { type Level struct {
Places [MaxLines * MaxCols]Place Places [MaxLines * MaxCols]Place
Rooms [MaxRooms]Room Rooms [MaxRooms]Room
Passages [MaxPass]Room // one pseudo-room per passage network Passages [MaxPass]Room // one pseudo-room per passage network
Objects []*Object // lvl_obj: objects on this level Objects []*Object // lvl_obj: objects on this level
Monsters []*Monster // mlist: monsters on the level Monsters []*Monster // mlist: monsters on the level
Stairs Coord // location of the staircase Stairs Coord // location of the staircase
NTraps int // number of traps on this level TrapCount int // number of traps on this level
} }
// At returns the map cell at (y, x); the C INDEX(y,x) macro, including its // At returns the map cell at (y, x); the C INDEX(y,x) macro, including its

View File

@@ -36,7 +36,7 @@ func (g *RogueGame) look(wakeup bool) {
if x < 0 || x >= NumCols { if x < 0 || x >= NumCols {
continue continue
} }
if !p.On(IsBlind) { if !p.On(Blind) {
if y == hero.Y && x == hero.X { if y == hero.Y && x == hero.X {
continue continue
} }
@@ -49,11 +49,11 @@ func (g *RogueGame) look(wakeup bool) {
} }
fp := &pp.Flags fp := &pp.Flags
if pch != Door && ch != Door { if pch != Door && ch != Door {
if (pfl & FPass) != (*fp & FPass) { if (pfl & FPassage) != (*fp & FPassage) {
continue continue
} }
} }
if (fp.Has(FPass) || ch == Door) && (pfl.Has(FPass) || pch == Door) { if (fp.Has(FPassage) || ch == Door) && (pfl.Has(FPassage) || pch == Door) {
if hero.X != x && hero.Y != y && if hero.X != x && hero.Y != y &&
!stepOk(g.Level.Char(y, hero.X)) && !stepOk(g.Level.Char(hero.Y, x)) { !stepOk(g.Level.Char(y, hero.X)) && !stepOk(g.Level.Char(hero.Y, x)) {
continue continue
@@ -63,7 +63,7 @@ func (g *RogueGame) look(wakeup bool) {
tp := pp.Monst tp := pp.Monst
if tp == nil { if tp == nil {
ch = g.tripCh(y, x, ch) ch = g.tripCh(y, x, ch)
} else if p.On(SeeMonst) && tp.On(IsInvis) { } else if p.On(SenseMonsters) && tp.On(Invisible) {
if g.DoorStop && !g.Firstmove { if g.DoorStop && !g.Firstmove {
g.Running = false g.Running = false
} }
@@ -73,20 +73,20 @@ func (g *RogueGame) look(wakeup bool) {
g.wakeMonster(y, x) g.wakeMonster(y, x)
} }
if g.seeMonst(tp) { if g.seeMonst(tp) {
if p.On(IsHalu) { if p.On(Hallucinating) {
ch = byte(g.rnd(26) + 'A') ch = byte(g.rnd(26) + 'A')
} else { } else {
ch = tp.Disguise ch = tp.Disguise
} }
} }
} }
if p.On(IsBlind) && (y != hero.Y || x != hero.X) { if p.On(Blind) && (y != hero.Y || x != hero.X) {
continue continue
} }
g.move(y, x) g.move(y, x)
if p.Room.Flags.Has(IsDark) && !g.Options.SeeFloor && ch == Floor { if p.Room.Flags.Has(Dark) && !g.Options.SeeFloor && ch == Floor {
ch = ' ' ch = ' '
} }
@@ -156,7 +156,7 @@ func (g *RogueGame) look(wakeup bool) {
// tripCh returns the character for this space, taking into account whether // tripCh returns the character for this space, taking into account whether
// or not the player is tripping (misc.c trip_ch). // or not the player is tripping (misc.c trip_ch).
func (g *RogueGame) tripCh(y, x int, ch byte) byte { func (g *RogueGame) tripCh(y, x int, ch byte) byte {
if g.Player.On(IsHalu) && g.After { if g.Player.On(Hallucinating) && g.After {
switch ch { switch ch {
case Floor, ' ', Passage, '-', '|', Door, Trap: case Floor, ' ', Passage, '-', '|', Door, Trap:
default: default:
@@ -171,8 +171,8 @@ func (g *RogueGame) tripCh(y, x int, ch byte) byte {
// eraseLamp erases the area shown by a lamp in a dark room // eraseLamp erases the area shown by a lamp in a dark room
// (misc.c erase_lamp). // (misc.c erase_lamp).
func (g *RogueGame) eraseLamp(pos Coord, rp *Room) { func (g *RogueGame) eraseLamp(pos Coord, rp *Room) {
if !(g.Options.SeeFloor && rp.Flags&(IsGone|IsDark) == IsDark && if !(g.Options.SeeFloor && rp.Flags&(Gone|Dark) == Dark &&
!g.Player.On(IsBlind)) { !g.Player.On(Blind)) {
return return
} }
@@ -195,7 +195,7 @@ func (g *RogueGame) eraseLamp(pos Coord, rp *Room) {
// showFloor reports whether we show the floor in her room at this time // showFloor reports whether we show the floor in her room at this time
// (misc.c show_floor). // (misc.c show_floor).
func (g *RogueGame) showFloor() bool { func (g *RogueGame) showFloor() bool {
if g.Player.Room.Flags&(IsGone|IsDark) == IsDark && !g.Player.On(IsBlind) { if g.Player.Room.Flags&(Gone|Dark) == Dark && !g.Player.On(Blind) {
return g.Options.SeeFloor return g.Options.SeeFloor
} }
return true return true
@@ -213,11 +213,11 @@ func (g *RogueGame) findObj(y, x int) *Object {
// eat lets her try to eat something (misc.c eat). // eat lets her try to eat something (misc.c eat).
func (g *RogueGame) eat() { func (g *RogueGame) eat() {
obj := g.getItem("eat", int(Food)) obj := g.getItem("eat", KindFood)
if obj == nil { if obj == nil {
return return
} }
if obj.Type != Food { if obj.Kind != KindFood {
if !g.Options.Terse { if !g.Options.Terse {
g.msg("ugh, you would get ill if you ate that") g.msg("ugh, you would get ill if you ate that")
} else { } else {
@@ -278,11 +278,11 @@ func (g *RogueGame) chgStr(amt int) {
p := &g.Player p := &g.Player
addStr(&p.Stats.Str, amt) addStr(&p.Stats.Str, amt)
comp := p.Stats.Str comp := p.Stats.Str
if p.IsRing(Left, RAddStr) { if p.IsRing(Left, RingAddStrength) {
addStr(&comp, -p.CurRing[Left].Arm) addStr(&comp, -p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RAddStr) { if p.IsRing(Right, RingAddStrength) {
addStr(&comp, -p.CurRing[Right].Arm) addStr(&comp, -p.CurRing[Right].Bonus)
} }
if comp > p.MaxStats.Str { if comp > p.MaxStats.Str {
p.MaxStats.Str = comp p.MaxStats.Str = comp
@@ -301,14 +301,14 @@ func addStr(sp *int, amt int) {
// addHaste adds a haste to the player (misc.c add_haste). // addHaste adds a haste to the player (misc.c add_haste).
func (g *RogueGame) addHaste(potion bool) bool { func (g *RogueGame) addHaste(potion bool) bool {
p := &g.Player p := &g.Player
if p.On(IsHaste) { if p.On(Hasted) {
g.NoCommand += g.rnd(8) g.NoCommand += g.rnd(8)
p.Flags.Clear(IsRun | IsHaste) p.Flags.Clear(Awake | Hasted)
g.Extinguish(DNohaste) g.Extinguish(DNohaste)
g.msg("you faint from exhaustion") g.msg("you faint from exhaustion")
return false return false
} }
p.Flags.Set(IsHaste) p.Flags.Set(Hasted)
if potion { if potion {
g.Fuse(DNohaste, 0, g.rnd(4)+4, After) g.Fuse(DNohaste, 0, g.rnd(4)+4, After)
} }
@@ -403,7 +403,7 @@ func (g *RogueGame) getDir() bool {
g.LastDir = g.DirCh g.LastDir = g.DirCh
g.lastDelt = g.Delta g.lastDelt = g.Delta
} }
if g.Player.On(IsHuh) && g.rnd(5) == 0 { if g.Player.On(Confused) && g.rnd(5) == 0 {
for { for {
g.Delta.Y = g.rnd(3) - 1 g.Delta.Y = g.rnd(3) - 1
g.Delta.X = g.rnd(3) - 1 g.Delta.X = g.rnd(3) - 1
@@ -451,7 +451,7 @@ func (g *RogueGame) rndThing() byte {
// chooseStr picks the first or second string depending on whether the // chooseStr picks the first or second string depending on whether the
// player is tripping (misc.c choose_str). // player is tripping (misc.c choose_str).
func (g *RogueGame) chooseStr(ts, ns string) string { func (g *RogueGame) chooseStr(ts, ns string) string {
if g.Player.On(IsHalu) { if g.Player.On(Hallucinating) {
return ts return ts
} }
return ns return ns

View File

@@ -54,17 +54,17 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
tp.Stats.Lvl = mp.Stats.Lvl + levAdd tp.Stats.Lvl = mp.Stats.Lvl + levAdd
tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8) tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8)
tp.Stats.HP = tp.Stats.MaxHP tp.Stats.HP = tp.Stats.MaxHP
tp.Stats.Arm = mp.Stats.Arm - levAdd tp.Stats.ArmorClass = mp.Stats.ArmorClass - levAdd
tp.Stats.Dmg = mp.Stats.Dmg tp.Stats.Dmg = mp.Stats.Dmg
tp.Stats.Str = mp.Stats.Str tp.Stats.Str = mp.Stats.Str
tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp) tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp)
tp.Flags = mp.Flags tp.Flags = mp.Flags
if g.Depth > 29 { if g.Depth > 29 {
tp.Flags.Set(IsHaste) tp.Flags.Set(Hasted)
} }
tp.Turn = true tp.Turn = true
tp.Pack = nil tp.Pack = nil
if g.Player.IsWearing(RAggr) { if g.Player.IsWearing(RingAggravateMonsters) {
g.runto(cp) g.runto(cp)
} }
if typ == 'X' { if typ == 'X' {
@@ -101,9 +101,9 @@ func (g *RogueGame) wanderer() {
} }
} }
g.newMonster(tp, g.randMonster(true), cp) g.newMonster(tp, g.randMonster(true), cp)
if g.Player.On(SeeMonst) { if g.Player.On(SenseMonsters) {
g.standout() g.standout()
if !g.Player.On(IsHalu) { if !g.Player.On(Hallucinating) {
g.addch(tp.Type) g.addch(tp.Type)
} else { } else {
g.addch(byte(g.rnd(26) + 'A')) g.addch(byte(g.rnd(26) + 'A'))
@@ -123,24 +123,24 @@ func (g *RogueGame) wakeMonster(y, x int) *Monster {
} }
ch := tp.Type ch := tp.Type
// Every time he sees a mean monster, it might start chasing him // Every time he sees a mean monster, it might start chasing him
if !tp.On(IsRun) && g.rnd(3) != 0 && tp.On(IsMean) && !tp.On(IsHeld) && if !tp.On(Awake) && g.rnd(3) != 0 && tp.On(Mean) && !tp.On(Held) &&
!p.IsWearing(RStealth) && !p.On(IsLevit) { !p.IsWearing(RingStealth) && !p.On(Levitating) {
tp.Dest = &p.Pos tp.Dest = &p.Pos
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
} }
if ch == 'M' && !p.On(IsBlind) && !p.On(IsHalu) && if ch == 'M' && !p.On(Blind) && !p.On(Hallucinating) &&
!tp.On(IsFound) && !tp.On(IsCanc) && tp.On(IsRun) { !tp.On(Found) && !tp.On(Cancelled) && tp.On(Awake) {
rp := p.Room rp := p.Room
if (rp != nil && !rp.Flags.Has(IsDark)) || if (rp != nil && !rp.Flags.Has(Dark)) ||
distance(y, x, p.Pos.Y, p.Pos.X) < LampDist { distance(y, x, p.Pos.Y, p.Pos.X) < LampDist {
tp.Flags.Set(IsFound) tp.Flags.Set(Found)
if !g.save(VsMagic) { if !g.save(VsMagic) {
if p.On(IsHuh) { if p.On(Confused) {
g.Lengthen(DUnconfuse, g.spread(HuhDuration)) g.Lengthen(DUnconfuse, g.spread(HuhDuration))
} else { } else {
g.Fuse(DUnconfuse, 0, g.spread(HuhDuration), After) g.Fuse(DUnconfuse, 0, g.spread(HuhDuration), After)
} }
p.Flags.Set(IsHuh) p.Flags.Set(Confused)
mname := g.setMname(tp) mname := g.setMname(tp)
g.addmsg("%s", mname) g.addmsg("%s", mname)
if mname != "it" { if mname != "it" {
@@ -151,8 +151,8 @@ func (g *RogueGame) wakeMonster(y, x int) *Monster {
} }
} }
// Let greedy ones guard gold // Let greedy ones guard gold
if tp.On(IsGreed) && !tp.On(IsRun) { if tp.On(Greedy) && !tp.On(Awake) {
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
if p.Room.GoldVal != 0 { if p.Room.GoldVal != 0 {
tp.Dest = &p.Room.Gold tp.Dest = &p.Room.Gold
} else { } else {
@@ -182,11 +182,11 @@ func (g *RogueGame) saveThrow(which int, st *Stats) bool {
func (g *RogueGame) save(which int) bool { func (g *RogueGame) save(which int) bool {
p := &g.Player p := &g.Player
if which == VsMagic { if which == VsMagic {
if p.IsRing(Left, RProtect) { if p.IsRing(Left, RingProtection) {
which -= p.CurRing[Left].Arm which -= p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RProtect) { if p.IsRing(Right, RingProtection) {
which -= p.CurRing[Right].Arm which -= p.CurRing[Right].Bonus
} }
} }
return g.saveThrow(which, &p.Stats) return g.saveThrow(which, &p.Stats)

View File

@@ -21,7 +21,7 @@ func (g *RogueGame) doMove(dy, dx int) {
} }
// Do a confused move (maybe) // Do a confused move (maybe)
var nh Coord var nh Coord
if p.On(IsHuh) && g.rnd(5) != 0 { if p.On(Confused) && g.rnd(5) != 0 {
nh = g.rndmove(&p.Creature) nh = g.rndmove(&p.Creature)
if nh == p.Pos { if nh == p.Pos {
g.After = false g.After = false
@@ -52,12 +52,12 @@ over:
fl = *g.Level.FlagsAt(nh.Y, nh.X) fl = *g.Level.FlagsAt(nh.Y, nh.X)
ch = g.Level.VisibleChar(nh.Y, nh.X) ch = g.Level.VisibleChar(nh.Y, nh.X)
if !fl.Has(FReal) && ch == Floor { if !fl.Has(FReal) && ch == Floor {
if !p.On(IsLevit) { if !p.On(Levitating) {
ch = Trap ch = Trap
g.Level.SetChar(nh.Y, nh.X, Trap) g.Level.SetChar(nh.Y, nh.X, Trap)
g.Level.FlagsAt(nh.Y, nh.X).Set(FReal) g.Level.FlagsAt(nh.Y, nh.X).Set(FReal)
} }
} else if p.On(IsHeld) && ch != 'F' { } else if p.On(Held) && ch != 'F' {
g.msg("you are being held") g.msg("you are being held")
return return
} }
@@ -67,8 +67,8 @@ over:
} }
switch ch { switch ch {
case ' ', '|', '-': case ' ', '|', '-':
if g.Options.PassGo && g.Running && p.Room.Flags.Has(IsGone) && if g.Options.PassGo && g.Running && p.Room.Flags.Has(Gone) &&
!p.On(IsBlind) { !p.On(Blind) {
var b1, b2 bool var b1, b2 bool
switch g.RunCh { switch g.RunCh {
case 'h', 'l': case 'h', 'l':
@@ -109,13 +109,13 @@ over:
g.After = false g.After = false
case Door: case Door:
g.Running = false g.Running = false
if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPass) { if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPassage) {
g.enterRoom(nh) g.enterRoom(nh)
} }
g.moveStuff(nh, fl) g.moveStuff(nh, fl)
case Trap: case Trap:
tr := g.beTrapped(nh) tr := g.beTrapped(nh)
if tr == TDoor || tr == TTelep { if tr == TrapDoor || tr == TrapTeleport {
return return
} }
g.moveStuff(nh, fl) g.moveStuff(nh, fl)
@@ -151,7 +151,7 @@ over:
func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) { func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) {
p := &g.Player p := &g.Player
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt())
if fl.Has(FPass) && g.Level.Char(g.Oldpos.Y, g.Oldpos.X) == Door { if fl.Has(FPassage) && g.Level.Char(g.Oldpos.Y, g.Oldpos.X) == Door {
g.leaveRoom(nh) g.leaveRoom(nh)
} }
p.Pos = nh p.Pos = nh
@@ -161,7 +161,7 @@ func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) {
// (move.c turn_ok). // (move.c turn_ok).
func (g *RogueGame) turnOk(y, x int) bool { func (g *RogueGame) turnOk(y, x int) bool {
pp := g.Level.At(y, x) pp := g.Level.At(y, x)
return pp.Ch == Door || pp.Flags&(FReal|FPass) == (FReal|FPass) return pp.Ch == Door || pp.Flags&(FReal|FPassage) == (FReal|FPassage)
} }
// turnref decides whether to refresh at a passage turning (move.c turnref). // turnref decides whether to refresh at a passage turning (move.c turnref).
@@ -179,7 +179,7 @@ func (g *RogueGame) turnref() {
// doorOpen is called to wake up things in a room that might move when the // doorOpen is called to wake up things in a room that might move when the
// hero enters (move.c door_open). // hero enters (move.c door_open).
func (g *RogueGame) doorOpen(rp *Room) { func (g *RogueGame) doorOpen(rp *Room) {
if rp.Flags.Has(IsGone) { if rp.Flags.Has(Gone) {
return return
} }
for y := rp.Pos.Y; y < rp.Pos.Y+rp.Max.Y; y++ { for y := rp.Pos.Y; y < rp.Pos.Y+rp.Max.Y; y++ {
@@ -192,26 +192,26 @@ func (g *RogueGame) doorOpen(rp *Room) {
} }
// beTrapped makes him pay for stepping on a trap (move.c be_trapped). // beTrapped makes him pay for stepping on a trap (move.c be_trapped).
func (g *RogueGame) beTrapped(tc Coord) int { func (g *RogueGame) beTrapped(tc Coord) TrapKind {
p := &g.Player p := &g.Player
if p.On(IsLevit) { if p.On(Levitating) {
return TRust // anything that's not a door or teleport return TrapRust // anything that's not a door or teleport
} }
g.Running = false g.Running = false
g.Count = 0 g.Count = 0
pp := g.Level.At(tc.Y, tc.X) pp := g.Level.At(tc.Y, tc.X)
pp.Ch = Trap pp.Ch = Trap
tr := int(pp.Flags & FTMask) tr := TrapKind(pp.Flags & FTrapMask)
pp.Flags.Set(FSeen) pp.Flags.Set(FSeen)
switch tr { switch tr {
case TDoor: case TrapDoor:
g.Depth++ g.Depth++
g.NewLevel() g.NewLevel()
g.msg("you fell into a trap!") g.msg("you fell into a trap!")
case TBear: case TrapBear:
g.NoMove += g.spread(3) // BEARTIME g.NoMove += g.spread(3) // BEARTIME
g.msg("you are caught in a bear trap") g.msg("you are caught in a bear trap")
case TMyst: case TrapMystery:
switch g.rnd(11) { switch g.rnd(11) {
case 0: case 0:
g.msg("you are suddenly in a parallel dimension") g.msg("you are suddenly in a parallel dimension")
@@ -236,12 +236,12 @@ func (g *RogueGame) beTrapped(tc Coord) int {
case 10: case 10:
g.msg("you pack turns %s!", rainbow[g.rnd(len(rainbow))]) g.msg("you pack turns %s!", rainbow[g.rnd(len(rainbow))])
} }
case TSleep: case TrapSleep:
g.NoCommand += g.spread(5) // SLEEPTIME g.NoCommand += g.spread(5) // SLEEPTIME
p.Flags.Clear(IsRun) p.Flags.Clear(Awake)
g.msg("a strange white mist envelops you and you fall asleep") g.msg("a strange white mist envelops you and you fall asleep")
case TArrow: case TrapArrow:
if g.swing(p.Stats.Lvl-1, p.Stats.Arm, 1) { if g.swing(p.Stats.Lvl-1, p.Stats.ArmorClass, 1) {
p.Stats.HP -= g.roll(1, 6) p.Stats.HP -= g.roll(1, 6)
if p.Stats.HP <= 0 { if p.Stats.HP <= 0 {
g.msg("an arrow killed you") g.msg("an arrow killed you")
@@ -251,19 +251,19 @@ func (g *RogueGame) beTrapped(tc Coord) int {
} }
} else { } else {
arrow := newObject() arrow := newObject()
g.initWeapon(arrow, Arrow) g.initWeapon(arrow, WeaponArrow)
arrow.Count = 1 arrow.Count = 1
arrow.Pos = p.Pos arrow.Pos = p.Pos
g.fall(arrow, false) g.fall(arrow, false)
g.msg("an arrow shoots past you") g.msg("an arrow shoots past you")
} }
case TTelep: case TrapTeleport:
// since the hero's leaving, look() won't put a TRAP down for us, // since the hero's leaving, look() won't put a TRAP down for us,
// so we have to do it ourself // so we have to do it ourself
g.teleport() g.teleport()
g.mvaddch(tc.Y, tc.X, Trap) g.mvaddch(tc.Y, tc.X, Trap)
case TDart: case TrapDart:
if !g.swing(p.Stats.Lvl+1, p.Stats.Arm, 1) { if !g.swing(p.Stats.Lvl+1, p.Stats.ArmorClass, 1) {
g.msg("a small dart whizzes by your ear and vanishes") g.msg("a small dart whizzes by your ear and vanishes")
} else { } else {
p.Stats.HP -= g.roll(1, 4) p.Stats.HP -= g.roll(1, 4)
@@ -271,12 +271,12 @@ func (g *RogueGame) beTrapped(tc Coord) int {
g.msg("a poisoned dart killed you") g.msg("a poisoned dart killed you")
g.death('d') g.death('d')
} }
if !p.IsWearing(RSustStr) && !g.save(VsPoison) { if !p.IsWearing(RingSustainStrength) && !g.save(VsPoison) {
g.chgStr(-1) g.chgStr(-1)
} }
g.msg("a small dart just hit you in the shoulder") g.msg("a small dart just hit you in the shoulder")
} }
case TRust: case TrapRust:
g.msg("a gush of water hits you on the head") g.msg("a gush of water hits you on the head")
g.rustArmor(p.CurArmor) g.rustArmor(p.CurArmor)
} }
@@ -311,7 +311,7 @@ func (g *RogueGame) rndmove(who *Creature) Coord {
break break
} }
} }
if found != nil && found.Which == SScare { if found != nil && found.ScrollKind() == ScrollScareMonster {
return who.Pos return who.Pos
} }
} }
@@ -321,17 +321,17 @@ func (g *RogueGame) rndmove(who *Creature) Coord {
// rustArmor rusts the given armor, if it is a legal kind to rust, and we // rustArmor rusts the given armor, if it is a legal kind to rust, and we
// aren't wearing a magic ring (move.c rust_armor). // aren't wearing a magic ring (move.c rust_armor).
func (g *RogueGame) rustArmor(arm *Object) { func (g *RogueGame) rustArmor(arm *Object) {
if arm == nil || arm.Type != Armor || arm.Which == Leather || if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather ||
arm.Arm >= 9 { arm.ArmorClass >= 9 {
return return
} }
if arm.Flags.Has(IsProt) || g.Player.IsWearing(RSustArm) { if arm.Flags.Has(Protected) || g.Player.IsWearing(RingMaintainArmor) {
if !g.ToDeath { if !g.ToDeath {
g.msg("the rust vanishes instantly") g.msg("the rust vanishes instantly")
} }
} else { } else {
arm.Arm++ arm.ArmorClass++
if !g.Options.Terse { if !g.Options.Terse {
g.msg("your armor appears to be weaker now. Oh my!") g.msg("your armor appears to be weaker now. Oh my!")
} else { } else {

View File

@@ -12,7 +12,7 @@ const (
// NewLevel digs and draws a new level (new_level.c new_level). // NewLevel digs and draws a new level (new_level.c new_level).
func (g *RogueGame) NewLevel() { func (g *RogueGame) NewLevel() {
p := &g.Player p := &g.Player
p.Flags.Clear(IsHeld) // unhold when you go down just in case p.Flags.Clear(Held) // unhold when you go down just in case
if g.Depth > g.MaxDepth { if g.Depth > g.MaxDepth {
g.MaxDepth = g.Depth g.MaxDepth = g.Depth
} }
@@ -31,11 +31,11 @@ func (g *RogueGame) NewLevel() {
g.putThings() // Place objects (if any) g.putThings() // Place objects (if any)
// Place the traps // Place the traps
if g.rnd(10) < g.Depth { if g.rnd(10) < g.Depth {
g.Level.NTraps = g.rnd(g.Depth/4) + 1 g.Level.TrapCount = g.rnd(g.Depth/4) + 1
if g.Level.NTraps > MaxTraps { if g.Level.TrapCount > MaxTraps {
g.Level.NTraps = MaxTraps g.Level.TrapCount = MaxTraps
} }
for i := g.Level.NTraps; i > 0; i-- { for i := g.Level.TrapCount; i > 0; i-- {
// not only wouldn't it be NICE to have traps in mazes (not // not only wouldn't it be NICE to have traps in mazes (not
// that we care about being nice), since the trap number is // that we care about being nice), since the trap number is
// stored where the passage number is, we can't actually do it. // stored where the passage number is, we can't actually do it.
@@ -48,7 +48,7 @@ func (g *RogueGame) NewLevel() {
} }
sp := g.Level.FlagsAt(stairs.Y, stairs.X) sp := g.Level.FlagsAt(stairs.Y, stairs.X)
sp.Clear(FReal) sp.Clear(FReal)
*sp |= PlaceFlags(g.rnd(NTraps)) *sp |= PlaceFlags(g.rnd(NumTrapTypes))
} }
} }
// Place the staircase down. // Place the staircase down.
@@ -65,10 +65,10 @@ func (g *RogueGame) NewLevel() {
p.Pos = hero p.Pos = hero
g.enterRoom(hero) g.enterRoom(hero)
g.mvaddch(hero.Y, hero.X, PlayerCh) g.mvaddch(hero.Y, hero.X, PlayerCh)
if p.On(SeeMonst) { if p.On(SenseMonsters) {
g.turnSee(false) g.turnSee(false)
} }
if p.On(IsHalu) { if p.On(Hallucinating) {
g.visuals(0) g.visuals(0)
} }
} }
@@ -77,7 +77,7 @@ func (g *RogueGame) NewLevel() {
func (g *RogueGame) rndRoom() int { func (g *RogueGame) rndRoom() int {
for { for {
rm := g.rnd(MaxRooms) rm := g.rnd(MaxRooms)
if !g.Level.Rooms[rm].Flags.Has(IsGone) { if !g.Level.Rooms[rm].Flags.Has(Gone) {
return rm return rm
} }
} }
@@ -103,7 +103,7 @@ func (g *RogueGame) putThings() {
attachObj(&g.Level.Objects, obj) attachObj(&g.Level.Objects, obj)
// Put it somewhere // Put it somewhere
obj.Pos, _ = g.findFloor(nil, 0, false) obj.Pos, _ = g.findFloor(nil, 0, false)
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Type) g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
} }
} }
// If he is really deep in the dungeon and he hasn't found the amulet // If he is really deep in the dungeon and he hasn't found the amulet
@@ -111,10 +111,10 @@ func (g *RogueGame) putThings() {
if g.Depth >= AmuletLevel && !g.HasAmulet { if g.Depth >= AmuletLevel && !g.HasAmulet {
obj := newObject() obj := newObject()
attachObj(&g.Level.Objects, obj) attachObj(&g.Level.Objects, obj)
obj.Damage = "0x0" obj.Damage = dice("0x0")
obj.HurlDmg = "0x0" obj.HurlDmg = dice("0x0")
obj.Arm = 11 obj.ArmorClass = 11
obj.Type = Amulet obj.Kind = KindAmulet
// Put it somewhere // Put it somewhere
obj.Pos, _ = g.findFloor(nil, 0, false) obj.Pos, _ = g.findFloor(nil, 0, false)
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Amulet) g.Level.SetChar(obj.Pos.Y, obj.Pos.X, Amulet)
@@ -134,7 +134,7 @@ func (g *RogueGame) treasRoom() {
tp := g.newThing() tp := g.newThing()
tp.Pos = mp tp.Pos = mp
attachObj(&g.Level.Objects, tp) attachObj(&g.Level.Objects, tp)
g.Level.SetChar(mp.Y, mp.X, tp.Type) g.Level.SetChar(mp.Y, mp.X, tp.Kind.Glyph())
} }
// fill up room with monsters from the next level down // fill up room with monsters from the next level down
@@ -151,7 +151,7 @@ func (g *RogueGame) treasRoom() {
if mp, ok := g.findFloorIn(rp, maxTries, true); ok { if mp, ok := g.findFloorIn(rp, maxTries, true); ok {
tp := &Monster{} tp := &Monster{}
g.newMonster(tp, g.randMonster(false), mp) g.newMonster(tp, g.randMonster(false), mp)
tp.Flags.Set(IsMean) // no sloughers in THIS room tp.Flags.Set(Mean) // no sloughers in THIS room
g.givePack(tp) g.givePack(tp)
} }
} }

View File

@@ -75,9 +75,10 @@ func TestNewLevelInvariants(t *testing.T) {
// share cells only with monsters standing on them). // share cells only with monsters standing on them).
for _, obj := range g.Level.Objects { for _, obj := range g.Level.Objects {
ch := g.Level.Char(obj.Pos.Y, obj.Pos.X) ch := g.Level.Char(obj.Pos.Y, obj.Pos.X)
if ch != obj.Type && g.Level.MonsterAt(obj.Pos.Y, obj.Pos.X) == nil { if ch != obj.Kind.Glyph() &&
t.Errorf("seed %d: object %q at (%d,%d) but map shows %q", g.Level.MonsterAt(obj.Pos.Y, obj.Pos.X) == nil {
seed, obj.Type, obj.Pos.Y, obj.Pos.X, ch) t.Errorf("seed %d: object %v at (%d,%d) but map shows %q",
seed, obj.Kind, obj.Pos.Y, obj.Pos.X, ch)
} }
} }
@@ -86,10 +87,12 @@ func TestNewLevelInvariants(t *testing.T) {
t.Errorf("seed %d: starting pack has %d items, want 5", t.Errorf("seed %d: starting pack has %d items, want 5",
seed, len(g.Player.Pack)) seed, len(g.Player.Pack))
} }
if g.Player.CurWeapon == nil || g.Player.CurWeapon.Which != Mace { if g.Player.CurWeapon == nil ||
g.Player.CurWeapon.WeaponKind() != WeaponMace {
t.Errorf("seed %d: not wielding the starting mace", seed) t.Errorf("seed %d: not wielding the starting mace", seed)
} }
if g.Player.CurArmor == nil || g.Player.CurArmor.Which != RingMail { if g.Player.CurArmor == nil ||
g.Player.CurArmor.ArmorKind() != ArmorRingMail {
t.Errorf("seed %d: not wearing the starting ring mail", seed) t.Errorf("seed %d: not wearing the starting ring mail", seed)
} }
} }

View File

@@ -1,42 +1,142 @@
package game package game
// ObjectKind is the category of an item. In C this was the o_type byte,
// which doubled as the character drawn on the map; the port separates the
// category (ObjectKind) from its display character (Glyph).
type ObjectKind int
const (
KindNone ObjectKind = iota
KindPotion
KindScroll
KindFood
KindWeapon
KindArmor
KindRing
KindWand // a "stick": wand or staff
KindAmulet
KindGold
)
// Prompt-filter pseudo-kinds for item selection (rogue.h CALLABLE and
// R_OR_S): they never appear on an Object, only as getItem filters.
const (
KindCallable ObjectKind = -1
KindRingOrStick ObjectKind = -2
)
// kindGlyphs maps each kind to the character Rogue draws for it.
var kindGlyphs = [...]byte{
KindNone: ' ',
KindPotion: Potion,
KindScroll: Scroll,
KindFood: Food,
KindWeapon: Weapon,
KindArmor: Armor,
KindRing: Ring,
KindWand: Stick,
KindAmulet: Amulet,
KindGold: Gold,
}
// Glyph returns the map/display character for this kind of object.
func (k ObjectKind) Glyph() byte {
if k < 0 || int(k) >= len(kindGlyphs) {
return ' '
}
return kindGlyphs[k]
}
// String names the category the way the C type_name() did.
func (k ObjectKind) String() string {
switch k {
case KindPotion:
return "potion"
case KindScroll:
return "scroll"
case KindFood:
return "food"
case KindWeapon:
return "weapon"
case KindArmor:
return "suit of armor"
case KindRing:
return "ring"
case KindWand:
return "wand or staff"
case KindAmulet:
return "amulet"
case KindGold:
return "gold"
case KindRingOrStick:
return "ring, wand or staff"
}
return "bizarre thing"
}
// objectKindForGlyph is the reverse of Glyph: what category of item does a
// map character denote. Returns KindNone for non-item characters.
func objectKindForGlyph(ch byte) ObjectKind {
for k, g := range kindGlyphs {
if g == ch && ObjectKind(k) != KindNone {
return ObjectKind(k)
}
}
return KindNone
}
// MergesInPack reports whether picking up another of this kind merges into
// an existing pack entry's count (rogue.h ISMULT).
func (k ObjectKind) MergesInPack() bool {
return k == KindPotion || k == KindScroll || k == KindFood
}
// Object is the _o arm of the C THING union: anything that can lie on the // Object is the _o arm of the C THING union: anything that can lie on the
// floor or ride in a pack. // floor or ride in a pack.
type Object struct { type Object struct {
Type byte // what kind of object it is (Potion, Scroll, Weapon, ...) Kind ObjectKind // what kind of object it is (o_type)
Pos Coord // where it lives on the screen Pos Coord // where it lives on the screen
Text string // what it says if you read it Text string // what it says if you read it
Launch int // what you need to launch it (weapon index, -1 none) Launch WeaponKind // what you need to launch it (noWeapon if none)
PackCh byte // what character it is in the pack PackCh byte // what character it is in the pack
Damage string // damage if used like sword Damage DiceSpec // damage if used like sword
HurlDmg string // damage if thrown HurlDmg DiceSpec // damage if thrown
Count int // count for plural objects Count int // count for plural objects
Which int // which object of a type it is Which int // which object of a type it is (index for the Kind's table)
HPlus int // plusses to hit HPlus int // plusses to hit
DPlus int // plusses to damage DPlus int // plusses to damage
Arm int // armor protection — overloaded as in C (see Charges/GoldVal) ArmorClass int // armor protection (armor only)
Flags ObjFlags Charges int // charges remaining (wands and staffs only)
Group int // group number for this object GoldValue int // worth (gold piles only)
Label string // label for object Bonus int // magic bonus (rings only: protection, add strength, ...)
Flags ObjFlags
Group int // group number for this object
Label string // label for object
} }
// newObject is list.c new_item() for objects: a zeroed Object with the // newObject is list.c new_item() for objects: a zeroed Object with the
// same non-zero defaults the C code relies on. // same non-zero defaults the C code relies on.
func newObject() *Object { func newObject() *Object {
return &Object{Launch: -1} return &Object{Launch: noWeapon}
} }
// Charges is the o_charges alias for Arm on wands and staffs. // PotionKind returns Which as a potion kind; valid only for KindPotion.
func (o *Object) Charges() int { return o.Arm } func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) }
// SetCharges stores a charge count in the overloaded Arm field. // ScrollKind returns Which as a scroll kind; valid only for KindScroll.
func (o *Object) SetCharges(n int) { o.Arm = n } func (o *Object) ScrollKind() ScrollKind { return ScrollKind(o.Which) }
// GoldVal is the o_goldval alias for Arm on gold piles. // RingKind returns Which as a ring kind; valid only for KindRing.
func (o *Object) GoldVal() int { return o.Arm } func (o *Object) RingKind() RingKind { return RingKind(o.Which) }
// SetGoldVal stores a gold value in the overloaded Arm field. // WandKind returns Which as a wand kind; valid only for KindWand.
func (o *Object) SetGoldVal(n int) { o.Arm = n } func (o *Object) WandKind() WandKind { return WandKind(o.Which) }
// WeaponKind returns Which as a weapon kind; valid only for KindWeapon.
func (o *Object) WeaponKind() WeaponKind { return WeaponKind(o.Which) }
// ArmorKind returns Which as an armor kind; valid only for KindArmor.
func (o *Object) ArmorKind() ArmorKind { return ArmorKind(o.Which) }
// attachObj is list.c attach(): push item onto the front of a list. // attachObj is list.c attach(): push item onto the front of a list.
func attachObj(list *[]*Object, item *Object) { func attachObj(list *[]*Object, item *Object) {

View File

@@ -15,10 +15,10 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
} }
// Check for and deal with scare monster scrolls // Check for and deal with scare monster scrolls
if obj.Type == Scroll && obj.Which == SScare && obj.Flags.Has(ObjIsFound) { if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) {
detachObj(&g.Level.Objects, obj) detachObj(&g.Level.Objects, obj)
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(IsGone) { if p.Room.Flags.Has(Gone) {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage) g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage)
} else { } else {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor) g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor)
@@ -39,12 +39,12 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
lp := -1 lp := -1
merged := false merged := false
for i := 0; i < len(p.Pack); i++ { for i := 0; i < len(p.Pack); i++ {
if p.Pack[i].Type != obj.Type { if p.Pack[i].Kind != obj.Kind {
lp = i lp = i
continue continue
} }
// found the group of our type: scan for matching subtype // found the group of our type: scan for matching subtype
for p.Pack[i].Type == obj.Type && p.Pack[i].Which != obj.Which { for p.Pack[i].Kind == obj.Kind && p.Pack[i].Which != obj.Which {
lp = i lp = i
if i+1 >= len(p.Pack) { if i+1 >= len(p.Pack) {
break break
@@ -52,8 +52,8 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
i++ i++
} }
op := p.Pack[i] op := p.Pack[i]
if op.Type == obj.Type && op.Which == obj.Which { if op.Kind == obj.Kind && op.Which == obj.Which {
if IsMult(op.Type) { if op.Kind.MergesInPack() {
if !g.packRoom(fromFloor, obj) { if !g.packRoom(fromFloor, obj) {
return return
} }
@@ -63,7 +63,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
merged = true merged = true
} else if obj.Group != 0 { } else if obj.Group != 0 {
lp = i lp = i
for p.Pack[i].Type == obj.Type && for p.Pack[i].Kind == obj.Kind &&
p.Pack[i].Which == obj.Which && p.Pack[i].Which == obj.Which &&
p.Pack[i].Group != obj.Group { p.Pack[i].Group != obj.Group {
lp = i lp = i
@@ -73,7 +73,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
i++ i++
} }
op = p.Pack[i] op = p.Pack[i]
if op.Type == obj.Type && op.Which == obj.Which && if op.Kind == obj.Kind && op.Which == obj.Which &&
op.Group == obj.Group { op.Group == obj.Group {
op.Count += obj.Count op.Count += obj.Count
p.Inpack-- p.Inpack--
@@ -101,7 +101,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
} }
} }
obj.Flags.Set(ObjIsFound) obj.Flags.Set(WasFound)
// If this was the object of something's desire, that monster will get // If this was the object of something's desire, that monster will get
// mad and run at the hero. // mad and run at the hero.
@@ -111,7 +111,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
} }
} }
if obj.Type == Amulet { if obj.Kind == KindAmulet {
g.HasAmulet = true g.HasAmulet = true
} }
// Notify the user // Notify the user
@@ -146,7 +146,7 @@ func (g *RogueGame) packRoom(fromFloor bool, obj *Object) bool {
if fromFloor { if fromFloor {
detachObj(&g.Level.Objects, obj) detachObj(&g.Level.Objects, obj)
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(IsGone) { if p.Room.Flags.Has(Gone) {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage) g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage)
} else { } else {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor) g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor)
@@ -193,12 +193,14 @@ func (g *RogueGame) packChar() byte {
// inventory lists what is in the pack; returns true if there is something // inventory lists what is in the pack; returns true if there is something
// of the given type (pack.c inventory). // of the given type (pack.c inventory).
func (g *RogueGame) inventory(list []*Object, typ int) bool { func (g *RogueGame) inventory(list []*Object, kind ObjectKind) bool {
g.NObjs = 0 g.NObjs = 0
for _, item := range list { for _, item := range list {
if typ != 0 && typ != int(item.Type) && if kind != KindNone && kind != item.Kind &&
!(typ == Callable && item.Type != Food && item.Type != Amulet) && !(kind == KindCallable && item.Kind != KindFood &&
!(typ == RorS && (item.Type == Ring || item.Type == Stick)) { item.Kind != KindAmulet) &&
!(kind == KindRingOrStick &&
(item.Kind == KindRing || item.Kind == KindWand)) {
continue continue
} }
g.NObjs++ g.NObjs++
@@ -213,13 +215,13 @@ func (g *RogueGame) inventory(list []*Object, typ int) bool {
} }
if g.NObjs == 0 { if g.NObjs == 0 {
if g.Options.Terse { if g.Options.Terse {
if typ == 0 { if kind == KindNone {
g.msg("empty handed") g.msg("empty handed")
} else { } else {
g.msg("nothing appropriate") g.msg("nothing appropriate")
} }
} else { } else {
if typ == 0 { if kind == KindNone {
g.msg("you are empty handed") g.msg("you are empty handed")
} else { } else {
g.msg("you don't have anything appropriate") g.msg("you don't have anything appropriate")
@@ -234,7 +236,7 @@ func (g *RogueGame) inventory(list []*Object, typ int) bool {
// pickUp adds something to the character's pack (pack.c pick_up). // pickUp adds something to the character's pack (pack.c pick_up).
func (g *RogueGame) pickUp(ch byte) { func (g *RogueGame) pickUp(ch byte) {
p := &g.Player p := &g.Player
if p.On(IsLevit) { if p.On(Levitating) {
return return
} }
@@ -248,7 +250,7 @@ func (g *RogueGame) pickUp(ch byte) {
if obj == nil { if obj == nil {
return return
} }
g.money(obj.GoldVal()) g.money(obj.GoldValue)
detachObj(&g.Level.Objects, obj) detachObj(&g.Level.Objects, obj)
p.Room.GoldVal = 0 p.Room.GoldVal = 0
default: default:
@@ -294,7 +296,7 @@ func (g *RogueGame) pickyInven() {
} }
// getItem picks something out of a pack for a purpose (pack.c get_item). // getItem picks something out of a pack for a purpose (pack.c get_item).
func (g *RogueGame) getItem(purpose string, typ int) *Object { func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
p := &g.Player p := &g.Player
if len(p.Pack) == 0 { if len(p.Pack) == 0 {
g.msg("you aren't carrying anything") g.msg("you aren't carrying anything")
@@ -328,7 +330,7 @@ func (g *RogueGame) getItem(purpose string, typ int) *Object {
g.NObjs = 1 // normal case: person types one char g.NObjs = 1 // normal case: person types one char
if ch == '*' { if ch == '*' {
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
if !g.inventory(p.Pack, typ) { if !g.inventory(p.Pack, kind) {
g.After = false g.After = false
return nil return nil
} }
@@ -348,7 +350,7 @@ func (g *RogueGame) money(value int) {
p := &g.Player p := &g.Player
p.Purse += value p.Purse += value
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(IsGone) { if p.Room.Flags.Has(Gone) {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage) g.Level.SetChar(p.Pos.Y, p.Pos.X, Passage)
} else { } else {
g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor) g.Level.SetChar(p.Pos.Y, p.Pos.X, Floor)
@@ -364,7 +366,7 @@ func (g *RogueGame) money(value int) {
// floorCh returns the appropriate floor character for her room // floorCh returns the appropriate floor character for her room
// (pack.c floor_ch). // (pack.c floor_ch).
func (g *RogueGame) floorCh() byte { func (g *RogueGame) floorCh() byte {
if g.Player.Room.Flags.Has(IsGone) { if g.Player.Room.Flags.Has(Gone) {
return Passage return Passage
} }
if g.showFloor() { if g.showFloor() {

View File

@@ -110,24 +110,24 @@ func (g *RogueGame) conn(r1, r2 int) {
var del, turnDelta, spos, epos Coord var del, turnDelta, spos, epos Coord
var distance, turnDistance int var distance, turnDistance int
if direc == 'd' { if direc == 'd' {
rmt := rm + 3 // room # of dest rmt := rm + 3 // room # of dest
rpt = &g.Level.Rooms[rmt] // room pointer of dest rpt = &g.Level.Rooms[rmt] // room pointer of dest
del = Coord{X: 0, Y: 1} // direction of move del = Coord{X: 0, Y: 1} // direction of move
spos = rpf.Pos // start of move spos = rpf.Pos // start of move
epos = rpt.Pos // end of move epos = rpt.Pos // end of move
if !rpf.Flags.Has(IsGone) { // if not gone pick door pos if !rpf.Flags.Has(Gone) { // if not gone pick door pos
for { for {
spos.X = rpf.Pos.X + g.rnd(rpf.Max.X-2) + 1 spos.X = rpf.Pos.X + g.rnd(rpf.Max.X-2) + 1
spos.Y = rpf.Pos.Y + rpf.Max.Y - 1 spos.Y = rpf.Pos.Y + rpf.Max.Y - 1
if !(rpf.Flags.Has(IsMaze) && !g.Level.FlagsAt(spos.Y, spos.X).Has(FPass)) { if !(rpf.Flags.Has(Maze) && !g.Level.FlagsAt(spos.Y, spos.X).Has(FPassage)) {
break break
} }
} }
} }
if !rpt.Flags.Has(IsGone) { if !rpt.Flags.Has(Gone) {
for { for {
epos.X = rpt.Pos.X + g.rnd(rpt.Max.X-2) + 1 epos.X = rpt.Pos.X + g.rnd(rpt.Max.X-2) + 1
if !(rpt.Flags.Has(IsMaze) && !g.Level.FlagsAt(epos.Y, epos.X).Has(FPass)) { if !(rpt.Flags.Has(Maze) && !g.Level.FlagsAt(epos.Y, epos.X).Has(FPassage)) {
break break
} }
} }
@@ -146,19 +146,19 @@ func (g *RogueGame) conn(r1, r2 int) {
del = Coord{X: 1, Y: 0} del = Coord{X: 1, Y: 0}
spos = rpf.Pos spos = rpf.Pos
epos = rpt.Pos epos = rpt.Pos
if !rpf.Flags.Has(IsGone) { if !rpf.Flags.Has(Gone) {
for { for {
spos.X = rpf.Pos.X + rpf.Max.X - 1 spos.X = rpf.Pos.X + rpf.Max.X - 1
spos.Y = rpf.Pos.Y + g.rnd(rpf.Max.Y-2) + 1 spos.Y = rpf.Pos.Y + g.rnd(rpf.Max.Y-2) + 1
if !(rpf.Flags.Has(IsMaze) && !g.Level.FlagsAt(spos.Y, spos.X).Has(FPass)) { if !(rpf.Flags.Has(Maze) && !g.Level.FlagsAt(spos.Y, spos.X).Has(FPassage)) {
break break
} }
} }
} }
if !rpt.Flags.Has(IsGone) { if !rpt.Flags.Has(Gone) {
for { for {
epos.Y = rpt.Pos.Y + g.rnd(rpt.Max.Y-2) + 1 epos.Y = rpt.Pos.Y + g.rnd(rpt.Max.Y-2) + 1
if !(rpt.Flags.Has(IsMaze) && !g.Level.FlagsAt(epos.Y, epos.X).Has(FPass)) { if !(rpt.Flags.Has(Maze) && !g.Level.FlagsAt(epos.Y, epos.X).Has(FPassage)) {
break break
} }
} }
@@ -177,12 +177,12 @@ func (g *RogueGame) conn(r1, r2 int) {
// Draw in the doors on either side of the passage or just put #'s if // Draw in the doors on either side of the passage or just put #'s if
// the rooms are gone. // the rooms are gone.
if !rpf.Flags.Has(IsGone) { if !rpf.Flags.Has(Gone) {
g.door(rpf, spos) g.door(rpf, spos)
} else { } else {
g.putpass(spos) g.putpass(spos)
} }
if !rpt.Flags.Has(IsGone) { if !rpt.Flags.Has(Gone) {
g.door(rpt, epos) g.door(rpt, epos)
} else { } else {
g.putpass(epos) g.putpass(epos)
@@ -216,7 +216,7 @@ func (g *RogueGame) conn(r1, r2 int) {
// putpass). // putpass).
func (g *RogueGame) putpass(cp Coord) { func (g *RogueGame) putpass(cp Coord) {
pp := g.Level.At(cp.Y, cp.X) pp := g.Level.At(cp.Y, cp.X)
pp.Flags.Set(FPass) pp.Flags.Set(FPassage)
if g.rnd(10)+1 < g.Depth && g.rnd(40) == 0 { if g.rnd(10)+1 < g.Depth && g.rnd(40) == 0 {
pp.Flags.Clear(FReal) pp.Flags.Clear(FReal)
} else { } else {
@@ -229,7 +229,7 @@ func (g *RogueGame) putpass(cp Coord) {
func (g *RogueGame) door(rm *Room, cp Coord) { func (g *RogueGame) door(rm *Room, cp Coord) {
rm.Exits = append(rm.Exits, cp) rm.Exits = append(rm.Exits, cp)
if rm.Flags.Has(IsMaze) { if rm.Flags.Has(Maze) {
return return
} }
@@ -252,10 +252,10 @@ func (g *RogueGame) addPass() {
for y := 1; y < NumLines-1; y++ { for y := 1; y < NumLines-1; y++ {
for x := 0; x < NumCols; x++ { for x := 0; x < NumCols; x++ {
pp := g.Level.At(y, x) pp := g.Level.At(y, x)
if pp.Flags.Has(FPass) || pp.Ch == Door || if pp.Flags.Has(FPassage) || pp.Ch == Door ||
(!pp.Flags.Has(FReal) && (pp.Ch == '|' || pp.Ch == '-')) { (!pp.Flags.Has(FReal) && (pp.Ch == '|' || pp.Ch == '-')) {
ch := pp.Ch ch := pp.Ch
if pp.Flags.Has(FPass) { if pp.Flags.Has(FPassage) {
ch = Passage ch = Passage
} }
pp.Flags.Set(FSeen) pp.Flags.Set(FSeen)
@@ -266,7 +266,7 @@ func (g *RogueGame) addPass() {
g.addch(ch) g.addch(ch)
} else { } else {
g.standout() g.standout()
if pp.Flags.Has(FPass) { if pp.Flags.Has(FPassage) {
g.addch(Passage) g.addch(Passage)
} else { } else {
g.addch(Door) g.addch(Door)
@@ -301,7 +301,7 @@ func (g *RogueGame) numpass(y, x int) {
return return
} }
fp := g.Level.FlagsAt(y, x) fp := g.Level.FlagsAt(y, x)
if fp.Has(FPNum) { if fp.Has(FPassNum) {
return return
} }
if g.newpnum { if g.newpnum {
@@ -314,7 +314,7 @@ func (g *RogueGame) numpass(y, x int) {
(!fp.Has(FReal) && (ch == '|' || ch == '-')) { (!fp.Has(FReal) && (ch == '|' || ch == '-')) {
rp := &g.Level.Passages[g.pnum] rp := &g.Level.Passages[g.pnum]
rp.Exits = append(rp.Exits, Coord{Y: y, X: x}) rp.Exits = append(rp.Exits, Coord{Y: y, X: x})
} else if !fp.Has(FPass) { } else if !fp.Has(FPassage) {
return return
} }
*fp |= PlaceFlags(g.pnum) *fp |= PlaceFlags(g.pnum)

View File

@@ -15,18 +15,18 @@ type pact struct {
// pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic // pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic
// (it names the fruit) and is computed in doPot. // (it names the fruit) and is computed in doPot.
var pActions = [MaxPotions]pact{ var pActions = [NumPotionTypes]pact{
PConfuse: {IsHuh, DUnconfuse, HuhDuration, PotionConfusion: {Confused, DUnconfuse, HuhDuration,
"what a tripy feeling!", "what a tripy feeling!",
"wait, what's going on here. Huh? What? Who?"}, "wait, what's going on here. Huh? What? Who?"},
PLSD: {IsHalu, DComeDown, SeeDuration, PotionLSD: {Hallucinating, DComeDown, SeeDuration,
"Oh, wow! Everything seems so cosmic!", "Oh, wow! Everything seems so cosmic!",
"Oh, wow! Everything seems so cosmic!"}, "Oh, wow! Everything seems so cosmic!"},
PSeeInvis: {CanSee, DUnsee, SeeDuration, "", ""}, PotionSeeInvisible: {CanSeeInvisible, DUnsee, SeeDuration, "", ""},
PBlind: {IsBlind, DSight, SeeDuration, PotionBlindness: {Blind, DSight, SeeDuration,
"oh, bummer! Everything is dark! Help!", "oh, bummer! Everything is dark! Help!",
"a cloak of darkness falls around you"}, "a cloak of darkness falls around you"},
PLevit: {IsLevit, DLand, HealTime, PotionLevitation: {Levitating, DLand, HealTime,
"oh, wow! You're floating in the air!", "oh, wow! You're floating in the air!",
"you start to float in the air"}, "you start to float in the air"},
} }
@@ -34,12 +34,12 @@ var pActions = [MaxPotions]pact{
// 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
obj := g.getItem("quaff", int(Potion)) obj := g.getItem("quaff", KindPotion)
// Make certain that it is something that we want to drink // Make certain that it is something that we want to drink
if obj == nil { if obj == nil {
return return
} }
if obj.Type != Potion { if obj.Kind != KindPotion {
if !g.Options.Terse { if !g.Options.Terse {
g.msg("yuk! Why would you want to drink that?") g.msg("yuk! Why would you want to drink that?")
} else { } else {
@@ -52,40 +52,40 @@ func (g *RogueGame) quaff() {
} }
// Calculate the effect it has on the poor guy. // Calculate the effect it has on the poor guy.
trip := p.On(IsHalu) trip := p.On(Hallucinating)
g.leavePack(obj, false, false) g.leavePack(obj, false, false)
switch obj.Which { switch obj.PotionKind() {
case PConfuse: case PotionConfusion:
g.doPot(PConfuse, !trip) g.doPot(PotionConfusion, !trip)
case PPoison: case PotionPoison:
g.Items.Potions[PPoison].Know = true g.Items.Potions[PotionPoison].Know = true
if p.IsWearing(RSustStr) { if p.IsWearing(RingSustainStrength) {
g.msg("you feel momentarily sick") g.msg("you feel momentarily sick")
} else { } else {
g.chgStr(-(g.rnd(3) + 1)) g.chgStr(-(g.rnd(3) + 1))
g.msg("you feel very sick now") g.msg("you feel very sick now")
g.comeDown(0) g.comeDown(0)
} }
case PHealing: case PotionHealing:
g.Items.Potions[PHealing].Know = true g.Items.Potions[PotionHealing].Know = true
if p.Stats.HP += g.roll(p.Stats.Lvl, 4); p.Stats.HP > p.Stats.MaxHP { if p.Stats.HP += g.roll(p.Stats.Lvl, 4); p.Stats.HP > p.Stats.MaxHP {
p.Stats.MaxHP++ p.Stats.MaxHP++
p.Stats.HP = p.Stats.MaxHP p.Stats.HP = p.Stats.MaxHP
} }
g.sight(0) g.sight(0)
g.msg("you begin to feel better") g.msg("you begin to feel better")
case PStrength: case PotionGainStrength:
g.Items.Potions[PStrength].Know = true g.Items.Potions[PotionGainStrength].Know = true
g.chgStr(1) g.chgStr(1)
g.msg("you feel stronger, now. What bulging muscles!") g.msg("you feel stronger, now. What bulging muscles!")
case PMFind: case PotionDetectMonsters:
p.Flags.Set(SeeMonst) p.Flags.Set(SenseMonsters)
g.Fuse(DTurnSee, 1, HuhDuration, After) g.Fuse(DTurnSee, 1, HuhDuration, After)
if !g.turnSee(false) { if !g.turnSee(false) {
g.msg("you have a %s feeling for a moment, then it passes", g.msg("you have a %s feeling for a moment, then it passes",
g.chooseStr("normal", "strange")) g.chooseStr("normal", "strange"))
} }
case PTFind: case PotionDetectMagic:
// Potion of magic detection. Show the potions and scrolls // Potion of magic detection. Show the potions and scrolls
show := false show := false
if len(g.Level.Objects) > 0 { if len(g.Level.Objects) > 0 {
@@ -94,7 +94,7 @@ func (g *RogueGame) quaff() {
if tp.isMagic() { if tp.isMagic() {
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)
g.Items.Potions[PTFind].Know = true g.Items.Potions[PotionDetectMagic].Know = true
} }
} }
for _, mp := range g.Level.Monsters { for _, mp := range g.Level.Monsters {
@@ -107,34 +107,34 @@ func (g *RogueGame) quaff() {
} }
} }
if show { if show {
g.Items.Potions[PTFind].Know = true g.Items.Potions[PotionDetectMagic].Know = true
g.showWin("You sense the presence of magic on this level.--More--") g.showWin("You sense the presence of magic on this level.--More--")
} else { } else {
g.msg("you have a %s feeling for a moment, then it passes", g.msg("you have a %s feeling for a moment, then it passes",
g.chooseStr("normal", "strange")) g.chooseStr("normal", "strange"))
} }
case PLSD: case PotionLSD:
if !trip { if !trip {
if p.On(SeeMonst) { if p.On(SenseMonsters) {
g.turnSee(false) g.turnSee(false)
} }
g.StartDaemon(DVisuals, 0, Before) g.StartDaemon(DVisuals, 0, Before)
g.SeenStairs = g.seenStairs() g.SeenStairs = g.seenStairs()
} }
g.doPot(PLSD, true) g.doPot(PotionLSD, true)
case PSeeInvis: case PotionSeeInvisible:
show := p.On(CanSee) show := p.On(CanSeeInvisible)
g.doPot(PSeeInvis, false) g.doPot(PotionSeeInvisible, false)
if !show { if !show {
g.invisOn() g.invisOn()
} }
g.sight(0) g.sight(0)
case PRaise: case PotionRaiseLevel:
g.Items.Potions[PRaise].Know = true g.Items.Potions[PotionRaiseLevel].Know = true
g.msg("you suddenly feel much more skillful") g.msg("you suddenly feel much more skillful")
g.raiseLevel() g.raiseLevel()
case PXHeal: case PotionExtraHealing:
g.Items.Potions[PXHeal].Know = true g.Items.Potions[PotionExtraHealing].Know = true
if p.Stats.HP += g.roll(p.Stats.Lvl, 8); p.Stats.HP > p.Stats.MaxHP { if p.Stats.HP += g.roll(p.Stats.Lvl, 8); p.Stats.HP > p.Stats.MaxHP {
if p.Stats.HP > p.Stats.MaxHP+p.Stats.Lvl+1 { if p.Stats.HP > p.Stats.MaxHP+p.Stats.Lvl+1 {
p.Stats.MaxHP++ p.Stats.MaxHP++
@@ -145,33 +145,33 @@ func (g *RogueGame) quaff() {
g.sight(0) g.sight(0)
g.comeDown(0) g.comeDown(0)
g.msg("you begin to feel much better") g.msg("you begin to feel much better")
case PHaste: case PotionHaste:
g.Items.Potions[PHaste].Know = true g.Items.Potions[PotionHaste].Know = true
g.After = false g.After = false
if g.addHaste(true) { if g.addHaste(true) {
g.msg("you feel yourself moving much faster") g.msg("you feel yourself moving much faster")
} }
case PRestore: case PotionRestoreStrength:
if p.IsRing(Left, RAddStr) { if p.IsRing(Left, RingAddStrength) {
addStr(&p.Stats.Str, -p.CurRing[Left].Arm) addStr(&p.Stats.Str, -p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RAddStr) { if p.IsRing(Right, RingAddStrength) {
addStr(&p.Stats.Str, -p.CurRing[Right].Arm) addStr(&p.Stats.Str, -p.CurRing[Right].Bonus)
} }
if p.Stats.Str < p.MaxStats.Str { if p.Stats.Str < p.MaxStats.Str {
p.Stats.Str = p.MaxStats.Str p.Stats.Str = p.MaxStats.Str
} }
if p.IsRing(Left, RAddStr) { if p.IsRing(Left, RingAddStrength) {
addStr(&p.Stats.Str, p.CurRing[Left].Arm) addStr(&p.Stats.Str, p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RAddStr) { if p.IsRing(Right, RingAddStrength) {
addStr(&p.Stats.Str, p.CurRing[Right].Arm) addStr(&p.Stats.Str, p.CurRing[Right].Bonus)
} }
g.msg("hey, this tastes great. It make you feel warm all over") g.msg("hey, this tastes great. It make you feel warm all over")
case PBlind: case PotionBlindness:
g.doPot(PBlind, true) g.doPot(PotionBlindness, true)
case PLevit: case PotionLevitation:
g.doPot(PLevit, true) g.doPot(PotionLevitation, true)
} }
g.status() g.status()
// Throw the item away // Throw the item away
@@ -187,10 +187,10 @@ func (g *RogueGame) raiseLevel() {
// 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(typ int, knowit bool) { func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
pp := &pActions[typ] pp := &pActions[kind]
if !g.Items.Potions[typ].Know { if !g.Items.Potions[kind].Know {
g.Items.Potions[typ].Know = knowit g.Items.Potions[kind].Know = knowit
} }
t := g.spread(pp.time) t := g.spread(pp.time)
if !g.Player.On(pp.flags) { if !g.Player.On(pp.flags) {
@@ -201,7 +201,7 @@ func (g *RogueGame) doPot(typ int, knowit bool) {
g.Lengthen(pp.daemon, t) g.Lengthen(pp.daemon, t)
} }
high, straight := pp.high, pp.straight high, straight := pp.high, pp.straight
if typ == PSeeInvis { if kind == PotionSeeInvisible {
s := fmt.Sprintf("this potion tastes like %s juice", g.Fruit) s := fmt.Sprintf("this potion tastes like %s juice", g.Fruit)
high, straight = s, s high, straight = s, s
} }
@@ -210,12 +210,12 @@ func (g *RogueGame) doPot(typ int, 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 (o *Object) isMagic() bool {
switch o.Type { switch o.Kind {
case Armor: case KindArmor:
return o.Flags.Has(IsProt) || o.Arm != aClass[o.Which] return o.Flags.Has(Protected) || o.ArmorClass != aClass[o.Which]
case Weapon: case KindWeapon:
return o.HPlus != 0 || o.DPlus != 0 return o.HPlus != 0 || o.DPlus != 0
case Potion, Scroll, Stick, Ring, Amulet: case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
return true return true
} }
return false return false
@@ -224,9 +224,9 @@ func (o *Object) isMagic() bool {
// invisOn turns on the ability to see invisible monsters (potions.c // invisOn turns on the ability to see invisible monsters (potions.c
// invis_on). // invis_on).
func (g *RogueGame) invisOn() { func (g *RogueGame) invisOn() {
g.Player.Flags.Set(CanSee) g.Player.Flags.Set(CanSeeInvisible)
for _, mp := range g.Level.Monsters { for _, mp := range g.Level.Monsters {
if mp.On(IsInvis) && g.seeMonst(mp) && !g.Player.On(IsHalu) { if mp.On(Invisible) && g.seeMonst(mp) && !g.Player.On(Hallucinating) {
g.mvaddch(mp.Pos.Y, mp.Pos.X, mp.Disguise) g.mvaddch(mp.Pos.Y, mp.Pos.X, mp.Disguise)
} }
} }
@@ -247,7 +247,7 @@ func (g *RogueGame) turnSee(turnOff bool) bool {
if !canSee { if !canSee {
g.standout() g.standout()
} }
if !g.Player.On(IsHalu) { if !g.Player.On(Hallucinating) {
g.addch(mp.Type) g.addch(mp.Type)
} else { } else {
g.addch(byte(g.rnd(26) + 'A')) g.addch(byte(g.rnd(26) + 'A'))
@@ -259,9 +259,9 @@ func (g *RogueGame) turnSee(turnOff bool) bool {
} }
} }
if turnOff { if turnOff {
g.Player.Flags.Clear(SeeMonst) g.Player.Flags.Clear(SenseMonsters)
} else { } else {
g.Player.Flags.Set(SeeMonst) g.Player.Flags.Set(SenseMonsters)
} }
return addNew return addNew
} }
@@ -279,10 +279,10 @@ func (g *RogueGame) seenStairs() bool {
} }
// if a monster is on the stairs, this gets hairy // if a monster is on the stairs, this gets hairy
if tp := g.Level.MonsterAt(st.Y, st.X); tp != nil { if tp := g.Level.MonsterAt(st.Y, st.X); tp != nil {
if g.seeMonst(tp) && tp.On(IsRun) { // visible and awake: if g.seeMonst(tp) && tp.On(Awake) { // visible and awake:
return true // it must have moved there return true // it must have moved there
} }
if g.Player.On(SeeMonst) && tp.OldCh == Stairs { if g.Player.On(SenseMonsters) && tp.OldCh == Stairs {
return true return true
} }
} }

View File

@@ -7,12 +7,12 @@ import "fmt"
// ringOn puts a ring on a hand (rings.c ring_on). // ringOn puts a ring on a hand (rings.c ring_on).
func (g *RogueGame) ringOn() { func (g *RogueGame) ringOn() {
p := &g.Player p := &g.Player
obj := g.getItem("put on", int(Ring)) obj := g.getItem("put on", KindRing)
// Make certain that it is something that we want to wear // Make certain that it is something that we want to wear
if obj == nil { if obj == nil {
return return
} }
if obj.Type != Ring { if obj.Kind != KindRing {
if !g.Options.Terse { if !g.Options.Terse {
g.msg("it would be difficult to wrap that around a finger") g.msg("it would be difficult to wrap that around a finger")
} else { } else {
@@ -47,12 +47,12 @@ func (g *RogueGame) ringOn() {
p.CurRing[ring] = obj p.CurRing[ring] = obj
// Calculate the effect it has on the poor guy. // Calculate the effect it has on the poor guy.
switch obj.Which { switch obj.RingKind() {
case RAddStr: case RingAddStrength:
g.chgStr(obj.Arm) g.chgStr(obj.Bonus)
case RSeeInvis: case RingSeeInvisible:
g.invisOn() g.invisOn()
case RAggr: case RingAggravateMonsters:
g.aggravate() g.aggravate()
} }
@@ -123,7 +123,7 @@ func (g *RogueGame) gethand() int {
// ringUses is the rings.c ring_eat static uses[] table: how much food each // 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). // ring type uses up per turn (negative = a 1-in-n chance of 1).
var ringUses = [MaxRings]int{ var ringUses = [NumRingTypes]int{
1, // R_PROTECT 1, // R_PROTECT
1, // R_ADDSTR 1, // R_ADDSTR
1, // R_SUSTSTR 1, // R_SUSTSTR
@@ -147,7 +147,7 @@ func (g *RogueGame) ringEat(hand int) int {
if ring == nil { if ring == nil {
return 0 return 0
} }
eat := ringUses[ring.Which] eat := ringUses[ring.RingKind()]
if eat < 0 { if eat < 0 {
if g.rnd(-eat) == 0 { if g.rnd(-eat) == 0 {
eat = 1 eat = 1
@@ -155,7 +155,7 @@ func (g *RogueGame) ringEat(hand int) int {
eat = 0 eat = 0
} }
} }
if ring.Which == RDigest { if ring.RingKind() == RingSlowDigestion {
eat = -eat eat = -eat
} }
return eat return eat
@@ -163,12 +163,12 @@ func (g *RogueGame) ringEat(hand int) int {
// ringNum prints ring bonuses (rings.c ring_num). // ringNum prints ring bonuses (rings.c ring_num).
func ringNum(g *RogueGame, obj *Object) string { func ringNum(g *RogueGame, obj *Object) string {
if !obj.Flags.Has(IsKnow) { if !obj.Flags.Has(Known) {
return "" return ""
} }
switch obj.Which { switch obj.RingKind() {
case RProtect, RAddStr, RAddDam, RAddHit: case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity:
return fmt.Sprintf(" [%s]", num(obj.Arm, 0, Ring)) return fmt.Sprintf(" [%s]", num(obj.Bonus, 0, Ring))
} }
return "" return ""
} }

View File

@@ -115,58 +115,58 @@ func (g *RogueGame) totalWinner() {
for _, obj := range p.Pack { for _, obj := range p.Pack {
worth := 0 worth := 0
it := &g.Items it := &g.Items
switch obj.Type { switch obj.Kind {
case Food: case KindFood:
worth = 2 * obj.Count worth = 2 * obj.Count
case Weapon: case KindWeapon:
worth = it.Weapons[obj.Which].Worth worth = it.Weapons[obj.Which].Worth
worth *= 3*(obj.HPlus+obj.DPlus) + obj.Count worth *= 3*(obj.HPlus+obj.DPlus) + obj.Count
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
case Armor: case KindArmor:
worth = it.Armors[obj.Which].Worth worth = it.Armors[obj.Which].Worth
worth += (9 - obj.Arm) * 100 worth += (9 - obj.ArmorClass) * 100
worth += 10 * (aClass[obj.Which] - obj.Arm) worth += 10 * (aClass[obj.Which] - obj.ArmorClass)
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
case Scroll: case KindScroll:
op := &it.Scrolls[obj.Which] op := &it.Scrolls[obj.Which]
worth = op.Worth * obj.Count worth = op.Worth * obj.Count
if !op.Know { if !op.Know {
worth /= 2 worth /= 2
} }
op.Know = true op.Know = true
case Potion: case KindPotion:
op := &it.Potions[obj.Which] op := &it.Potions[obj.Which]
worth = op.Worth * obj.Count worth = op.Worth * obj.Count
if !op.Know { if !op.Know {
worth /= 2 worth /= 2
} }
op.Know = true op.Know = true
case Ring: case KindRing:
op := &it.Rings[obj.Which] op := &it.Rings[obj.Which]
worth = op.Worth worth = op.Worth
if obj.Which == RAddStr || obj.Which == RAddDam || if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage ||
obj.Which == RProtect || obj.Which == RAddHit { obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity {
if obj.Arm > 0 { if obj.Bonus > 0 {
worth += obj.Arm * 100 worth += obj.Bonus * 100
} else { } else {
worth = 10 worth = 10
} }
} }
if !obj.Flags.Has(IsKnow) { if !obj.Flags.Has(Known) {
worth /= 2 worth /= 2
} }
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
op.Know = true op.Know = true
case Stick: case KindWand:
op := &it.Sticks[obj.Which] op := &it.Sticks[obj.Which]
worth = op.Worth worth = op.Worth
worth += 20 * obj.Charges() worth += 20 * obj.Charges
if !obj.Flags.Has(IsKnow) { if !obj.Flags.Has(Known) {
worth /= 2 worth /= 2
} }
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
op.Know = true op.Know = true
case Amulet: case KindAmulet:
worth = 1000 worth = 1000
} }
if worth < 0 { if worth < 0 {

View File

@@ -33,14 +33,14 @@ func (g *RogueGame) doRooms() {
// Put the gone rooms, if any, on the level // Put the gone rooms, if any, on the level
leftOut := g.rnd(4) leftOut := g.rnd(4)
for i := 0; i < leftOut; i++ { for i := 0; i < leftOut; i++ {
g.Level.Rooms[g.rndRoom()].Flags.Set(IsGone) g.Level.Rooms[g.rndRoom()].Flags.Set(Gone)
} }
// dig and populate all the rooms on the level // dig and populate all the rooms on the level
for i := range g.Level.Rooms { for i := range g.Level.Rooms {
rp := &g.Level.Rooms[i] rp := &g.Level.Rooms[i]
// Find upper left corner of box that this room goes in // Find upper left corner of box that this room goes in
top := Coord{X: (i%3)*bsze.X + 1, Y: (i / 3) * bsze.Y} top := Coord{X: (i%3)*bsze.X + 1, Y: (i / 3) * bsze.Y}
if rp.Flags.Has(IsGone) { if rp.Flags.Has(Gone) {
// Place a gone room. Make certain that there is a blank line // Place a gone room. Make certain that there is a blank line
// for passage drawing. // for passage drawing.
for { for {
@@ -56,13 +56,13 @@ func (g *RogueGame) doRooms() {
} }
// set room type // set room type
if g.rnd(10) < g.Depth-1 { if g.rnd(10) < g.Depth-1 {
rp.Flags.Set(IsDark) // dark room rp.Flags.Set(Dark) // dark room
if g.rnd(15) == 0 { if g.rnd(15) == 0 {
rp.Flags = IsMaze // maze room rp.Flags = Maze // maze room
} }
} }
// Find a place and size for a random room // Find a place and size for a random room
if rp.Flags.Has(IsMaze) { if rp.Flags.Has(Maze) {
rp.Max.X = bsze.X - 1 rp.Max.X = bsze.X - 1
rp.Max.Y = bsze.Y - 1 rp.Max.Y = bsze.Y - 1
if rp.Pos.X = top.X; rp.Pos.X == 1 { if rp.Pos.X = top.X; rp.Pos.X == 1 {
@@ -88,13 +88,13 @@ func (g *RogueGame) doRooms() {
if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) { if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) {
gold := newObject() gold := newObject()
rp.GoldVal = g.goldCalc() rp.GoldVal = g.goldCalc()
gold.SetGoldVal(rp.GoldVal) gold.GoldValue = rp.GoldVal
rp.Gold, _ = g.findFloorIn(rp, 0, false) rp.Gold, _ = g.findFloorIn(rp, 0, false)
gold.Pos = rp.Gold gold.Pos = rp.Gold
g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold) g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold)
gold.Flags = IsMany gold.Flags = Stackable
gold.Group = goldGrp gold.Group = goldGrp
gold.Type = Gold gold.Kind = KindGold
attachObj(&g.Level.Objects, gold) attachObj(&g.Level.Objects, gold)
} }
// Put the monster in // Put the monster in
@@ -114,7 +114,7 @@ func (g *RogueGame) doRooms() {
// drawRoom draws a box around a room and lays down the floor for normal // drawRoom draws a box around a room and lays down the floor for normal
// rooms; for maze rooms, draws the maze (rooms.c draw_room). // rooms; for maze rooms, draws the maze (rooms.c draw_room).
func (g *RogueGame) drawRoom(rp *Room) { func (g *RogueGame) drawRoom(rp *Room) {
if rp.Flags.Has(IsMaze) { if rp.Flags.Has(Maze) {
g.doMaze(rp) g.doMaze(rp)
return return
} }
@@ -180,7 +180,7 @@ func (g *RogueGame) dig(y, x int) {
if newy < 0 || newy > m.maxy || newx < 0 || newx > m.maxx { if newy < 0 || newy > m.maxy || newx < 0 || newx > m.maxx {
continue continue
} }
if g.Level.FlagsAt(newy+m.starty, newx+m.startx).Has(FPass) { if g.Level.FlagsAt(newy+m.starty, newx+m.startx).Has(FPassage) {
continue continue
} }
if cnt++; g.rnd(cnt) == 0 { if cnt++; g.rnd(cnt) == 0 {
@@ -257,7 +257,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co
var compchar byte var compchar byte
if !pickroom { if !pickroom {
compchar = Floor compchar = Floor
if rp.Flags.Has(IsMaze) { if rp.Flags.Has(Maze) {
compchar = Passage compchar = Passage
} }
} }
@@ -271,7 +271,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co
if pickroom { if pickroom {
rp = &g.Level.Rooms[g.rndRoom()] rp = &g.Level.Rooms[g.rndRoom()]
compchar = Floor compchar = Floor
if rp.Flags.Has(IsMaze) { if rp.Flags.Has(Maze) {
compchar = Passage compchar = Passage
} }
} }
@@ -294,7 +294,7 @@ func (g *RogueGame) enterRoom(cp Coord) {
rp := g.roomin(cp) rp := g.roomin(cp)
p.Room = rp p.Room = rp
g.doorOpen(rp) g.doorOpen(rp)
if !rp.Flags.Has(IsDark) && !p.On(IsBlind) { if !rp.Flags.Has(Dark) && !p.On(Blind) {
for y := rp.Pos.Y; y < rp.Max.Y+rp.Pos.Y; y++ { for y := rp.Pos.Y; y < rp.Max.Y+rp.Pos.Y; y++ {
g.move(y, rp.Pos.X) g.move(y, rp.Pos.X)
for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ { for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ {
@@ -309,7 +309,7 @@ func (g *RogueGame) enterRoom(cp Coord) {
} else { } else {
tp.OldCh = ch tp.OldCh = ch
if !g.seeMonst(tp) { if !g.seeMonst(tp) {
if p.On(SeeMonst) { if p.On(SenseMonsters) {
g.standout() g.standout()
g.addch(tp.Disguise) g.addch(tp.Disguise)
g.standend() g.standend()
@@ -330,21 +330,21 @@ func (g *RogueGame) leaveRoom(cp Coord) {
p := &g.Player p := &g.Player
rp := p.Room rp := p.Room
if rp.Flags.Has(IsMaze) { if rp.Flags.Has(Maze) {
return return
} }
var floor byte var floor byte
switch { switch {
case rp.Flags.Has(IsGone): case rp.Flags.Has(Gone):
floor = Passage floor = Passage
case !rp.Flags.Has(IsDark) || p.On(IsBlind): case !rp.Flags.Has(Dark) || p.On(Blind):
floor = Floor floor = Floor
default: default:
floor = ' ' floor = ' '
} }
p.Room = &g.Level.Passages[*g.Level.FlagsAt(cp.Y, cp.X)&FPNum] p.Room = &g.Level.Passages[*g.Level.FlagsAt(cp.Y, cp.X)&FPassNum]
for y := rp.Pos.Y; y < rp.Max.Y+rp.Pos.Y; y++ { for y := rp.Pos.Y; y < rp.Max.Y+rp.Pos.Y; y++ {
for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ { for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ {
g.move(y, x) g.move(y, x)
@@ -357,7 +357,7 @@ func (g *RogueGame) leaveRoom(cp Coord) {
// to check for monster, we have to strip out the standout // to check for monster, we have to strip out the standout
// bit (our Window returns the bare character already) // bit (our Window returns the bare character already)
if isUpper(ch) { if isUpper(ch) {
if p.On(SeeMonst) { if p.On(SenseMonsters) {
g.standout() g.standout()
g.addch(ch) g.addch(ch)
g.standend() g.standend()

View File

@@ -11,6 +11,13 @@ import (
// what state.c's rs_fix_thing did: pointers that alias other structures // what state.c's rs_fix_thing did: pointers that alias other structures
// (equipment, chase targets, room membership) are encoded as indices. // (equipment, chase targets, room membership) are encoded as indices.
// saveFormatVersion identifies the snapshot layout; it changes whenever a
// field rename or retype would make gob silently mis-decode an older
// save ("go2": Object.Type became Kind ObjectKind; "go3": Object.Arm split
// into ArmorClass/Charges/GoldValue/Bonus, damage strings became
// DiceSpec).
const saveFormatVersion = Release + "-go3"
// destRef encodes a monster's chase target (state.c rs_write_thing's // destRef encodes a monster's chase target (state.c rs_write_thing's
// (kind, index) pairs). // (kind, index) pairs).
type destRef struct { type destRef struct {
@@ -74,14 +81,14 @@ type SaveState struct {
HasAmulet bool HasAmulet bool
SeenStairs bool SeenStairs bool
Places []savedPlace Places []savedPlace
Rooms [MaxRooms]Room Rooms [MaxRooms]Room
Passages [MaxPass]Room Passages [MaxPass]Room
Objects []Object Objects []Object
Monsters []savedCreature Monsters []savedCreature
Dests []destRef // parallel to Monsters Dests []destRef // parallel to Monsters
Stairs Coord Stairs Coord
NTraps int TrapCount int
After bool After bool
Again bool Again bool
@@ -165,7 +172,7 @@ func (g *RogueGame) packIdx(obj *Object) int {
func (g *RogueGame) snapshot() *SaveState { func (g *RogueGame) snapshot() *SaveState {
p := &g.Player p := &g.Player
st := &SaveState{ st := &SaveState{
Version: Release, Version: saveFormatVersion,
Seed: g.Rng.Seed, Seed: g.Rng.Seed,
Dnum: g.Dnum, Dnum: g.Dnum,
Whoami: g.Whoami, Whoami: g.Whoami,
@@ -179,7 +186,7 @@ func (g *RogueGame) snapshot() *SaveState {
Rooms: g.Level.Rooms, Rooms: g.Level.Rooms,
Passages: g.Level.Passages, Passages: g.Level.Passages,
Stairs: g.Level.Stairs, Stairs: g.Level.Stairs,
NTraps: g.Level.NTraps, TrapCount: g.Level.TrapCount,
After: g.After, After: g.After,
Again: g.Again, Again: g.Again,
NoScoreF: g.NoScore, NoScoreF: g.NoScore,
@@ -308,7 +315,7 @@ func (g *RogueGame) applySnapshot(st *SaveState) {
g.Level.Rooms = st.Rooms g.Level.Rooms = st.Rooms
g.Level.Passages = st.Passages g.Level.Passages = st.Passages
g.Level.Stairs = st.Stairs g.Level.Stairs = st.Stairs
g.Level.NTraps = st.NTraps g.Level.TrapCount = st.TrapCount
g.After = st.After g.After = st.After
g.Again = st.Again g.Again = st.Again
g.NoScore = st.NoScoreF g.NoScore = st.NoScoreF
@@ -530,7 +537,7 @@ func Restore(path string, cfg Config) (*RogueGame, error) {
if err := gob.NewDecoder(f).Decode(&st); err != nil { if err := gob.NewDecoder(f).Decode(&st); err != nil {
return nil, fmt.Errorf("%s: corrupt or incompatible save file: %w", path, err) return nil, fmt.Errorf("%s: corrupt or incompatible save file: %w", path, err)
} }
if st.Version != Release { if st.Version != saveFormatVersion {
return nil, fmt.Errorf("sorry, saved game is out of date") return nil, fmt.Errorf("sorry, saved game is out of date")
} }

View File

@@ -13,11 +13,11 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
g.Player.Purse = 123 g.Player.Purse = 123
g.Player.FoodLeft = 777 g.Player.FoodLeft = 777
g.HasAmulet = true g.HasAmulet = true
g.Items.Potions[PHealing].Know = true g.Items.Potions[PotionHealing].Know = true
g.Items.Scrolls[SMap].Guess = "map???" g.Items.Scrolls[ScrollMagicMapping].Guess = "map???"
g.Monsters['F'-'A'].Stats.Dmg = "3x1" // mutated bestiary must survive g.Monsters['F'-'A'].Stats.Dmg = dice("3x1") // mutated bestiary must survive
if len(g.Level.Monsters) > 0 { if len(g.Level.Monsters) > 0 {
g.Level.Monsters[0].Flags.Set(IsRun) g.Level.Monsters[0].Flags.Set(Awake)
g.Level.Monsters[0].Dest = &g.Player.Pos g.Level.Monsters[0].Dest = &g.Player.Pos
} }
@@ -42,13 +42,13 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
if !h.HasAmulet { if !h.HasAmulet {
t.Error("amulet flag lost") t.Error("amulet flag lost")
} }
if !h.Items.Potions[PHealing].Know { if !h.Items.Potions[PotionHealing].Know {
t.Error("potion identification lost") t.Error("potion identification lost")
} }
if h.Items.Scrolls[SMap].Guess != "map???" { if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" {
t.Error("scroll guess lost") t.Error("scroll guess lost")
} }
if h.Monsters['F'-'A'].Stats.Dmg != "3x1" { if h.Monsters['F'-'A'].Stats.Dmg.String() != "3x1" {
t.Error("mutated bestiary lost") t.Error("mutated bestiary lost")
} }
if h.Rng.Seed != g.Rng.Seed { if h.Rng.Seed != g.Rng.Seed {
@@ -83,7 +83,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
len(h.Player.Pack)) len(h.Player.Pack))
found := false found := false
for i, o := range h.Player.Pack { for i, o := range h.Player.Pack {
t.Logf(" pack[%d]=%p type=%c which=%d", i, o, o.Type, o.Which) t.Logf(" pack[%d]=%p type=%v which=%d", i, o, o.Kind, o.Which)
if o == h.Player.CurWeapon { if o == h.Player.CurWeapon {
found = true found = true
} }

View File

@@ -2,25 +2,25 @@ 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 type they identify (scrolls.c // idType maps identify scrolls to the kind of item they identify
// static id_type). // (scrolls.c static id_type).
var idType = [SIDRorS + 1]int{ var idType = [ScrollIdentifyRingOrStick + 1]ObjectKind{
SIDPotion: int(Potion), ScrollIdentifyPotion: KindPotion,
SIDScroll: int(Scroll), ScrollIdentifyScroll: KindScroll,
SIDWeapon: int(Weapon), ScrollIdentifyWeapon: KindWeapon,
SIDArmor: int(Armor), ScrollIdentifyArmor: KindArmor,
SIDRorS: RorS, 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() {
p := &g.Player p := &g.Player
obj := g.getItem("read", int(Scroll)) obj := g.getItem("read", KindScroll)
if obj == nil { if obj == nil {
return return
} }
if obj.Type != Scroll { if obj.Kind != KindScroll {
if !g.Options.Terse { if !g.Options.Terse {
g.msg("there is nothing on it to read") g.msg("there is nothing on it to read")
} else { } else {
@@ -35,18 +35,18 @@ func (g *RogueGame) readScroll() {
// Get rid of the thing // Get rid of the thing
g.leavePack(obj, false, false) g.leavePack(obj, false, false)
switch obj.Which { switch obj.ScrollKind() {
case SConfuse: case ScrollMonsterConfusion:
// Scroll of monster confusion. Give him that power. // Scroll of monster confusion. Give him that power.
p.Flags.Set(CanHuh) p.Flags.Set(CanConfuse)
g.msg("your hands begin to glow %s", g.pickColor("red")) g.msg("your hands begin to glow %s", g.pickColor("red"))
case SArmor: case ScrollEnchantArmor:
if p.CurArmor != nil { if p.CurArmor != nil {
p.CurArmor.Arm-- p.CurArmor.ArmorClass--
p.CurArmor.Flags.Clear(IsCursed) p.CurArmor.Flags.Clear(Cursed)
g.msg("your armor glows %s for a moment", g.pickColor("silver")) g.msg("your armor glows %s for a moment", g.pickColor("silver"))
} }
case SHold: case ScrollHoldMonster:
// Hold monster scroll. Stop all monsters within two spaces from // Hold monster scroll. Stop all monsters within two spaces from
// chasing after the hero. // chasing after the hero.
held := 0 held := 0
@@ -58,9 +58,9 @@ func (g *RogueGame) readScroll() {
if y < 0 || y > NumLines-1 { if y < 0 || y > NumLines-1 {
continue continue
} }
if mp := g.Level.MonsterAt(y, x); mp != nil && mp.On(IsRun) { if mp := g.Level.MonsterAt(y, x); mp != nil && mp.On(Awake) {
mp.Flags.Clear(IsRun) mp.Flags.Clear(Awake)
mp.Flags.Set(IsHeld) mp.Flags.Set(Held)
held++ held++
} }
} }
@@ -75,17 +75,17 @@ func (g *RogueGame) readScroll() {
g.addmsg("s") g.addmsg("s")
} }
g.endmsg() g.endmsg()
g.Items.Scrolls[SHold].Know = true g.Items.Scrolls[ScrollHoldMonster].Know = true
} else { } else {
g.msg("you feel a strange sense of loss") g.msg("you feel a strange sense of loss")
} }
case SSleep: case ScrollSleep:
// Scroll which makes you fall asleep // Scroll which makes you fall asleep
g.Items.Scrolls[SSleep].Know = true g.Items.Scrolls[ScrollSleep].Know = true
g.NoCommand += g.rnd(g.spread(5)) + 4 // SLEEPTIME g.NoCommand += g.rnd(g.spread(5)) + 4 // SLEEPTIME
p.Flags.Clear(IsRun) p.Flags.Clear(Awake)
g.msg("you fall asleep") g.msg("you fall asleep")
case SCreate: case ScrollCreateMonster:
// Create a monster: first look in a circle around him, next try // Create a monster: first look in a circle around him, next try
// his room, otherwise give up // his room, otherwise give up
i := 0 i := 0
@@ -99,7 +99,7 @@ func (g *RogueGame) readScroll() {
// Or anything else nasty // Or anything else nasty
if ch := g.Level.VisibleChar(y, x); stepOk(ch) { if ch := g.Level.VisibleChar(y, x); stepOk(ch) {
if ch == Scroll { if ch == Scroll {
if fo := g.findObj(y, x); fo != nil && fo.Which == SScare { if fo := g.findObj(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster {
continue continue
} }
} }
@@ -115,14 +115,14 @@ func (g *RogueGame) readScroll() {
tp := &Monster{} tp := &Monster{}
g.newMonster(tp, g.randMonster(false), mp) g.newMonster(tp, g.randMonster(false), mp)
} }
case SIDPotion, SIDScroll, SIDWeapon, SIDArmor, SIDRorS: case ScrollIdentifyPotion, ScrollIdentifyScroll, ScrollIdentifyWeapon, ScrollIdentifyArmor, ScrollIdentifyRingOrStick:
// 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.Which]) g.whatis(true, idType[obj.ScrollKind()])
case SMap: case ScrollMagicMapping:
// Scroll of magic mapping. // Scroll of magic mapping.
g.Items.Scrolls[SMap].Know = true g.Items.Scrolls[ScrollMagicMapping].Know = true
g.msg("oh, now this scroll has a map on it") g.msg("oh, now this scroll has a map on it")
// take all the things we want to keep hidden out of the window // take all the things we want to keep hidden out of the window
for y := 1; y < NumLines-1; y++ { for y := 1; y < NumLines-1; y++ {
@@ -141,7 +141,7 @@ func (g *RogueGame) readScroll() {
case ' ': case ' ':
if pp.Flags.Has(FReal) { if pp.Flags.Has(FReal) {
// def: hidden things in walls stay hidden // def: hidden things in walls stay hidden
if pp.Flags.Has(FPass) { if pp.Flags.Has(FPassage) {
pass = true pass = true
} else { } else {
ch = ' ' ch = ' '
@@ -162,7 +162,7 @@ func (g *RogueGame) readScroll() {
pp.Flags.Set(FSeen | FReal) pp.Flags.Set(FSeen | FReal)
} }
default: default:
if pp.Flags.Has(FPass) { if pp.Flags.Has(FPassage) {
pass = true pass = true
} else { } else {
ch = ' ' ch = ' '
@@ -178,7 +178,7 @@ func (g *RogueGame) readScroll() {
if ch != ' ' { if ch != ' ' {
if tp := pp.Monst; tp != nil { if tp := pp.Monst; tp != nil {
tp.OldCh = ch tp.OldCh = ch
if !p.On(SeeMonst) { if !p.On(SenseMonsters) {
g.mvaddch(y, x, ch) g.mvaddch(y, x, ch)
} }
} else { } else {
@@ -187,34 +187,34 @@ func (g *RogueGame) readScroll() {
} }
} }
} }
case SFDet: case ScrollFoodDetection:
// Food detection // Food detection
found := false found := false
g.scr.Hw.Clear() g.scr.Hw.Clear()
for _, fo := range g.Level.Objects { for _, fo := range g.Level.Objects {
if fo.Type == Food { if fo.Kind == KindFood {
found = true found = true
g.scr.Hw.MvAddCh(fo.Pos.Y, fo.Pos.X, Food) g.scr.Hw.MvAddCh(fo.Pos.Y, fo.Pos.X, Food)
} }
} }
if found { if found {
g.Items.Scrolls[SFDet].Know = true g.Items.Scrolls[ScrollFoodDetection].Know = true
g.showWin("Your nose tingles and you smell food.--More--") g.showWin("Your nose tingles and you smell food.--More--")
} else { } else {
g.msg("your nose tingles") g.msg("your nose tingles")
} }
case STelep: case ScrollTeleportation:
// Scroll of teleportation: make him disappear and reappear // Scroll of teleportation: make him disappear and reappear
curRoom := p.Room curRoom := p.Room
g.teleport() g.teleport()
if curRoom != p.Room { if curRoom != p.Room {
g.Items.Scrolls[STelep].Know = true g.Items.Scrolls[ScrollTeleportation].Know = true
} }
case SEnch: case ScrollEnchantWeapon:
if p.CurWeapon == nil || p.CurWeapon.Type != Weapon { if p.CurWeapon == nil || p.CurWeapon.Kind != KindWeapon {
g.msg("you feel a strange sense of loss") g.msg("you feel a strange sense of loss")
} else { } else {
p.CurWeapon.Flags.Clear(IsCursed) p.CurWeapon.Flags.Clear(Cursed)
if g.rnd(2) == 0 { if g.rnd(2) == 0 {
p.CurWeapon.HPlus++ p.CurWeapon.HPlus++
} else { } else {
@@ -223,25 +223,25 @@ func (g *RogueGame) readScroll() {
g.msg("your %s glows %s for a moment", g.msg("your %s glows %s for a moment",
g.Items.Weapons[p.CurWeapon.Which].Name, g.pickColor("blue")) g.Items.Weapons[p.CurWeapon.Which].Name, g.pickColor("blue"))
} }
case SScare: case ScrollScareMonster:
// Reading it is a mistake and produces laughter at her poor boo // Reading it is a mistake and produces laughter at her poor boo
// boo. // boo.
g.msg("you hear maniacal laughter in the distance") g.msg("you hear maniacal laughter in the distance")
case SRemove: case ScrollRemoveCurse:
uncurse(p.CurArmor) uncurse(p.CurArmor)
uncurse(p.CurWeapon) uncurse(p.CurWeapon)
uncurse(p.CurRing[Left]) uncurse(p.CurRing[Left])
uncurse(p.CurRing[Right]) uncurse(p.CurRing[Right])
g.msg("%s", g.chooseStr("you feel in touch with the Universal Onenes", g.msg("%s", g.chooseStr("you feel in touch with the Universal Onenes",
"you feel as if somebody is watching over you")) "you feel as if somebody is watching over you"))
case SAggr: case ScrollAggravateMonsters:
// This scroll aggravates all the monsters on the current level // This scroll aggravates all the monsters on the current level
// and sets them running towards the hero // and sets them running towards the hero
g.aggravate() g.aggravate()
g.msg("you hear a high pitched humming noise") g.msg("you hear a high pitched humming noise")
case SProtect: case ScrollProtectArmor:
if p.CurArmor != nil { if p.CurArmor != nil {
p.CurArmor.Flags.Set(IsProt) p.CurArmor.Flags.Set(Protected)
g.msg("your armor is covered by a shimmering %s shield", g.msg("your armor is covered by a shimmering %s shield",
g.pickColor("gold")) g.pickColor("gold"))
} else { } else {
@@ -257,6 +257,6 @@ func (g *RogueGame) readScroll() {
// uncurse uncurses an item (scrolls.c uncurse). // uncurse uncurses an item (scrolls.c uncurse).
func uncurse(obj *Object) { func uncurse(obj *Object) {
if obj != nil { if obj != nil {
obj.Flags.Clear(IsCursed) obj.Flags.Clear(Cursed)
} }
} }

View File

@@ -7,27 +7,27 @@ import "fmt"
// 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
obj := g.getItem("zap with", int(Stick)) obj := g.getItem("zap with", KindWand)
if obj == nil { if obj == nil {
return return
} }
if obj.Type != Stick { if obj.Kind != KindWand {
g.After = false g.After = false
g.msg("you can't zap with that!") g.msg("you can't zap with that!")
return return
} }
if obj.Charges() == 0 { if obj.Charges == 0 {
g.msg("nothing happens") g.msg("nothing happens")
return return
} }
switch obj.Which { switch obj.WandKind() {
case WsLight: case WandLight:
// Reddy Kilowatt wand. Light up the room // Reddy Kilowatt wand. Light up the room
g.Items.Sticks[WsLight].Know = true g.Items.Sticks[WandLight].Know = true
if p.Room.Flags.Has(IsGone) { if p.Room.Flags.Has(Gone) {
g.msg("the corridor glows and then fades") g.msg("the corridor glows and then fades")
} else { } else {
p.Room.Flags.Clear(IsDark) p.Room.Flags.Clear(Dark)
// Light the room and put the player back up // Light the room and put the player back up
g.enterRoom(p.Pos) g.enterRoom(p.Pos)
g.addmsg("the room is lit") g.addmsg("the room is lit")
@@ -36,7 +36,7 @@ func (g *RogueGame) doZap() {
} }
g.endmsg() g.endmsg()
} }
case WsDrain: case WandDrainLife:
// take away 1/2 of hero's hit points, then take it away evenly // take away 1/2 of hero's hit points, then take it away evenly
// from the monsters in the room (or next to hero if he is in a // from the monsters in the room (or next to hero if he is in a
// passage) // passage)
@@ -45,7 +45,7 @@ func (g *RogueGame) doZap() {
return return
} }
g.drain() g.drain()
case WsInvis, WsPolymorph, WsTelAway, WsTelTo, WsCancel: case WandInvisibility, WandPolymorph, WandTeleportAway, WandTeleportTo, WandCancellation:
y := p.Pos.Y y := p.Pos.Y
x := p.Pos.X x := p.Pos.X
for stepOk(g.Level.VisibleChar(y, x)) { for stepOk(g.Level.VisibleChar(y, x)) {
@@ -55,15 +55,15 @@ func (g *RogueGame) doZap() {
if tp := g.Level.MonsterAt(y, x); tp != nil { if tp := g.Level.MonsterAt(y, x); tp != nil {
monster := tp.Type monster := tp.Type
if monster == 'F' { if monster == 'F' {
p.Flags.Clear(IsHeld) p.Flags.Clear(Held)
} }
switch obj.Which { switch obj.WandKind() {
case WsInvis: case WandInvisibility:
tp.Flags.Set(IsInvis) tp.Flags.Set(Invisible)
if g.cansee(y, x) { if g.cansee(y, x) {
g.mvaddch(y, x, tp.OldCh) g.mvaddch(y, x, tp.OldCh)
} }
case WsPolymorph: case WandPolymorph:
pp := tp.Pack pp := tp.Pack
detachMon(&g.Level.Monsters, tp) detachMon(&g.Level.Monsters, tp)
if g.seeMonst(tp) { if g.seeMonst(tp) {
@@ -80,18 +80,18 @@ func (g *RogueGame) doZap() {
tp.OldCh = oldch tp.OldCh = oldch
tp.Pack = pp tp.Pack = pp
if g.seeMonst(tp) { if g.seeMonst(tp) {
g.Items.Sticks[WsPolymorph].Know = true g.Items.Sticks[WandPolymorph].Know = true
} }
case WsCancel: case WandCancellation:
tp.Flags.Set(IsCanc) tp.Flags.Set(Cancelled)
tp.Flags.Clear(IsInvis | CanHuh) tp.Flags.Clear(Invisible | CanConfuse)
tp.Disguise = tp.Type tp.Disguise = tp.Type
if g.seeMonst(tp) { if g.seeMonst(tp) {
g.mvaddch(y, x, tp.Disguise) g.mvaddch(y, x, tp.Disguise)
} }
case WsTelAway, WsTelTo: case WandTeleportAway, WandTeleportTo:
var newPos Coord var newPos Coord
if obj.Which == WsTelAway { if obj.WandKind() == WandTeleportAway {
for { for {
newPos, _ = g.findFloor(nil, 0, true) newPos, _ = g.findFloor(nil, 0, true)
if newPos != p.Pos { if newPos != p.Pos {
@@ -103,20 +103,20 @@ func (g *RogueGame) doZap() {
newPos.X = p.Pos.X + g.Delta.X newPos.X = p.Pos.X + g.Delta.X
} }
tp.Dest = &p.Pos tp.Dest = &p.Pos
tp.Flags.Set(IsRun) tp.Flags.Set(Awake)
g.relocate(tp, newPos) g.relocate(tp, newPos)
} }
} }
case WsMissile: case WandMagicMissile:
g.Items.Sticks[WsMissile].Know = true g.Items.Sticks[WandMagicMissile].Know = true
bolt := newObject() bolt := newObject()
bolt.Type = '*' bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
bolt.HurlDmg = "1x4" bolt.HurlDmg = dice("1x4")
bolt.HPlus = 100 bolt.HPlus = 100
bolt.DPlus = 1 bolt.DPlus = 1
bolt.Flags = IsMissl bolt.Flags = Missile
if p.CurWeapon != nil { if p.CurWeapon != nil {
bolt.Launch = p.CurWeapon.Which bolt.Launch = WeaponKind(p.CurWeapon.Which)
} }
g.doMotion(bolt, g.Delta.Y, g.Delta.X) g.doMotion(bolt, g.Delta.Y, g.Delta.X)
if tp := g.Level.MonsterAt(bolt.Pos.Y, bolt.Pos.X); tp != nil && if tp := g.Level.MonsterAt(bolt.Pos.Y, bolt.Pos.X); tp != nil &&
@@ -127,7 +127,7 @@ func (g *RogueGame) doZap() {
} else { } else {
g.msg("the missle vanishes with a puff of smoke") g.msg("the missle vanishes with a puff of smoke")
} }
case WsHasteM, WsSlowM: case WandHasteMonster, WandSlowMonster:
y := p.Pos.Y y := p.Pos.Y
x := p.Pos.X x := p.Pos.X
for stepOk(g.Level.VisibleChar(y, x)) { for stepOk(g.Level.VisibleChar(y, x)) {
@@ -135,17 +135,17 @@ func (g *RogueGame) doZap() {
x += g.Delta.X x += g.Delta.X
} }
if tp := g.Level.MonsterAt(y, x); tp != nil { if tp := g.Level.MonsterAt(y, x); tp != nil {
if obj.Which == WsHasteM { if obj.WandKind() == WandHasteMonster {
if tp.On(IsSlow) { if tp.On(Slowed) {
tp.Flags.Clear(IsSlow) tp.Flags.Clear(Slowed)
} else { } else {
tp.Flags.Set(IsHaste) tp.Flags.Set(Hasted)
} }
} else { } else {
if tp.On(IsHaste) { if tp.On(Hasted) {
tp.Flags.Clear(IsHaste) tp.Flags.Clear(Hasted)
} else { } else {
tp.Flags.Set(IsSlow) tp.Flags.Set(Slowed)
} }
tp.Turn = true tp.Turn = true
} }
@@ -153,21 +153,21 @@ func (g *RogueGame) doZap() {
g.Delta.X = x g.Delta.X = x
g.runto(g.Delta) g.runto(g.Delta)
} }
case WsElect, WsFire, WsCold: case WandLightning, WandFire, WandCold:
var name string var name string
switch obj.Which { switch obj.WandKind() {
case WsElect: case WandLightning:
name = "bolt" name = "bolt"
case WsFire: case WandFire:
name = "flame" name = "flame"
default: default:
name = "ice" name = "ice"
} }
g.fireBolt(p.Pos, &g.Delta, name) g.fireBolt(p.Pos, &g.Delta, name)
g.Items.Sticks[obj.Which].Know = true g.Items.Sticks[obj.Which].Know = true
case WsNop: case WandNothing:
} }
obj.SetCharges(obj.Charges() - 1) obj.Charges--
} }
// drain does the drain-hit-points-from-player schtick (sticks.c drain). // drain does the drain-hit-points-from-player schtick (sticks.c drain).
@@ -176,14 +176,14 @@ func (g *RogueGame) drain() {
// First count how many things we need to spread the hit points among // First count how many things we need to spread the hit points among
var corp *Room var corp *Room
if g.Level.Char(p.Pos.Y, p.Pos.X) == Door { if g.Level.Char(p.Pos.Y, p.Pos.X) == Door {
corp = &g.Level.Passages[*g.Level.FlagsAt(p.Pos.Y, p.Pos.X)&FPNum] corp = &g.Level.Passages[*g.Level.FlagsAt(p.Pos.Y, p.Pos.X)&FPassNum]
} }
inpass := p.Room.Flags.Has(IsGone) inpass := p.Room.Flags.Has(Gone)
var drainee []*Monster var drainee []*Monster
for _, mp := range g.Level.Monsters { for _, mp := range g.Level.Monsters {
if mp.Room == p.Room || mp.Room == corp || if mp.Room == p.Room || mp.Room == corp ||
(inpass && g.Level.Char(mp.Pos.Y, mp.Pos.X) == Door && (inpass && g.Level.Char(mp.Pos.Y, mp.Pos.X) == Door &&
&g.Level.Passages[*g.Level.FlagsAt(mp.Pos.Y, mp.Pos.X)&FPNum] == p.Room) { &g.Level.Passages[*g.Level.FlagsAt(mp.Pos.Y, mp.Pos.X)&FPassNum] == p.Room) {
drainee = append(drainee, mp) drainee = append(drainee, mp)
} }
} }
@@ -211,12 +211,12 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
fromHero := start == p.Pos fromHero := start == p.Pos
bolt := newObject() bolt := newObject()
bolt.Type = Weapon bolt.Kind = KindWeapon
bolt.Which = Flame bolt.Which = int(WeaponFlame)
bolt.HurlDmg = "6x6" bolt.HurlDmg = dice("6x6")
bolt.HPlus = 100 bolt.HPlus = 100
bolt.DPlus = 0 bolt.DPlus = 0
g.Items.Weapons[Flame].Name = name g.Items.Weapons[WeaponFlame].Name = name
var dirch byte var dirch byte
switch dir.Y + dir.X { switch dir.Y + dir.X {
case 0: case 0:
@@ -322,28 +322,28 @@ 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] == "staff" {
cur.Damage = "2x3" cur.Damage = dice("2x3")
} else { } else {
cur.Damage = "1x1" cur.Damage = dice("1x1")
} }
cur.HurlDmg = "1x1" cur.HurlDmg = dice("1x1")
switch cur.Which { switch cur.WandKind() {
case WsLight: case WandLight:
cur.SetCharges(g.rnd(10) + 10) cur.Charges = g.rnd(10) + 10
default: default:
cur.SetCharges(g.rnd(5) + 3) cur.Charges = g.rnd(5) + 3
} }
} }
// chargeStr returns the charge-count suffix for an identified stick // chargeStr returns the charge-count suffix for an identified stick
// (sticks.c charge_str). // (sticks.c charge_str).
func chargeStr(g *RogueGame, obj *Object) string { func chargeStr(g *RogueGame, obj *Object) string {
if !obj.Flags.Has(IsKnow) { if !obj.Flags.Has(Known) {
return "" return ""
} }
if g.Options.Terse { if g.Options.Terse {
return fmt.Sprintf(" [%d]", obj.Charges()) return fmt.Sprintf(" [%d]", obj.Charges)
} }
return fmt.Sprintf(" [%d charges]", obj.Charges()) return fmt.Sprintf(" [%d charges]", obj.Charges)
} }

View File

@@ -6,10 +6,10 @@ package game
// change during play) are cloned into RogueGame.Items by NewGame. // change during play) are cloned into RogueGame.Items by NewGame.
// 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, Arm: 10, HP: 12, Dmg: "1x4", MaxHP: 12} var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, ArmorClass: 10, HP: 12, Dmg: dice("1x4"), MaxHP: 12}
// 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 = [MaxArmors]int{ var aClass = [NumArmorTypes]int{
8, // LEATHER 8, // LEATHER
7, // RING_MAIL 7, // RING_MAIL
7, // STUDDED_LEATHER 7, // STUDDED_LEATHER
@@ -28,7 +28,7 @@ var eLevels = []int{
} }
// trName is extern.c tr_name[]: names of the traps. // trName is extern.c tr_name[]: names of the traps.
var trName = [NTraps]string{ var trName = [NumTrapTypes]string{
"a trapdoor", "a trapdoor",
"an arrow trap", "an arrow trap",
"a sleeping gas trap", "a sleeping gas trap",
@@ -47,32 +47,32 @@ var invTName = []string{"Overwrite", "Slow", "Clear"}
// rolled from the level at creation time. // rolled from the level at creation time.
var monsterTable = [26]MonsterKind{ var monsterTable = [26]MonsterKind{
/* Name CARRY FLAGS str exp lvl arm hp dmg */ /* Name CARRY FLAGS str exp lvl arm hp dmg */
{"aquator", 0, IsMean, Stats{10, 20, 5, 2, 1, "0x0/0x0", 0}}, {"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}},
{"bat", 0, IsFly, Stats{10, 1, 1, 3, 1, "1x2", 0}}, {"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}},
{"centaur", 15, 0, Stats{10, 17, 4, 4, 1, "1x2/1x5/1x5", 0}}, {"centaur", 15, 0, Stats{10, 17, 4, 4, 1, dice("1x2/1x5/1x5"), 0}},
{"dragon", 100, IsMean, Stats{10, 5000, 10, -1, 1, "1x8/1x8/3x10", 0}}, {"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, dice("1x8/1x8/3x10"), 0}},
{"emu", 0, IsMean, Stats{10, 2, 1, 7, 1, "1x2", 0}}, {"emu", 0, Mean, Stats{10, 2, 1, 7, 1, dice("1x2"), 0}},
{"venus flytrap", 0, IsMean, Stats{10, 80, 8, 3, 1, "%%%x0", 0}}, {"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, dice("%%%x0"), 0}},
{"griffin", 20, IsMean | IsFly | IsRegen, Stats{10, 2000, 13, 2, 1, "4x3/3x5", 0}}, {"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, dice("4x3/3x5"), 0}},
{"hobgoblin", 0, IsMean, Stats{10, 3, 1, 5, 1, "1x8", 0}}, {"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, dice("1x8"), 0}},
{"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, "0x0", 0}}, {"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, dice("0x0"), 0}},
{"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, "2x12/2x4", 0}}, {"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, dice("2x12/2x4"), 0}},
{"kestrel", 0, IsMean | IsFly, Stats{10, 1, 1, 7, 1, "1x4", 0}}, {"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, dice("1x4"), 0}},
{"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, "1x1", 0}}, {"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, dice("1x1"), 0}},
{"medusa", 40, IsMean, Stats{10, 200, 8, 2, 1, "3x4/3x4/2x5", 0}}, {"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, dice("3x4/3x4/2x5"), 0}},
{"nymph", 100, 0, Stats{10, 37, 3, 9, 1, "0x0", 0}}, {"nymph", 100, 0, Stats{10, 37, 3, 9, 1, dice("0x0"), 0}},
{"orc", 15, IsGreed, Stats{10, 5, 1, 6, 1, "1x8", 0}}, {"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, dice("1x8"), 0}},
{"phantom", 0, IsInvis, Stats{10, 120, 8, 3, 1, "4x4", 0}}, {"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, dice("4x4"), 0}},
{"quagga", 0, IsMean, Stats{10, 15, 3, 3, 1, "1x5/1x5", 0}}, {"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, dice("1x5/1x5"), 0}},
{"rattlesnake", 0, IsMean, Stats{10, 9, 2, 3, 1, "1x6", 0}}, {"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, dice("1x6"), 0}},
{"snake", 0, IsMean, Stats{10, 2, 1, 5, 1, "1x3", 0}}, {"snake", 0, Mean, Stats{10, 2, 1, 5, 1, dice("1x3"), 0}},
{"troll", 50, IsRegen | IsMean, Stats{10, 120, 6, 4, 1, "1x8/1x8/2x6", 0}}, {"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, dice("1x8/1x8/2x6"), 0}},
{"black unicorn", 0, IsMean, Stats{10, 190, 7, -2, 1, "1x9/1x9/2x9", 0}}, {"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, dice("1x9/1x9/2x9"), 0}},
{"vampire", 20, IsRegen | IsMean, Stats{10, 350, 8, 1, 1, "1x10", 0}}, {"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, dice("1x10"), 0}},
{"wraith", 0, 0, Stats{10, 55, 5, 4, 1, "1x6", 0}}, {"wraith", 0, 0, Stats{10, 55, 5, 4, 1, dice("1x6"), 0}},
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, "4x4", 0}}, {"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}},
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, "1x6/1x6", 0}}, {"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}},
{"zombie", 0, IsMean, Stats{10, 6, 2, 8, 1, "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 // Base ObjInfo tables (extern.c). These are templates: NewGame copies them
@@ -88,7 +88,7 @@ var baseThings = [NumThings]ObjInfo{
{Prob: 4}, // stick {Prob: 4}, // stick
} }
var baseArmInfo = [MaxArmors]ObjInfo{ var 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},
@@ -99,7 +99,7 @@ var baseArmInfo = [MaxArmors]ObjInfo{
{Name: "plate mail", Prob: 5, Worth: 150}, {Name: "plate mail", Prob: 5, Worth: 150},
} }
var basePotInfo = [MaxPotions]ObjInfo{ var 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},
@@ -116,7 +116,7 @@ var basePotInfo = [MaxPotions]ObjInfo{
{Name: "levitation", Prob: 6, Worth: 75}, {Name: "levitation", Prob: 6, Worth: 75},
} }
var baseRingInfo = [MaxRings]ObjInfo{ var 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},
@@ -133,7 +133,7 @@ var baseRingInfo = [MaxRings]ObjInfo{
{Name: "maintain armor", Prob: 5, Worth: 380}, {Name: "maintain armor", Prob: 5, Worth: 380},
} }
var baseScrInfo = [MaxScrolls]ObjInfo{ var 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},
@@ -154,7 +154,7 @@ var baseScrInfo = [MaxScrolls]ObjInfo{
{Name: "protect armor", Prob: 2, Worth: 250}, {Name: "protect armor", Prob: 2, Worth: 250},
} }
var baseWeapInfo = [MaxWeapons + 1]ObjInfo{ var 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},
@@ -167,7 +167,7 @@ var baseWeapInfo = [MaxWeapons + 1]ObjInfo{
{}, // DO NOT REMOVE: fake entry for dragon's breath {}, // DO NOT REMOVE: fake entry for dragon's breath
} }
var baseWsInfo = [MaxSticks]ObjInfo{ var 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},
@@ -323,5 +323,5 @@ var helpStr = []helpEntry{
// the Go save format does not use them. // the Go save format does not use them.
const ( const (
Release = "5.4.4" Release = "5.4.4"
Version = "rogue (sneak.berlin/go/rogue port of rogueforge 5.4.4)" Version = "rogue (git.eeqj.de/sneak/rgoue port of rogueforge 5.4.4)"
) )

View File

@@ -17,7 +17,7 @@ func TestProbabilitiesSumTo100(t *testing.T) {
"scrolls": baseScrInfo[:], "scrolls": baseScrInfo[:],
"rings": baseRingInfo[:], "rings": baseRingInfo[:],
"sticks": baseWsInfo[:], "sticks": baseWsInfo[:],
"weapons": baseWeapInfo[:MaxWeapons], // excludes the flame entry "weapons": baseWeapInfo[:NumWeaponTypes], // excludes the flame entry
"armor": baseArmInfo[:], "armor": baseArmInfo[:],
} }
for name, tab := range tables { for name, tab := range tables {
@@ -29,11 +29,11 @@ func TestProbabilitiesSumTo100(t *testing.T) {
func TestInitProbsCumulative(t *testing.T) { func TestInitProbsCumulative(t *testing.T) {
g := NewGame(Config{Seed: 1}) g := NewGame(Config{Seed: 1})
last := g.Items.Potions[MaxPotions-1].Prob last := g.Items.Potions[NumPotionTypes-1].Prob
if last != 100 { if last != 100 {
t.Errorf("cumulative potion probability ends at %d, want 100", last) t.Errorf("cumulative potion probability ends at %d, want 100", last)
} }
for i := 1; i < MaxPotions; i++ { for i := PotionKind(1); i < NumPotionTypes; i++ {
if g.Items.Potions[i].Prob < g.Items.Potions[i-1].Prob { if g.Items.Potions[i].Prob < g.Items.Potions[i-1].Prob {
t.Errorf("potion probs not nondecreasing at %d", i) t.Errorf("potion probs not nondecreasing at %d", i)
} }

View File

@@ -13,14 +13,14 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
var pb strings.Builder var pb strings.Builder
which := obj.Which which := obj.Which
it := &g.Items it := &g.Items
switch obj.Type { switch obj.Kind {
case Potion: case KindPotion:
g.nameit(&pb, obj, "potion", it.PotColors[which], &it.Potions[which], nullstr) g.nameit(&pb, obj, "potion", it.PotColors[which], &it.Potions[which], nullstr)
case Ring: case KindRing:
g.nameit(&pb, obj, "ring", it.RingStones[which], &it.Rings[which], ringNum) g.nameit(&pb, obj, "ring", it.RingStones[which], &it.Rings[which], ringNum)
case Stick: 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 Scroll: case KindScroll:
if obj.Count == 1 { if obj.Count == 1 {
pb.WriteString("A scroll ") pb.WriteString("A scroll ")
} else { } else {
@@ -34,7 +34,7 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
} else { } else {
fmt.Fprintf(&pb, "titled '%s'", it.ScrNames[which]) fmt.Fprintf(&pb, "titled '%s'", it.ScrNames[which])
} }
case Food: case KindFood:
if which == 1 { if which == 1 {
if obj.Count == 1 { if obj.Count == 1 {
fmt.Fprintf(&pb, "A%s %s", vowelstr(g.Fruit), g.Fruit) fmt.Fprintf(&pb, "A%s %s", vowelstr(g.Fruit), g.Fruit)
@@ -48,14 +48,14 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
fmt.Fprintf(&pb, "%d rations of food", obj.Count) fmt.Fprintf(&pb, "%d rations of food", obj.Count)
} }
} }
case Weapon: case KindWeapon:
sp := it.Weapons[which].Name sp := it.Weapons[which].Name
if obj.Count > 1 { if obj.Count > 1 {
fmt.Fprintf(&pb, "%d ", obj.Count) fmt.Fprintf(&pb, "%d ", obj.Count)
} else { } else {
fmt.Fprintf(&pb, "A%s ", vowelstr(sp)) fmt.Fprintf(&pb, "A%s ", vowelstr(sp))
} }
if obj.Flags.Has(IsKnow) { if obj.Flags.Has(Known) {
fmt.Fprintf(&pb, "%s %s", num(obj.HPlus, obj.DPlus, Weapon), sp) fmt.Fprintf(&pb, "%s %s", num(obj.HPlus, obj.DPlus, Weapon), sp)
} else { } else {
pb.WriteString(sp) pb.WriteString(sp)
@@ -66,24 +66,24 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
if obj.Label != "" { if obj.Label != "" {
fmt.Fprintf(&pb, " called %s", obj.Label) fmt.Fprintf(&pb, " called %s", obj.Label)
} }
case Armor: case KindArmor:
sp := it.Armors[which].Name sp := it.Armors[which].Name
if obj.Flags.Has(IsKnow) { if obj.Flags.Has(Known) {
fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.Arm, 0, Armor), sp) fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.ArmorClass, 0, Armor), sp)
if !g.Options.Terse { if !g.Options.Terse {
pb.WriteString("protection ") pb.WriteString("protection ")
} }
fmt.Fprintf(&pb, "%d]", 10-obj.Arm) fmt.Fprintf(&pb, "%d]", 10-obj.ArmorClass)
} else { } else {
pb.WriteString(sp) pb.WriteString(sp)
} }
if obj.Label != "" { if obj.Label != "" {
fmt.Fprintf(&pb, " called %s", obj.Label) fmt.Fprintf(&pb, " called %s", obj.Label)
} }
case Amulet: case KindAmulet:
pb.WriteString("The Amulet of Yendor") pb.WriteString("The Amulet of Yendor")
case Gold: case KindGold:
fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldVal()) fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldValue)
} }
out := pb.String() out := pb.String()
@@ -121,20 +121,20 @@ func (g *RogueGame) dropIt() {
g.msg("there is something there already") g.msg("there is something there already")
return return
} }
obj := g.getItem("drop", 0) obj := g.getItem("drop", KindNone)
if obj == nil { if obj == nil {
return return
} }
if !g.dropCheck(obj) { if !g.dropCheck(obj) {
return return
} }
obj = g.leavePack(obj, true, !IsMult(obj.Type)) obj = g.leavePack(obj, true, !obj.Kind.MergesInPack())
// Link it into the level object list // Link it into the level object list
attachObj(&g.Level.Objects, obj) attachObj(&g.Level.Objects, obj)
g.Level.SetChar(p.Pos.Y, p.Pos.X, obj.Type) g.Level.SetChar(p.Pos.Y, p.Pos.X, obj.Kind.Glyph())
g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Set(FDropped) g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Set(FDropped)
obj.Pos = p.Pos obj.Pos = p.Pos
if obj.Type == Amulet { if obj.Kind == KindAmulet {
g.HasAmulet = false g.HasAmulet = false
} }
g.msg("dropped %s", g.invName(obj, true)) g.msg("dropped %s", g.invName(obj, true))
@@ -151,7 +151,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
obj != p.CurRing[Left] && obj != p.CurRing[Right] { obj != p.CurRing[Left] && obj != p.CurRing[Right] {
return true return true
} }
if obj.Flags.Has(IsCursed) { if obj.Flags.Has(Cursed) {
g.msg("you can't. It appears to be cursed") g.msg("you can't. It appears to be cursed")
return false return false
} }
@@ -166,10 +166,10 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
hand = Left hand = Left
} }
p.CurRing[hand] = nil p.CurRing[hand] = nil
switch obj.Which { switch obj.RingKind() {
case RAddStr: case RingAddStrength:
g.chgStr(-obj.Arm) g.chgStr(-obj.Bonus)
case RSeeInvis: case RingSeeInvisible:
g.unsee(0) g.unsee(0)
g.Extinguish(DUnsee) g.Extinguish(DUnsee)
} }
@@ -180,9 +180,9 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
// newThing returns a new random thing for the dungeon (things.c new_thing). // newThing returns a new random thing for the dungeon (things.c new_thing).
func (g *RogueGame) newThing() *Object { func (g *RogueGame) newThing() *Object {
cur := newObject() cur := newObject()
cur.Damage = "0x0" cur.Damage = dice("0x0")
cur.HurlDmg = "0x0" cur.HurlDmg = dice("0x0")
cur.Arm = 11 cur.ArmorClass = 11
cur.Count = 1 cur.Count = 1
// Decide what kind of object it will be; if we haven't had food for a // Decide what kind of object it will be; if we haven't had food for a
@@ -195,13 +195,13 @@ func (g *RogueGame) newThing() *Object {
} }
switch kind { switch kind {
case 0: case 0:
cur.Type = Potion cur.Kind = KindPotion
cur.Which = pickOne(g, g.Items.Potions[:]) cur.Which = pickOne(g, g.Items.Potions[:])
case 1: case 1:
cur.Type = Scroll cur.Kind = KindScroll
cur.Which = pickOne(g, g.Items.Scrolls[:]) cur.Which = pickOne(g, g.Items.Scrolls[:])
case 2: case 2:
cur.Type = Food cur.Kind = KindFood
g.Player.NoFood = 0 g.Player.NoFood = 0
if g.rnd(10) != 0 { if g.rnd(10) != 0 {
cur.Which = 0 cur.Which = 0
@@ -209,37 +209,37 @@ func (g *RogueGame) newThing() *Object {
cur.Which = 1 cur.Which = 1
} }
case 3: case 3:
g.initWeapon(cur, pickOne(g, g.Items.Weapons[:MaxWeapons])) g.initWeapon(cur, WeaponKind(pickOne(g, g.Items.Weapons[:NumWeaponTypes])))
if r := g.rnd(100); r < 10 { if r := g.rnd(100); r < 10 {
cur.Flags.Set(IsCursed) cur.Flags.Set(Cursed)
cur.HPlus -= g.rnd(3) + 1 cur.HPlus -= g.rnd(3) + 1
} else if r < 15 { } else if r < 15 {
cur.HPlus += g.rnd(3) + 1 cur.HPlus += g.rnd(3) + 1
} }
case 4: case 4:
cur.Type = Armor cur.Kind = KindArmor
cur.Which = pickOne(g, g.Items.Armors[:]) cur.Which = pickOne(g, g.Items.Armors[:])
cur.Arm = aClass[cur.Which] cur.ArmorClass = aClass[cur.Which]
if r := g.rnd(100); r < 20 { if r := g.rnd(100); r < 20 {
cur.Flags.Set(IsCursed) cur.Flags.Set(Cursed)
cur.Arm += g.rnd(3) + 1 cur.ArmorClass += g.rnd(3) + 1
} else if r < 28 { } else if r < 28 {
cur.Arm -= g.rnd(3) + 1 cur.ArmorClass -= g.rnd(3) + 1
} }
case 5: case 5:
cur.Type = Ring cur.Kind = KindRing
cur.Which = pickOne(g, g.Items.Rings[:]) cur.Which = pickOne(g, g.Items.Rings[:])
switch cur.Which { switch cur.RingKind() {
case RAddStr, RProtect, RAddHit, RAddDam: case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
if cur.Arm = g.rnd(3); cur.Arm == 0 { if cur.Bonus = g.rnd(3); cur.Bonus == 0 {
cur.Arm = -1 cur.Bonus = -1
cur.Flags.Set(IsCursed) cur.Flags.Set(Cursed)
} }
case RAggr, RTeleport: case RingAggravateMonsters, RingTeleportation:
cur.Flags.Set(IsCursed) cur.Flags.Set(Cursed)
} }
case 6: case 6:
cur.Type = Stick cur.Kind = KindWand
cur.Which = pickOne(g, g.Items.Sticks[:]) cur.Which = pickOne(g, g.Items.Sticks[:])
g.fixStick(cur) g.fixStick(cur)
} }
@@ -336,7 +336,7 @@ func (g *RogueGame) printDisc(typ byte) {
numFound := 0 numFound := 0
for i := range info { for i := range info {
if info[order[i]].Know || info[order[i]].Guess != "" { if info[order[i]].Know || info[order[i]].Guess != "" {
obj.Type = typ obj.Kind = objectKindForGlyph(typ)
obj.Which = order[i] obj.Which = order[i]
g.addLine("%s", g.invName(&obj, false)) g.addLine("%s", g.invName(&obj, false))
numFound++ numFound++
@@ -525,7 +525,7 @@ func (g *RogueGame) prList() {
case Armor: case Armor:
g.prSpec(g.Items.Armors[:]) g.prSpec(g.Items.Armors[:])
case Weapon: case Weapon:
g.prSpec(g.Items.Weapons[:MaxWeapons]) g.prSpec(g.Items.Weapons[:NumWeaponTypes])
} }
} }

View File

@@ -62,12 +62,6 @@ const (
Stick byte = '/' Stick byte = '/'
) )
// Pseudo-types for item-selection prompts (rogue.h)
const (
Callable = -1
RorS = -2
)
// Various constants (rogue.h) // Various constants (rogue.h)
const ( const (
HealTime = 30 HealTime = 30
@@ -100,9 +94,9 @@ const (
type RoomFlags int16 type RoomFlags int16
const ( const (
IsDark RoomFlags = 1 << iota // room is dark Dark RoomFlags = 1 << iota // room is dark
IsGone // room is gone (a corridor) Gone // room is gone (a corridor)
IsMaze // room is a maze Maze // room is a maze
) )
func (f RoomFlags) Has(b RoomFlags) bool { return f&b != 0 } func (f RoomFlags) Has(b RoomFlags) bool { return f&b != 0 }
@@ -113,12 +107,12 @@ func (f *RoomFlags) Clear(b RoomFlags) { *f &^= b }
type ObjFlags int32 type ObjFlags int32
const ( const (
IsCursed ObjFlags = 1 << iota // object is cursed Cursed ObjFlags = 1 << iota // ISCURSED: object is cursed
IsKnow // player knows details about the object Known // ISKNOW: player knows details about the object
IsMissl // object is a missile type Missile // ISMISL: object is a missile type
IsMany // object comes in groups Stackable // ISMANY: object comes in groups
ObjIsFound // object has been seen (ISFOUND shares the bit with creatures) WasFound // ISFOUND (objects): object has been seen (ISFOUND shares the bit with creatures)
IsProt // armor is permanently protected Protected // ISPROT: armor is permanently protected
) )
func (f ObjFlags) Has(b ObjFlags) bool { return f&b != 0 } func (f ObjFlags) Has(b ObjFlags) bool { return f&b != 0 }
@@ -131,25 +125,25 @@ func (f *ObjFlags) Clear(b ObjFlags) { *f &^= b }
type CreatureFlags int32 type CreatureFlags int32
const ( const (
CanHuh CreatureFlags = 0o000001 // creature can confuse CanConfuse CreatureFlags = 0o000001 // CANHUH: creature can confuse
CanSee CreatureFlags = 0o000002 // creature can see invisible creatures CanSeeInvisible CreatureFlags = 0o000002 // CANSEE: creature can see invisible creatures
IsBlind CreatureFlags = 0o000004 // creature is blind Blind CreatureFlags = 0o000004 // ISBLIND: creature is blind
IsCanc CreatureFlags = 0o000010 // creature has special qualities cancelled Cancelled CreatureFlags = 0o000010 // ISCANC: creature has special qualities cancelled
IsLevit CreatureFlags = 0o000010 // hero is levitating Levitating CreatureFlags = 0o000010 // ISLEVIT: hero is levitating
IsFound CreatureFlags = 0o000020 // creature has been seen Found CreatureFlags = 0o000020 // ISFOUND: creature has been seen
IsGreed CreatureFlags = 0o000040 // creature runs to protect gold Greedy CreatureFlags = 0o000040 // ISGREED: creature runs to protect gold
IsHaste CreatureFlags = 0o000100 // creature has been hastened Hasted CreatureFlags = 0o000100 // ISHASTE: creature has been hastened
IsTarget CreatureFlags = 0o000200 // creature is the target of an 'f' command Targeted CreatureFlags = 0o000200 // ISTARGET: creature is the target of an 'f' command
IsHeld CreatureFlags = 0o000400 // creature has been held Held CreatureFlags = 0o000400 // ISHELD: creature has been held
IsHuh CreatureFlags = 0o001000 // creature is confused Confused CreatureFlags = 0o001000 // ISHUH: creature is confused
IsInvis CreatureFlags = 0o002000 // creature is invisible Invisible CreatureFlags = 0o002000 // ISINVIS: creature is invisible
IsMean CreatureFlags = 0o004000 // creature can wake when player enters room Mean CreatureFlags = 0o004000 // ISMEAN: creature can wake when player enters room
IsHalu CreatureFlags = 0o004000 // hero is on acid trip Hallucinating CreatureFlags = 0o004000 // ISHALU: hero is on acid trip
IsRegen CreatureFlags = 0o010000 // creature can regenerate Regenerates CreatureFlags = 0o010000 // ISREGEN: creature can regenerate
IsRun CreatureFlags = 0o020000 // creature is running at the player Awake CreatureFlags = 0o020000 // ISRUN: creature is running at the player
SeeMonst CreatureFlags = 0o040000 // hero can detect unseen monsters SenseMonsters CreatureFlags = 0o040000 // SEEMONST: hero can detect unseen monsters
IsFly CreatureFlags = 0o040000 // creature can fly Flying CreatureFlags = 0o040000 // ISFLY: creature can fly
IsSlow CreatureFlags = 0o100000 // creature has been slowed Slowed CreatureFlags = 0o100000 // ISSLOW: creature has been slowed
) )
func (f CreatureFlags) Has(b CreatureFlags) bool { return f&b != 0 } func (f CreatureFlags) Has(b CreatureFlags) bool { return f&b != 0 }
@@ -160,140 +154,218 @@ func (f *CreatureFlags) Clear(b CreatureFlags) { *f &^= b }
type PlaceFlags uint8 type PlaceFlags uint8
const ( const (
FPass PlaceFlags = 0x80 // is a passageway FPassage PlaceFlags = 0x80 // F_PASS: is a passageway
FSeen PlaceFlags = 0x40 // have seen this spot before FSeen PlaceFlags = 0x40 // have seen this spot before
FDropped PlaceFlags = 0x20 // object was dropped here FDropped PlaceFlags = 0x20 // object was dropped here
FLocked PlaceFlags = 0x20 // door is locked FLocked PlaceFlags = 0x20 // door is locked
FReal PlaceFlags = 0x10 // what you see is what you get FReal PlaceFlags = 0x10 // what you see is what you get
FPNum PlaceFlags = 0x0f // passage number mask FPassNum PlaceFlags = 0x0f // F_PNUM: passage number mask
FTMask PlaceFlags = 0x07 // trap number mask FTrapMask PlaceFlags = 0x07 // F_TMASK: trap number mask
) )
func (f PlaceFlags) Has(b PlaceFlags) bool { return f&b != 0 } func (f PlaceFlags) Has(b PlaceFlags) bool { return f&b != 0 }
func (f *PlaceFlags) Set(b PlaceFlags) { *f |= b } func (f *PlaceFlags) Set(b PlaceFlags) { *f |= b }
func (f *PlaceFlags) Clear(b PlaceFlags) { *f &^= b } func (f *PlaceFlags) Clear(b PlaceFlags) { *f &^= b }
// Trap types (rogue.h) // TrapKind identifies a trap (rogue.h trap types). The kind is stored in
// the low bits of a map cell's PlaceFlags (FTrapMask).
type TrapKind int
const ( const (
TDoor = 0 TrapDoor TrapKind = 0
TArrow = 1 TrapArrow TrapKind = 1
TSleep = 2 TrapSleep TrapKind = 2
TBear = 3 TrapBear TrapKind = 3
TTelep = 4 TrapTeleport TrapKind = 4
TDart = 5 TrapDart TrapKind = 5
TRust = 6 TrapRust TrapKind = 6
TMyst = 7 TrapMystery TrapKind = 7
NTraps = 8 NumTrapTypes = 8
) )
// Potion types (rogue.h) // 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).
type PotionKind int
// Potion kinds (rogue.h)
const ( const (
PConfuse = iota PotionConfusion PotionKind = iota
PLSD PotionLSD
PPoison PotionPoison
PStrength PotionGainStrength
PSeeInvis PotionSeeInvisible
PHealing PotionHealing
PMFind PotionDetectMonsters
PTFind PotionDetectMagic
PRaise PotionRaiseLevel
PXHeal PotionExtraHealing
PHaste PotionHaste
PRestore PotionRestoreStrength
PBlind PotionBlindness
PLevit PotionLevitation
MaxPotions NumPotionTypes
) )
// Scroll types (rogue.h) // 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).
type ScrollKind int
// Scroll kinds (rogue.h)
const ( const (
SConfuse = iota ScrollMonsterConfusion ScrollKind = iota
SMap ScrollMagicMapping
SHold ScrollHoldMonster
SSleep ScrollSleep
SArmor ScrollEnchantArmor
SIDPotion ScrollIdentifyPotion
SIDScroll ScrollIdentifyScroll
SIDWeapon ScrollIdentifyWeapon
SIDArmor ScrollIdentifyArmor
SIDRorS ScrollIdentifyRingOrStick
SScare ScrollScareMonster
SFDet ScrollFoodDetection
STelep ScrollTeleportation
SEnch ScrollEnchantWeapon
SCreate ScrollCreateMonster
SRemove ScrollRemoveCurse
SAggr ScrollAggravateMonsters
SProtect ScrollProtectArmor
MaxScrolls NumScrollTypes
) )
// Weapon types (rogue.h) // 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).
type WeaponKind int
// Weapon kinds (rogue.h)
const ( const (
Mace = iota WeaponMace WeaponKind = iota
Sword WeaponLongSword
Bow WeaponBow
Arrow WeaponArrow
Dagger WeaponDagger
TwoSword WeaponTwoHandedSword
Dart WeaponDart
Shiraken WeaponShuriken
Spear WeaponSpear
Flame // fake entry for dragon breath (ick) WeaponFlame // fake entry for dragon breath (ick)
MaxWeapons = Flame NumWeaponTypes = WeaponFlame
) )
// Armor types (rogue.h) // 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).
type ArmorKind int
// Armor kinds (rogue.h)
const ( const (
Leather = iota ArmorLeather ArmorKind = iota
RingMail ArmorRingMail
StuddedLeather ArmorStuddedLeather
ScaleMail ArmorScaleMail
ChainMail ArmorChainMail
SplintMail ArmorSplintMail
BandedMail ArmorBandedMail
PlateMail ArmorPlateMail
MaxArmors NumArmorTypes
) )
// Ring types (rogue.h) // 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).
type RingKind int
// Ring kinds (rogue.h)
const ( const (
RProtect = iota RingProtection RingKind = iota
RAddStr RingAddStrength
RSustStr RingSustainStrength
RSearch RingSearching
RSeeInvis RingSeeInvisible
RNop RingAdornment
RAggr RingAggravateMonsters
RAddHit RingDexterity
RAddDam RingIncreaseDamage
RRegen RingRegeneration
RDigest RingSlowDigestion
RTeleport RingTeleportation
RStealth RingStealth
RSustArm RingMaintainArmor
MaxRings NumRingTypes
) )
// Rod/Wand/Staff types (rogue.h) // 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).
type WandKind int
// Wand kinds (rogue.h)
const ( const (
WsLight = iota WandLight WandKind = iota
WsInvis WandInvisibility
WsElect WandLightning
WsFire WandFire
WsCold WandCold
WsPolymorph WandPolymorph
WsMissile WandMagicMissile
WsHasteM WandHasteMonster
WsSlowM WandSlowMonster
WsDrain WandDrainLife
WsNop WandNothing
WsTelAway WandTeleportAway
WsTelTo WandTeleportTo
WsCancel WandCancellation
MaxSticks 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 {
@@ -302,13 +374,13 @@ type Coord struct {
// Stats describes a fighting being (rogue.h struct stats). // Stats describes a fighting being (rogue.h struct stats).
type Stats struct { type Stats struct {
Str int // strength (s_str; 3..31) Str int // strength (s_str; 3..31)
Exp int // experience Exp int // experience
Lvl int // level of mastery Lvl int // level of mastery
Arm int // armor class ArmorClass int // armor class (s_arm)
HP int // hit points (s_hpt) HP int // hit points (s_hpt)
Dmg string // damage dice, e.g. "1x4/1x2" Dmg DiceSpec // damage dice, e.g. "1x4/1x2"
MaxHP int MaxHP int
} }
// Room describes a room or passage network (rogue.h struct room). // Room describes a room or passage network (rogue.h struct room).
@@ -349,9 +421,6 @@ type Stone struct {
// CTRL maps a letter to its control character, as the C CTRL() macro. // CTRL maps a letter to its control character, as the C CTRL() macro.
func CTRL(c byte) byte { return c & 0o37 } func CTRL(c byte) byte { return c & 0o37 }
// IsMult reports whether an object type stacks in the pack (rogue.h ISMULT).
func IsMult(typ byte) bool { return typ == Potion || typ == Scroll || typ == Food }
// distance returns the squared distance between two points (chase.c dist). // distance returns the squared distance between two points (chase.c dist).
func distance(y1, x1, y2, x2 int) int { func distance(y1, x1, y2, x2 int) int {
dx := x2 - x1 dx := x2 - x1

View File

@@ -4,12 +4,12 @@ import "fmt"
// weapons.c — functions for dealing with problems brought about by weapons. // weapons.c — functions for dealing with problems brought about by weapons.
const noWeapon = -1 const noWeapon WeaponKind = -1
// missile fires a missile in a given direction (weapons.c missile). // missile fires a missile in a given direction (weapons.c missile).
func (g *RogueGame) missile(ydelta, xdelta int) { func (g *RogueGame) missile(ydelta, xdelta int) {
// Get which thing we are hurling // Get which thing we are hurling
obj := g.getItem("throw", int(Weapon)) obj := g.getItem("throw", KindWeapon)
if obj == nil { if obj == nil {
return return
} }
@@ -48,7 +48,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
if stepOk(ch) && ch != Door { if stepOk(ch) && ch != Door {
// It hasn't hit anything yet, so display it if it's alright. // It hasn't hit anything yet, so display it if it's alright.
if g.cansee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse { if g.cansee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse {
g.mvaddch(obj.Pos.Y, obj.Pos.X, obj.Type) g.mvaddch(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
g.refresh() g.refresh()
} }
continue continue
@@ -61,13 +61,13 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
func (g *RogueGame) fall(obj *Object, pr bool) { func (g *RogueGame) fall(obj *Object, pr bool) {
if fpos, ok := g.fallpos(obj.Pos); ok { if fpos, ok := g.fallpos(obj.Pos); ok {
pp := g.Level.At(fpos.Y, fpos.X) pp := g.Level.At(fpos.Y, fpos.X)
pp.Ch = obj.Type pp.Ch = obj.Kind.Glyph()
obj.Pos = fpos obj.Pos = fpos
if g.cansee(fpos.Y, fpos.X) { if g.cansee(fpos.Y, fpos.X) {
if pp.Monst != nil { if pp.Monst != nil {
pp.Monst.OldCh = obj.Type pp.Monst.OldCh = obj.Kind.Glyph()
} else { } else {
g.mvaddch(fpos.Y, fpos.X, obj.Type) g.mvaddch(fpos.Y, fpos.X, obj.Kind.Glyph())
} }
} }
attachObj(&g.Level.Objects, obj) attachObj(&g.Level.Objects, obj)
@@ -98,12 +98,12 @@ func (g *RogueGame) wield() {
return return
} }
p.CurWeapon = oweapon p.CurWeapon = oweapon
obj := g.getItem("wield", int(Weapon)) obj := g.getItem("wield", KindWeapon)
if obj == nil { if obj == nil {
g.After = false g.After = false
return return
} }
if obj.Type == Armor { if obj.Kind == KindArmor {
g.msg("you can't wield armor") g.msg("you can't wield armor")
g.After = false g.After = false
return return
@@ -122,39 +122,39 @@ func (g *RogueGame) wield() {
} }
// initWeaps is the weapons.c init_dam[] table. // initWeaps is the weapons.c init_dam[] table.
var initWeaps = [MaxWeapons]struct { var initWeaps = [NumWeaponTypes]struct {
dam string // damage when wielded dam DiceSpec // damage when wielded
hrl string // damage when thrown hrl DiceSpec // damage when thrown
launch int // launching weapon launch WeaponKind // launching weapon
flags ObjFlags flags ObjFlags
}{ }{
{"2x4", "1x3", noWeapon, 0}, // Mace {dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace
{"3x4", "1x2", noWeapon, 0}, // Long sword {dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword
{"1x1", "1x1", noWeapon, 0}, // Bow {dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow
{"1x1", "2x3", Bow, IsMany | IsMissl}, // Arrow {dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow
{"1x6", "1x4", noWeapon, IsMissl}, // Dagger {dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger
{"4x4", "1x2", noWeapon, 0}, // 2h sword {dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword
{"1x1", "1x3", noWeapon, IsMany | IsMissl}, // Dart {dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart
{"1x2", "2x4", noWeapon, IsMany | IsMissl}, // Shuriken {dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken
{"2x3", "1x6", noWeapon, IsMissl}, // Spear {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 int) { func (g *RogueGame) initWeapon(weap *Object, which WeaponKind) {
iwp := &initWeaps[which] iwp := &initWeaps[which]
weap.Type = Weapon weap.Kind = KindWeapon
weap.Which = which weap.Which = int(which)
weap.Damage = iwp.dam weap.Damage = iwp.dam
weap.HurlDmg = iwp.hrl weap.HurlDmg = iwp.hrl
weap.Launch = iwp.launch weap.Launch = iwp.launch
weap.Flags = iwp.flags weap.Flags = iwp.flags
weap.HPlus = 0 weap.HPlus = 0
weap.DPlus = 0 weap.DPlus = 0
if which == Dagger { if which == WeaponDagger {
weap.Count = g.rnd(4) + 2 weap.Count = g.rnd(4) + 2
weap.Group = g.Items.Group weap.Group = g.Items.Group
g.Items.Group++ g.Items.Group++
} else if weap.Flags.Has(IsMany) { } else if weap.Flags.Has(Stackable) {
weap.Count = g.rnd(8) + 8 weap.Count = g.rnd(8) + 8
weap.Group = g.Items.Group weap.Group = g.Items.Group
g.Items.Group++ g.Items.Group++

View File

@@ -9,9 +9,9 @@ package game
func (g *RogueGame) createObj() { func (g *RogueGame) createObj() {
obj := newObject() obj := newObject()
g.msg("type of item: ") g.msg("type of item: ")
obj.Type = g.readchar() obj.Kind = objectKindForGlyph(g.readchar())
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
g.msg("which %c do you want? (0-f)", obj.Type) g.msg("which %c do you want? (0-f)", obj.Kind.Glyph())
ch := g.readchar() ch := g.readchar()
if isDigit(ch) { if isDigit(ch) {
obj.Which = int(ch - '0') obj.Which = int(ch - '0')
@@ -22,15 +22,15 @@ func (g *RogueGame) createObj() {
obj.Count = 1 obj.Count = 1
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
switch { switch {
case obj.Type == Weapon || obj.Type == Armor: case obj.Kind == KindWeapon || obj.Kind == KindArmor:
g.msg("blessing? (+,-,n)") g.msg("blessing? (+,-,n)")
bless := g.readchar() bless := g.readchar()
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
if bless == '-' { if bless == '-' {
obj.Flags.Set(IsCursed) obj.Flags.Set(Cursed)
} }
if obj.Type == Weapon { if obj.Kind == KindWeapon {
g.initWeapon(obj, obj.Which) g.initWeapon(obj, WeaponKind(obj.Which))
if bless == '-' { if bless == '-' {
obj.HPlus -= g.rnd(3) + 1 obj.HPlus -= g.rnd(3) + 1
} }
@@ -38,36 +38,36 @@ func (g *RogueGame) createObj() {
obj.HPlus += g.rnd(3) + 1 obj.HPlus += g.rnd(3) + 1
} }
} else { } else {
obj.Arm = aClass[obj.Which] obj.ArmorClass = aClass[obj.Which]
if bless == '-' { if bless == '-' {
obj.Arm += g.rnd(3) + 1 obj.ArmorClass += g.rnd(3) + 1
} }
if bless == '+' { if bless == '+' {
obj.Arm -= g.rnd(3) + 1 obj.ArmorClass -= g.rnd(3) + 1
} }
} }
case obj.Type == Ring: case obj.Kind == KindRing:
switch obj.Which { switch obj.RingKind() {
case RProtect, RAddStr, RAddHit, RAddDam: case RingProtection, RingAddStrength, RingDexterity, RingIncreaseDamage:
g.msg("blessing? (+,-,n)") g.msg("blessing? (+,-,n)")
bless := g.readchar() bless := g.readchar()
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
if bless == '-' { if bless == '-' {
obj.Flags.Set(IsCursed) obj.Flags.Set(Cursed)
obj.Arm = -1 obj.Bonus = -1
} else { } else {
obj.Arm = g.rnd(2) + 1 obj.Bonus = g.rnd(2) + 1
} }
case RAggr, RTeleport: case RingAggravateMonsters, RingTeleportation:
obj.Flags.Set(IsCursed) obj.Flags.Set(Cursed)
} }
case obj.Type == Stick: case obj.Kind == KindWand:
g.fixStick(obj) g.fixStick(obj)
case obj.Type == Gold: case obj.Kind == KindGold:
g.msg("how much?") g.msg("how much?")
buf := "" buf := ""
if g.getStr(&buf, g.scr.Std) == Norm { if g.getStr(&buf, g.scr.Std) == Norm {
obj.SetGoldVal(cAtoi(buf)) obj.GoldValue = cAtoi(buf)
} }
} }
g.addPack(obj, false) g.addPack(obj, false)
@@ -93,7 +93,7 @@ func (g *RogueGame) showMap() {
} }
// whatis identifies what a certain object is (wizard.c whatis). // whatis identifies what a certain object is (wizard.c whatis).
func (g *RogueGame) whatis(insist bool, typ int) { func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
p := &g.Player p := &g.Player
if len(p.Pack) == 0 { if len(p.Pack) == 0 {
g.msg("you don't have anything in your pack to identify") g.msg("you don't have anything in your pack to identify")
@@ -102,7 +102,7 @@ func (g *RogueGame) whatis(insist bool, typ int) {
var obj *Object var obj *Object
for { for {
obj = g.getItem("identify", typ) obj = g.getItem("identify", kind)
if !insist { if !insist {
break break
} }
@@ -111,9 +111,10 @@ func (g *RogueGame) whatis(insist bool, typ int) {
} }
if obj == nil { if obj == nil {
g.msg("you must identify something") g.msg("you must identify something")
} else if typ != 0 && int(obj.Type) != typ && } else if kind != KindNone && obj.Kind != kind &&
!(typ == RorS && (obj.Type == Ring || obj.Type == Stick)) { !(kind == KindRingOrStick &&
g.msg("you must identify a %s", typeName(typ)) (obj.Kind == KindRing || obj.Kind == KindWand)) {
g.msg("you must identify a %s", kind)
} else { } else {
break break
} }
@@ -123,16 +124,16 @@ func (g *RogueGame) whatis(insist bool, typ int) {
return return
} }
switch obj.Type { switch obj.Kind {
case Scroll: case KindScroll:
setKnow(obj, g.Items.Scrolls[:]) setKnow(obj, g.Items.Scrolls[:])
case Potion: case KindPotion:
setKnow(obj, g.Items.Potions[:]) setKnow(obj, g.Items.Potions[:])
case Stick: case KindWand:
setKnow(obj, g.Items.Sticks[:]) setKnow(obj, g.Items.Sticks[:])
case Weapon, Armor: case KindWeapon, KindArmor:
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
case Ring: case KindRing:
setKnow(obj, g.Items.Rings[:]) setKnow(obj, g.Items.Rings[:])
} }
g.msg("%s", g.invName(obj, false)) g.msg("%s", g.invName(obj, false))
@@ -142,34 +143,12 @@ func (g *RogueGame) whatis(insist bool, typ int) {
// set_know). // set_know).
func setKnow(obj *Object, info []ObjInfo) { func setKnow(obj *Object, info []ObjInfo) {
info[obj.Which].Know = true info[obj.Which].Know = true
obj.Flags.Set(IsKnow) obj.Flags.Set(Known)
info[obj.Which].Guess = "" info[obj.Which].Guess = ""
} }
// typeNameTable is the wizard.c type_name static tlist. // The C type_name()/tlist table is gone: ObjectKind.String() carries the
var typeNameTable = []struct { // same vocabulary.
ch int
desc string
}{
{int(Potion), "potion"},
{int(Scroll), "scroll"},
{int(Food), "food"},
{RorS, "ring, wand or staff"},
{int(Ring), "ring"},
{int(Stick), "wand or staff"},
{int(Weapon), "weapon"},
{int(Armor), "suit of armor"},
}
// typeName returns the name of an object type (wizard.c type_name).
func typeName(typ int) string {
for _, hp := range typeNameTable {
if typ == hp.ch {
return hp.desc
}
}
return ""
}
// teleport bamfs the hero someplace else (wizard.c teleport). // teleport bamfs the hero someplace else (wizard.c teleport).
func (g *RogueGame) teleport() { func (g *RogueGame) teleport() {
@@ -187,10 +166,10 @@ func (g *RogueGame) teleport() {
g.mvaddch(p.Pos.Y, p.Pos.X, PlayerCh) g.mvaddch(p.Pos.Y, p.Pos.X, PlayerCh)
// turn off ISHELD in case teleportation was done while fighting a // turn off ISHELD in case teleportation was done while fighting a
// Flytrap // Flytrap
if p.On(IsHeld) { if p.On(Held) {
p.Flags.Clear(IsHeld) p.Flags.Clear(Held)
p.VfHit = 0 p.VfHit = 0
g.Monsters['F'-'A'].Stats.Dmg = "000x0" g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
} }
g.NoMove = 0 g.NoMove = 0
g.Count = 0 g.Count = 0

5
go.mod
View File

@@ -1,10 +1,11 @@
module sneak.berlin/go/rogue module git.eeqj.de/sneak/rgoue
go 1.25.7 go 1.25.7
require github.com/gdamore/tcell/v2 v2.13.10
require ( require (
github.com/gdamore/encoding v1.0.1 // indirect github.com/gdamore/encoding v1.0.1 // indirect
github.com/gdamore/tcell/v2 v2.13.10 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/sys v0.38.0 // indirect golang.org/x/sys v0.38.0 // indirect

View File

@@ -11,7 +11,7 @@ import (
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"sneak.berlin/go/rogue/game" "git.eeqj.de/sneak/rgoue/game"
) )
// Tcell renders game Windows on a tcell screen and turns key events into // Tcell renders game Windows on a tcell screen and turns key events into