Compare commits
12 Commits
a49d857970
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| acef593288 | |||
| 0b56ac8019 | |||
| a094f7c6c3 | |||
| 0caaa14198 | |||
| d3ef07cfa7 | |||
| f432c8718c | |||
| ae79fd5e84 | |||
| 6d798c56ed | |||
| 0554f5d4f1 | |||
| 6850c87ae7 | |||
| 65a1cd68b8 | |||
| 525465a68b |
78
TODO.md
78
TODO.md
@@ -29,14 +29,44 @@ Refactor ground rules:
|
|||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Refactor step 4: method renames, movement/world subsystem
|
Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
||||||
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
switches become per-kind handler tables of small named methods,
|
||||||
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
keeping effect order and RNG call sequence identical. This step also
|
||||||
chgStr→changeStrength, ...); remove the goto/label flows in doMove,
|
clears the outstanding cyclop/gocognit/nestif lint findings.
|
||||||
dispatch, and saveGame in favor of loops and helpers.
|
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-07-07 Refactor step 6 (refactor/god-object-extraction):
|
||||||
|
MessageLine (was MsgLine) owns the msg/addmsg/endmsg machinery,
|
||||||
|
wired to its screen/look/input needs via attach(); RogueGame keeps
|
||||||
|
one-line msg/addmsgf/endmsg shorthands so call sites are unchanged.
|
||||||
|
Player owns pack bookkeeping (nextPackChar, removeFromPack — the
|
||||||
|
state half of leave_pack; leavePack keeps only LastPick tracking).
|
||||||
|
Level owns object/monster list management and lookup (ObjectAt
|
||||||
|
replaces findObj; AddObject/RemoveObject/AddMonster/RemoveMonster
|
||||||
|
replace direct attachObj/detachObj/attachMon/detachMon on level
|
||||||
|
lists). Inventory/pickup UI flows stay on RogueGame deliberately:
|
||||||
|
they are display and turn orchestration, not state surgery.
|
||||||
|
|
||||||
|
- 2026-07-07 Refactor step 5 (refactor/item-combat-ui-renames, three
|
||||||
|
commits, one subsystem each): items — getItem→promptPackItem now
|
||||||
|
returning (obj, ok), invName→inventoryName, doPot→applyPotionFuse;
|
||||||
|
combat — rollEm→rollAttacks, attack/moveMonster/chaseStep return
|
||||||
|
(removed bool) instead of C -1/0 int codes; UI —
|
||||||
|
getDir→promptDirection. C breadcrumbs kept; suite green.
|
||||||
|
|
||||||
|
- 2026-07-07 Refactor step 4 (refactor/movement-renames): movement/world
|
||||||
|
renames (doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
|
||||||
|
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze,
|
||||||
|
chgStr→changeStrength, doRun→startRun, moveStuff→finishMove,
|
||||||
|
turnref→turnRefresh, moveMonst→moveMonster, doChase→chaseStep,
|
||||||
|
setOldch→setOldChar, cansee→canSee, roomin→roomIn, runto→runTo,
|
||||||
|
conn→connectRooms, putpass→putPassage, passnum→numberPassages,
|
||||||
|
numpass→numberPassage, rndPos→randomPos, rndRoom→randomRoom,
|
||||||
|
treasRoom→treasureRoom, accntMaze→accountMaze); all goto/label flows
|
||||||
|
replaced with loops (moveHero retry loop + extracted passageTurn,
|
||||||
|
dispatch re-dispatch loop, chaseStep passage loop, saveGame labeled
|
||||||
|
prompt loop); C breadcrumbs kept in doc comments.
|
||||||
- 2026-07-07 Lint adoption finished (refactor/no-package-globals): all
|
- 2026-07-07 Lint adoption finished (refactor/no-package-globals): all
|
||||||
37 package-level vars moved into `gameData` (built by `newGameData`,
|
37 package-level vars moved into `gameData` (built by `newGameData`,
|
||||||
hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind
|
hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind
|
||||||
@@ -98,41 +128,27 @@ dispatch, and saveGame in favor of loops and helpers.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
1. Refactor step 5: method renames, items/combat/UI subsystems
|
1. Refactor step 8: constructor and style pass per the house
|
||||||
(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse,
|
|
||||||
getItem→promptPackItem returning (obj, ok), getDir→promptDirection);
|
|
||||||
int status codes (attack returning -1) become named results. Two or
|
|
||||||
three commits, one subsystem each.
|
|
||||||
2. Refactor step 6: extract types from the god object — MessageLine
|
|
||||||
owns the msg/addmsg/endmsg machinery; pack/inventory operations move
|
|
||||||
onto *Player; monster/object list management and map queries
|
|
||||||
consolidate onto *Level; RogueGame keeps turn orchestration and
|
|
||||||
cross-system effects only.
|
|
||||||
3. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
|
|
||||||
switches become per-kind handler tables of small named methods,
|
|
||||||
keeping effect order and RNG call sequence identical. This step also
|
|
||||||
clears the outstanding cyclop/gocognit/nestif lint findings.
|
|
||||||
4. Refactor step 8: constructor and style pass per the house
|
|
||||||
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
styleguide — game.New(game.Params{...}) replacing NewGame(Config);
|
||||||
replace the gameEnd panic unwind with error-based turn results where
|
replace the gameEnd panic unwind with error-based turn results where
|
||||||
feasible; 77-column wrap sweep.
|
feasible; 77-column wrap sweep.
|
||||||
5. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
|
2. 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.
|
||||||
6. Playtest hardening pass: play several full games with the tcell
|
3. 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.
|
||||||
7. Verify the seed-compatibility claim against the C reference on
|
4. 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.
|
||||||
8. Broaden unit test coverage where playtesting finds thin spots
|
5. Broaden unit test coverage where playtesting finds thin spots
|
||||||
(rings, sticks, wizard commands).
|
(rings, sticks, wizard commands).
|
||||||
9. Tag a release once a full game (Amulet retrieval and score entry)
|
6. Tag a release once a full game (Amulet retrieval and score entry)
|
||||||
completes without defects.
|
completes without defects.
|
||||||
10. Full-terminal-size support (deferred by explicit decision
|
7. 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.
|
||||||
11. Note: this repo is exempt from the standard policy scaffold. Do not
|
8. 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.
|
||||||
|
|||||||
@@ -133,6 +133,6 @@ func chooseSeed(wizard bool) int32 {
|
|||||||
|
|
||||||
// The C game computed `lowtime + getpid()` in int; the truncation to
|
// The C game computed `lowtime + getpid()` in int; the truncation to
|
||||||
// 32 bits is the same wraparound the C int arithmetic performed.
|
// 32 bits is the same wraparound the C int arithmetic performed.
|
||||||
return int32(time.Now().Unix()&0x7fffffff) + //nolint:gosec // G115: deliberate wrap
|
return int32(time.Now().Unix()&0x7fffffff) +
|
||||||
int32(os.Getpid()&0x7fffffff) //nolint:gosec // G115: deliberate wrap
|
int32(os.Getpid()&0x7fffffff)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ package game
|
|||||||
func (g *RogueGame) wear() {
|
func (g *RogueGame) wear() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
obj := g.getItem("wear", KindArmor)
|
obj, ok := g.promptPackItem("wear", KindArmor)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ func (g *RogueGame) wear() {
|
|||||||
|
|
||||||
g.wasteTime()
|
g.wasteTime()
|
||||||
obj.Flags.Set(Known)
|
obj.Flags.Set(Known)
|
||||||
sp := g.invName(obj, true)
|
sp := g.inventoryName(obj, true)
|
||||||
p.CurArmor = obj
|
p.CurArmor = obj
|
||||||
|
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
@@ -70,7 +70,7 @@ func (g *RogueGame) takeOff() {
|
|||||||
g.addmsgf("you used to be")
|
g.addmsgf("you used to be")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg(" wearing %c) %s", obj.PackCh, g.invName(obj, true))
|
g.msg(" wearing %c) %s", obj.PackCh, g.inventoryName(obj, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// wasteTime does nothing but let other things happen (armor.c waste_time).
|
// wasteTime does nothing but let other things happen (armor.c waste_time).
|
||||||
|
|||||||
151
game/chase.go
151
game/chase.go
@@ -13,12 +13,12 @@ func (g *RogueGame) runners(int) {
|
|||||||
origPos := tp.Pos
|
origPos := tp.Pos
|
||||||
|
|
||||||
wastarget := tp.On(Targeted)
|
wastarget := tp.On(Targeted)
|
||||||
if g.moveMonst(tp) == -1 {
|
if removed := g.moveMonster(tp); removed {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 {
|
if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 {
|
||||||
if g.moveMonst(tp) == -1 {
|
if removed := g.moveMonster(tp); removed {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,24 +37,25 @@ func (g *RogueGame) runners(int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveMonst executes a single turn of running for a monster (chase.c
|
// moveMonster 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). The result reports that the monster died or left the
|
||||||
func (g *RogueGame) moveMonst(tp *Monster) int {
|
// level (the C -1 return).
|
||||||
|
func (g *RogueGame) moveMonster(tp *Monster) bool {
|
||||||
if !tp.On(Slowed) || tp.Turn {
|
if !tp.On(Slowed) || tp.Turn {
|
||||||
if g.doChase(tp) == -1 {
|
if g.chaseStep(tp) {
|
||||||
return -1
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tp.On(Hasted) {
|
if tp.On(Hasted) {
|
||||||
if g.doChase(tp) == -1 {
|
if g.chaseStep(tp) {
|
||||||
return -1
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tp.Turn = !tp.Turn
|
tp.Turn = !tp.Turn
|
||||||
|
|
||||||
return 0
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// relocate makes the monster's new location be the specified one, updating
|
// relocate makes the monster's new location be the specified one, updating
|
||||||
@@ -62,8 +63,8 @@ func (g *RogueGame) moveMonst(tp *Monster) int {
|
|||||||
func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
|
func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
|
||||||
if newLoc != th.Pos {
|
if newLoc != th.Pos {
|
||||||
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
|
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
|
||||||
th.Room = g.roomin(newLoc)
|
th.Room = g.roomIn(newLoc)
|
||||||
g.setOldch(th, newLoc)
|
g.setOldChar(th, newLoc)
|
||||||
oroom := th.Room
|
oroom := th.Room
|
||||||
g.Level.SetMonsterAt(th.Pos.Y, th.Pos.X, nil)
|
g.Level.SetMonsterAt(th.Pos.Y, th.Pos.X, nil)
|
||||||
|
|
||||||
@@ -86,9 +87,10 @@ func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// doChase makes one thing chase another (chase.c do_chase). Returns -1 if
|
// chaseStep makes one thing chase another (chase.c do_chase). removed
|
||||||
// the chaser died in the attempt.
|
// reports that the chaser died or left the level in the attempt (the C
|
||||||
func (g *RogueGame) doChase(th *Monster) int {
|
// -1 return).
|
||||||
|
func (g *RogueGame) chaseStep(th *Monster) (removed bool) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
stoprun := false // true means we are there
|
stoprun := false // true means we are there
|
||||||
mindist := 32767
|
mindist := 32767
|
||||||
@@ -102,59 +104,62 @@ func (g *RogueGame) doChase(th *Monster) int {
|
|||||||
if th.Dest == &p.Pos {
|
if th.Dest == &p.Pos {
|
||||||
ree = p.Room
|
ree = p.Room
|
||||||
} else {
|
} else {
|
||||||
ree = g.roomin(*th.Dest)
|
ree = g.roomIn(*th.Dest)
|
||||||
}
|
}
|
||||||
// We don't count doors as inside rooms for this routine
|
// We don't count doors as inside rooms for this routine
|
||||||
door := g.Level.Char(th.Pos.Y, th.Pos.X) == Door
|
door := g.Level.Char(th.Pos.Y, th.Pos.X) == Door
|
||||||
|
|
||||||
var this Coord
|
var this Coord
|
||||||
|
|
||||||
over:
|
for {
|
||||||
// If the object of our desire is in a different room, and we are not
|
// If the object of our desire is in a different room, and we are
|
||||||
// in a corridor, run to the door nearest to our goal.
|
// not in a corridor, run to the door nearest to our goal.
|
||||||
if rer != ree {
|
if rer != ree {
|
||||||
for i := range rer.Exits {
|
for i := range rer.Exits {
|
||||||
curdist := distCp(*th.Dest, rer.Exits[i])
|
curdist := distCp(*th.Dest, rer.Exits[i])
|
||||||
if curdist < mindist {
|
if curdist < mindist {
|
||||||
this = rer.Exits[i]
|
this = rer.Exits[i]
|
||||||
mindist = curdist
|
mindist = curdist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if door {
|
||||||
|
rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum]
|
||||||
|
door = false
|
||||||
|
|
||||||
|
continue // the C goto over: redo with the passage as room
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this = *th.Dest
|
||||||
|
// For dragons check and see if (a) the hero is on a straight
|
||||||
|
// line from it, and (b) that it is within shooting distance,
|
||||||
|
// but outside of striking range.
|
||||||
|
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)) &&
|
||||||
|
distCp(th.Pos, p.Pos) <= BoltLength*BoltLength &&
|
||||||
|
!th.On(Cancelled) && g.rnd(dragonShot) == 0 {
|
||||||
|
g.Delta.Y = sign(p.Pos.Y - th.Pos.Y)
|
||||||
|
|
||||||
|
g.Delta.X = sign(p.Pos.X - th.Pos.X)
|
||||||
|
if g.HasHit {
|
||||||
|
g.endmsg()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.fireBolt(th.Pos, &g.Delta, "flame")
|
||||||
|
g.Running = false
|
||||||
|
g.Count = 0
|
||||||
|
|
||||||
|
g.Quiet = 0
|
||||||
|
if g.ToDeath && !th.On(Targeted) {
|
||||||
|
g.ToDeath = false
|
||||||
|
g.Kamikaze = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if door {
|
break
|
||||||
rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum]
|
|
||||||
door = false
|
|
||||||
|
|
||||||
goto over
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this = *th.Dest
|
|
||||||
// For dragons check and see if (a) the hero is on a straight line
|
|
||||||
// from it, and (b) that it is within shooting distance, but
|
|
||||||
// outside of striking range.
|
|
||||||
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)) &&
|
|
||||||
distCp(th.Pos, p.Pos) <= BoltLength*BoltLength &&
|
|
||||||
!th.On(Cancelled) && g.rnd(dragonShot) == 0 {
|
|
||||||
g.Delta.Y = sign(p.Pos.Y - th.Pos.Y)
|
|
||||||
|
|
||||||
g.Delta.X = sign(p.Pos.X - th.Pos.X)
|
|
||||||
if g.HasHit {
|
|
||||||
g.endmsg()
|
|
||||||
}
|
|
||||||
|
|
||||||
g.fireBolt(th.Pos, &g.Delta, "flame")
|
|
||||||
g.Running = false
|
|
||||||
g.Count = 0
|
|
||||||
|
|
||||||
g.Quiet = 0
|
|
||||||
if g.ToDeath && !th.On(Targeted) {
|
|
||||||
g.ToDeath = false
|
|
||||||
g.Kamikaze = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// This now contains what we want to run to this time so we run to it.
|
// This now contains what we want to run to this time so we run to it.
|
||||||
// If we hit it we either want to fight it or stop running
|
// If we hit it we either want to fight it or stop running
|
||||||
@@ -164,7 +169,7 @@ over:
|
|||||||
} else if this == *th.Dest {
|
} else if this == *th.Dest {
|
||||||
for _, obj := range g.Level.Objects {
|
for _, obj := range g.Level.Objects {
|
||||||
if th.Dest == &obj.Pos {
|
if th.Dest == &obj.Pos {
|
||||||
detachObj(&g.Level.Objects, obj)
|
g.Level.RemoveObject(obj)
|
||||||
attachObj(&th.Pack, obj)
|
attachObj(&th.Pack, obj)
|
||||||
|
|
||||||
if th.Room.Flags.Has(Gone) {
|
if th.Room.Flags.Has(Gone) {
|
||||||
@@ -185,7 +190,7 @@ over:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if th.Type == 'F' {
|
if th.Type == 'F' {
|
||||||
return 0
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +200,7 @@ over:
|
|||||||
th.Flags.Clear(Awake)
|
th.Flags.Clear(Awake)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// chase finds the spot for the chaser to move closer to the chasee
|
// chase finds the spot for the chaser to move closer to the chasee
|
||||||
@@ -214,7 +219,7 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool {
|
|||||||
if (tp.On(Confused) && 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.randomStep(&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 {
|
||||||
@@ -294,8 +299,8 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool {
|
|||||||
return curdist != 0 && g.chRet != p.Pos
|
return curdist != 0 && g.chRet != p.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOldch sets the oldch character for the monster (chase.c set_oldch).
|
// setOldChar sets the oldch character for the monster (chase.c set_oldch).
|
||||||
func (g *RogueGame) setOldch(tp *Monster, cp Coord) {
|
func (g *RogueGame) setOldChar(tp *Monster, cp Coord) {
|
||||||
if tp.Pos == cp {
|
if tp.Pos == cp {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -341,8 +346,8 @@ func (g *RogueGame) seeMonst(mp *Monster) bool {
|
|||||||
return !mp.Room.Flags.Has(Dark)
|
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).
|
||||||
func (g *RogueGame) runto(runner Coord) {
|
func (g *RogueGame) runTo(runner Coord) {
|
||||||
tp := g.Level.MonsterAt(runner.Y, runner.X)
|
tp := g.Level.MonsterAt(runner.Y, runner.X)
|
||||||
if tp == nil {
|
if tp == nil {
|
||||||
return
|
return
|
||||||
@@ -353,9 +358,9 @@ func (g *RogueGame) runto(runner Coord) {
|
|||||||
tp.Dest = g.findDest(tp)
|
tp.Dest = g.findDest(tp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// roomin finds what room some coordinates are in; nil means they aren't in
|
// roomIn finds what room some coordinates are in; nil means they aren't in
|
||||||
// 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(FPassage) {
|
if fp.Has(FPassage) {
|
||||||
return &g.Level.Passages[fp&FPassNum]
|
return &g.Level.Passages[fp&FPassNum]
|
||||||
@@ -387,9 +392,9 @@ func (g *RogueGame) diagOk(sp, ep Coord) bool {
|
|||||||
return stepOk(g.Level.Char(ep.Y, sp.X)) && stepOk(g.Level.Char(sp.Y, ep.X))
|
return stepOk(g.Level.Char(ep.Y, sp.X)) && stepOk(g.Level.Char(sp.Y, ep.X))
|
||||||
}
|
}
|
||||||
|
|
||||||
// cansee returns true if the hero can see a certain coordinate (chase.c
|
// canSee returns true if the hero can see a certain coordinate (chase.c
|
||||||
// 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(Blind) {
|
if p.On(Blind) {
|
||||||
return false
|
return false
|
||||||
@@ -408,7 +413,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(Dark)
|
return rer == p.Room && !rer.Flags.Has(Dark)
|
||||||
}
|
}
|
||||||
@@ -426,7 +431,7 @@ func (g *RogueGame) findDest(tp *Monster) *Coord {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.roomin(obj.Pos) == tp.Room && g.rnd(100) < prob {
|
if g.roomIn(obj.Pos) == tp.Room && g.rnd(100) < prob {
|
||||||
claimed := false
|
claimed := false
|
||||||
|
|
||||||
for _, other := range g.Level.Monsters {
|
for _, other := range g.Level.Monsters {
|
||||||
|
|||||||
507
game/command.go
507
game/command.go
@@ -151,270 +151,273 @@ func (g *RogueGame) command() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch runs one command character (the big switch in command.c,
|
// dispatch runs one command character (the big switch in command.c; the
|
||||||
// including its `goto over` re-dispatch).
|
// loop stands in for its `goto over` re-dispatch).
|
||||||
func (g *RogueGame) dispatch(ch byte) {
|
func (g *RogueGame) dispatch(ch byte) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
over:
|
for {
|
||||||
switch ch {
|
switch ch {
|
||||||
case ',':
|
case ',':
|
||||||
var found *Object
|
var found *Object
|
||||||
|
|
||||||
for _, obj := range g.Level.Objects {
|
for _, obj := range g.Level.Objects {
|
||||||
if obj.Pos.Y == p.Pos.Y && obj.Pos.X == p.Pos.X {
|
if obj.Pos.Y == p.Pos.Y && obj.Pos.X == p.Pos.X {
|
||||||
found = obj
|
found = obj
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found != nil {
|
||||||
|
if !g.levitCheck() {
|
||||||
|
g.pickUp(found.Kind.Glyph())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !g.Options.Terse {
|
||||||
|
g.addmsgf("there is ")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.addmsgf("nothing here")
|
||||||
|
|
||||||
|
if !g.Options.Terse {
|
||||||
|
g.addmsgf(" to pick up")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.endmsg()
|
||||||
|
}
|
||||||
|
case '!':
|
||||||
|
g.shell()
|
||||||
|
case 'h':
|
||||||
|
g.moveHero(0, -1)
|
||||||
|
case 'j':
|
||||||
|
g.moveHero(1, 0)
|
||||||
|
case 'k':
|
||||||
|
g.moveHero(-1, 0)
|
||||||
|
case 'l':
|
||||||
|
g.moveHero(0, 1)
|
||||||
|
case 'y':
|
||||||
|
g.moveHero(-1, -1)
|
||||||
|
case 'u':
|
||||||
|
g.moveHero(-1, 1)
|
||||||
|
case 'b':
|
||||||
|
g.moveHero(1, -1)
|
||||||
|
case 'n':
|
||||||
|
g.moveHero(1, 1)
|
||||||
|
case 'H':
|
||||||
|
g.startRun('h')
|
||||||
|
case 'J':
|
||||||
|
g.startRun('j')
|
||||||
|
case 'K':
|
||||||
|
g.startRun('k')
|
||||||
|
case 'L':
|
||||||
|
g.startRun('l')
|
||||||
|
case 'Y':
|
||||||
|
g.startRun('y')
|
||||||
|
case 'U':
|
||||||
|
g.startRun('u')
|
||||||
|
case 'B':
|
||||||
|
g.startRun('b')
|
||||||
|
case 'N':
|
||||||
|
g.startRun('n')
|
||||||
|
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
|
||||||
|
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
|
||||||
|
if !p.On(Blind) {
|
||||||
|
g.DoorStop = true
|
||||||
|
g.Firstmove = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.Count != 0 && !g.newCount {
|
||||||
|
ch = g.direction
|
||||||
|
} else {
|
||||||
|
ch += 'A' - CTRL('A')
|
||||||
|
g.direction = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
continue // the C goto over: re-dispatch as the run command
|
||||||
|
case 'F', 'f':
|
||||||
|
if ch == 'F' {
|
||||||
|
g.Kamikaze = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !g.promptDirection() {
|
||||||
|
g.After = false
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if found != nil {
|
|
||||||
if !g.levitCheck() {
|
|
||||||
g.pickUp(found.Kind.Glyph())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.addmsgf("there is ")
|
|
||||||
}
|
|
||||||
|
|
||||||
g.addmsgf("nothing here")
|
|
||||||
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.addmsgf(" to pick up")
|
|
||||||
}
|
|
||||||
|
|
||||||
g.endmsg()
|
|
||||||
}
|
|
||||||
case '!':
|
|
||||||
g.shell()
|
|
||||||
case 'h':
|
|
||||||
g.doMove(0, -1)
|
|
||||||
case 'j':
|
|
||||||
g.doMove(1, 0)
|
|
||||||
case 'k':
|
|
||||||
g.doMove(-1, 0)
|
|
||||||
case 'l':
|
|
||||||
g.doMove(0, 1)
|
|
||||||
case 'y':
|
|
||||||
g.doMove(-1, -1)
|
|
||||||
case 'u':
|
|
||||||
g.doMove(-1, 1)
|
|
||||||
case 'b':
|
|
||||||
g.doMove(1, -1)
|
|
||||||
case 'n':
|
|
||||||
g.doMove(1, 1)
|
|
||||||
case 'H':
|
|
||||||
g.doRun('h')
|
|
||||||
case 'J':
|
|
||||||
g.doRun('j')
|
|
||||||
case 'K':
|
|
||||||
g.doRun('k')
|
|
||||||
case 'L':
|
|
||||||
g.doRun('l')
|
|
||||||
case 'Y':
|
|
||||||
g.doRun('y')
|
|
||||||
case 'U':
|
|
||||||
g.doRun('u')
|
|
||||||
case 'B':
|
|
||||||
g.doRun('b')
|
|
||||||
case 'N':
|
|
||||||
g.doRun('n')
|
|
||||||
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
|
|
||||||
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
|
|
||||||
if !p.On(Blind) {
|
|
||||||
g.DoorStop = true
|
|
||||||
g.Firstmove = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if g.Count != 0 && !g.newCount {
|
|
||||||
ch = g.direction
|
|
||||||
} else {
|
|
||||||
ch += 'A' - CTRL('A')
|
|
||||||
g.direction = ch
|
|
||||||
}
|
|
||||||
|
|
||||||
goto over
|
|
||||||
case 'F', 'f':
|
|
||||||
if ch == 'F' {
|
|
||||||
g.Kamikaze = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !g.getDir() {
|
|
||||||
g.After = false
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Delta.Y += p.Pos.Y
|
|
||||||
g.Delta.X += p.Pos.X
|
|
||||||
|
|
||||||
mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X)
|
|
||||||
if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) {
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.addmsgf("I see ")
|
|
||||||
}
|
|
||||||
|
|
||||||
g.msg("no monster there")
|
|
||||||
g.After = false
|
|
||||||
} else if g.diagOk(p.Pos, g.Delta) {
|
|
||||||
g.ToDeath = true
|
|
||||||
g.MaxHit = 0
|
|
||||||
|
|
||||||
mp.Flags.Set(Targeted)
|
|
||||||
|
|
||||||
g.RunCh = g.DirCh
|
|
||||||
ch = g.DirCh
|
|
||||||
|
|
||||||
goto over
|
|
||||||
}
|
|
||||||
case 't':
|
|
||||||
if !g.getDir() {
|
|
||||||
g.After = false
|
|
||||||
} else {
|
|
||||||
g.missile(g.Delta.Y, g.Delta.X)
|
|
||||||
}
|
|
||||||
case 'a':
|
|
||||||
if g.LastComm == 0 {
|
|
||||||
g.msg("you haven't typed a command yet")
|
|
||||||
g.After = false
|
|
||||||
} else {
|
|
||||||
ch = g.LastComm
|
|
||||||
g.Again = true
|
|
||||||
|
|
||||||
goto over
|
|
||||||
}
|
|
||||||
case 'q':
|
|
||||||
g.quaff()
|
|
||||||
case 'Q':
|
|
||||||
g.After = false
|
|
||||||
g.QComm = true
|
|
||||||
g.quit(0)
|
|
||||||
g.QComm = false
|
|
||||||
case 'i':
|
|
||||||
g.After = false
|
|
||||||
g.inventory(p.Pack, 0)
|
|
||||||
case 'I':
|
|
||||||
g.After = false
|
|
||||||
g.pickyInven()
|
|
||||||
case 'd':
|
|
||||||
g.dropIt()
|
|
||||||
case 'r':
|
|
||||||
g.readScroll()
|
|
||||||
case 'e':
|
|
||||||
g.eat()
|
|
||||||
case 'w':
|
|
||||||
g.wield()
|
|
||||||
case 'W':
|
|
||||||
g.wear()
|
|
||||||
case 'T':
|
|
||||||
g.takeOff()
|
|
||||||
case 'P':
|
|
||||||
g.ringOn()
|
|
||||||
case 'R':
|
|
||||||
g.ringOff()
|
|
||||||
case 'o':
|
|
||||||
g.option()
|
|
||||||
g.After = false
|
|
||||||
case 'c':
|
|
||||||
g.call()
|
|
||||||
g.After = false
|
|
||||||
case '>':
|
|
||||||
g.After = false
|
|
||||||
g.dLevel()
|
|
||||||
case '<':
|
|
||||||
g.After = false
|
|
||||||
g.uLevel()
|
|
||||||
case '?':
|
|
||||||
g.After = false
|
|
||||||
g.help()
|
|
||||||
case '/':
|
|
||||||
g.After = false
|
|
||||||
g.identify()
|
|
||||||
case 's':
|
|
||||||
g.search()
|
|
||||||
case 'z':
|
|
||||||
if g.getDir() {
|
|
||||||
g.doZap()
|
|
||||||
} else {
|
|
||||||
g.After = false
|
|
||||||
}
|
|
||||||
case 'D':
|
|
||||||
g.After = false
|
|
||||||
g.discovered()
|
|
||||||
case CTRL('P'):
|
|
||||||
g.After = false
|
|
||||||
g.msg("%s", g.Msgs.Huh)
|
|
||||||
case CTRL('R'):
|
|
||||||
g.After = false
|
|
||||||
g.refresh()
|
|
||||||
case 'v':
|
|
||||||
g.After = false
|
|
||||||
g.msg("version %s. (mctesq was here)", Release)
|
|
||||||
case 'S':
|
|
||||||
g.After = false
|
|
||||||
g.saveGame()
|
|
||||||
case '.':
|
|
||||||
// rest command
|
|
||||||
case ' ':
|
|
||||||
g.After = false // "legal" illegal command
|
|
||||||
case '^':
|
|
||||||
g.After = false
|
|
||||||
if g.getDir() {
|
|
||||||
g.Delta.Y += p.Pos.Y
|
g.Delta.Y += p.Pos.Y
|
||||||
g.Delta.X += p.Pos.X
|
g.Delta.X += p.Pos.X
|
||||||
|
|
||||||
fp := g.Level.FlagsAt(g.Delta.Y, g.Delta.X)
|
mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X)
|
||||||
if !g.Options.Terse {
|
if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) {
|
||||||
g.addmsgf("You have found ")
|
if !g.Options.Terse {
|
||||||
}
|
g.addmsgf("I see ")
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
g.msg("no monster there")
|
||||||
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
|
g.After = false
|
||||||
g.msg("no trap there")
|
} else if g.diagOk(p.Pos, g.Delta) {
|
||||||
case p.On(Hallucinating):
|
g.ToDeath = true
|
||||||
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
g.MaxHit = 0
|
||||||
default:
|
|
||||||
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
mp.Flags.Set(Targeted)
|
||||||
fp.Set(FSeen)
|
|
||||||
|
g.RunCh = g.DirCh
|
||||||
|
ch = g.DirCh
|
||||||
|
|
||||||
|
continue // the C goto over: fight by running at it
|
||||||
}
|
}
|
||||||
}
|
case 't':
|
||||||
case Escape: // escape
|
if !g.promptDirection() {
|
||||||
g.DoorStop = false
|
g.After = false
|
||||||
g.Count = 0
|
} else {
|
||||||
g.After = false
|
g.missile(g.Delta.Y, g.Delta.X)
|
||||||
g.Again = false
|
}
|
||||||
case 'm':
|
case 'a':
|
||||||
g.MoveOn = true
|
if g.LastComm == 0 {
|
||||||
if !g.getDir() {
|
g.msg("you haven't typed a command yet")
|
||||||
|
g.After = false
|
||||||
|
} else {
|
||||||
|
ch = g.LastComm
|
||||||
|
g.Again = true
|
||||||
|
|
||||||
|
continue // the C goto over: replay the last command
|
||||||
|
}
|
||||||
|
case 'q':
|
||||||
|
g.quaff()
|
||||||
|
case 'Q':
|
||||||
g.After = false
|
g.After = false
|
||||||
} else {
|
g.QComm = true
|
||||||
ch = g.DirCh
|
g.quit(0)
|
||||||
g.countCh = g.DirCh
|
g.QComm = false
|
||||||
|
case 'i':
|
||||||
|
g.After = false
|
||||||
|
g.inventory(p.Pack, 0)
|
||||||
|
case 'I':
|
||||||
|
g.After = false
|
||||||
|
g.pickyInven()
|
||||||
|
case 'd':
|
||||||
|
g.dropIt()
|
||||||
|
case 'r':
|
||||||
|
g.readScroll()
|
||||||
|
case 'e':
|
||||||
|
g.eat()
|
||||||
|
case 'w':
|
||||||
|
g.wield()
|
||||||
|
case 'W':
|
||||||
|
g.wear()
|
||||||
|
case 'T':
|
||||||
|
g.takeOff()
|
||||||
|
case 'P':
|
||||||
|
g.ringOn()
|
||||||
|
case 'R':
|
||||||
|
g.ringOff()
|
||||||
|
case 'o':
|
||||||
|
g.option()
|
||||||
|
g.After = false
|
||||||
|
case 'c':
|
||||||
|
g.call()
|
||||||
|
g.After = false
|
||||||
|
case '>':
|
||||||
|
g.After = false
|
||||||
|
g.dLevel()
|
||||||
|
case '<':
|
||||||
|
g.After = false
|
||||||
|
g.uLevel()
|
||||||
|
case '?':
|
||||||
|
g.After = false
|
||||||
|
g.help()
|
||||||
|
case '/':
|
||||||
|
g.After = false
|
||||||
|
g.identify()
|
||||||
|
case 's':
|
||||||
|
g.search()
|
||||||
|
case 'z':
|
||||||
|
if g.promptDirection() {
|
||||||
|
g.doZap()
|
||||||
|
} else {
|
||||||
|
g.After = false
|
||||||
|
}
|
||||||
|
case 'D':
|
||||||
|
g.After = false
|
||||||
|
g.discovered()
|
||||||
|
case CTRL('P'):
|
||||||
|
g.After = false
|
||||||
|
g.msg("%s", g.Msgs.Huh)
|
||||||
|
case CTRL('R'):
|
||||||
|
g.After = false
|
||||||
|
g.refresh()
|
||||||
|
case 'v':
|
||||||
|
g.After = false
|
||||||
|
g.msg("version %s. (mctesq was here)", Release)
|
||||||
|
case 'S':
|
||||||
|
g.After = false
|
||||||
|
g.saveGame()
|
||||||
|
case '.':
|
||||||
|
// rest command
|
||||||
|
case ' ':
|
||||||
|
g.After = false // "legal" illegal command
|
||||||
|
case '^':
|
||||||
|
g.After = false
|
||||||
|
if g.promptDirection() {
|
||||||
|
g.Delta.Y += p.Pos.Y
|
||||||
|
g.Delta.X += p.Pos.X
|
||||||
|
|
||||||
goto over
|
fp := g.Level.FlagsAt(g.Delta.Y, g.Delta.X)
|
||||||
}
|
if !g.Options.Terse {
|
||||||
case ')':
|
g.addmsgf("You have found ")
|
||||||
g.current(p.CurWeapon, "wielding", "")
|
}
|
||||||
case ']':
|
|
||||||
g.current(p.CurArmor, "wearing", "")
|
switch {
|
||||||
case '=':
|
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
|
||||||
g.current(p.CurRing[Left], "wearing",
|
g.msg("no trap there")
|
||||||
g.chooseTerse("(L)", "on left hand"))
|
case p.On(Hallucinating):
|
||||||
g.current(p.CurRing[Right], "wearing",
|
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
||||||
g.chooseTerse("(R)", "on right hand"))
|
default:
|
||||||
case '@':
|
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||||
g.StatMsg = true
|
fp.Set(FSeen)
|
||||||
g.status()
|
}
|
||||||
g.StatMsg = false
|
}
|
||||||
g.After = false
|
case Escape: // escape
|
||||||
default:
|
g.DoorStop = false
|
||||||
g.After = false
|
g.Count = 0
|
||||||
if g.Wizard {
|
g.After = false
|
||||||
g.wizardCommand(ch)
|
g.Again = false
|
||||||
} else {
|
case 'm':
|
||||||
g.illcom(ch)
|
g.MoveOn = true
|
||||||
|
if !g.promptDirection() {
|
||||||
|
g.After = false
|
||||||
|
} else {
|
||||||
|
ch = g.DirCh
|
||||||
|
g.countCh = g.DirCh
|
||||||
|
|
||||||
|
continue // the C goto over: move onto it without pickup
|
||||||
|
}
|
||||||
|
case ')':
|
||||||
|
g.current(p.CurWeapon, "wielding", "")
|
||||||
|
case ']':
|
||||||
|
g.current(p.CurArmor, "wearing", "")
|
||||||
|
case '=':
|
||||||
|
g.current(p.CurRing[Left], "wearing",
|
||||||
|
g.chooseTerse("(L)", "on left hand"))
|
||||||
|
g.current(p.CurRing[Right], "wearing",
|
||||||
|
g.chooseTerse("(R)", "on right hand"))
|
||||||
|
case '@':
|
||||||
|
g.StatMsg = true
|
||||||
|
g.status()
|
||||||
|
g.StatMsg = false
|
||||||
|
g.After = false
|
||||||
|
default:
|
||||||
|
g.After = false
|
||||||
|
if g.Wizard {
|
||||||
|
g.wizardCommand(ch)
|
||||||
|
} else {
|
||||||
|
g.illcom(ch)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +453,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
|||||||
case CTRL('X'):
|
case CTRL('X'):
|
||||||
g.turnSee(p.On(SenseMonsters))
|
g.turnSee(p.On(SenseMonsters))
|
||||||
case CTRL('~'):
|
case CTRL('~'):
|
||||||
if item := g.getItem("charge", KindWand); item != nil {
|
if item, ok := g.promptPackItem("charge", KindWand); ok {
|
||||||
item.Charges = 10000
|
item.Charges = 10000
|
||||||
}
|
}
|
||||||
case CTRL('I'):
|
case CTRL('I'):
|
||||||
@@ -735,9 +738,9 @@ 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", KindCallable)
|
obj, ok := g.promptPackItem("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 !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,7 +820,7 @@ func (g *RogueGame) current(cur *Object, how, where string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.InvDescribe = false
|
g.InvDescribe = false
|
||||||
g.addmsgf("%c) %s", cur.PackCh, g.invName(cur, true))
|
g.addmsgf("%c) %s", cur.PackCh, g.inventoryName(cur, true))
|
||||||
|
|
||||||
g.InvDescribe = true
|
g.InvDescribe = true
|
||||||
if where != "" {
|
if where != "" {
|
||||||
|
|||||||
@@ -50,6 +50,47 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nextPackChar claims and returns the next unused pack character (pack.c
|
||||||
|
// pack_char).
|
||||||
|
func (p *Player) nextPackChar() byte {
|
||||||
|
for i := range p.PackUsed {
|
||||||
|
if !p.PackUsed[i] {
|
||||||
|
p.PackUsed[i] = true
|
||||||
|
|
||||||
|
return byte(i) + 'a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte(len(p.PackUsed)) + 'a' // C would walk off the array here
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeFromPack takes an item out of the pack: the whole entry, or one
|
||||||
|
// of a stack when all is false (the bookkeeping half of pack.c
|
||||||
|
// leave_pack). It returns the object that left the pack — a copy when
|
||||||
|
// newobj asks for a split.
|
||||||
|
func (p *Player) removeFromPack(obj *Object, newobj, all bool) *Object {
|
||||||
|
p.Inpack--
|
||||||
|
|
||||||
|
nobj := obj
|
||||||
|
if obj.Count > 1 && !all {
|
||||||
|
obj.Count--
|
||||||
|
if obj.Group != 0 {
|
||||||
|
p.Inpack++
|
||||||
|
}
|
||||||
|
|
||||||
|
if newobj {
|
||||||
|
copied := *obj
|
||||||
|
nobj = &copied
|
||||||
|
nobj.Count = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.PackUsed[obj.PackCh-'a'] = false
|
||||||
|
detachObj(&p.Pack, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nobj
|
||||||
|
}
|
||||||
|
|
||||||
// attachMon pushes a monster onto the front of a list (list.c attach).
|
// attachMon pushes a monster onto the front of a list (list.c attach).
|
||||||
func attachMon(list *[]*Monster, item *Monster) {
|
func attachMon(list *[]*Monster, item *Monster) {
|
||||||
*list = append([]*Monster{item}, *list...)
|
*list = append([]*Monster{item}, *list...)
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ func (g *RogueGame) comeDown(int) {
|
|||||||
|
|
||||||
// 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.Kind.Glyph())
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Kind.Glyph())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ func (g *RogueGame) comeDown(int) {
|
|||||||
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(Invisible) || p.On(CanSeeInvisible) {
|
if !tp.On(Invisible) || p.On(CanSeeInvisible) {
|
||||||
g.addch(tp.Disguise)
|
g.addch(tp.Disguise)
|
||||||
} else {
|
} else {
|
||||||
@@ -248,13 +248,13 @@ func (g *RogueGame) visuals(int) {
|
|||||||
}
|
}
|
||||||
// change the things
|
// change 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, g.rndThing())
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, g.rndThing())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the stairs
|
// change the stairs
|
||||||
if !g.SeenStairs && g.cansee(g.Level.Stairs.Y, g.Level.Stairs.X) {
|
if !g.SeenStairs && g.canSee(g.Level.Stairs.Y, g.Level.Stairs.X) {
|
||||||
g.mvaddch(g.Level.Stairs.Y, g.Level.Stairs.X, g.rndThing())
|
g.mvaddch(g.Level.Stairs.Y, g.Level.Stairs.X, g.rndThing())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ func mkGameInput(t *testing.T) *RogueGame {
|
|||||||
g := NewGame(Config{Seed: 5, Term: &testTerm{}})
|
g := NewGame(Config{Seed: 5, Term: &testTerm{}})
|
||||||
g.NewLevel()
|
g.NewLevel()
|
||||||
g.Oldpos = g.Player.Pos
|
g.Oldpos = g.Player.Pos
|
||||||
g.Oldrp = g.roomin(g.Player.Pos)
|
g.Oldrp = g.roomIn(g.Player.Pos)
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
|
|||||||
// place.
|
// place.
|
||||||
g.Count = 0
|
g.Count = 0
|
||||||
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(Blind) {
|
if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) {
|
||||||
tp.Disguise = 'X'
|
tp.Disguise = 'X'
|
||||||
@@ -67,7 +67,7 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
|
|||||||
didHit := false
|
didHit := false
|
||||||
|
|
||||||
g.HasHit = g.Options.Terse && !g.ToDeath
|
g.HasHit = g.Options.Terse && !g.ToDeath
|
||||||
if g.rollEm(&p.Creature, &tp.Creature, weap, thrown) {
|
if g.rollAttacks(&p.Creature, &tp.Creature, weap, thrown) {
|
||||||
didHit = false
|
didHit = false
|
||||||
|
|
||||||
if thrown {
|
if thrown {
|
||||||
@@ -104,9 +104,10 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
|
|||||||
return didHit
|
return didHit
|
||||||
}
|
}
|
||||||
|
|
||||||
// attack has the monster attack the player (fight.c attack). Returns -1 if
|
// attack has the monster attack the player (fight.c attack). removed
|
||||||
// the monster removed itself from the level during its own attack.
|
// reports that the monster took itself off the level during its own
|
||||||
func (g *RogueGame) attack(mp *Monster) int {
|
// attack (the C -1 return).
|
||||||
|
func (g *RogueGame) attack(mp *Monster) (removed bool) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
// Since this is an attack, stop running and any healing that was
|
// Since this is an attack, stop running and any healing that was
|
||||||
// going on at the time.
|
// going on at the time.
|
||||||
@@ -128,9 +129,8 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
|
|
||||||
mname := g.setMname(mp)
|
mname := g.setMname(mp)
|
||||||
oldhp := p.Stats.HP
|
oldhp := p.Stats.HP
|
||||||
removed := false
|
|
||||||
|
|
||||||
if g.rollEm(&mp.Creature, &p.Creature, nil, false) {
|
if g.rollAttacks(&mp.Creature, &p.Creature, nil, false) {
|
||||||
if mp.Type != 'I' {
|
if mp.Type != 'I' {
|
||||||
if g.HasHit {
|
if g.HasHit {
|
||||||
g.addmsgf(". ")
|
g.addmsgf(". ")
|
||||||
@@ -182,7 +182,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
// Rattlesnakes have poisonous bites
|
// Rattlesnakes have poisonous bites
|
||||||
if !g.save(VsPoison) {
|
if !g.save(VsPoison) {
|
||||||
if !p.IsWearing(RingSustainStrength) {
|
if !p.IsWearing(RingSustainStrength) {
|
||||||
g.chgStr(-1)
|
g.changeStrength(-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")
|
||||||
@@ -290,7 +290,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
removed = true
|
removed = true
|
||||||
|
|
||||||
g.leavePack(steal, false, false)
|
g.leavePack(steal, false, false)
|
||||||
g.msg("she stole %s!", g.invName(steal, true))
|
g.msg("she stole %s!", g.inventoryName(steal, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,11 +317,7 @@ func (g *RogueGame) attack(mp *Monster) int {
|
|||||||
g.Count = 0
|
g.Count = 0
|
||||||
g.status()
|
g.status()
|
||||||
|
|
||||||
if removed {
|
return removed
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swing returns true if the swing hits (fight.c swing).
|
// swing returns true if the swing hits (fight.c swing).
|
||||||
@@ -332,8 +328,8 @@ func (g *RogueGame) swing(atLvl, opArm, wplus int) bool {
|
|||||||
return res+wplus >= need
|
return res+wplus >= need
|
||||||
}
|
}
|
||||||
|
|
||||||
// rollEm rolls several attacks (fight.c roll_em).
|
// rollAttacks rolls several attacks (fight.c roll_em).
|
||||||
func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool {
|
func (g *RogueGame) rollAttacks(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
|
||||||
@@ -553,7 +549,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)
|
g.Level.RemoveMonster(tp)
|
||||||
|
|
||||||
if tp.On(Targeted) {
|
if tp.On(Targeted) {
|
||||||
g.Kamikaze = false
|
g.Kamikaze = false
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ func mkGame(t *testing.T, seed int32) *RogueGame {
|
|||||||
g := NewGame(Config{Seed: seed, Term: &testTerm{}})
|
g := NewGame(Config{Seed: seed, Term: &testTerm{}})
|
||||||
g.NewLevel()
|
g.NewLevel()
|
||||||
g.Oldpos = g.Player.Pos
|
g.Oldpos = g.Player.Pos
|
||||||
g.Oldrp = g.roomin(g.Player.Pos)
|
g.Oldrp = g.roomIn(g.Player.Pos)
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ func TestRollEmParsesMultiAttackDice(t *testing.T) {
|
|||||||
// 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.
|
||||||
if !g.rollEm(att, def, nil, false) {
|
if !g.rollAttacks(att, def, nil, false) {
|
||||||
t.Fatal("attack with guaranteed swing missed")
|
t.Fatal("attack with guaranteed swing missed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ type RogueGame struct {
|
|||||||
|
|
||||||
// screen / messages
|
// screen / messages
|
||||||
scr *Screen
|
scr *Screen
|
||||||
Msgs MsgLine
|
Msgs MessageLine
|
||||||
statusCache statusCache
|
statusCache statusCache
|
||||||
invPage invPage // things.c discovery-list pagination statics
|
invPage invPage // things.c discovery-list pagination statics
|
||||||
|
|
||||||
@@ -169,6 +169,7 @@ func NewGame(cfg Config) *RogueGame {
|
|||||||
g.InvDescribe = true
|
g.InvDescribe = true
|
||||||
g.Msgs.SaveMsg = true
|
g.Msgs.SaveMsg = true
|
||||||
g.scr = NewScreen(cfg.Term)
|
g.scr = NewScreen(cfg.Term)
|
||||||
|
g.Msgs.attach(g.scr, g.look, g.readchar)
|
||||||
g.FileName = cfg.Home + "/rogue.save"
|
g.FileName = cfg.Home + "/rogue.save"
|
||||||
|
|
||||||
g.rogueOpts = cfg.RogueOpts
|
g.rogueOpts = cfg.RogueOpts
|
||||||
@@ -242,7 +243,7 @@ func (g *RogueGame) playit() {
|
|||||||
|
|
||||||
g.Oldpos = g.Player.Pos
|
g.Oldpos = g.Player.Pos
|
||||||
|
|
||||||
g.Oldrp = g.roomin(g.Player.Pos)
|
g.Oldrp = g.roomIn(g.Player.Pos)
|
||||||
for g.Playing {
|
for g.Playing {
|
||||||
g.command() // command execution
|
g.command() // command execution
|
||||||
}
|
}
|
||||||
|
|||||||
93
game/io.go
93
game/io.go
@@ -10,9 +10,11 @@ import (
|
|||||||
// maxMsg is io.c MAXMSG: how much message fits before --More--.
|
// maxMsg is io.c MAXMSG: how much message fits before --More--.
|
||||||
const maxMsg = NumCols - len("--More--") - 1
|
const maxMsg = NumCols - len("--More--") - 1
|
||||||
|
|
||||||
// MsgLine is the io.c message machinery: the static msgbuf/newpos pair plus
|
// MessageLine is the io.c message machinery: the static msgbuf/newpos
|
||||||
// the related globals (mpos, huh, and the message-behavior flags).
|
// pair plus the related globals (mpos, huh, and the message-behavior
|
||||||
type MsgLine struct {
|
// flags). It owns the top line of the screen; attach wires in the
|
||||||
|
// display and input it needs.
|
||||||
|
type MessageLine struct {
|
||||||
buf strings.Builder // msgbuf
|
buf strings.Builder // msgbuf
|
||||||
newpos int
|
newpos int
|
||||||
Mpos int // where cursor is on top line
|
Mpos int // where cursor is on top line
|
||||||
@@ -20,49 +22,52 @@ type MsgLine struct {
|
|||||||
SaveMsg bool // remember last msg
|
SaveMsg bool // remember last msg
|
||||||
LowerMsg bool // messages should start w/lower case
|
LowerMsg bool // messages should start w/lower case
|
||||||
MsgEsc bool // check for ESC from msg's --More--
|
MsgEsc bool // check for ESC from msg's --More--
|
||||||
|
|
||||||
|
scr *Screen // the top line lives on scr.Std
|
||||||
|
look func(wakeup bool) // redraw before a --More-- (misc.c look)
|
||||||
|
readChar func() byte // input for --More-- prompts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Msg displays a message at the top of the screen (io.c msg). It returns
|
// Msg displays a message at the top of the screen (io.c msg). It returns
|
||||||
// Escape if the player escaped out of a --More--, ^Escape otherwise (the C
|
// Escape if the player escaped out of a --More--, ^Escape otherwise (the
|
||||||
// convention: callers compare against ESCAPE).
|
// C convention: callers compare against ESCAPE).
|
||||||
func (g *RogueGame) msg(format string, a ...any) int {
|
func (m *MessageLine) Msg(format string, a ...any) int {
|
||||||
// if the string is "", just clear the line
|
// if the string is "", just clear the line
|
||||||
if format == "" {
|
if format == "" {
|
||||||
g.move(0, 0)
|
m.scr.Std.Move(0, 0)
|
||||||
g.clrtoeol()
|
m.scr.Std.Clrtoeol()
|
||||||
g.Msgs.Mpos = 0
|
m.Mpos = 0
|
||||||
|
|
||||||
return ^Escape
|
return ^Escape
|
||||||
}
|
}
|
||||||
// otherwise add to the message and flush it out
|
// otherwise add to the message and flush it out
|
||||||
g.doaddf(format, a...)
|
m.doaddf(format, a...)
|
||||||
|
|
||||||
return g.endmsg()
|
return m.End()
|
||||||
}
|
}
|
||||||
|
|
||||||
// addmsgf adds things to the current message (io.c addmsg).
|
// Addf adds things to the current message (io.c addmsg).
|
||||||
func (g *RogueGame) addmsgf(format string, a ...any) {
|
func (m *MessageLine) Addf(format string, a ...any) {
|
||||||
g.doaddf(format, a...)
|
m.doaddf(format, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endmsg displays a new msg, giving the player a chance to see the previous
|
// End displays a new msg, giving the player a chance to see the previous
|
||||||
// one if it is up there with the --More-- (io.c endmsg).
|
// one if it is up there with the --More-- (io.c endmsg).
|
||||||
func (g *RogueGame) endmsg() int {
|
func (m *MessageLine) End() int {
|
||||||
m := &g.Msgs
|
|
||||||
if m.SaveMsg {
|
if m.SaveMsg {
|
||||||
m.Huh = m.buf.String()
|
m.Huh = m.buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Mpos != 0 {
|
if m.Mpos != 0 {
|
||||||
g.look(false)
|
m.look(false)
|
||||||
g.mvaddstr(0, m.Mpos, "--More--")
|
m.scr.Std.MvAddStr(0, m.Mpos, "--More--")
|
||||||
g.refresh()
|
m.scr.Refresh()
|
||||||
|
|
||||||
if !m.MsgEsc {
|
if !m.MsgEsc {
|
||||||
g.waitFor(' ')
|
m.waitForSpace()
|
||||||
} else {
|
} else {
|
||||||
for {
|
for {
|
||||||
ch := g.readchar()
|
ch := m.readChar()
|
||||||
if ch == ' ' {
|
if ch == ' ' {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -85,30 +90,60 @@ func (g *RogueGame) endmsg() int {
|
|||||||
out = string(toUpper(out[0])) + out[1:]
|
out = string(toUpper(out[0])) + out[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
g.mvaddstr(0, 0, out)
|
m.scr.Std.MvAddStr(0, 0, out)
|
||||||
g.clrtoeol()
|
m.scr.Std.Clrtoeol()
|
||||||
|
|
||||||
m.Mpos = m.newpos
|
m.Mpos = m.newpos
|
||||||
m.newpos = 0
|
m.newpos = 0
|
||||||
m.buf.Reset()
|
m.buf.Reset()
|
||||||
g.refresh()
|
m.scr.Refresh()
|
||||||
|
|
||||||
return ^Escape
|
return ^Escape
|
||||||
}
|
}
|
||||||
|
|
||||||
// doaddf performs an add onto the message buffer (io.c doadd).
|
// attach wires the message line to its display and input; NewGame and
|
||||||
func (g *RogueGame) doaddf(format string, a ...any) {
|
// Restore call it once the screen and game exist.
|
||||||
m := &g.Msgs
|
func (m *MessageLine) attach(scr *Screen, look func(bool), readChar func() byte) {
|
||||||
|
m.scr = scr
|
||||||
|
m.look = look
|
||||||
|
m.readChar = readChar
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForSpace absorbs input until the player types a space: the
|
||||||
|
// --More-- acknowledgement (io.c wait_for).
|
||||||
|
func (m *MessageLine) waitForSpace() {
|
||||||
|
for {
|
||||||
|
if m.readChar() == ' ' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doaddf performs an add onto the message buffer (io.c doadd).
|
||||||
|
func (m *MessageLine) doaddf(format string, a ...any) {
|
||||||
s := fmt.Sprintf(format, a...)
|
s := fmt.Sprintf(format, a...)
|
||||||
if len(s)+m.newpos >= maxMsg {
|
if len(s)+m.newpos >= maxMsg {
|
||||||
g.endmsg()
|
m.End()
|
||||||
}
|
}
|
||||||
|
|
||||||
m.buf.WriteString(s)
|
m.buf.WriteString(s)
|
||||||
m.newpos = m.buf.Len()
|
m.newpos = m.buf.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// msg, addmsgf, and endmsg are the game-side shorthands for the message
|
||||||
|
// line; the machinery lives on MessageLine.
|
||||||
|
func (g *RogueGame) msg(format string, a ...any) int {
|
||||||
|
return g.Msgs.Msg(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) addmsgf(format string, a ...any) {
|
||||||
|
g.Msgs.Addf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) endmsg() {
|
||||||
|
g.Msgs.End()
|
||||||
|
}
|
||||||
|
|
||||||
// stepOk returns true if it is ok to step on ch (io.c step_ok).
|
// stepOk returns true if it is ok to step on ch (io.c step_ok).
|
||||||
func stepOk(ch byte) bool {
|
func stepOk(ch byte) bool {
|
||||||
switch ch {
|
switch ch {
|
||||||
|
|||||||
@@ -51,6 +51,29 @@ func (l *Level) VisibleChar(y, x int) byte {
|
|||||||
return l.Char(y, x)
|
return l.Char(y, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ObjectAt finds the unclaimed object at (y, x) (misc.c find_obj).
|
||||||
|
func (l *Level) ObjectAt(y, x int) *Object {
|
||||||
|
for _, obj := range l.Objects {
|
||||||
|
if obj.Pos.Y == y && obj.Pos.X == x {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddObject puts an object on the level (list.c attach on lvl_obj).
|
||||||
|
func (l *Level) AddObject(obj *Object) { attachObj(&l.Objects, obj) }
|
||||||
|
|
||||||
|
// RemoveObject takes an object off the level (list.c detach on lvl_obj).
|
||||||
|
func (l *Level) RemoveObject(obj *Object) { detachObj(&l.Objects, obj) }
|
||||||
|
|
||||||
|
// AddMonster puts a monster on the level (list.c attach on mlist).
|
||||||
|
func (l *Level) AddMonster(m *Monster) { attachMon(&l.Monsters, m) }
|
||||||
|
|
||||||
|
// RemoveMonster takes a monster off the level (list.c detach on mlist).
|
||||||
|
func (l *Level) RemoveMonster(m *Monster) { detachMon(&l.Monsters, m) }
|
||||||
|
|
||||||
// goldCalc is the GOLDCALC macro: how much a gold pile is worth at depth.
|
// goldCalc is the GOLDCALC macro: how much a gold pile is worth at depth.
|
||||||
func (g *RogueGame) goldCalc() int {
|
func (g *RogueGame) goldCalc() int {
|
||||||
return g.rnd(50+10*g.Depth) + 2
|
return g.rnd(50+10*g.Depth) + 2
|
||||||
|
|||||||
31
game/misc.go
31
game/misc.go
@@ -223,21 +223,10 @@ func (g *RogueGame) showFloor() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// findObj finds the unclaimed object at (y, x) (misc.c find_obj).
|
|
||||||
func (g *RogueGame) findObj(y, x int) *Object {
|
|
||||||
for _, obj := range g.Level.Objects {
|
|
||||||
if obj.Pos.Y == y && obj.Pos.X == x {
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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", KindFood)
|
obj, ok := g.promptPackItem("eat", KindFood)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,9 +294,9 @@ func (g *RogueGame) checkLevel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// chgStr modifies the player's strength, keeping track of the highest it
|
// changeStrength modifies the player's strength, keeping track of the
|
||||||
// has been (misc.c chg_str).
|
// highest it has been (misc.c chg_str).
|
||||||
func (g *RogueGame) chgStr(amt int) {
|
func (g *RogueGame) changeStrength(amt int) {
|
||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -362,10 +351,10 @@ func (g *RogueGame) addHaste(potion bool) bool {
|
|||||||
|
|
||||||
// aggravate aggravates all the monsters on this level (misc.c aggravate).
|
// aggravate aggravates all the monsters on this level (misc.c aggravate).
|
||||||
func (g *RogueGame) aggravate() {
|
func (g *RogueGame) aggravate() {
|
||||||
// runto() can splice the monster list while we walk it, so iterate a copy.
|
// runTo() can splice the monster list while we walk it, so iterate a copy.
|
||||||
monsters := append([]*Monster(nil), g.Level.Monsters...)
|
monsters := append([]*Monster(nil), g.Level.Monsters...)
|
||||||
for _, mp := range monsters {
|
for _, mp := range monsters {
|
||||||
g.runto(mp.Pos)
|
g.runTo(mp.Pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,9 +395,9 @@ func (g *RogueGame) isCurrent(obj *Object) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDir sets up the direction coordinate for use in various "prefix"
|
// promptDirection sets up the direction coordinate for use in various
|
||||||
// commands (misc.c get_dir).
|
// "prefix" commands (misc.c get_dir).
|
||||||
func (g *RogueGame) getDir() bool {
|
func (g *RogueGame) promptDirection() bool {
|
||||||
if g.Again && g.LastDir != 0 {
|
if g.Again && g.LastDir != 0 {
|
||||||
g.Delta = g.lastDelt
|
g.Delta = g.lastDelt
|
||||||
g.DirCh = g.LastDir
|
g.DirCh = g.LastDir
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ func (g *RogueGame) randMonster(wander bool) byte {
|
|||||||
func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
|
func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
|
||||||
levAdd := max(g.Depth-AmuletLevel, 0)
|
levAdd := max(g.Depth-AmuletLevel, 0)
|
||||||
|
|
||||||
attachMon(&g.Level.Monsters, tp)
|
g.Level.AddMonster(tp)
|
||||||
tp.Type = typ
|
tp.Type = typ
|
||||||
tp.Disguise = typ
|
tp.Disguise = typ
|
||||||
tp.Pos = cp
|
tp.Pos = cp
|
||||||
g.move(cp.Y, cp.X)
|
g.move(cp.Y, cp.X)
|
||||||
tp.OldCh = g.inch()
|
tp.OldCh = g.inch()
|
||||||
tp.Room = g.roomin(cp)
|
tp.Room = g.roomIn(cp)
|
||||||
g.Level.SetMonsterAt(cp.Y, cp.X, tp)
|
g.Level.SetMonsterAt(cp.Y, cp.X, tp)
|
||||||
mp := &g.Monsters[tp.Type-'A']
|
mp := &g.Monsters[tp.Type-'A']
|
||||||
tp.Stats.Lvl = mp.Stats.Lvl + levAdd
|
tp.Stats.Lvl = mp.Stats.Lvl + levAdd
|
||||||
@@ -57,7 +57,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
|
|||||||
tp.Pack = nil
|
tp.Pack = nil
|
||||||
|
|
||||||
if g.Player.IsWearing(RingAggravateMonsters) {
|
if g.Player.IsWearing(RingAggravateMonsters) {
|
||||||
g.runto(cp)
|
g.runTo(cp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == 'X' {
|
if typ == 'X' {
|
||||||
@@ -92,7 +92,7 @@ func (g *RogueGame) wanderer() {
|
|||||||
var cp Coord
|
var cp Coord
|
||||||
for {
|
for {
|
||||||
cp, _ = g.findFloor(true)
|
cp, _ = g.findFloor(true)
|
||||||
if g.roomin(cp) != g.Player.Room {
|
if g.roomIn(cp) != g.Player.Room {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ func (g *RogueGame) wanderer() {
|
|||||||
g.standend()
|
g.standend()
|
||||||
}
|
}
|
||||||
|
|
||||||
g.runto(tp.Pos)
|
g.runTo(tp.Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wakeMonster is what to do when the hero steps next to a monster
|
// wakeMonster is what to do when the hero steps next to a monster
|
||||||
|
|||||||
289
game/move.go
289
game/move.go
@@ -2,16 +2,17 @@ package game
|
|||||||
|
|
||||||
// move.c — hero movement commands.
|
// move.c — hero movement commands.
|
||||||
|
|
||||||
// doRun starts the hero running (move.c do_run).
|
// startRun starts the hero running (move.c do_run).
|
||||||
func (g *RogueGame) doRun(ch byte) {
|
func (g *RogueGame) startRun(ch byte) {
|
||||||
g.Running = true
|
g.Running = true
|
||||||
g.After = false
|
g.After = false
|
||||||
g.RunCh = ch
|
g.RunCh = ch
|
||||||
}
|
}
|
||||||
|
|
||||||
// doMove checks that a move is legal and handles the consequences —
|
// moveHero checks that a move is legal and handles the consequences —
|
||||||
// fighting, picking up, etc. (move.c do_move).
|
// fighting, picking up, etc. (move.c do_move). The C `goto over`
|
||||||
func (g *RogueGame) doMove(dy, dx int) {
|
// re-check after a passage turn is the retry loop.
|
||||||
|
func (g *RogueGame) moveHero(dy, dx int) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
g.Firstmove = false
|
g.Firstmove = false
|
||||||
@@ -24,7 +25,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(Confused) && g.rnd(5) != 0 {
|
if p.On(Confused) && g.rnd(5) != 0 {
|
||||||
nh = g.rndmove(&p.Creature)
|
nh = g.randomStep(&p.Creature)
|
||||||
if nh == p.Pos {
|
if nh == p.Pos {
|
||||||
g.After = false
|
g.After = false
|
||||||
g.Running = false
|
g.Running = false
|
||||||
@@ -36,150 +37,161 @@ func (g *RogueGame) doMove(dy, dx int) {
|
|||||||
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
|
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
|
||||||
}
|
}
|
||||||
|
|
||||||
over:
|
for {
|
||||||
// Check if he tried to move off the screen or make an illegal diagonal
|
// Check if he tried to move off the screen or make an illegal
|
||||||
// move, and stop him if he did.
|
// diagonal move, and stop him if he did.
|
||||||
hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1
|
hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ch byte
|
ch byte
|
||||||
fl PlaceFlags
|
fl PlaceFlags
|
||||||
)
|
)
|
||||||
|
|
||||||
if !hitBound {
|
if !hitBound {
|
||||||
if !g.diagOk(p.Pos, nh) {
|
if !g.diagOk(p.Pos, nh) {
|
||||||
g.After = false
|
g.After = false
|
||||||
g.Running = false
|
g.Running = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
if g.Running && p.Pos == nh {
|
|
||||||
g.After = false
|
|
||||||
g.Running = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fl = *g.Level.FlagsAt(nh.Y, nh.X)
|
|
||||||
|
|
||||||
ch = g.Level.VisibleChar(nh.Y, nh.X)
|
|
||||||
if !fl.Has(FReal) && ch == Floor {
|
|
||||||
if !p.On(Levitating) {
|
|
||||||
ch = Trap
|
|
||||||
g.Level.SetChar(nh.Y, nh.X, Trap)
|
|
||||||
g.Level.FlagsAt(nh.Y, nh.X).Set(FReal)
|
|
||||||
}
|
}
|
||||||
} else if p.On(Held) && ch != 'F' {
|
|
||||||
g.msg("you are being held")
|
|
||||||
|
|
||||||
return
|
if g.Running && p.Pos == nh {
|
||||||
}
|
g.After = false
|
||||||
}
|
g.Running = false
|
||||||
|
}
|
||||||
|
|
||||||
if hitBound {
|
fl = *g.Level.FlagsAt(nh.Y, nh.X)
|
||||||
ch = ' ' // fall into the wall case below
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ch {
|
ch = g.Level.VisibleChar(nh.Y, nh.X)
|
||||||
case ' ', '|', '-':
|
if !fl.Has(FReal) && ch == Floor {
|
||||||
if g.Options.PassGo && g.Running && p.Room.Flags.Has(Gone) &&
|
if !p.On(Levitating) {
|
||||||
!p.On(Blind) {
|
ch = Trap
|
||||||
var b1, b2 bool
|
g.Level.SetChar(nh.Y, nh.X, Trap)
|
||||||
|
g.Level.FlagsAt(nh.Y, nh.X).Set(FReal)
|
||||||
switch g.RunCh {
|
|
||||||
case 'h', 'l':
|
|
||||||
b1 = p.Pos.Y != 1 && g.turnOk(p.Pos.Y-1, p.Pos.X)
|
|
||||||
|
|
||||||
b2 = p.Pos.Y != NumLines-2 && g.turnOk(p.Pos.Y+1, p.Pos.X)
|
|
||||||
if b1 != b2 {
|
|
||||||
if b1 {
|
|
||||||
g.RunCh = 'k'
|
|
||||||
dy = -1
|
|
||||||
} else {
|
|
||||||
g.RunCh = 'j'
|
|
||||||
dy = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
dx = 0
|
|
||||||
|
|
||||||
g.turnref()
|
|
||||||
|
|
||||||
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
|
|
||||||
|
|
||||||
goto over
|
|
||||||
}
|
}
|
||||||
case 'j', 'k':
|
} else if p.On(Held) && ch != 'F' {
|
||||||
b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1)
|
g.msg("you are being held")
|
||||||
|
|
||||||
b2 = p.Pos.X != NumCols-1 && g.turnOk(p.Pos.Y, p.Pos.X+1)
|
return
|
||||||
if b1 != b2 {
|
}
|
||||||
if b1 {
|
}
|
||||||
g.RunCh = 'h'
|
|
||||||
dx = -1
|
|
||||||
} else {
|
|
||||||
g.RunCh = 'l'
|
|
||||||
dx = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
dy = 0
|
if hitBound {
|
||||||
|
ch = ' ' // fall into the wall case below
|
||||||
|
}
|
||||||
|
|
||||||
g.turnref()
|
switch ch {
|
||||||
|
case ' ', '|', '-':
|
||||||
|
if turn, ndy, ndx := g.passageTurn(dy, dx); turn {
|
||||||
|
dy, dx = ndy, ndx
|
||||||
|
|
||||||
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
|
g.turnRefresh()
|
||||||
|
|
||||||
goto over
|
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
|
||||||
|
|
||||||
|
continue // the C goto over: re-check the turned move
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Running = false
|
||||||
|
g.After = false
|
||||||
|
case Door:
|
||||||
|
g.Running = false
|
||||||
|
if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPassage) {
|
||||||
|
g.enterRoom(nh)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.finishMove(nh, fl)
|
||||||
|
case Trap:
|
||||||
|
tr := g.springTrap(nh)
|
||||||
|
if tr == TrapDoor || tr == TrapTeleport {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.finishMove(nh, fl)
|
||||||
|
case Passage:
|
||||||
|
// when you're in a corridor, you don't know if you're in a maze
|
||||||
|
// room or not, and there ain't no way to find out if you're
|
||||||
|
// leaving a maze room, so it is necessary to always recalculate
|
||||||
|
// proom.
|
||||||
|
p.Room = g.roomIn(p.Pos)
|
||||||
|
g.finishMove(nh, fl)
|
||||||
|
case Floor:
|
||||||
|
if !fl.Has(FReal) {
|
||||||
|
g.springTrap(p.Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.finishMove(nh, fl)
|
||||||
|
default:
|
||||||
|
if ch == Stairs {
|
||||||
|
g.SeenStairs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Running = false
|
||||||
|
if isUpper(ch) || g.Level.MonsterAt(nh.Y, nh.X) != nil {
|
||||||
|
g.fight(nh, p.CurWeapon, false)
|
||||||
|
} else {
|
||||||
|
if ch != Stairs {
|
||||||
|
g.Take = ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.finishMove(nh, fl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Running = false
|
return
|
||||||
g.After = false
|
|
||||||
case Door:
|
|
||||||
g.Running = false
|
|
||||||
if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPassage) {
|
|
||||||
g.enterRoom(nh)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.moveStuff(nh, fl)
|
|
||||||
case Trap:
|
|
||||||
tr := g.beTrapped(nh)
|
|
||||||
if tr == TrapDoor || tr == TrapTeleport {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
g.moveStuff(nh, fl)
|
|
||||||
case Passage:
|
|
||||||
// when you're in a corridor, you don't know if you're in a maze
|
|
||||||
// room or not, and there ain't no way to find out if you're
|
|
||||||
// leaving a maze room, so it is necessary to always recalculate
|
|
||||||
// proom.
|
|
||||||
p.Room = g.roomin(p.Pos)
|
|
||||||
g.moveStuff(nh, fl)
|
|
||||||
case Floor:
|
|
||||||
if !fl.Has(FReal) {
|
|
||||||
g.beTrapped(p.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.moveStuff(nh, fl)
|
|
||||||
default:
|
|
||||||
if ch == Stairs {
|
|
||||||
g.SeenStairs = true
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Running = false
|
|
||||||
if isUpper(ch) || g.Level.MonsterAt(nh.Y, nh.X) != nil {
|
|
||||||
g.fight(nh, p.CurWeapon, false)
|
|
||||||
} else {
|
|
||||||
if ch != Stairs {
|
|
||||||
g.Take = ch
|
|
||||||
}
|
|
||||||
|
|
||||||
g.moveStuff(nh, fl)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveStuff is the move_stuff label in do_move: complete the step.
|
// passageTurn checks whether a runner in a gone-room passage should turn
|
||||||
func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) {
|
// the corner instead of stopping at a wall (the PASSGO block of move.c
|
||||||
|
// do_move). It reports whether to turn and the new deltas, updating RunCh.
|
||||||
|
func (g *RogueGame) passageTurn(dy, dx int) (bool, int, int) {
|
||||||
|
p := &g.Player
|
||||||
|
if !g.Options.PassGo || !g.Running || !p.Room.Flags.Has(Gone) ||
|
||||||
|
p.On(Blind) {
|
||||||
|
return false, dy, dx
|
||||||
|
}
|
||||||
|
|
||||||
|
var b1, b2 bool
|
||||||
|
|
||||||
|
switch g.RunCh {
|
||||||
|
case 'h', 'l':
|
||||||
|
b1 = p.Pos.Y != 1 && g.turnOk(p.Pos.Y-1, p.Pos.X)
|
||||||
|
|
||||||
|
b2 = p.Pos.Y != NumLines-2 && g.turnOk(p.Pos.Y+1, p.Pos.X)
|
||||||
|
if b1 != b2 {
|
||||||
|
if b1 {
|
||||||
|
g.RunCh = 'k'
|
||||||
|
dy = -1
|
||||||
|
} else {
|
||||||
|
g.RunCh = 'j'
|
||||||
|
dy = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, dy, 0
|
||||||
|
}
|
||||||
|
case 'j', 'k':
|
||||||
|
b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1)
|
||||||
|
|
||||||
|
b2 = p.Pos.X != NumCols-1 && g.turnOk(p.Pos.Y, p.Pos.X+1)
|
||||||
|
if b1 != b2 {
|
||||||
|
if b1 {
|
||||||
|
g.RunCh = 'h'
|
||||||
|
dx = -1
|
||||||
|
} else {
|
||||||
|
g.RunCh = 'l'
|
||||||
|
dx = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, 0, dx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, dy, dx
|
||||||
|
}
|
||||||
|
|
||||||
|
// finishMove is the move_stuff label in do_move: complete the step.
|
||||||
|
func (g *RogueGame) finishMove(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())
|
||||||
|
|
||||||
@@ -198,8 +210,9 @@ func (g *RogueGame) turnOk(y, x int) bool {
|
|||||||
return pp.Ch == Door || pp.Flags&(FReal|FPassage) == (FReal|FPassage)
|
return pp.Ch == Door || pp.Flags&(FReal|FPassage) == (FReal|FPassage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// turnref decides whether to refresh at a passage turning (move.c turnref).
|
// turnRefresh decides whether to refresh at a passage turning (move.c
|
||||||
func (g *RogueGame) turnref() {
|
// turnref).
|
||||||
|
func (g *RogueGame) turnRefresh() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
pp := g.Level.At(p.Pos.Y, p.Pos.X)
|
pp := g.Level.At(p.Pos.Y, p.Pos.X)
|
||||||
@@ -228,8 +241,8 @@ func (g *RogueGame) doorOpen(rp *Room) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// beTrapped makes him pay for stepping on a trap (move.c be_trapped).
|
// springTrap makes him pay for stepping on a trap (move.c be_trapped).
|
||||||
func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
func (g *RogueGame) springTrap(tc Coord) TrapKind {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
if p.On(Levitating) {
|
if p.On(Levitating) {
|
||||||
return TrapRust // anything that's not a door or teleport
|
return TrapRust // anything that's not a door or teleport
|
||||||
@@ -313,7 +326,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !p.IsWearing(RingSustainStrength) && !g.save(VsPoison) {
|
if !p.IsWearing(RingSustainStrength) && !g.save(VsPoison) {
|
||||||
g.chgStr(-1)
|
g.changeStrength(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("a small dart just hit you in the shoulder")
|
g.msg("a small dart just hit you in the shoulder")
|
||||||
@@ -328,9 +341,9 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
|
|||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
// rndmove moves in a random direction if the monster/person is confused
|
// randomStep moves in a random direction if the monster/person is
|
||||||
// (move.c rndmove).
|
// confused (move.c rndmove).
|
||||||
func (g *RogueGame) rndmove(who *Creature) Coord {
|
func (g *RogueGame) randomStep(who *Creature) Coord {
|
||||||
ret := Coord{
|
ret := Coord{
|
||||||
Y: who.Pos.Y + g.rnd(3) - 1,
|
Y: who.Pos.Y + g.rnd(3) - 1,
|
||||||
X: who.Pos.X + g.rnd(3) - 1,
|
X: who.Pos.X + g.rnd(3) - 1,
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ func (g *RogueGame) NewLevel() {
|
|||||||
// go with them (the garbage collector is our free_list).
|
// go with them (the garbage collector is our free_list).
|
||||||
g.Level.Monsters = nil
|
g.Level.Monsters = nil
|
||||||
g.Level.Objects = nil
|
g.Level.Objects = nil
|
||||||
g.doRooms() // Draw rooms
|
g.digRooms() // Draw rooms
|
||||||
g.doPassages() // Draw passages
|
g.digPassages() // Draw passages
|
||||||
|
|
||||||
p.NoFood++
|
p.NoFood++
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ func (g *RogueGame) NewLevel() {
|
|||||||
g.SeenStairs = false
|
g.SeenStairs = false
|
||||||
|
|
||||||
for _, tp := range g.Level.Monsters {
|
for _, tp := range g.Level.Monsters {
|
||||||
tp.Room = g.roomin(tp.Pos)
|
tp.Room = g.roomIn(tp.Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
hero, _ := g.findFloor(true)
|
hero, _ := g.findFloor(true)
|
||||||
@@ -78,8 +78,8 @@ func (g *RogueGame) NewLevel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rndRoom picks a room that is really there (new_level.c rnd_room).
|
// randomRoom picks a room that is really there (new_level.c rnd_room).
|
||||||
func (g *RogueGame) rndRoom() int {
|
func (g *RogueGame) randomRoom() int {
|
||||||
for {
|
for {
|
||||||
rm := g.rnd(MaxRooms)
|
rm := g.rnd(MaxRooms)
|
||||||
if !g.Level.Rooms[rm].Flags.Has(Gone) {
|
if !g.Level.Rooms[rm].Flags.Has(Gone) {
|
||||||
@@ -98,14 +98,14 @@ func (g *RogueGame) putThings() {
|
|||||||
}
|
}
|
||||||
// check for treasure rooms, and if so, put it in.
|
// check for treasure rooms, and if so, put it in.
|
||||||
if g.rnd(treasRoomChance) == 0 {
|
if g.rnd(treasRoomChance) == 0 {
|
||||||
g.treasRoom()
|
g.treasureRoom()
|
||||||
}
|
}
|
||||||
// Do MAXOBJ attempts to put things on a level
|
// Do MAXOBJ attempts to put things on a level
|
||||||
for range MaxObj {
|
for range MaxObj {
|
||||||
if g.rnd(100) < 36 {
|
if g.rnd(100) < 36 {
|
||||||
// Pick a new object and link it in the list
|
// Pick a new object and link it in the list
|
||||||
obj := g.newThing()
|
obj := g.newThing()
|
||||||
attachObj(&g.Level.Objects, obj)
|
g.Level.AddObject(obj)
|
||||||
// Put it somewhere
|
// Put it somewhere
|
||||||
obj.Pos, _ = g.findFloor(false)
|
obj.Pos, _ = g.findFloor(false)
|
||||||
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
|
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
|
||||||
@@ -115,7 +115,7 @@ func (g *RogueGame) putThings() {
|
|||||||
// yet, put it somewhere on the ground
|
// yet, put it somewhere on the ground
|
||||||
if g.Depth >= AmuletLevel && !g.HasAmulet {
|
if g.Depth >= AmuletLevel && !g.HasAmulet {
|
||||||
obj := newObject()
|
obj := newObject()
|
||||||
attachObj(&g.Level.Objects, obj)
|
g.Level.AddObject(obj)
|
||||||
obj.Damage = dice("0x0")
|
obj.Damage = dice("0x0")
|
||||||
obj.HurlDmg = dice("0x0")
|
obj.HurlDmg = dice("0x0")
|
||||||
obj.ArmorClass = 11
|
obj.ArmorClass = 11
|
||||||
@@ -126,9 +126,9 @@ func (g *RogueGame) putThings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// treasRoom adds a treasure room (new_level.c treas_room).
|
// treasureRoom adds a treasure room (new_level.c treas_room).
|
||||||
func (g *RogueGame) treasRoom() {
|
func (g *RogueGame) treasureRoom() {
|
||||||
rp := &g.Level.Rooms[g.rndRoom()]
|
rp := &g.Level.Rooms[g.randomRoom()]
|
||||||
|
|
||||||
spots := min((rp.Max.Y-2)*(rp.Max.X-2)-minTreas, maxTreas-minTreas)
|
spots := min((rp.Max.Y-2)*(rp.Max.X-2)-minTreas, maxTreas-minTreas)
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ func (g *RogueGame) treasRoom() {
|
|||||||
mp, _ := g.findFloorIn(rp, 2*maxTries, false)
|
mp, _ := g.findFloorIn(rp, 2*maxTries, false)
|
||||||
tp := g.newThing()
|
tp := g.newThing()
|
||||||
tp.Pos = mp
|
tp.Pos = mp
|
||||||
attachObj(&g.Level.Objects, tp)
|
g.Level.AddObject(tp)
|
||||||
g.Level.SetChar(mp.Y, mp.X, tp.Kind.Glyph())
|
g.Level.SetChar(mp.Y, mp.X, tp.Kind.Glyph())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
game/pack.go
78
game/pack.go
@@ -9,7 +9,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
|
|||||||
fromFloor := false
|
fromFloor := false
|
||||||
|
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
if obj = g.findObj(p.Pos.Y, p.Pos.X); obj == nil {
|
if obj = g.Level.ObjectAt(p.Pos.Y, p.Pos.X); obj == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ 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.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) {
|
if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) {
|
||||||
detachObj(&g.Level.Objects, obj)
|
g.Level.RemoveObject(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(Gone) {
|
if p.Room.Flags.Has(Gone) {
|
||||||
@@ -34,7 +34,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
|
|||||||
|
|
||||||
if len(p.Pack) == 0 {
|
if len(p.Pack) == 0 {
|
||||||
p.Pack = append(p.Pack, obj)
|
p.Pack = append(p.Pack, obj)
|
||||||
obj.PackCh = g.packChar()
|
obj.PackCh = p.nextPackChar()
|
||||||
p.Inpack++
|
p.Inpack++
|
||||||
} else {
|
} else {
|
||||||
// Walk the pack looking for the insertion point, keeping items of
|
// Walk the pack looking for the insertion point, keeping items of
|
||||||
@@ -112,7 +112,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.PackCh = g.packChar()
|
obj.PackCh = p.nextPackChar()
|
||||||
p.Pack = append(p.Pack[:lp+1],
|
p.Pack = append(p.Pack[:lp+1],
|
||||||
append([]*Object{obj}, p.Pack[lp+1:]...)...)
|
append([]*Object{obj}, p.Pack[lp+1:]...)...)
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
|
|||||||
g.addmsgf("you now have ")
|
g.addmsgf("you now have ")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("%s (%c)", g.invName(obj, !g.Options.Terse), obj.PackCh)
|
g.msg("%s (%c)", g.inventoryName(obj, !g.Options.Terse), obj.PackCh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ func (g *RogueGame) packRoom(fromFloor bool, obj *Object) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fromFloor {
|
if fromFloor {
|
||||||
detachObj(&g.Level.Objects, obj)
|
g.Level.RemoveObject(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(Gone) {
|
if p.Room.Flags.Has(Gone) {
|
||||||
@@ -181,46 +181,17 @@ func (g *RogueGame) packRoom(fromFloor bool, obj *Object) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// leavePack takes an item out of the pack (pack.c leave_pack).
|
// leavePack takes an item out of the pack (pack.c leave_pack), keeping
|
||||||
|
// the repeat-command bookkeeping; the pack surgery is
|
||||||
|
// Player.removeFromPack.
|
||||||
func (g *RogueGame) leavePack(obj *Object, newobj, all bool) *Object {
|
func (g *RogueGame) leavePack(obj *Object, newobj, all bool) *Object {
|
||||||
p := &g.Player
|
|
||||||
p.Inpack--
|
|
||||||
|
|
||||||
nobj := obj
|
|
||||||
if obj.Count > 1 && !all {
|
if obj.Count > 1 && !all {
|
||||||
g.LastPick = obj
|
g.LastPick = obj
|
||||||
|
|
||||||
obj.Count--
|
|
||||||
if obj.Group != 0 {
|
|
||||||
p.Inpack++
|
|
||||||
}
|
|
||||||
|
|
||||||
if newobj {
|
|
||||||
copied := *obj
|
|
||||||
nobj = &copied
|
|
||||||
nobj.Count = 1
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
g.LastPick = nil
|
g.LastPick = nil
|
||||||
p.PackUsed[obj.PackCh-'a'] = false
|
|
||||||
detachObj(&p.Pack, obj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nobj
|
return g.Player.removeFromPack(obj, newobj, all)
|
||||||
}
|
|
||||||
|
|
||||||
// packChar returns the next unused pack character (pack.c pack_char).
|
|
||||||
func (g *RogueGame) packChar() byte {
|
|
||||||
p := &g.Player
|
|
||||||
for i := range p.PackUsed {
|
|
||||||
if !p.PackUsed[i] {
|
|
||||||
p.PackUsed[i] = true
|
|
||||||
|
|
||||||
return byte(i) + 'a'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return byte(len(p.PackUsed)) + 'a' // C would walk off the array here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -236,7 +207,7 @@ func (g *RogueGame) inventory(list []*Object, kind ObjectKind) bool {
|
|||||||
g.NObjs++
|
g.NObjs++
|
||||||
g.Msgs.MsgEsc = true
|
g.Msgs.MsgEsc = true
|
||||||
|
|
||||||
line := string(item.PackCh) + ") " + g.invName(item, false)
|
line := string(item.PackCh) + ") " + g.inventoryName(item, false)
|
||||||
if g.addLine("%s", line) == Escape {
|
if g.addLine("%s", line) == Escape {
|
||||||
g.Msgs.MsgEsc = false
|
g.Msgs.MsgEsc = false
|
||||||
g.msg("")
|
g.msg("")
|
||||||
@@ -277,7 +248,7 @@ func (g *RogueGame) pickUp(ch byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := g.findObj(p.Pos.Y, p.Pos.X)
|
obj := g.Level.ObjectAt(p.Pos.Y, p.Pos.X)
|
||||||
if g.MoveOn {
|
if g.MoveOn {
|
||||||
g.moveMsg(obj)
|
g.moveMsg(obj)
|
||||||
|
|
||||||
@@ -291,7 +262,7 @@ func (g *RogueGame) pickUp(ch byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.money(obj.GoldValue)
|
g.money(obj.GoldValue)
|
||||||
detachObj(&g.Level.Objects, obj)
|
g.Level.RemoveObject(obj)
|
||||||
|
|
||||||
p.Room.GoldVal = 0
|
p.Room.GoldVal = 0
|
||||||
default:
|
default:
|
||||||
@@ -323,7 +294,7 @@ func (g *RogueGame) moveMsg(obj *Object) {
|
|||||||
g.addmsgf("you ")
|
g.addmsgf("you ")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("moved onto %s", g.invName(obj, true))
|
g.msg("moved onto %s", g.inventoryName(obj, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// pickyInven allows the player to inventory a single item (pack.c
|
// pickyInven allows the player to inventory a single item (pack.c
|
||||||
@@ -337,7 +308,7 @@ func (g *RogueGame) pickyInven() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(p.Pack) == 1 {
|
if len(p.Pack) == 1 {
|
||||||
g.msg("a) %s", g.invName(p.Pack[0], false))
|
g.msg("a) %s", g.inventoryName(p.Pack[0], false))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -354,7 +325,7 @@ func (g *RogueGame) pickyInven() {
|
|||||||
|
|
||||||
for _, obj := range p.Pack {
|
for _, obj := range p.Pack {
|
||||||
if mch == obj.PackCh {
|
if mch == obj.PackCh {
|
||||||
g.msg("%c) %s", mch, g.invName(obj, false))
|
g.msg("%c) %s", mch, g.inventoryName(obj, false))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -363,23 +334,24 @@ func (g *RogueGame) pickyInven() {
|
|||||||
g.msg("'%s' not in pack", unctrl(mch))
|
g.msg("'%s' not in pack", unctrl(mch))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getItem picks something out of a pack for a purpose (pack.c get_item).
|
// promptPackItem picks something out of a pack for a purpose (pack.c
|
||||||
func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
|
// get_item); ok reports whether the player chose an item.
|
||||||
|
func (g *RogueGame) promptPackItem(purpose string, kind ObjectKind) (obj *Object, ok bool) {
|
||||||
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")
|
||||||
|
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.Again {
|
if g.Again {
|
||||||
if g.LastPick != nil {
|
if g.LastPick != nil {
|
||||||
return g.LastPick
|
return g.LastPick, true
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("you ran out")
|
g.msg("you ran out")
|
||||||
|
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -402,7 +374,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
|
|||||||
g.After = false
|
g.After = false
|
||||||
g.msg("")
|
g.msg("")
|
||||||
|
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
g.NObjs = 1 // normal case: person types one char
|
g.NObjs = 1 // normal case: person types one char
|
||||||
@@ -411,7 +383,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
|
|||||||
if !g.inventory(p.Pack, kind) {
|
if !g.inventory(p.Pack, kind) {
|
||||||
g.After = false
|
g.After = false
|
||||||
|
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@@ -419,7 +391,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
|
|||||||
|
|
||||||
for _, obj := range p.Pack {
|
for _, obj := range p.Pack {
|
||||||
if obj.PackCh == ch {
|
if obj.PackCh == ch {
|
||||||
return obj
|
return obj, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package game
|
|||||||
|
|
||||||
// passages.c — draw the connecting passages.
|
// passages.c — draw the connecting passages.
|
||||||
|
|
||||||
// doPassages draws all the passages on a level (passages.c do_passages).
|
// digPassages draws all the passages on a level (passages.c do_passages).
|
||||||
func (g *RogueGame) doPassages() {
|
func (g *RogueGame) digPassages() {
|
||||||
var (
|
var (
|
||||||
isconn [MaxRooms][MaxRooms]bool
|
isconn [MaxRooms][MaxRooms]bool
|
||||||
ingraph [MaxRooms]bool
|
ingraph [MaxRooms]bool
|
||||||
@@ -41,7 +41,7 @@ func (g *RogueGame) doPassages() {
|
|||||||
// otherwise, connect new room to the graph, and draw a tunnel
|
// otherwise, connect new room to the graph, and draw a tunnel
|
||||||
// to it
|
// to it
|
||||||
ingraph[r2] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
ingraph[r2] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
||||||
g.conn(r1, r2)
|
g.connectRooms(r1, r2)
|
||||||
isconn[r1][r2] = true
|
isconn[r1][r2] = true
|
||||||
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
||||||
roomcount++
|
roomcount++
|
||||||
@@ -69,18 +69,18 @@ func (g *RogueGame) doPassages() {
|
|||||||
}
|
}
|
||||||
// if there is one, connect it and look for the next added passage
|
// if there is one, connect it and look for the next added passage
|
||||||
if j != 0 {
|
if j != 0 {
|
||||||
g.conn(r1, r2)
|
g.connectRooms(r1, r2)
|
||||||
isconn[r1][r2] = true
|
isconn[r1][r2] = true
|
||||||
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.passnum()
|
g.numberPassages()
|
||||||
}
|
}
|
||||||
|
|
||||||
// conn draws a corridor from a room in a certain direction (passages.c
|
// connectRooms draws a corridor from a room in a certain direction
|
||||||
// conn).
|
// (passages.c conn).
|
||||||
func (g *RogueGame) conn(r1, r2 int) {
|
func (g *RogueGame) connectRooms(r1, r2 int) {
|
||||||
var (
|
var (
|
||||||
rm int
|
rm int
|
||||||
direc byte
|
direc byte
|
||||||
@@ -193,13 +193,13 @@ func (g *RogueGame) conn(r1, r2 int) {
|
|||||||
if !rpf.Flags.Has(Gone) {
|
if !rpf.Flags.Has(Gone) {
|
||||||
g.door(rpf, spos)
|
g.door(rpf, spos)
|
||||||
} else {
|
} else {
|
||||||
g.putpass(spos)
|
g.putPassage(spos)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rpt.Flags.Has(Gone) {
|
if !rpt.Flags.Has(Gone) {
|
||||||
g.door(rpt, epos)
|
g.door(rpt, epos)
|
||||||
} else {
|
} else {
|
||||||
g.putpass(epos)
|
g.putPassage(epos)
|
||||||
}
|
}
|
||||||
// Get ready to move...
|
// Get ready to move...
|
||||||
curr := spos
|
curr := spos
|
||||||
@@ -210,13 +210,13 @@ func (g *RogueGame) conn(r1, r2 int) {
|
|||||||
// Check if we are at the turn place, if so do the turn
|
// Check if we are at the turn place, if so do the turn
|
||||||
if distance == turnSpot {
|
if distance == turnSpot {
|
||||||
for ; turnDistance > 0; turnDistance-- {
|
for ; turnDistance > 0; turnDistance-- {
|
||||||
g.putpass(curr)
|
g.putPassage(curr)
|
||||||
curr.X += turnDelta.X
|
curr.X += turnDelta.X
|
||||||
curr.Y += turnDelta.Y
|
curr.Y += turnDelta.Y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Continue digging along
|
// Continue digging along
|
||||||
g.putpass(curr)
|
g.putPassage(curr)
|
||||||
|
|
||||||
distance--
|
distance--
|
||||||
}
|
}
|
||||||
@@ -229,9 +229,9 @@ func (g *RogueGame) conn(r1, r2 int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// putpass adds a passage character or secret passage here (passages.c
|
// putPassage adds a passage character or secret passage here (passages.c
|
||||||
// putpass).
|
// putpass).
|
||||||
func (g *RogueGame) putpass(cp Coord) {
|
func (g *RogueGame) putPassage(cp Coord) {
|
||||||
pp := g.Level.At(cp.Y, cp.X)
|
pp := g.Level.At(cp.Y, cp.X)
|
||||||
pp.Flags.Set(FPassage)
|
pp.Flags.Set(FPassage)
|
||||||
|
|
||||||
@@ -302,8 +302,8 @@ func (g *RogueGame) addPass() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// passnum assigns a number to each passageway (passages.c passnum).
|
// numberPassages assigns a number to each passageway (passages.c passnum).
|
||||||
func (g *RogueGame) passnum() {
|
func (g *RogueGame) numberPassages() {
|
||||||
g.pnum = 0
|
g.pnum = 0
|
||||||
|
|
||||||
g.newpnum = false
|
g.newpnum = false
|
||||||
@@ -315,14 +315,14 @@ func (g *RogueGame) passnum() {
|
|||||||
rp := &g.Level.Rooms[i]
|
rp := &g.Level.Rooms[i]
|
||||||
for j := range rp.Exits {
|
for j := range rp.Exits {
|
||||||
g.newpnum = true
|
g.newpnum = true
|
||||||
g.numpass(rp.Exits[j].Y, rp.Exits[j].X)
|
g.numberPassage(rp.Exits[j].Y, rp.Exits[j].X)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// numpass numbers a passageway square and its brethren (passages.c
|
// numberPassage numbers a passageway square and its brethren (passages.c
|
||||||
// numpass).
|
// numpass).
|
||||||
func (g *RogueGame) numpass(y, x int) {
|
func (g *RogueGame) numberPassage(y, x int) {
|
||||||
if x >= NumCols || x < 0 || y >= NumLines || y <= 0 {
|
if x >= NumCols || x < 0 || y >= NumLines || y <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -348,10 +348,10 @@ func (g *RogueGame) numpass(y, x int) {
|
|||||||
|
|
||||||
*fp |= PlaceFlags(g.pnum) //nolint:gosec // G115: pnum < MaxPass=13
|
*fp |= PlaceFlags(g.pnum) //nolint:gosec // G115: pnum < MaxPass=13
|
||||||
// recurse on the surrounding places
|
// recurse on the surrounding places
|
||||||
g.numpass(y+1, x)
|
g.numberPassage(y+1, x)
|
||||||
g.numpass(y-1, x)
|
g.numberPassage(y-1, x)
|
||||||
g.numpass(y, x+1)
|
g.numberPassage(y, x+1)
|
||||||
g.numpass(y, x-1)
|
g.numberPassage(y, x-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// abs is C abs() for ints.
|
// abs is C abs() for ints.
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ type pact struct {
|
|||||||
// 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", KindPotion)
|
obj, ok := g.promptPackItem("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 !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,13 +43,13 @@ func (g *RogueGame) quaff() {
|
|||||||
|
|
||||||
switch obj.PotionKind() {
|
switch obj.PotionKind() {
|
||||||
case PotionConfusion:
|
case PotionConfusion:
|
||||||
g.doPot(PotionConfusion, !trip)
|
g.applyPotionFuse(PotionConfusion, !trip)
|
||||||
case PotionPoison:
|
case PotionPoison:
|
||||||
g.Items.Potions[PotionPoison].Know = true
|
g.Items.Potions[PotionPoison].Know = true
|
||||||
if p.IsWearing(RingSustainStrength) {
|
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.changeStrength(-(g.rnd(3) + 1))
|
||||||
g.msg("you feel very sick now")
|
g.msg("you feel very sick now")
|
||||||
g.comeDown(0)
|
g.comeDown(0)
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ func (g *RogueGame) quaff() {
|
|||||||
g.msg("you begin to feel better")
|
g.msg("you begin to feel better")
|
||||||
case PotionGainStrength:
|
case PotionGainStrength:
|
||||||
g.Items.Potions[PotionGainStrength].Know = true
|
g.Items.Potions[PotionGainStrength].Know = true
|
||||||
g.chgStr(1)
|
g.changeStrength(1)
|
||||||
g.msg("you feel stronger, now. What bulging muscles!")
|
g.msg("you feel stronger, now. What bulging muscles!")
|
||||||
case PotionDetectMonsters:
|
case PotionDetectMonsters:
|
||||||
p.Flags.Set(SenseMonsters)
|
p.Flags.Set(SenseMonsters)
|
||||||
@@ -118,11 +118,11 @@ func (g *RogueGame) quaff() {
|
|||||||
g.SeenStairs = g.seenStairs()
|
g.SeenStairs = g.seenStairs()
|
||||||
}
|
}
|
||||||
|
|
||||||
g.doPot(PotionLSD, true)
|
g.applyPotionFuse(PotionLSD, true)
|
||||||
case PotionSeeInvisible:
|
case PotionSeeInvisible:
|
||||||
show := p.On(CanSeeInvisible)
|
show := p.On(CanSeeInvisible)
|
||||||
|
|
||||||
g.doPot(PotionSeeInvisible, false)
|
g.applyPotionFuse(PotionSeeInvisible, false)
|
||||||
|
|
||||||
if !show {
|
if !show {
|
||||||
g.invisOn()
|
g.invisOn()
|
||||||
@@ -177,9 +177,9 @@ func (g *RogueGame) quaff() {
|
|||||||
|
|
||||||
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 PotionBlindness:
|
case PotionBlindness:
|
||||||
g.doPot(PotionBlindness, true)
|
g.applyPotionFuse(PotionBlindness, true)
|
||||||
case PotionLevitation:
|
case PotionLevitation:
|
||||||
g.doPot(PotionLevitation, true)
|
g.applyPotionFuse(PotionLevitation, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.status()
|
g.status()
|
||||||
@@ -194,9 +194,9 @@ func (g *RogueGame) raiseLevel() {
|
|||||||
g.checkLevel()
|
g.checkLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// doPot does a potion with standard setup: it uses a fuse and turns on a
|
// applyPotionFuse does a potion with standard setup: it uses a fuse and
|
||||||
// flag (potions.c do_pot).
|
// turns on a flag (potions.c do_pot).
|
||||||
func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
func (g *RogueGame) applyPotionFuse(kind PotionKind, knowit bool) {
|
||||||
pp := &g.data.pActions[kind]
|
pp := &g.data.pActions[kind]
|
||||||
if !g.Items.Potions[kind].Know {
|
if !g.Items.Potions[kind].Know {
|
||||||
g.Items.Potions[kind].Know = knowit
|
g.Items.Potions[kind].Know = knowit
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ 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", KindRing)
|
obj, ok := g.promptPackItem("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 !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ func (g *RogueGame) ringOn() {
|
|||||||
// Calculate the effect it has on the poor guy.
|
// Calculate the effect it has on the poor guy.
|
||||||
switch obj.RingKind() {
|
switch obj.RingKind() {
|
||||||
case RingAddStrength:
|
case RingAddStrength:
|
||||||
g.chgStr(obj.Bonus)
|
g.changeStrength(obj.Bonus)
|
||||||
case RingSeeInvisible:
|
case RingSeeInvisible:
|
||||||
g.invisOn()
|
g.invisOn()
|
||||||
case RingAggravateMonsters:
|
case RingAggravateMonsters:
|
||||||
@@ -65,7 +65,7 @@ func (g *RogueGame) ringOn() {
|
|||||||
g.addmsgf("you are now wearing ")
|
g.addmsgf("you are now wearing ")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("%s (%c)", g.invName(obj, true), obj.PackCh)
|
g.msg("%s (%c)", g.inventoryName(obj, true), obj.PackCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ringOff takes off a ring (rings.c ring_off).
|
// ringOff takes off a ring (rings.c ring_off).
|
||||||
@@ -103,7 +103,7 @@ func (g *RogueGame) ringOff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if g.dropCheck(obj) {
|
if g.dropCheck(obj) {
|
||||||
g.msg("was wearing %s(%c)", g.invName(obj, true), obj.PackCh)
|
g.msg("was wearing %s(%c)", g.inventoryName(obj, true), obj.PackCh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ func (g *RogueGame) totalWinner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.scr.Std.MvPrintwf(line, 0, "%c) %5d %s", obj.PackCh, worth,
|
g.scr.Std.MvPrintwf(line, 0, "%c) %5d %s", obj.PackCh, worth,
|
||||||
g.invName(obj, false))
|
g.inventoryName(obj, false))
|
||||||
line++
|
line++
|
||||||
p.Purse += worth
|
p.Purse += worth
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ type mazeState struct {
|
|||||||
|
|
||||||
const goldGrp = 1
|
const goldGrp = 1
|
||||||
|
|
||||||
// doRooms creates rooms and corridors with a connectivity graph (rooms.c
|
// digRooms creates rooms and corridors with a connectivity graph (rooms.c
|
||||||
// do_rooms).
|
// do_rooms).
|
||||||
func (g *RogueGame) doRooms() {
|
func (g *RogueGame) digRooms() {
|
||||||
var bsze Coord // maximum room size
|
var bsze Coord // maximum room size
|
||||||
|
|
||||||
bsze.X = NumCols / 3
|
bsze.X = NumCols / 3
|
||||||
@@ -34,7 +34,7 @@ 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 range leftOut {
|
for range leftOut {
|
||||||
g.Level.Rooms[g.rndRoom()].Flags.Set(Gone)
|
g.Level.Rooms[g.randomRoom()].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 {
|
||||||
@@ -105,7 +105,7 @@ func (g *RogueGame) doRooms() {
|
|||||||
gold.Flags = Stackable
|
gold.Flags = Stackable
|
||||||
gold.Group = goldGrp
|
gold.Group = goldGrp
|
||||||
gold.Kind = KindGold
|
gold.Kind = KindGold
|
||||||
attachObj(&g.Level.Objects, gold)
|
g.Level.AddObject(gold)
|
||||||
}
|
}
|
||||||
// Put the monster in
|
// Put the monster in
|
||||||
prob := 25
|
prob := 25
|
||||||
@@ -126,7 +126,7 @@ func (g *RogueGame) doRooms() {
|
|||||||
// 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(Maze) {
|
if rp.Flags.Has(Maze) {
|
||||||
g.doMaze(rp)
|
g.digMaze(rp)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -158,8 +158,8 @@ func (g *RogueGame) horiz(rp *Room, starty int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// doMaze digs a maze (rooms.c do_maze).
|
// digMaze digs a maze (rooms.c do_maze).
|
||||||
func (g *RogueGame) doMaze(rp *Room) {
|
func (g *RogueGame) digMaze(rp *Room) {
|
||||||
m := &g.maze
|
m := &g.maze
|
||||||
for y := range m.maze {
|
for y := range m.maze {
|
||||||
for x := range m.maze[y] {
|
for x := range m.maze[y] {
|
||||||
@@ -175,7 +175,7 @@ func (g *RogueGame) doMaze(rp *Room) {
|
|||||||
starty := (g.rnd(rp.Max.Y) / 2) * 2
|
starty := (g.rnd(rp.Max.Y) / 2) * 2
|
||||||
startx := (g.rnd(rp.Max.X) / 2) * 2
|
startx := (g.rnd(rp.Max.X) / 2) * 2
|
||||||
pos := Coord{Y: starty + m.starty, X: startx + m.startx}
|
pos := Coord{Y: starty + m.starty, X: startx + m.startx}
|
||||||
g.putpass(pos)
|
g.putPassage(pos)
|
||||||
g.dig(starty, startx)
|
g.dig(starty, startx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +211,8 @@ func (g *RogueGame) dig(y, x int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g.accntMaze(y, x, nexty, nextx)
|
g.accountMaze(y, x, nexty, nextx)
|
||||||
g.accntMaze(nexty, nextx, y, x)
|
g.accountMaze(nexty, nextx, y, x)
|
||||||
|
|
||||||
var pos Coord
|
var pos Coord
|
||||||
if nexty == y {
|
if nexty == y {
|
||||||
@@ -231,16 +231,16 @@ func (g *RogueGame) dig(y, x int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.putpass(pos)
|
g.putPassage(pos)
|
||||||
pos.Y = nexty + m.starty
|
pos.Y = nexty + m.starty
|
||||||
pos.X = nextx + m.startx
|
pos.X = nextx + m.startx
|
||||||
g.putpass(pos)
|
g.putPassage(pos)
|
||||||
g.dig(nexty, nextx)
|
g.dig(nexty, nextx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// accntMaze accounts for maze exits (rooms.c accnt_maze).
|
// accountMaze accounts for maze exits (rooms.c accnt_maze).
|
||||||
func (g *RogueGame) accntMaze(y, x, ny, nx int) {
|
func (g *RogueGame) accountMaze(y, x, ny, nx int) {
|
||||||
sp := &g.maze.maze[y][x]
|
sp := &g.maze.maze[y][x]
|
||||||
for i := range sp.nexits {
|
for i := range sp.nexits {
|
||||||
if sp.exits[i].Y == ny && sp.exits[i].X == nx {
|
if sp.exits[i].Y == ny && sp.exits[i].X == nx {
|
||||||
@@ -255,8 +255,8 @@ func (g *RogueGame) accntMaze(y, x, ny, nx int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rndPos picks a random spot in a room (rooms.c rnd_pos).
|
// randomPos picks a random spot in a room (rooms.c rnd_pos).
|
||||||
func (g *RogueGame) rndPos(rp *Room) Coord {
|
func (g *RogueGame) randomPos(rp *Room) Coord {
|
||||||
var cp Coord
|
var cp Coord
|
||||||
|
|
||||||
cp.X = rp.Pos.X + g.rnd(rp.Max.X-2) + 1
|
cp.X = rp.Pos.X + g.rnd(rp.Max.X-2) + 1
|
||||||
@@ -296,7 +296,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.randomRoom()]
|
||||||
|
|
||||||
compchar = Floor
|
compchar = Floor
|
||||||
if rp.Flags.Has(Maze) {
|
if rp.Flags.Has(Maze) {
|
||||||
@@ -304,7 +304,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cp := g.rndPos(rp)
|
cp := g.randomPos(rp)
|
||||||
|
|
||||||
pp := g.Level.At(cp.Y, cp.X)
|
pp := g.Level.At(cp.Y, cp.X)
|
||||||
if monst {
|
if monst {
|
||||||
@@ -321,7 +321,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co
|
|||||||
// enter_room).
|
// enter_room).
|
||||||
func (g *RogueGame) enterRoom(cp Coord) {
|
func (g *RogueGame) enterRoom(cp Coord) {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
rp := g.roomin(cp)
|
rp := g.roomIn(cp)
|
||||||
p.Room = rp
|
p.Room = rp
|
||||||
g.doorOpen(rp)
|
g.doorOpen(rp)
|
||||||
|
|
||||||
|
|||||||
142
game/save.go
142
game/save.go
@@ -449,99 +449,102 @@ func (g *RogueGame) applySnapshot(st *SaveState) {
|
|||||||
g.scr.Std.SetContents(st.Screen)
|
g.scr.Std.SetContents(st.Screen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveGame implements the "save game" command (save.c save_game). The C
|
// saveGame implements the "save game" command (save.c save_game). The
|
||||||
// goto over/gotfile flow becomes the useDefault flag.
|
// labeled prompt loop and the useDefault flag stand in for the C
|
||||||
|
// goto over/gotfile flow.
|
||||||
func (g *RogueGame) saveGame() {
|
func (g *RogueGame) saveGame() {
|
||||||
g.Msgs.Mpos = 0
|
g.Msgs.Mpos = 0
|
||||||
|
|
||||||
over:
|
prompt:
|
||||||
useDefault := false
|
|
||||||
|
|
||||||
if g.FileName != "" {
|
|
||||||
var c byte
|
|
||||||
|
|
||||||
for {
|
|
||||||
g.msg("save file (%s)? ", g.FileName)
|
|
||||||
c = g.readchar()
|
|
||||||
|
|
||||||
g.Msgs.Mpos = 0
|
|
||||||
if c == Escape {
|
|
||||||
g.msg("")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == 'n' || c == 'N' || c == 'y' || c == 'Y' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
g.msg("please answer Y or N")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == 'y' || c == 'Y' {
|
|
||||||
g.addstr("Yes\n")
|
|
||||||
g.refresh()
|
|
||||||
|
|
||||||
useDefault = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var buf string
|
useDefault := false
|
||||||
if useDefault {
|
|
||||||
buf = g.FileName
|
|
||||||
useDefault = false
|
|
||||||
} else {
|
|
||||||
g.Msgs.Mpos = 0
|
|
||||||
g.msg("file name: ")
|
|
||||||
|
|
||||||
if g.getStr(&buf, g.scr.Std) == Quit {
|
if g.FileName != "" {
|
||||||
g.msg("")
|
var c byte
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Msgs.Mpos = 0
|
|
||||||
}
|
|
||||||
// test to see if the file exists
|
|
||||||
_, statErr := os.Stat(buf)
|
|
||||||
if statErr == nil {
|
|
||||||
for {
|
for {
|
||||||
g.msg("File exists. Do you wish to overwrite it?")
|
g.msg("save file (%s)? ", g.FileName)
|
||||||
g.Msgs.Mpos = 0
|
c = g.readchar()
|
||||||
|
|
||||||
c := g.readchar()
|
g.Msgs.Mpos = 0
|
||||||
if c == Escape {
|
if c == Escape {
|
||||||
g.msg("")
|
g.msg("")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == 'y' || c == 'Y' {
|
if c == 'n' || c == 'N' || c == 'y' || c == 'Y' {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == 'n' || c == 'N' {
|
g.msg("please answer Y or N")
|
||||||
goto over
|
|
||||||
}
|
|
||||||
|
|
||||||
g.msg("Please answer Y or N")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("file name: %s", buf)
|
if c == 'y' || c == 'Y' {
|
||||||
_ = os.Remove(g.FileName) // best effort, as in C (md_unlink)
|
g.addstr("Yes\n")
|
||||||
|
g.refresh()
|
||||||
|
|
||||||
|
useDefault = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.FileName = buf
|
for {
|
||||||
|
var buf string
|
||||||
|
if useDefault {
|
||||||
|
buf = g.FileName
|
||||||
|
useDefault = false
|
||||||
|
} else {
|
||||||
|
g.Msgs.Mpos = 0
|
||||||
|
g.msg("file name: ")
|
||||||
|
|
||||||
err := g.saveFile(g.FileName)
|
if g.getStr(&buf, g.scr.Std) == Quit {
|
||||||
if err != nil {
|
g.msg("")
|
||||||
g.msg("%s", err.Error())
|
|
||||||
|
|
||||||
continue
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Msgs.Mpos = 0
|
||||||
|
}
|
||||||
|
// test to see if the file exists
|
||||||
|
_, statErr := os.Stat(buf)
|
||||||
|
if statErr == nil {
|
||||||
|
for {
|
||||||
|
g.msg("File exists. Do you wish to overwrite it?")
|
||||||
|
g.Msgs.Mpos = 0
|
||||||
|
|
||||||
|
c := g.readchar()
|
||||||
|
if c == Escape {
|
||||||
|
g.msg("")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == 'y' || c == 'Y' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == 'n' || c == 'N' {
|
||||||
|
continue prompt // the C goto over: start again
|
||||||
|
}
|
||||||
|
|
||||||
|
g.msg("Please answer Y or N")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.msg("file name: %s", buf)
|
||||||
|
_ = os.Remove(g.FileName) // best effort, as in C (md_unlink)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.FileName = buf
|
||||||
|
|
||||||
|
err := g.saveFile(g.FileName)
|
||||||
|
if err != nil {
|
||||||
|
g.msg("%s", err.Error())
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g.myExit()
|
g.myExit()
|
||||||
@@ -615,6 +618,7 @@ func Restore(path string, cfg Config) (*RogueGame, error) {
|
|||||||
restored: true,
|
restored: true,
|
||||||
}
|
}
|
||||||
g.scr = NewScreen(cfg.Term)
|
g.scr = NewScreen(cfg.Term)
|
||||||
|
g.Msgs.attach(g.scr, g.look, g.readchar)
|
||||||
g.applySnapshot(&st)
|
g.applySnapshot(&st)
|
||||||
|
|
||||||
// defeat multiple restarting from the same place
|
// defeat multiple restarting from the same place
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ package game
|
|||||||
func (g *RogueGame) readScroll() {
|
func (g *RogueGame) readScroll() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
obj := g.getItem("read", KindScroll)
|
obj, ok := g.promptPackItem("read", KindScroll)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +104,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.ScrollKind() == ScrollScareMonster {
|
if fo := g.Level.ObjectAt(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ const (
|
|||||||
func (g *RogueGame) doZap() {
|
func (g *RogueGame) doZap() {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
|
|
||||||
obj := g.getItem("zap with", KindWand)
|
obj, ok := g.promptPackItem("zap with", KindWand)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +80,12 @@ func (g *RogueGame) doZap() {
|
|||||||
case WandInvisibility:
|
case WandInvisibility:
|
||||||
tp.Flags.Set(Invisible)
|
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 WandPolymorph:
|
case WandPolymorph:
|
||||||
pp := tp.Pack
|
pp := tp.Pack
|
||||||
detachMon(&g.Level.Monsters, tp)
|
g.Level.RemoveMonster(tp)
|
||||||
|
|
||||||
if g.seeMonst(tp) {
|
if g.seeMonst(tp) {
|
||||||
g.mvaddch(y, x, g.Level.Char(y, x))
|
g.mvaddch(y, x, g.Level.Char(y, x))
|
||||||
@@ -186,7 +186,7 @@ func (g *RogueGame) doZap() {
|
|||||||
|
|
||||||
g.Delta.Y = y
|
g.Delta.Y = y
|
||||||
g.Delta.X = x
|
g.Delta.X = x
|
||||||
g.runto(g.Delta)
|
g.runTo(g.Delta)
|
||||||
}
|
}
|
||||||
case WandLightning, WandFire, WandCold:
|
case WandLightning, WandFire, WandCold:
|
||||||
var name string
|
var name string
|
||||||
@@ -243,7 +243,7 @@ func (g *RogueGame) drain() {
|
|||||||
if mp.Stats.HP -= cnt; mp.Stats.HP <= 0 {
|
if mp.Stats.HP -= cnt; mp.Stats.HP <= 0 {
|
||||||
g.killed(mp, g.seeMonst(mp))
|
g.killed(mp, g.seeMonst(mp))
|
||||||
} else {
|
} else {
|
||||||
g.runto(mp.Pos)
|
g.runTo(mp.Pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,7 +339,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
|
|||||||
}
|
}
|
||||||
} else if ch != 'M' || tp.Disguise == 'M' {
|
} else if ch != 'M' || tp.Disguise == 'M' {
|
||||||
if fromHero {
|
if fromHero {
|
||||||
g.runto(pos)
|
g.runTo(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.Options.Terse {
|
if g.Options.Terse {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ type gameData struct {
|
|||||||
initWeaps [NumWeaponTypes]weaponSetup
|
initWeaps [NumWeaponTypes]weaponSetup
|
||||||
|
|
||||||
// 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 applyPotionFuse.
|
||||||
pActions [NumPotionTypes]pact
|
pActions [NumPotionTypes]pact
|
||||||
|
|
||||||
// idType maps identify scrolls to the kind of item they identify
|
// idType maps identify scrolls to the kind of item they identify
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// invName returns the name of something as it would appear in an inventory
|
// invName returns the name of something as it would appear in an inventory
|
||||||
// (things.c inv_name).
|
// (things.c inv_name).
|
||||||
func (g *RogueGame) invName(obj *Object, drop bool) string {
|
func (g *RogueGame) inventoryName(obj *Object, drop bool) string {
|
||||||
var pb strings.Builder
|
var pb strings.Builder
|
||||||
|
|
||||||
which := obj.Which
|
which := obj.Which
|
||||||
@@ -142,8 +142,8 @@ func (g *RogueGame) dropIt() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := g.getItem("drop", KindNone)
|
obj, ok := g.promptPackItem("drop", KindNone)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ func (g *RogueGame) dropIt() {
|
|||||||
|
|
||||||
obj = g.leavePack(obj, true, !obj.Kind.MergesInPack())
|
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)
|
g.Level.AddObject(obj)
|
||||||
g.Level.SetChar(p.Pos.Y, p.Pos.X, obj.Kind.Glyph())
|
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)
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ func (g *RogueGame) dropIt() {
|
|||||||
g.HasAmulet = false
|
g.HasAmulet = false
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("dropped %s", g.invName(obj, true))
|
g.msg("dropped %s", g.inventoryName(obj, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropCheck does special checks for dropping or unwielding|unwearing|
|
// dropCheck does special checks for dropping or unwielding|unwearing|
|
||||||
@@ -201,7 +201,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
|
|||||||
|
|
||||||
switch obj.RingKind() {
|
switch obj.RingKind() {
|
||||||
case RingAddStrength:
|
case RingAddStrength:
|
||||||
g.chgStr(-obj.Bonus)
|
g.changeStrength(-obj.Bonus)
|
||||||
case RingSeeInvisible:
|
case RingSeeInvisible:
|
||||||
g.unsee(0)
|
g.unsee(0)
|
||||||
g.Extinguish(DUnsee)
|
g.Extinguish(DUnsee)
|
||||||
@@ -392,7 +392,7 @@ func (g *RogueGame) printDisc(typ byte) {
|
|||||||
if info[order[i]].Know || info[order[i]].Guess != "" {
|
if info[order[i]].Know || info[order[i]].Guess != "" {
|
||||||
obj.Kind = objectKindForGlyph(typ)
|
obj.Kind = objectKindForGlyph(typ)
|
||||||
obj.Which = order[i]
|
obj.Which = order[i]
|
||||||
g.addLine("%s", g.invName(&obj, false))
|
g.addLine("%s", g.inventoryName(&obj, false))
|
||||||
|
|
||||||
numFound++
|
numFound++
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ 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", KindWeapon)
|
obj, ok := g.promptPackItem("throw", KindWeapon)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
|
|||||||
obj.Pos = p.Pos
|
obj.Pos = p.Pos
|
||||||
for {
|
for {
|
||||||
// Erase the old one
|
// Erase the old one
|
||||||
if obj.Pos != p.Pos && g.cansee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse {
|
if obj.Pos != p.Pos && g.canSee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse {
|
||||||
ch := g.Level.Char(obj.Pos.Y, obj.Pos.X)
|
ch := g.Level.Char(obj.Pos.Y, obj.Pos.X)
|
||||||
if ch == Floor && !g.showFloor() {
|
if ch == Floor && !g.showFloor() {
|
||||||
ch = ' '
|
ch = ' '
|
||||||
@@ -51,7 +51,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
|
|||||||
ch := g.Level.VisibleChar(obj.Pos.Y, obj.Pos.X)
|
ch := g.Level.VisibleChar(obj.Pos.Y, obj.Pos.X)
|
||||||
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.Kind.Glyph())
|
g.mvaddch(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
|
||||||
g.refresh()
|
g.refresh()
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ func (g *RogueGame) fall(obj *Object, pr bool) {
|
|||||||
pp.Ch = obj.Kind.Glyph()
|
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.Kind.Glyph()
|
pp.Monst.OldCh = obj.Kind.Glyph()
|
||||||
} else {
|
} else {
|
||||||
@@ -78,7 +78,7 @@ func (g *RogueGame) fall(obj *Object, pr bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attachObj(&g.Level.Objects, obj)
|
g.Level.AddObject(obj)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -113,8 +113,8 @@ func (g *RogueGame) wield() {
|
|||||||
|
|
||||||
p.CurWeapon = oweapon
|
p.CurWeapon = oweapon
|
||||||
|
|
||||||
obj := g.getItem("wield", KindWeapon)
|
obj, ok := g.promptPackItem("wield", KindWeapon)
|
||||||
if obj == nil {
|
if !ok {
|
||||||
g.After = false
|
g.After = false
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -133,7 +133,7 @@ func (g *RogueGame) wield() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sp := g.invName(obj, true)
|
sp := g.inventoryName(obj, true)
|
||||||
p.CurWeapon = obj
|
p.CurWeapon = obj
|
||||||
|
|
||||||
if !g.Options.Terse {
|
if !g.Options.Terse {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
|
|||||||
|
|
||||||
var obj *Object
|
var obj *Object
|
||||||
for {
|
for {
|
||||||
obj = g.getItem("identify", kind)
|
obj, _ = g.promptPackItem("identify", kind)
|
||||||
|
|
||||||
if !insist {
|
if !insist {
|
||||||
break
|
break
|
||||||
@@ -161,7 +161,7 @@ func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
|
|||||||
setKnow(obj, g.Items.Rings[:])
|
setKnow(obj, g.Items.Rings[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
g.msg("%s", g.invName(obj, false))
|
g.msg("%s", g.inventoryName(obj, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// setKnow sets things up when we really know what a thing is (wizard.c
|
// setKnow sets things up when we really know what a thing is (wizard.c
|
||||||
@@ -181,7 +181,7 @@ func (g *RogueGame) teleport() {
|
|||||||
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt())
|
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt())
|
||||||
|
|
||||||
c, _ := g.findFloor(true)
|
c, _ := g.findFloor(true)
|
||||||
if g.roomin(c) != p.Room {
|
if g.roomIn(c) != p.Room {
|
||||||
g.leaveRoom(p.Pos)
|
g.leaveRoom(p.Pos)
|
||||||
p.Pos = c
|
p.Pos = c
|
||||||
g.enterRoom(p.Pos)
|
g.enterRoom(p.Pos)
|
||||||
|
|||||||
Reference in New Issue
Block a user