From ebe477ba2875cbe44fa39993dc916dc5ab71fc5a Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 7 Jul 2026 02:53:09 +0200 Subject: [PATCH] Decompose remaining effects-file hot spots (refactor step 7) drain gains drainReaches; zapSpeed gains hasteTarget/slowTarget; readHoldMonster gains holdMonstersNear; readCreateMonster gains createMonsterSpot; revealSpot splits into revealChar/revealWall/ revealSolid/revealFloor; turnSee gains showSensed. potions.go, scrolls.go, and sticks.go are complexity-clean. Behavior and RNG call order unchanged. --- game/potions.go | 41 +++++++----- game/scrolls.go | 163 ++++++++++++++++++++++++++++++------------------ game/sticks.go | 71 ++++++++++++++------- 3 files changed, 176 insertions(+), 99 deletions(-) diff --git a/game/potions.go b/game/potions.go index 8d39e2b..70c2e09 100644 --- a/game/potions.go +++ b/game/potions.go @@ -298,22 +298,8 @@ func (g *RogueGame) turnSee(turnOff bool) bool { if !canSee { g.addch(mp.OldCh) } - } else { - if !canSee { - g.standout() - } - - if !g.Player.On(Hallucinating) { - g.addch(mp.Type) - } else { - g.addch(g.randomMonsterLetter()) - } - - if !canSee { - g.standend() - - addNew = true - } + } else if g.showSensed(mp, canSee) { + addNew = true } } @@ -326,6 +312,29 @@ func (g *RogueGame) turnSee(turnOff bool) bool { return addNew } +// showSensed draws one monster for monster sense, standout when it is +// otherwise invisible; it reports whether the monster was newly revealed +// (the turn-on arm of the C turn_see loop). +func (g *RogueGame) showSensed(mp *Monster, canSee bool) bool { + if !canSee { + g.standout() + } + + if !g.Player.On(Hallucinating) { + g.addch(mp.Type) + } else { + g.addch(g.randomMonsterLetter()) + } + + if !canSee { + g.standend() + + return true + } + + return false +} + // seenStairs reports whether the player has seen the stairs (potions.c // seen_stairs). func (g *RogueGame) seenStairs() bool { diff --git a/game/scrolls.go b/game/scrolls.go index fb03628..75303cc 100644 --- a/game/scrolls.go +++ b/game/scrolls.go @@ -59,6 +59,31 @@ func (g *RogueGame) readEnchantArmor(*Object) { func (g *RogueGame) readHoldMonster(*Object) { // Hold monster scroll. Stop all monsters within two spaces from // chasing after the hero. + held := g.holdMonstersNear() + + if held > 0 { + g.addmsgf("the monster") + + if held > 1 { + g.addmsgf("s around you") + } + + g.addmsgf(" freeze") + + if held == 1 { + g.addmsgf("s") + } + + g.endmsg() + g.Items.Scrolls[ScrollHoldMonster].Know = true + } else { + g.msg("you feel a strange sense of loss") + } +} + +// holdMonstersNear freezes every awake monster within two spaces of the +// hero and reports how many froze (the scan of the C S_HOLD case). +func (g *RogueGame) holdMonstersNear() int { p := &g.Player held := 0 @@ -81,24 +106,7 @@ func (g *RogueGame) readHoldMonster(*Object) { } } - if held > 0 { - g.addmsgf("the monster") - - if held > 1 { - g.addmsgf("s around you") - } - - g.addmsgf(" freeze") - - if held == 1 { - g.addmsgf("s") - } - - g.endmsg() - g.Items.Scrolls[ScrollHoldMonster].Know = true - } else { - g.msg("you feel a strange sense of loss") - } + return held } func (g *RogueGame) readSleep(*Object) { @@ -113,6 +121,19 @@ func (g *RogueGame) readSleep(*Object) { func (g *RogueGame) readCreateMonster(*Object) { // Create a monster: first look in a circle around him, next try // his room, otherwise give up + mp, ok := g.createMonsterSpot() + if !ok { + g.msg("you hear a faint cry of anguish in the distance") + } else { + tp := &Monster{} + g.newMonster(tp, g.randMonster(false), mp) + } +} + +// createMonsterSpot reservoir-samples a legal spot around the hero for a +// created monster; ok is false when every neighbor is blocked (the scan +// of the C S_CREATE case). +func (g *RogueGame) createMonsterSpot() (Coord, bool) { p := &g.Player i := 0 @@ -139,12 +160,7 @@ func (g *RogueGame) readCreateMonster(*Object) { } } - if i == 0 { - g.msg("you hear a faint cry of anguish in the distance") - } else { - tp := &Monster{} - g.newMonster(tp, g.randMonster(false), mp) - } + return mp, i != 0 } func (g *RogueGame) readIdentify(obj *Object) { @@ -166,45 +182,41 @@ func (g *RogueGame) readMagicMapping(*Object) { } } -// revealSpot uncovers one map cell for magic mapping: secret doors and -// passages become real, hidden traps show, and the visible character is -// drawn (the loop body of the C SCR_MAP case). +// revealSpot uncovers one map cell for magic mapping and draws it (the +// loop body of the C SCR_MAP case). func (g *RogueGame) revealSpot(y, x int) { pp := g.Level.At(y, x) + + ch := revealChar(pp) + if ch != ' ' { + if tp := pp.Monst; tp != nil { + tp.OldCh = ch + if !g.Player.On(SenseMonsters) { + g.mvaddch(y, x, ch) + } + } else { + g.mvaddch(y, x, ch) + } + } +} + +// revealChar decides what magic mapping shows at a cell, making secret +// doors, hidden passages, and hidden traps real as a side effect; ' ' +// means show nothing (the switch of the C SCR_MAP loop). +func revealChar(pp *Place) byte { ch := pp.Ch pass := false switch ch { case Door, Stairs: case '-', '|': - if !pp.Flags.Has(FReal) { - ch = Door - pp.Ch = Door - pp.Flags.Set(FReal) - } + ch = revealWall(pp) case ' ': - if pp.Flags.Has(FReal) { - // def: hidden things in walls stay hidden - if pp.Flags.Has(FPassage) { - pass = true - } else { - ch = ' ' - } - } else { - pp.Flags.Set(FReal) - pp.Ch = Passage - pass = true - } + pass = revealSolid(pp) case Passage: pass = true case Floor: - if pp.Flags.Has(FReal) { - ch = ' ' - } else { - ch = Trap - pp.Ch = Trap - pp.Flags.Set(FSeen | FReal) - } + ch = revealFloor(pp) default: if pp.Flags.Has(FPassage) { pass = true @@ -223,16 +235,47 @@ func (g *RogueGame) revealSpot(y, x int) { ch = Passage } - if ch != ' ' { - if tp := pp.Monst; tp != nil { - tp.OldCh = ch - if !g.Player.On(SenseMonsters) { - g.mvaddch(y, x, ch) - } - } else { - g.mvaddch(y, x, ch) - } + return ch +} + +// revealWall handles a wall cell for magic mapping: a secret door +// becomes a real door (the '-'/'|' arm). +func revealWall(pp *Place) byte { + if !pp.Flags.Has(FReal) { + pp.Ch = Door + pp.Flags.Set(FReal) + + return Door } + + return pp.Ch +} + +// revealSolid handles a blank cell for magic mapping, reporting whether +// it is passage: hidden passages become real; hidden things in walls +// stay hidden (the ' ' arm). +func revealSolid(pp *Place) bool { + if pp.Flags.Has(FReal) { + return pp.Flags.Has(FPassage) + } + + pp.Flags.Set(FReal) + pp.Ch = Passage + + return true +} + +// revealFloor handles a floor cell for magic mapping: a hidden trap +// becomes a real, seen trap; real floor shows nothing (the FLOOR arm). +func revealFloor(pp *Place) byte { + if pp.Flags.Has(FReal) { + return ' ' + } + + pp.Ch = Trap + pp.Flags.Set(FSeen | FReal) + + return Trap } func (g *RogueGame) readFoodDetection(*Object) { diff --git a/game/sticks.go b/game/sticks.go index 4302294..5675d16 100644 --- a/game/sticks.go +++ b/game/sticks.go @@ -229,31 +229,46 @@ func (g *RogueGame) zapMagicMissile(*Object) bool { } func (g *RogueGame) zapSpeed(obj *Object) bool { - if tp := g.zapRayMonster(); tp != nil { - if obj.WandKind() == WandHasteMonster { - if tp.On(Slowed) { - tp.Flags.Clear(Slowed) - } else { - tp.Flags.Set(Hasted) - } - } else { - if tp.On(Hasted) { - tp.Flags.Clear(Hasted) - } else { - tp.Flags.Set(Slowed) - } - - tp.Turn = true - } - - g.Delta.Y = tp.Pos.Y - g.Delta.X = tp.Pos.X - g.runTo(g.Delta) + tp := g.zapRayMonster() + if tp == nil { + return true } + if obj.WandKind() == WandHasteMonster { + hasteTarget(tp) + } else { + slowTarget(tp) + } + + g.Delta.Y = tp.Pos.Y + g.Delta.X = tp.Pos.X + g.runTo(g.Delta) + return true } +// hasteTarget cancels a slow or applies a haste (the WS_HASTE_M arm of +// do_zap). +func hasteTarget(tp *Monster) { + if tp.On(Slowed) { + tp.Flags.Clear(Slowed) + } else { + tp.Flags.Set(Hasted) + } +} + +// slowTarget cancels a haste or applies a slow (the WS_SLOW_M arm of +// do_zap). +func slowTarget(tp *Monster) { + if tp.On(Hasted) { + tp.Flags.Clear(Hasted) + } else { + tp.Flags.Set(Slowed) + } + + tp.Turn = true +} + func (g *RogueGame) zapBolt(obj *Object) bool { var name string @@ -286,9 +301,7 @@ func (g *RogueGame) drain() { var drainee []*Monster for _, mp := range g.Level.Monsters { - if mp.Room == p.Room || mp.Room == corp || - (inpass && g.Level.Char(mp.Pos.Y, mp.Pos.X) == Door && - &g.Level.Passages[*g.Level.FlagsAt(mp.Pos.Y, mp.Pos.X)&FPassNum] == p.Room) { + if g.drainReaches(mp, corp, inpass) { drainee = append(drainee, mp) } } @@ -312,6 +325,18 @@ func (g *RogueGame) drain() { } } +// drainReaches reports whether the drain-life wand reaches this monster: +// the hero's room, the passage behind the door he stands on, or — when +// he is in a passage — a door of that same passage (the drainee +// condition of sticks.c drain). +func (g *RogueGame) drainReaches(mp *Monster, corp *Room, inpass bool) bool { + p := &g.Player + + return mp.Room == p.Room || mp.Room == corp || + (inpass && g.Level.Char(mp.Pos.Y, mp.Pos.X) == Door && + &g.Level.Passages[*g.Level.FlagsAt(mp.Pos.Y, mp.Pos.X)&FPassNum] == p.Room) +} + // fireBolt fires a bolt in a given direction from a specific starting // place (sticks.c fire_bolt). func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {