From 1a25beead80f574aad9679790da35eaf8c173776 Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 7 Jul 2026 02:49:37 +0200 Subject: [PATCH] Decompose fireBolt (refactor step 7) The fire_bolt loop splits into boltDirChar, boltBounces, boltStrikesMonster, and boltStrikesHero; loop state (hitHero/changed/ used) stays in fireBolt. Effect order and RNG calls unchanged. --- game/sticks.go | 181 ++++++++++++++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 78 deletions(-) diff --git a/game/sticks.go b/game/sticks.go index 4d1d816..4302294 100644 --- a/game/sticks.go +++ b/game/sticks.go @@ -326,21 +326,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) { bolt.DPlus = 0 g.Items.Weapons[WeaponFlame].Name = name - var dirch byte - - switch dir.Y + dir.X { - case 0: - dirch = '/' - case 1, -1: - if dir.Y == 0 { - dirch = '-' - } else { - dirch = '|' - } - case 2, -2: - dirch = '\\' - } - + dirch := boltDirChar(*dir) pos := start hitHero := !fromHero used := false @@ -351,22 +337,9 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) { pos.Y += dir.Y pos.X += dir.X spotpos = append(spotpos, pos) + ch := g.Level.VisibleChar(pos.Y, pos.X) - bounce := false - - switch ch { - case Door: - // this code is necessary if the hero is on a door and he - // fires at the wall the door is in, it would otherwise loop - // infinitely - if p.Pos != pos { - bounce = true - } - case '|', '-', ' ': - bounce = true - } - - if bounce { + if boltBounces(ch, p.Pos, pos) { if !changed { hitHero = !hitHero } @@ -384,57 +357,11 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) { if tp := g.Level.MonsterAt(pos.Y, pos.X); !hitHero && tp != nil { hitHero = true changed = !changed - - tp.OldCh = g.Level.Char(pos.Y, pos.X) - if !g.saveThrow(VsMagic, &tp.Stats) { - bolt.Pos = pos - used = true - - if tp.Type == 'D' && name == "flame" { - g.addmsgf("the flame bounces") - - if !g.Options.Terse { - g.addmsgf(" off the dragon") - } - - g.endmsg() - } else { - g.hitMonster(pos, bolt) - } - } else if ch != 'M' || tp.Disguise == 'M' { - if fromHero { - g.runTo(pos) - } - - if g.Options.Terse { - g.msg("%s misses", name) - } else { - g.msg("the %s whizzes past %s", name, g.setMname(tp)) - } - } + used = g.boltStrikesMonster(tp, bolt, pos, ch, name, fromHero) } else if hitHero && pos == p.Pos { hitHero = false changed = !changed - - if !g.save(VsMagic) { - if p.Stats.HP -= g.roll(6, 6); p.Stats.HP <= 0 { - if fromHero { - g.death('b') - } else { - g.death(g.Level.MonsterAt(start.Y, start.X).Type) - } - } - - used = true - - if g.Options.Terse { - g.msg("the %s hits", name) - } else { - g.msg("you are hit by the %s", name) - } - } else { - g.msg("the %s whizzes by you", name) - } + used = g.boltStrikesHero(start, name, fromHero) } g.mvaddch(pos.Y, pos.X, dirch) @@ -446,6 +373,104 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) { } } +// boltDirChar picks the character a traveling bolt is drawn with for its +// direction (the dirch switch of sticks.c fire_bolt). +func boltDirChar(dir Coord) byte { + switch dir.Y + dir.X { + case 0: + return '/' + case 1, -1: + if dir.Y == 0 { + return '-' + } + + return '|' + case 2, -2: + return '\\' + } + + return 0 // unreachable for the eight legal directions, as in C +} + +// boltBounces reports whether a bolt bounces off this spot: walls, and +// any door except the one the hero stands on (which would otherwise loop +// infinitely, per the C comment in fire_bolt). +func boltBounces(ch byte, heroPos, pos Coord) bool { + switch ch { + case Door: + return heroPos != pos + case '|', '-', ' ': + return true + } + + return false +} + +// boltStrikesMonster resolves a bolt arriving on a monster's square (the +// monster arm of the fire_bolt loop). It reports whether the bolt was +// used up. +func (g *RogueGame) boltStrikesMonster(tp *Monster, bolt *Object, pos Coord, ch byte, name string, fromHero bool) bool { + tp.OldCh = g.Level.Char(pos.Y, pos.X) + if !g.saveThrow(VsMagic, &tp.Stats) { + bolt.Pos = pos + + if tp.Type == 'D' && name == "flame" { + g.addmsgf("the flame bounces") + + if !g.Options.Terse { + g.addmsgf(" off the dragon") + } + + g.endmsg() + } else { + g.hitMonster(pos, bolt) + } + + return true + } + + if ch != 'M' || tp.Disguise == 'M' { + if fromHero { + g.runTo(pos) + } + + if g.Options.Terse { + g.msg("%s misses", name) + } else { + g.msg("the %s whizzes past %s", name, g.setMname(tp)) + } + } + + return false +} + +// boltStrikesHero resolves a bolt arriving on the hero (the hero arm of +// the fire_bolt loop). It reports whether the bolt was used up. +func (g *RogueGame) boltStrikesHero(start Coord, name string, fromHero bool) bool { + p := &g.Player + if g.save(VsMagic) { + g.msg("the %s whizzes by you", name) + + return false + } + + if p.Stats.HP -= g.roll(6, 6); p.Stats.HP <= 0 { + if fromHero { + g.death('b') + } else { + g.death(g.Level.MonsterAt(start.Y, start.X).Type) + } + } + + if g.Options.Terse { + g.msg("the %s hits", name) + } else { + g.msg("you are hit by the %s", name) + } + + return true +} + // fixStick sets up a new wand or staff (sticks.c fix_stick). func (g *RogueGame) fixStick(cur *Object) { if g.Items.WandType[cur.Which] == staffName {