Convert doZap to a per-wand handler table (refactor step 7)
The do_zap switch becomes gameData.zapHandlers, indexed by WandKind; the shared monster-ray preamble is zapRayMonster/zapVictim, teleport away/to and the three bolt wands share handlers, and a false return aborts the zap without spending a charge (drain life on a too-weak hero, as in C). Effect order and RNG call sequence unchanged.
This commit is contained in:
158
game/sticks.go
158
game/sticks.go
@@ -12,8 +12,6 @@ const (
|
|||||||
|
|
||||||
// doZap performs a zap with a wand (sticks.c do_zap).
|
// doZap performs a zap with a wand (sticks.c do_zap).
|
||||||
func (g *RogueGame) doZap() {
|
func (g *RogueGame) doZap() {
|
||||||
p := &g.Player
|
|
||||||
|
|
||||||
obj, ok := g.promptPackItem("zap with", KindWand)
|
obj, ok := g.promptPackItem("zap with", KindWand)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@@ -32,9 +30,51 @@ func (g *RogueGame) doZap() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch obj.WandKind() {
|
if h := g.data.zapHandlers[obj.WandKind()]; h != nil {
|
||||||
case WandLight:
|
if !h(g, obj) {
|
||||||
|
return // the zap aborted; no charge is used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.Charges--
|
||||||
|
}
|
||||||
|
|
||||||
|
// zapRayMonster walks the zap ray from the hero to the first blocking
|
||||||
|
// spot and returns the monster standing there, if any (the shared
|
||||||
|
// preamble of the C monster-affecting zap cases).
|
||||||
|
func (g *RogueGame) zapRayMonster() *Monster {
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
y := p.Pos.Y
|
||||||
|
|
||||||
|
x := p.Pos.X
|
||||||
|
for stepOk(g.Level.VisibleChar(y, x)) {
|
||||||
|
y += g.Delta.Y
|
||||||
|
x += g.Delta.X
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.Level.MonsterAt(y, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// zapVictim is zapRayMonster plus the flytrap release the C code does
|
||||||
|
// before the invisibility-family effects.
|
||||||
|
func (g *RogueGame) zapVictim() *Monster {
|
||||||
|
tp := g.zapRayMonster()
|
||||||
|
if tp != nil && tp.Type == 'F' {
|
||||||
|
g.Player.Flags.Clear(Held)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tp
|
||||||
|
}
|
||||||
|
|
||||||
|
// The per-wand effect handlers, dispatched through gameData.zapHandlers.
|
||||||
|
// Each is one case of the C do_zap switch; returning false aborts the
|
||||||
|
// zap without using a charge.
|
||||||
|
|
||||||
|
func (g *RogueGame) zapLight(*Object) bool {
|
||||||
// Reddy Kilowatt wand. Light up the room
|
// Reddy Kilowatt wand. Light up the room
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
g.Items.Sticks[WandLight].Know = true
|
g.Items.Sticks[WandLight].Know = true
|
||||||
if p.Room.Flags.Has(Gone) {
|
if p.Room.Flags.Has(Gone) {
|
||||||
g.msg("the corridor glows and then fades")
|
g.msg("the corridor glows and then fades")
|
||||||
@@ -50,40 +90,45 @@ func (g *RogueGame) doZap() {
|
|||||||
|
|
||||||
g.endmsg()
|
g.endmsg()
|
||||||
}
|
}
|
||||||
case WandDrainLife:
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapDrainLife(*Object) bool {
|
||||||
// take away 1/2 of hero's hit points, then take it away evenly
|
// take away 1/2 of hero's hit points, then take it away evenly
|
||||||
// from the monsters in the room (or next to hero if he is in a
|
// from the monsters in the room (or next to hero if he is in a
|
||||||
// passage)
|
// passage)
|
||||||
if p.Stats.HP < 2 {
|
if g.Player.Stats.HP < 2 {
|
||||||
g.msg("you are too weak to use it")
|
g.msg("you are too weak to use it")
|
||||||
|
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
g.drain()
|
g.drain()
|
||||||
case WandInvisibility, WandPolymorph, WandTeleportAway, WandTeleportTo, WandCancellation:
|
|
||||||
y := p.Pos.Y
|
|
||||||
|
|
||||||
x := p.Pos.X
|
return true
|
||||||
for stepOk(g.Level.VisibleChar(y, x)) {
|
}
|
||||||
y += g.Delta.Y
|
|
||||||
x += g.Delta.X
|
|
||||||
}
|
|
||||||
|
|
||||||
if tp := g.Level.MonsterAt(y, x); tp != nil {
|
func (g *RogueGame) zapInvisibility(*Object) bool {
|
||||||
monster := tp.Type
|
if tp := g.zapVictim(); tp != nil {
|
||||||
if monster == 'F' {
|
|
||||||
p.Flags.Clear(Held)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch obj.WandKind() {
|
|
||||||
case WandInvisibility:
|
|
||||||
tp.Flags.Set(Invisible)
|
tp.Flags.Set(Invisible)
|
||||||
|
|
||||||
if g.canSee(y, x) {
|
if g.canSee(tp.Pos.Y, tp.Pos.X) {
|
||||||
g.mvaddch(y, x, tp.OldCh)
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.OldCh)
|
||||||
}
|
}
|
||||||
case WandPolymorph:
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapPolymorph(*Object) bool {
|
||||||
|
tp := g.zapVictim()
|
||||||
|
if tp == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
y, x := tp.Pos.Y, tp.Pos.X
|
||||||
|
|
||||||
pp := tp.Pack
|
pp := tp.Pack
|
||||||
g.Level.RemoveMonster(tp)
|
g.Level.RemoveMonster(tp)
|
||||||
|
|
||||||
@@ -94,7 +139,7 @@ func (g *RogueGame) doZap() {
|
|||||||
oldch := tp.OldCh
|
oldch := tp.OldCh
|
||||||
g.Delta.Y = y
|
g.Delta.Y = y
|
||||||
g.Delta.X = x
|
g.Delta.X = x
|
||||||
monster = g.randomMonsterLetter()
|
monster := g.randomMonsterLetter()
|
||||||
g.newMonster(tp, monster, g.Delta)
|
g.newMonster(tp, monster, g.Delta)
|
||||||
|
|
||||||
if g.seeMonst(tp) {
|
if g.seeMonst(tp) {
|
||||||
@@ -107,15 +152,32 @@ func (g *RogueGame) doZap() {
|
|||||||
if g.seeMonst(tp) {
|
if g.seeMonst(tp) {
|
||||||
g.Items.Sticks[WandPolymorph].Know = true
|
g.Items.Sticks[WandPolymorph].Know = true
|
||||||
}
|
}
|
||||||
case WandCancellation:
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapCancellation(*Object) bool {
|
||||||
|
if tp := g.zapVictim(); tp != nil {
|
||||||
tp.Flags.Set(Cancelled)
|
tp.Flags.Set(Cancelled)
|
||||||
tp.Flags.Clear(Invisible | CanConfuse)
|
tp.Flags.Clear(Invisible | CanConfuse)
|
||||||
|
|
||||||
tp.Disguise = tp.Type
|
tp.Disguise = tp.Type
|
||||||
if g.seeMonst(tp) {
|
if g.seeMonst(tp) {
|
||||||
g.mvaddch(y, x, tp.Disguise)
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Disguise)
|
||||||
}
|
}
|
||||||
case WandTeleportAway, WandTeleportTo:
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapTeleport(obj *Object) bool {
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
tp := g.zapVictim()
|
||||||
|
if tp == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
var newPos Coord
|
var newPos Coord
|
||||||
|
|
||||||
if obj.WandKind() == WandTeleportAway {
|
if obj.WandKind() == WandTeleportAway {
|
||||||
@@ -133,9 +195,13 @@ func (g *RogueGame) doZap() {
|
|||||||
tp.Dest = &p.Pos
|
tp.Dest = &p.Pos
|
||||||
tp.Flags.Set(Awake)
|
tp.Flags.Set(Awake)
|
||||||
g.relocate(tp, newPos)
|
g.relocate(tp, newPos)
|
||||||
}
|
|
||||||
}
|
return true
|
||||||
case WandMagicMissile:
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapMagicMissile(*Object) bool {
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
g.Items.Sticks[WandMagicMissile].Know = true
|
g.Items.Sticks[WandMagicMissile].Know = true
|
||||||
bolt := newObject()
|
bolt := newObject()
|
||||||
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
|
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
|
||||||
@@ -158,16 +224,12 @@ func (g *RogueGame) doZap() {
|
|||||||
} else {
|
} else {
|
||||||
g.msg("the missle vanishes with a puff of smoke") //nolint:misspell // C's spelling
|
g.msg("the missle vanishes with a puff of smoke") //nolint:misspell // C's spelling
|
||||||
}
|
}
|
||||||
case WandHasteMonster, WandSlowMonster:
|
|
||||||
y := p.Pos.Y
|
|
||||||
|
|
||||||
x := p.Pos.X
|
return true
|
||||||
for stepOk(g.Level.VisibleChar(y, x)) {
|
}
|
||||||
y += g.Delta.Y
|
|
||||||
x += g.Delta.X
|
|
||||||
}
|
|
||||||
|
|
||||||
if tp := g.Level.MonsterAt(y, x); tp != nil {
|
func (g *RogueGame) zapSpeed(obj *Object) bool {
|
||||||
|
if tp := g.zapRayMonster(); tp != nil {
|
||||||
if obj.WandKind() == WandHasteMonster {
|
if obj.WandKind() == WandHasteMonster {
|
||||||
if tp.On(Slowed) {
|
if tp.On(Slowed) {
|
||||||
tp.Flags.Clear(Slowed)
|
tp.Flags.Clear(Slowed)
|
||||||
@@ -184,11 +246,15 @@ func (g *RogueGame) doZap() {
|
|||||||
tp.Turn = true
|
tp.Turn = true
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Delta.Y = y
|
g.Delta.Y = tp.Pos.Y
|
||||||
g.Delta.X = x
|
g.Delta.X = tp.Pos.X
|
||||||
g.runTo(g.Delta)
|
g.runTo(g.Delta)
|
||||||
}
|
}
|
||||||
case WandLightning, WandFire, WandCold:
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) zapBolt(obj *Object) bool {
|
||||||
var name string
|
var name string
|
||||||
|
|
||||||
switch obj.WandKind() {
|
switch obj.WandKind() {
|
||||||
@@ -200,12 +266,10 @@ func (g *RogueGame) doZap() {
|
|||||||
name = "ice"
|
name = "ice"
|
||||||
}
|
}
|
||||||
|
|
||||||
g.fireBolt(p.Pos, &g.Delta, name)
|
g.fireBolt(g.Player.Pos, &g.Delta, name)
|
||||||
g.Items.Sticks[obj.Which].Know = true
|
g.Items.Sticks[obj.Which].Know = true
|
||||||
case WandNothing:
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.Charges--
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// drain does the drain-hit-points-from-player schtick (sticks.c drain).
|
// drain does the drain-hit-points-from-player schtick (sticks.c drain).
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ type gameData struct {
|
|||||||
// the cases of the scrolls.c read_scroll switch. The handler gets
|
// the cases of the scrolls.c read_scroll switch. The handler gets
|
||||||
// the scroll being read (the identify family needs its subtype).
|
// the scroll being read (the identify family needs its subtype).
|
||||||
readHandlers [NumScrollTypes]func(g *RogueGame, obj *Object)
|
readHandlers [NumScrollTypes]func(g *RogueGame, obj *Object)
|
||||||
|
|
||||||
|
// zapHandlers dispatches each wand kind to its effect method: the
|
||||||
|
// cases of the sticks.c do_zap switch. A false return aborts the
|
||||||
|
// zap without using a charge (drain life on a too-weak hero).
|
||||||
|
zapHandlers [NumWandTypes]func(g *RogueGame, obj *Object) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ripWall is the repeated blank wall line of the tombstone art.
|
// ripWall is the repeated blank wall line of the tombstone art.
|
||||||
@@ -635,6 +640,22 @@ func newGameData() *gameData {
|
|||||||
ScrollAggravateMonsters: (*RogueGame).readAggravateMonsters,
|
ScrollAggravateMonsters: (*RogueGame).readAggravateMonsters,
|
||||||
ScrollProtectArmor: (*RogueGame).readProtectArmor,
|
ScrollProtectArmor: (*RogueGame).readProtectArmor,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
zapHandlers: [NumWandTypes]func(*RogueGame, *Object) bool{
|
||||||
|
WandLight: (*RogueGame).zapLight,
|
||||||
|
WandInvisibility: (*RogueGame).zapInvisibility,
|
||||||
|
WandLightning: (*RogueGame).zapBolt,
|
||||||
|
WandFire: (*RogueGame).zapBolt,
|
||||||
|
WandCold: (*RogueGame).zapBolt,
|
||||||
|
WandPolymorph: (*RogueGame).zapPolymorph,
|
||||||
|
WandMagicMissile: (*RogueGame).zapMagicMissile,
|
||||||
|
WandHasteMonster: (*RogueGame).zapSpeed,
|
||||||
|
WandSlowMonster: (*RogueGame).zapSpeed,
|
||||||
|
WandDrainLife: (*RogueGame).zapDrainLife,
|
||||||
|
WandTeleportAway: (*RogueGame).zapTeleport,
|
||||||
|
WandTeleportTo: (*RogueGame).zapTeleport,
|
||||||
|
WandCancellation: (*RogueGame).zapCancellation,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user