package game // daemon.c — the daemon/fuse scheduler. // // C identifies daemons and fuses by function pointer; the port uses DaemonID // (which the C save format itself resorted to when serializing d_list), and // dispatches through one switch in daemons.go. // DaemonID names a daemon/fuse callback. The numeric values match the // function-pointer-to-int mapping in state.c rs_write_daemons. type DaemonID int // Daemon and fuse callback identifiers. The first block's numeric values // match the function-pointer-to-int mapping in state.c rs_write_daemons; // the second block covers fuses state.c never saved (the Go save format // handles them all uniformly). const ( DNone DaemonID = 0 DRollwand DaemonID = 1 DDoctor DaemonID = 2 DStomach DaemonID = 3 DRunners DaemonID = 4 DSwander DaemonID = 5 DNohaste DaemonID = 6 DUnconfuse DaemonID = 7 DUnsee DaemonID = 8 DSight DaemonID = 9 DVisuals DaemonID = 10 DComeDown DaemonID = 11 DLand DaemonID = 12 DTurnSee DaemonID = 13 // potions.c casts turn_see to a fuse callback ) // Scheduling phases and slot states (daemon.c). const ( dEmpty = 0 Before = 1 // run before the player's command After = 2 // run after the player's command daemonTime = -1 // d_time sentinel: a recurring daemon, not a fuse ) // delayedAction is rogue.h struct delayed_action. type delayedAction struct { Type int // dEmpty, Before or After Func DaemonID Arg int Time int // daemonTime for daemons; remaining turns for fuses } // DaemonList is the C d_list[MAXDAEMONS] plus the file statics that ride // along with the daemon system. type DaemonList struct { List [MaxDaemons]delayedAction Between int // daemons.c rollwand static `between` } // dSlot finds an empty slot in the daemon/fuse list (daemon.c d_slot). func (g *RogueGame) dSlot() *delayedAction { for i := range g.Daemons.List { if g.Daemons.List[i].Type == dEmpty { return &g.Daemons.List[i] } } panic("ran out of fuse slots") // C: debug message in MASTER, NULL deref otherwise } // findSlot finds a particular slot in the table (daemon.c find_slot). func (g *RogueGame) findSlot(f DaemonID) *delayedAction { for i := range g.Daemons.List { d := &g.Daemons.List[i] if d.Type != dEmpty && d.Func == f { return d } } return nil } // StartDaemon starts a recurring daemon (daemon.c start_daemon). func (g *RogueGame) StartDaemon(f DaemonID, arg, typ int) { dev := g.dSlot() dev.Type = typ dev.Func = f dev.Arg = arg dev.Time = daemonTime } // KillDaemon removes a daemon from the list (daemon.c kill_daemon). func (g *RogueGame) KillDaemon(f DaemonID) { if dev := g.findSlot(f); dev != nil { dev.Type = dEmpty } } // DoDaemons runs all the daemons that are active with the current flag // (daemon.c do_daemons). func (g *RogueGame) DoDaemons(flag int) { for i := range g.Daemons.List { dev := &g.Daemons.List[i] if dev.Type == flag && dev.Time == daemonTime { g.runDaemon(dev.Func, dev.Arg) } } } // Fuse starts a countdown fuse (daemon.c fuse). func (g *RogueGame) Fuse(f DaemonID, arg, time, typ int) { wire := g.dSlot() wire.Type = typ wire.Func = f wire.Arg = arg wire.Time = time } // Lengthen extends a fuse's countdown (daemon.c lengthen). func (g *RogueGame) Lengthen(f DaemonID, xtime int) { if wire := g.findSlot(f); wire != nil { wire.Time += xtime } } // Extinguish puts out a fuse (daemon.c extinguish). func (g *RogueGame) Extinguish(f DaemonID) { if wire := g.findSlot(f); wire != nil { wire.Type = dEmpty } } // DoFuses decrements all fuses in the given phase and fires any that burn // down (daemon.c do_fuses). func (g *RogueGame) DoFuses(flag int) { for i := range g.Daemons.List { wire := &g.Daemons.List[i] if wire.Type == flag && wire.Time > 0 { wire.Time-- if wire.Time == 0 { wire.Type = dEmpty g.runDaemon(wire.Func, wire.Arg) } } } }