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.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(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.Type) } } // 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(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(Levitating) g.msg("%s", g.chooseStr("bummer! You've hit the ground", "you float gently to the ground")) }