diff --git a/game/scrolls.go b/game/scrolls.go index f1eea9e..fb03628 100644 --- a/game/scrolls.go +++ b/game/scrolls.go @@ -28,245 +28,8 @@ func (g *RogueGame) readScroll() { // Get rid of the thing g.leavePack(obj, false, false) - switch obj.ScrollKind() { - case ScrollMonsterConfusion: - // Scroll of monster confusion. Give him that power. - p.Flags.Set(CanConfuse) - g.msg("your hands begin to glow %s", g.pickColor("red")) - case ScrollEnchantArmor: - if p.CurArmor != nil { - p.CurArmor.ArmorClass-- - p.CurArmor.Flags.Clear(Cursed) - g.msg("your armor glows %s for a moment", g.pickColor("silver")) - } - case ScrollHoldMonster: - // Hold monster scroll. Stop all monsters within two spaces from - // chasing after the hero. - held := 0 - - for x := p.Pos.X - 2; x <= p.Pos.X+2; x++ { - if x < 0 || x >= NumCols { - continue - } - - for y := p.Pos.Y - 2; y <= p.Pos.Y+2; y++ { - if y < 0 || y > NumLines-1 { - continue - } - - if mp := g.Level.MonsterAt(y, x); mp != nil && mp.On(Awake) { - mp.Flags.Clear(Awake) - mp.Flags.Set(Held) - - held++ - } - } - } - - 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") - } - case ScrollSleep: - // Scroll which makes you fall asleep - g.Items.Scrolls[ScrollSleep].Know = true - g.NoCommand += g.rnd(g.spread(5)) + 4 // SLEEPTIME - - p.Flags.Clear(Awake) - g.msg("you fall asleep") - case ScrollCreateMonster: - // Create a monster: first look in a circle around him, next try - // his room, otherwise give up - i := 0 - - var mp Coord - - for y := p.Pos.Y - 1; y <= p.Pos.Y+1; y++ { - for x := p.Pos.X - 1; x <= p.Pos.X+1; x++ { - // Don't put a monster on top of the player. - if y == p.Pos.Y && x == p.Pos.X { - continue - } - // Or anything else nasty - if ch := g.Level.VisibleChar(y, x); stepOk(ch) { - if ch == Scroll { - if fo := g.Level.ObjectAt(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster { - continue - } - } - - if i++; g.rnd(i) == 0 { - mp = Coord{Y: y, X: x} - } - } - } - } - - 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) - } - case ScrollIdentifyPotion, ScrollIdentifyScroll, ScrollIdentifyWeapon, ScrollIdentifyArmor, ScrollIdentifyRingOrStick: - // Identify, let him figure something out - g.Items.Scrolls[obj.Which].Know = true - g.msg("this scroll is an %s scroll", g.Items.Scrolls[obj.Which].Name) - g.whatis(true, g.data.idType[obj.ScrollKind()]) - case ScrollMagicMapping: - // Scroll of magic mapping. - g.Items.Scrolls[ScrollMagicMapping].Know = true - g.msg("oh, now this scroll has a map on it") - // take all the things we want to keep hidden out of the window - for y := 1; y < NumLines-1; y++ { - for x := range NumCols { - pp := g.Level.At(y, x) - 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) - } - 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 - } - case Passage: - pass = true - case Floor: - if pp.Flags.Has(FReal) { - ch = ' ' - } else { - ch = Trap - pp.Ch = Trap - pp.Flags.Set(FSeen | FReal) - } - default: - if pp.Flags.Has(FPassage) { - pass = true - } else { - ch = ' ' - } - } - - if pass { - if !pp.Flags.Has(FReal) { - pp.Ch = Passage - } - - pp.Flags.Set(FSeen | FReal) - - ch = Passage - } - - if ch != ' ' { - if tp := pp.Monst; tp != nil { - tp.OldCh = ch - if !p.On(SenseMonsters) { - g.mvaddch(y, x, ch) - } - } else { - g.mvaddch(y, x, ch) - } - } - } - } - case ScrollFoodDetection: - // Food detection - found := false - - g.scr.Hw.Clear() - - for _, fo := range g.Level.Objects { - if fo.Kind == KindFood { - found = true - - g.scr.Hw.MvAddCh(fo.Pos.Y, fo.Pos.X, Food) - } - } - - if found { - g.Items.Scrolls[ScrollFoodDetection].Know = true - g.showWin("Your nose tingles and you smell food.--More--") - } else { - g.msg("your nose tingles") - } - case ScrollTeleportation: - // Scroll of teleportation: make him disappear and reappear - curRoom := p.Room - - g.teleport() - - if curRoom != p.Room { - g.Items.Scrolls[ScrollTeleportation].Know = true - } - case ScrollEnchantWeapon: - if p.CurWeapon == nil || p.CurWeapon.Kind != KindWeapon { - g.msg("you feel a strange sense of loss") - } else { - p.CurWeapon.Flags.Clear(Cursed) - - if g.rnd(2) == 0 { - p.CurWeapon.HPlus++ - } else { - p.CurWeapon.DPlus++ - } - - g.msg("your %s glows %s for a moment", - g.Items.Weapons[p.CurWeapon.Which].Name, g.pickColor("blue")) - } - case ScrollScareMonster: - // Reading it is a mistake and produces laughter at her poor boo - // boo. - g.msg("you hear maniacal laughter in the distance") - case ScrollRemoveCurse: - uncurse(p.CurArmor) - uncurse(p.CurWeapon) - uncurse(p.CurRing[Left]) - uncurse(p.CurRing[Right]) - g.msg("%s", g.chooseStr("you feel in touch with the Universal Onenes", - "you feel as if somebody is watching over you")) - case ScrollAggravateMonsters: - // This scroll aggravates all the monsters on the current level - // and sets them running towards the hero - g.aggravate() - g.msg("you hear a high pitched humming noise") - case ScrollProtectArmor: - if p.CurArmor != nil { - p.CurArmor.Flags.Set(Protected) - g.msg("your armor is covered by a shimmering %s shield", - g.pickColor("gold")) - } else { - g.msg("you feel a strange sense of loss") - } + if h := g.data.readHandlers[obj.ScrollKind()]; h != nil { + h(g, obj) } g.look(true) // put the result of the scroll on the screen @@ -275,6 +38,290 @@ func (g *RogueGame) readScroll() { g.callIt(&g.Items.Scrolls[obj.Which]) } +// The per-scroll effect handlers, dispatched through +// gameData.readHandlers. Each is one case of the C read_scroll switch. + +func (g *RogueGame) readMonsterConfusion(*Object) { + // Scroll of monster confusion. Give him that power. + g.Player.Flags.Set(CanConfuse) + g.msg("your hands begin to glow %s", g.pickColor("red")) +} + +func (g *RogueGame) readEnchantArmor(*Object) { + p := &g.Player + if p.CurArmor != nil { + p.CurArmor.ArmorClass-- + p.CurArmor.Flags.Clear(Cursed) + g.msg("your armor glows %s for a moment", g.pickColor("silver")) + } +} + +func (g *RogueGame) readHoldMonster(*Object) { + // Hold monster scroll. Stop all monsters within two spaces from + // chasing after the hero. + p := &g.Player + held := 0 + + for x := p.Pos.X - 2; x <= p.Pos.X+2; x++ { + if x < 0 || x >= NumCols { + continue + } + + for y := p.Pos.Y - 2; y <= p.Pos.Y+2; y++ { + if y < 0 || y > NumLines-1 { + continue + } + + if mp := g.Level.MonsterAt(y, x); mp != nil && mp.On(Awake) { + mp.Flags.Clear(Awake) + mp.Flags.Set(Held) + + held++ + } + } + } + + 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") + } +} + +func (g *RogueGame) readSleep(*Object) { + // Scroll which makes you fall asleep + g.Items.Scrolls[ScrollSleep].Know = true + g.NoCommand += g.rnd(g.spread(5)) + 4 // SLEEPTIME + + g.Player.Flags.Clear(Awake) + g.msg("you fall asleep") +} + +func (g *RogueGame) readCreateMonster(*Object) { + // Create a monster: first look in a circle around him, next try + // his room, otherwise give up + p := &g.Player + i := 0 + + var mp Coord + + for y := p.Pos.Y - 1; y <= p.Pos.Y+1; y++ { + for x := p.Pos.X - 1; x <= p.Pos.X+1; x++ { + // Don't put a monster on top of the player. + if y == p.Pos.Y && x == p.Pos.X { + continue + } + // Or anything else nasty + if ch := g.Level.VisibleChar(y, x); stepOk(ch) { + if ch == Scroll { + if fo := g.Level.ObjectAt(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster { + continue + } + } + + if i++; g.rnd(i) == 0 { + mp = Coord{Y: y, X: x} + } + } + } + } + + 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) + } +} + +func (g *RogueGame) readIdentify(obj *Object) { + // Identify, let him figure something out + g.Items.Scrolls[obj.Which].Know = true + g.msg("this scroll is an %s scroll", g.Items.Scrolls[obj.Which].Name) + g.whatis(true, g.data.idType[obj.ScrollKind()]) +} + +func (g *RogueGame) readMagicMapping(*Object) { + // Scroll of magic mapping. + g.Items.Scrolls[ScrollMagicMapping].Know = true + g.msg("oh, now this scroll has a map on it") + // take all the things we want to keep hidden out of the window + for y := 1; y < NumLines-1; y++ { + for x := range NumCols { + g.revealSpot(y, x) + } + } +} + +// 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). +func (g *RogueGame) revealSpot(y, x int) { + pp := g.Level.At(y, x) + 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) + } + 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 + } + case Passage: + pass = true + case Floor: + if pp.Flags.Has(FReal) { + ch = ' ' + } else { + ch = Trap + pp.Ch = Trap + pp.Flags.Set(FSeen | FReal) + } + default: + if pp.Flags.Has(FPassage) { + pass = true + } else { + ch = ' ' + } + } + + if pass { + if !pp.Flags.Has(FReal) { + pp.Ch = Passage + } + + pp.Flags.Set(FSeen | FReal) + + 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) + } + } +} + +func (g *RogueGame) readFoodDetection(*Object) { + // Food detection + found := false + + g.scr.Hw.Clear() + + for _, fo := range g.Level.Objects { + if fo.Kind == KindFood { + found = true + + g.scr.Hw.MvAddCh(fo.Pos.Y, fo.Pos.X, Food) + } + } + + if found { + g.Items.Scrolls[ScrollFoodDetection].Know = true + g.showWin("Your nose tingles and you smell food.--More--") + } else { + g.msg("your nose tingles") + } +} + +func (g *RogueGame) readTeleportation(*Object) { + // Scroll of teleportation: make him disappear and reappear + p := &g.Player + curRoom := p.Room + + g.teleport() + + if curRoom != p.Room { + g.Items.Scrolls[ScrollTeleportation].Know = true + } +} + +func (g *RogueGame) readEnchantWeapon(*Object) { + p := &g.Player + if p.CurWeapon == nil || p.CurWeapon.Kind != KindWeapon { + g.msg("you feel a strange sense of loss") + } else { + p.CurWeapon.Flags.Clear(Cursed) + + if g.rnd(2) == 0 { + p.CurWeapon.HPlus++ + } else { + p.CurWeapon.DPlus++ + } + + g.msg("your %s glows %s for a moment", + g.Items.Weapons[p.CurWeapon.Which].Name, g.pickColor("blue")) + } +} + +func (g *RogueGame) readScareMonster(*Object) { + // Reading it is a mistake and produces laughter at her poor boo + // boo. + g.msg("you hear maniacal laughter in the distance") +} + +func (g *RogueGame) readRemoveCurse(*Object) { + p := &g.Player + + uncurse(p.CurArmor) + uncurse(p.CurWeapon) + uncurse(p.CurRing[Left]) + uncurse(p.CurRing[Right]) + g.msg("%s", g.chooseStr("you feel in touch with the Universal Onenes", + "you feel as if somebody is watching over you")) +} + +func (g *RogueGame) readAggravateMonsters(*Object) { + // This scroll aggravates all the monsters on the current level + // and sets them running towards the hero + g.aggravate() + g.msg("you hear a high pitched humming noise") +} + +func (g *RogueGame) readProtectArmor(*Object) { + p := &g.Player + if p.CurArmor != nil { + p.CurArmor.Flags.Set(Protected) + g.msg("your armor is covered by a shimmering %s shield", + g.pickColor("gold")) + } else { + g.msg("you feel a strange sense of loss") + } +} + // uncurse uncurses an item (scrolls.c uncurse). func uncurse(obj *Object) { if obj != nil { diff --git a/game/tables.go b/game/tables.go index 3f44b0e..665aaed 100644 --- a/game/tables.go +++ b/game/tables.go @@ -127,6 +127,11 @@ type gameData struct { // the cases of the potions.c quaff switch. The bool is trip — was // the hero hallucinating when the potion went down. quaffHandlers [NumPotionTypes]func(g *RogueGame, trip bool) + + // readHandlers dispatches each scroll kind to its effect method: + // the cases of the scrolls.c read_scroll switch. The handler gets + // the scroll being read (the identify family needs its subtype). + readHandlers [NumScrollTypes]func(g *RogueGame, obj *Object) } // ripWall is the repeated blank wall line of the tombstone art. @@ -609,6 +614,27 @@ func newGameData() *gameData { PotionBlindness: (*RogueGame).quaffBlindness, PotionLevitation: (*RogueGame).quaffLevitation, }, + + readHandlers: [NumScrollTypes]func(*RogueGame, *Object){ + ScrollMonsterConfusion: (*RogueGame).readMonsterConfusion, + ScrollMagicMapping: (*RogueGame).readMagicMapping, + ScrollHoldMonster: (*RogueGame).readHoldMonster, + ScrollSleep: (*RogueGame).readSleep, + ScrollEnchantArmor: (*RogueGame).readEnchantArmor, + ScrollIdentifyPotion: (*RogueGame).readIdentify, + ScrollIdentifyScroll: (*RogueGame).readIdentify, + ScrollIdentifyWeapon: (*RogueGame).readIdentify, + ScrollIdentifyArmor: (*RogueGame).readIdentify, + ScrollIdentifyRingOrStick: (*RogueGame).readIdentify, + ScrollScareMonster: (*RogueGame).readScareMonster, + ScrollFoodDetection: (*RogueGame).readFoodDetection, + ScrollTeleportation: (*RogueGame).readTeleportation, + ScrollEnchantWeapon: (*RogueGame).readEnchantWeapon, + ScrollCreateMonster: (*RogueGame).readCreateMonster, + ScrollRemoveCurse: (*RogueGame).readRemoveCurse, + ScrollAggravateMonsters: (*RogueGame).readAggravateMonsters, + ScrollProtectArmor: (*RogueGame).readProtectArmor, + }, } }