- potions.c completed: quaff with all 14 potion effects, the PACT standard-fuse table (do_pot), raise_level; the see-invisible fruit- juice message is computed at quaff time as in C - scrolls.c in full: read_scroll with all 18 scroll effects including the magic-mapping map rewrite and identify dispatch, uncurse - options.c in full: the interactive options screen, get_bool/get_str/ get_inv_t/get_sf editors, ROGUEOPTS parsing (prefix matching, no- prefixed booleans, ~ expansion), strucpy - misc.c call_it via the get_str line editor - turn_see gets a DaemonID (C casts it to a fuse callback for the monster-detection potion) Tests: healing/confusion potions with fuse burn-down, enchant armor, hold monster, slow-monster zap, ROGUEOPTS parsing, and a regression test documenting the C wake_monster ISGREED/ISHELD quirk the port keeps faithfully.
263 lines
6.1 KiB
Go
263 lines
6.1 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, RRegen) {
|
|
p.Stats.HP++
|
|
}
|
|
if p.IsRing(Right, RRegen) {
|
|
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(IsHuh)
|
|
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(IsInvis) && g.seeMonst(th) {
|
|
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
|
|
}
|
|
}
|
|
g.Player.Flags.Clear(CanSee)
|
|
}
|
|
|
|
// sight gives the hero his sight back (daemons.c sight).
|
|
func (g *RogueGame) sight(int) {
|
|
p := &g.Player
|
|
if p.On(IsBlind) {
|
|
g.Extinguish(DSight)
|
|
p.Flags.Clear(IsBlind)
|
|
if !p.Room.Flags.Has(IsGone) {
|
|
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(IsHaste)
|
|
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.addmsg("%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 capabilites",
|
|
"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(IsRun)
|
|
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(IsHalu) {
|
|
return
|
|
}
|
|
|
|
g.KillDaemon(DVisuals)
|
|
p.Flags.Clear(IsHalu)
|
|
|
|
if p.On(IsBlind) {
|
|
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.Type)
|
|
}
|
|
}
|
|
|
|
// undo the monsters
|
|
seemonst := p.On(SeeMonst)
|
|
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(IsInvis) || p.On(CanSee) {
|
|
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(SeeMonst)
|
|
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(byte(g.rnd(26) + 'A'))
|
|
}
|
|
} else if seemonst {
|
|
g.standout()
|
|
g.addch(byte(g.rnd(26) + 'A'))
|
|
g.standend()
|
|
}
|
|
}
|
|
}
|
|
|
|
// land lands the hero from a levitation potion (daemons.c land).
|
|
func (g *RogueGame) land(int) {
|
|
g.Player.Flags.Clear(IsLevit)
|
|
g.msg("%s", g.chooseStr("bummer! You've hit the ground",
|
|
"you float gently to the ground"))
|
|
}
|