Refactor step 4. Renames (C breadcrumbs kept in doc comments): doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, doRooms→digRooms, doPassages→digPassages, doMaze→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 are gone: moveHero uses a retry loop with the PASSGO corner logic extracted into passageTurn; dispatch re-dispatches via a loop; chaseStep re-checks via a loop; saveGame uses a labeled prompt loop. Control flow and RNG call order are unchanged; suite green.
287 lines
6.2 KiB
Go
287 lines
6.2 KiB
Go
package game
|
|
|
|
// daemons.c — the daemon and fuse callbacks, dispatched by DaemonID.
|
|
// stomach() arrives with the endgame phase (starvation calls death()).
|
|
|
|
// runDaemon invokes the callback named by id (the call through d_func in C).
|
|
func (g *RogueGame) runDaemon(id DaemonID, arg int) {
|
|
switch id {
|
|
case DRollwand:
|
|
g.rollwand(arg)
|
|
case DDoctor:
|
|
g.doctor(arg)
|
|
case DStomach:
|
|
g.stomach(arg)
|
|
case DRunners:
|
|
g.runners(arg)
|
|
case DSwander:
|
|
g.swander(arg)
|
|
case DNohaste:
|
|
g.nohaste(arg)
|
|
case DUnconfuse:
|
|
g.unconfuse(arg)
|
|
case DUnsee:
|
|
g.unsee(arg)
|
|
case DSight:
|
|
g.sight(arg)
|
|
case DVisuals:
|
|
g.visuals(arg)
|
|
case DComeDown:
|
|
g.comeDown(arg)
|
|
case DLand:
|
|
g.land(arg)
|
|
case DTurnSee:
|
|
g.turnSee(arg != 0)
|
|
default:
|
|
// Callbacks are added to this switch as their subsystems are
|
|
// ported; reaching one that isn't here is a porting bug.
|
|
panic("daemon not yet ported")
|
|
}
|
|
}
|
|
|
|
// doctor is the healing daemon that restores hit points after rest
|
|
// (daemons.c doctor).
|
|
func (g *RogueGame) doctor(int) {
|
|
p := &g.Player
|
|
lv := p.Stats.Lvl
|
|
ohp := p.Stats.HP
|
|
|
|
g.Quiet++
|
|
if lv < 8 {
|
|
if g.Quiet+(lv<<1) > 20 {
|
|
p.Stats.HP++
|
|
}
|
|
} else if g.Quiet >= 3 {
|
|
p.Stats.HP += g.rnd(lv-7) + 1
|
|
}
|
|
|
|
if p.IsRing(Left, RingRegeneration) {
|
|
p.Stats.HP++
|
|
}
|
|
|
|
if p.IsRing(Right, RingRegeneration) {
|
|
p.Stats.HP++
|
|
}
|
|
|
|
if ohp != p.Stats.HP {
|
|
if p.Stats.HP > p.Stats.MaxHP {
|
|
p.Stats.HP = p.Stats.MaxHP
|
|
}
|
|
|
|
g.Quiet = 0
|
|
}
|
|
}
|
|
|
|
// swander is called when it is time to start rolling for wandering monsters
|
|
// (daemons.c swander).
|
|
func (g *RogueGame) swander(int) {
|
|
g.StartDaemon(DRollwand, 0, Before)
|
|
}
|
|
|
|
// rollwand rolls to see if a wandering monster starts up (daemons.c
|
|
// rollwand).
|
|
func (g *RogueGame) rollwand(int) {
|
|
if g.Daemons.Between++; g.Daemons.Between >= 4 {
|
|
if g.roll(1, 6) == 4 {
|
|
g.wanderer()
|
|
g.KillDaemon(DRollwand)
|
|
g.Fuse(DSwander, 0, wanderTime(g), Before)
|
|
}
|
|
|
|
g.Daemons.Between = 0
|
|
}
|
|
}
|
|
|
|
// wanderTime is the WANDERTIME macro: spread(70).
|
|
func wanderTime(g *RogueGame) int { return g.spread(70) }
|
|
|
|
// unconfuse releases the poor player from his confusion (daemons.c
|
|
// unconfuse).
|
|
func (g *RogueGame) unconfuse(int) {
|
|
g.Player.Flags.Clear(Confused)
|
|
g.msg("you feel less %s now", g.chooseStr("trippy", "confused"))
|
|
}
|
|
|
|
// unsee turns off the ability to see invisible (daemons.c unsee).
|
|
func (g *RogueGame) unsee(int) {
|
|
for _, th := range g.Level.Monsters {
|
|
if th.On(Invisible) && g.seeMonst(th) {
|
|
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
|
|
}
|
|
}
|
|
|
|
g.Player.Flags.Clear(CanSeeInvisible)
|
|
}
|
|
|
|
// sight gives the hero his sight back (daemons.c sight).
|
|
func (g *RogueGame) sight(int) {
|
|
p := &g.Player
|
|
if p.On(Blind) {
|
|
g.Extinguish(DSight)
|
|
p.Flags.Clear(Blind)
|
|
|
|
if !p.Room.Flags.Has(Gone) {
|
|
g.enterRoom(p.Pos)
|
|
}
|
|
|
|
g.msg("%s", g.chooseStr("far out! Everything is all cosmic again",
|
|
"the veil of darkness lifts"))
|
|
}
|
|
}
|
|
|
|
// nohaste ends the hasting (daemons.c nohaste).
|
|
func (g *RogueGame) nohaste(int) {
|
|
g.Player.Flags.Clear(Hasted)
|
|
g.msg("you feel yourself slowing down")
|
|
}
|
|
|
|
// stomach digests the hero's food (daemons.c stomach).
|
|
func (g *RogueGame) stomach(int) {
|
|
p := &g.Player
|
|
|
|
origHungry := p.HungryState
|
|
if p.FoodLeft <= 0 {
|
|
if p.FoodLeft--; p.FoodLeft < -StarveTime {
|
|
g.death('s')
|
|
}
|
|
// the hero is fainting
|
|
if g.NoCommand != 0 || g.rnd(5) != 0 {
|
|
return
|
|
}
|
|
|
|
g.NoCommand += g.rnd(8) + 4
|
|
p.HungryState = 3
|
|
|
|
if !g.Options.Terse {
|
|
g.addmsgf("%s", g.chooseStr(
|
|
"the munchies overpower your motor capabilities. ",
|
|
"you feel too weak from lack of food. "))
|
|
}
|
|
|
|
g.msg("%s", g.chooseStr("You freak out", "You faint"))
|
|
} else {
|
|
oldfood := p.FoodLeft
|
|
|
|
amulet := 0
|
|
if g.HasAmulet {
|
|
amulet = 1
|
|
}
|
|
|
|
p.FoodLeft -= g.ringEat(Left) + g.ringEat(Right) + 1 - amulet
|
|
|
|
if p.FoodLeft < MoreTime && oldfood >= MoreTime {
|
|
p.HungryState = 2
|
|
|
|
g.msg("%s", g.chooseStr(
|
|
"the munchies are interfering with your motor capabilities",
|
|
"you are starting to feel weak"))
|
|
} else if p.FoodLeft < 2*MoreTime && oldfood >= 2*MoreTime {
|
|
p.HungryState = 1
|
|
|
|
if g.Options.Terse {
|
|
g.msg("%s", g.chooseStr("getting the munchies", "getting hungry"))
|
|
} else {
|
|
g.msg("%s", g.chooseStr("you are getting the munchies",
|
|
"you are starting to get hungry"))
|
|
}
|
|
}
|
|
}
|
|
|
|
if p.HungryState != origHungry {
|
|
p.Flags.Clear(Awake)
|
|
|
|
g.Running = false
|
|
g.ToDeath = false
|
|
g.Count = 0
|
|
}
|
|
}
|
|
|
|
// comeDown takes the hero down off her acid trip (daemons.c come_down).
|
|
func (g *RogueGame) comeDown(int) {
|
|
p := &g.Player
|
|
if !p.On(Hallucinating) {
|
|
return
|
|
}
|
|
|
|
g.KillDaemon(DVisuals)
|
|
p.Flags.Clear(Hallucinating)
|
|
|
|
if p.On(Blind) {
|
|
return
|
|
}
|
|
|
|
// undo the things
|
|
for _, tp := range g.Level.Objects {
|
|
if g.canSee(tp.Pos.Y, tp.Pos.X) {
|
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Kind.Glyph())
|
|
}
|
|
}
|
|
|
|
// undo the monsters
|
|
seemonst := p.On(SenseMonsters)
|
|
|
|
for _, tp := range g.Level.Monsters {
|
|
g.move(tp.Pos.Y, tp.Pos.X)
|
|
|
|
if g.canSee(tp.Pos.Y, tp.Pos.X) {
|
|
if !tp.On(Invisible) || p.On(CanSeeInvisible) {
|
|
g.addch(tp.Disguise)
|
|
} else {
|
|
g.addch(g.Level.Char(tp.Pos.Y, tp.Pos.X))
|
|
}
|
|
} else if seemonst {
|
|
g.standout()
|
|
g.addch(tp.Type)
|
|
g.standend()
|
|
}
|
|
}
|
|
|
|
g.msg("Everything looks SO boring now.")
|
|
}
|
|
|
|
// visuals changes the characters for the player while hallucinating
|
|
// (daemons.c visuals).
|
|
func (g *RogueGame) visuals(int) {
|
|
p := &g.Player
|
|
if !g.After || (g.Running && g.Options.Jump) {
|
|
return
|
|
}
|
|
// change the things
|
|
for _, tp := range g.Level.Objects {
|
|
if g.canSee(tp.Pos.Y, tp.Pos.X) {
|
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, g.rndThing())
|
|
}
|
|
}
|
|
|
|
// change the stairs
|
|
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())
|
|
}
|
|
|
|
// change the monsters
|
|
seemonst := p.On(SenseMonsters)
|
|
|
|
for _, tp := range g.Level.Monsters {
|
|
g.move(tp.Pos.Y, tp.Pos.X)
|
|
|
|
if g.seeMonst(tp) {
|
|
if tp.Type == 'X' && tp.Disguise != 'X' {
|
|
g.addch(g.rndThing())
|
|
} else {
|
|
g.addch(g.randomMonsterLetter())
|
|
}
|
|
} else if seemonst {
|
|
g.standout()
|
|
g.addch(g.randomMonsterLetter())
|
|
g.standend()
|
|
}
|
|
}
|
|
}
|
|
|
|
// land lands the hero from a levitation potion (daemons.c land).
|
|
func (g *RogueGame) land(int) {
|
|
g.Player.Flags.Clear(Levitating)
|
|
g.msg("%s", g.chooseStr("bummer! You've hit the ground",
|
|
"you float gently to the ground"))
|
|
}
|