ObjectKind separates what an item is from how it draws: Object.Type (a byte that doubled as the map character) becomes Kind ObjectKind, with Glyph() producing the display character and objectKindForGlyph converting back at the map boundary. KindWand covers the C 'stick' category. The magic-missile bolt uses KindGold, matching C's literal o_type='*' trick, with a comment. PotionKind, ScrollKind, RingKind, WandKind, WeaponKind, ArmorKind and TrapKind are now iota enums with Stringer (names come from the base info tables); Object gains typed accessors (obj.RingKind(), ...) and Launch is typed WeaponKind. beTrapped returns TrapKind. The getItem/inventory/whatis prompt filters take ObjectKind, with KindCallable/KindRingOrStick replacing the C CALLABLE/R_OR_S sentinels; wizard.c's type_name table is subsumed by ObjectKind.String(). IsRing/IsWearing/initWeapon/doPot signatures are typed accordingly. The save format version becomes 5.4.4-go2: the gob field rename would otherwise silently zero Kind when reading old saves. No behavior change; full suite green.
291 lines
7.6 KiB
Go
291 lines
7.6 KiB
Go
package game
|
|
|
|
import "fmt"
|
|
|
|
// potions.c — functions for dealing with potions.
|
|
|
|
// pact describes a standard fuse-based potion (potions.c PACT).
|
|
type pact struct {
|
|
flags CreatureFlags
|
|
daemon DaemonID
|
|
time int
|
|
high string // message when hallucinating
|
|
straight string
|
|
}
|
|
|
|
// pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic
|
|
// (it names the fruit) and is computed in doPot.
|
|
var pActions = [NumPotionTypes]pact{
|
|
PotionConfusion: {Confused, DUnconfuse, HuhDuration,
|
|
"what a tripy feeling!",
|
|
"wait, what's going on here. Huh? What? Who?"},
|
|
PotionLSD: {Hallucinating, DComeDown, SeeDuration,
|
|
"Oh, wow! Everything seems so cosmic!",
|
|
"Oh, wow! Everything seems so cosmic!"},
|
|
PotionSeeInvisible: {CanSeeInvisible, DUnsee, SeeDuration, "", ""},
|
|
PotionBlindness: {Blind, DSight, SeeDuration,
|
|
"oh, bummer! Everything is dark! Help!",
|
|
"a cloak of darkness falls around you"},
|
|
PotionLevitation: {Levitating, DLand, HealTime,
|
|
"oh, wow! You're floating in the air!",
|
|
"you start to float in the air"},
|
|
}
|
|
|
|
// quaff drinks a potion from the pack (potions.c quaff).
|
|
func (g *RogueGame) quaff() {
|
|
p := &g.Player
|
|
obj := g.getItem("quaff", KindPotion)
|
|
// Make certain that it is something that we want to drink
|
|
if obj == nil {
|
|
return
|
|
}
|
|
if obj.Kind != KindPotion {
|
|
if !g.Options.Terse {
|
|
g.msg("yuk! Why would you want to drink that?")
|
|
} else {
|
|
g.msg("that's undrinkable")
|
|
}
|
|
return
|
|
}
|
|
if obj == p.CurWeapon {
|
|
p.CurWeapon = nil
|
|
}
|
|
|
|
// Calculate the effect it has on the poor guy.
|
|
trip := p.On(Hallucinating)
|
|
g.leavePack(obj, false, false)
|
|
switch obj.PotionKind() {
|
|
case PotionConfusion:
|
|
g.doPot(PotionConfusion, !trip)
|
|
case PotionPoison:
|
|
g.Items.Potions[PotionPoison].Know = true
|
|
if p.IsWearing(RingSustainStrength) {
|
|
g.msg("you feel momentarily sick")
|
|
} else {
|
|
g.chgStr(-(g.rnd(3) + 1))
|
|
g.msg("you feel very sick now")
|
|
g.comeDown(0)
|
|
}
|
|
case PotionHealing:
|
|
g.Items.Potions[PotionHealing].Know = true
|
|
if p.Stats.HP += g.roll(p.Stats.Lvl, 4); p.Stats.HP > p.Stats.MaxHP {
|
|
p.Stats.MaxHP++
|
|
p.Stats.HP = p.Stats.MaxHP
|
|
}
|
|
g.sight(0)
|
|
g.msg("you begin to feel better")
|
|
case PotionGainStrength:
|
|
g.Items.Potions[PotionGainStrength].Know = true
|
|
g.chgStr(1)
|
|
g.msg("you feel stronger, now. What bulging muscles!")
|
|
case PotionDetectMonsters:
|
|
p.Flags.Set(SenseMonsters)
|
|
g.Fuse(DTurnSee, 1, HuhDuration, After)
|
|
if !g.turnSee(false) {
|
|
g.msg("you have a %s feeling for a moment, then it passes",
|
|
g.chooseStr("normal", "strange"))
|
|
}
|
|
case PotionDetectMagic:
|
|
// Potion of magic detection. Show the potions and scrolls
|
|
show := false
|
|
if len(g.Level.Objects) > 0 {
|
|
g.scr.Hw.Clear()
|
|
for _, tp := range g.Level.Objects {
|
|
if tp.isMagic() {
|
|
show = true
|
|
g.scr.Hw.MvAddCh(tp.Pos.Y, tp.Pos.X, Magic)
|
|
g.Items.Potions[PotionDetectMagic].Know = true
|
|
}
|
|
}
|
|
for _, mp := range g.Level.Monsters {
|
|
for _, tp := range mp.Pack {
|
|
if tp.isMagic() {
|
|
show = true
|
|
g.scr.Hw.MvAddCh(mp.Pos.Y, mp.Pos.X, Magic)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if show {
|
|
g.Items.Potions[PotionDetectMagic].Know = true
|
|
g.showWin("You sense the presence of magic on this level.--More--")
|
|
} else {
|
|
g.msg("you have a %s feeling for a moment, then it passes",
|
|
g.chooseStr("normal", "strange"))
|
|
}
|
|
case PotionLSD:
|
|
if !trip {
|
|
if p.On(SenseMonsters) {
|
|
g.turnSee(false)
|
|
}
|
|
g.StartDaemon(DVisuals, 0, Before)
|
|
g.SeenStairs = g.seenStairs()
|
|
}
|
|
g.doPot(PotionLSD, true)
|
|
case PotionSeeInvisible:
|
|
show := p.On(CanSeeInvisible)
|
|
g.doPot(PotionSeeInvisible, false)
|
|
if !show {
|
|
g.invisOn()
|
|
}
|
|
g.sight(0)
|
|
case PotionRaiseLevel:
|
|
g.Items.Potions[PotionRaiseLevel].Know = true
|
|
g.msg("you suddenly feel much more skillful")
|
|
g.raiseLevel()
|
|
case PotionExtraHealing:
|
|
g.Items.Potions[PotionExtraHealing].Know = true
|
|
if p.Stats.HP += g.roll(p.Stats.Lvl, 8); p.Stats.HP > p.Stats.MaxHP {
|
|
if p.Stats.HP > p.Stats.MaxHP+p.Stats.Lvl+1 {
|
|
p.Stats.MaxHP++
|
|
}
|
|
p.Stats.MaxHP++
|
|
p.Stats.HP = p.Stats.MaxHP
|
|
}
|
|
g.sight(0)
|
|
g.comeDown(0)
|
|
g.msg("you begin to feel much better")
|
|
case PotionHaste:
|
|
g.Items.Potions[PotionHaste].Know = true
|
|
g.After = false
|
|
if g.addHaste(true) {
|
|
g.msg("you feel yourself moving much faster")
|
|
}
|
|
case PotionRestoreStrength:
|
|
if p.IsRing(Left, RingAddStrength) {
|
|
addStr(&p.Stats.Str, -p.CurRing[Left].Arm)
|
|
}
|
|
if p.IsRing(Right, RingAddStrength) {
|
|
addStr(&p.Stats.Str, -p.CurRing[Right].Arm)
|
|
}
|
|
if p.Stats.Str < p.MaxStats.Str {
|
|
p.Stats.Str = p.MaxStats.Str
|
|
}
|
|
if p.IsRing(Left, RingAddStrength) {
|
|
addStr(&p.Stats.Str, p.CurRing[Left].Arm)
|
|
}
|
|
if p.IsRing(Right, RingAddStrength) {
|
|
addStr(&p.Stats.Str, p.CurRing[Right].Arm)
|
|
}
|
|
g.msg("hey, this tastes great. It make you feel warm all over")
|
|
case PotionBlindness:
|
|
g.doPot(PotionBlindness, true)
|
|
case PotionLevitation:
|
|
g.doPot(PotionLevitation, true)
|
|
}
|
|
g.status()
|
|
// Throw the item away
|
|
g.callIt(&g.Items.Potions[obj.Which])
|
|
}
|
|
|
|
// raiseLevel: the guy just magically went up a level (potions.c
|
|
// raise_level).
|
|
func (g *RogueGame) raiseLevel() {
|
|
g.Player.Stats.Exp = eLevels[g.Player.Stats.Lvl-1] + 1
|
|
g.checkLevel()
|
|
}
|
|
|
|
// doPot does a potion with standard setup: it uses a fuse and turns on a
|
|
// flag (potions.c do_pot).
|
|
func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
|
|
pp := &pActions[kind]
|
|
if !g.Items.Potions[kind].Know {
|
|
g.Items.Potions[kind].Know = knowit
|
|
}
|
|
t := g.spread(pp.time)
|
|
if !g.Player.On(pp.flags) {
|
|
g.Player.Flags.Set(pp.flags)
|
|
g.Fuse(pp.daemon, 0, t, After)
|
|
g.look(false)
|
|
} else {
|
|
g.Lengthen(pp.daemon, t)
|
|
}
|
|
high, straight := pp.high, pp.straight
|
|
if kind == PotionSeeInvisible {
|
|
s := fmt.Sprintf("this potion tastes like %s juice", g.Fruit)
|
|
high, straight = s, s
|
|
}
|
|
g.msg("%s", g.chooseStr(high, straight))
|
|
}
|
|
|
|
// isMagic reports whether an object radiates magic (potions.c is_magic).
|
|
func (o *Object) isMagic() bool {
|
|
switch o.Kind {
|
|
case KindArmor:
|
|
return o.Flags.Has(Protected) || o.Arm != aClass[o.Which]
|
|
case KindWeapon:
|
|
return o.HPlus != 0 || o.DPlus != 0
|
|
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// invisOn turns on the ability to see invisible monsters (potions.c
|
|
// invis_on).
|
|
func (g *RogueGame) invisOn() {
|
|
g.Player.Flags.Set(CanSeeInvisible)
|
|
for _, mp := range g.Level.Monsters {
|
|
if mp.On(Invisible) && g.seeMonst(mp) && !g.Player.On(Hallucinating) {
|
|
g.mvaddch(mp.Pos.Y, mp.Pos.X, mp.Disguise)
|
|
}
|
|
}
|
|
}
|
|
|
|
// turnSee puts on or off seeing monsters on this level (potions.c
|
|
// turn_see).
|
|
func (g *RogueGame) turnSee(turnOff bool) bool {
|
|
addNew := false
|
|
for _, mp := range g.Level.Monsters {
|
|
g.move(mp.Pos.Y, mp.Pos.X)
|
|
canSee := g.seeMonst(mp)
|
|
if turnOff {
|
|
if !canSee {
|
|
g.addch(mp.OldCh)
|
|
}
|
|
} else {
|
|
if !canSee {
|
|
g.standout()
|
|
}
|
|
if !g.Player.On(Hallucinating) {
|
|
g.addch(mp.Type)
|
|
} else {
|
|
g.addch(byte(g.rnd(26) + 'A'))
|
|
}
|
|
if !canSee {
|
|
g.standend()
|
|
addNew = true
|
|
}
|
|
}
|
|
}
|
|
if turnOff {
|
|
g.Player.Flags.Clear(SenseMonsters)
|
|
} else {
|
|
g.Player.Flags.Set(SenseMonsters)
|
|
}
|
|
return addNew
|
|
}
|
|
|
|
// seenStairs reports whether the player has seen the stairs (potions.c
|
|
// seen_stairs).
|
|
func (g *RogueGame) seenStairs() bool {
|
|
st := g.Level.Stairs
|
|
g.move(st.Y, st.X)
|
|
if g.inch() == Stairs { // it's on the map
|
|
return true
|
|
}
|
|
if g.Player.Pos == st { // it's under him
|
|
return true
|
|
}
|
|
// if a monster is on the stairs, this gets hairy
|
|
if tp := g.Level.MonsterAt(st.Y, st.X); tp != nil {
|
|
if g.seeMonst(tp) && tp.On(Awake) { // visible and awake:
|
|
return true // it must have moved there
|
|
}
|
|
if g.Player.On(SenseMonsters) && tp.OldCh == Stairs {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|