Decompose fight.go: hit-handler table, weapon/armor helpers (step 7)
The monster special-power switch in attack becomes gameData.hitHandlers (indexed by monster letter); attack splits into monsterHit/monsterMiss; fight gains revealXeroc and heroHits; rollAttacks gains weaponAttack, wieldedRingBonus, and defenderArmor; killed gains killedSpecial. fight.go is complexity-clean. Behavior and RNG call order unchanged.
This commit is contained in:
691
game/fight.go
691
game/fight.go
@@ -48,66 +48,81 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
|
|||||||
g.Count = 0
|
g.Count = 0
|
||||||
g.Quiet = 0
|
g.Quiet = 0
|
||||||
g.runTo(mp)
|
g.runTo(mp)
|
||||||
// Let him know it was really a xeroc (if it was one).
|
|
||||||
if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) {
|
|
||||||
tp.Disguise = 'X'
|
|
||||||
if p.On(Hallucinating) {
|
|
||||||
g.mvaddch(tp.Pos.Y, tp.Pos.X, g.randomMonsterLetter())
|
|
||||||
}
|
|
||||||
|
|
||||||
g.msg("%s", g.chooseStr("heavy! That's a nasty critter!",
|
if g.revealXeroc(tp) && !thrown {
|
||||||
"wait! That's a xeroc!"))
|
return false
|
||||||
|
|
||||||
if !thrown {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mname := g.setMname(tp)
|
mname := g.setMname(tp)
|
||||||
didHit := false
|
|
||||||
|
|
||||||
g.HasHit = g.Options.Terse && !g.ToDeath
|
g.HasHit = g.Options.Terse && !g.ToDeath
|
||||||
if g.rollAttacks(&p.Creature, &tp.Creature, weap, thrown) {
|
if g.rollAttacks(&p.Creature, &tp.Creature, weap, thrown) {
|
||||||
didHit = false
|
g.heroHits(tp, mname, weap, thrown)
|
||||||
|
|
||||||
if thrown {
|
return true
|
||||||
g.thunk(weap, mname, g.Options.Terse)
|
|
||||||
} else {
|
|
||||||
g.hit("", mname, g.Options.Terse)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.On(CanConfuse) {
|
|
||||||
didHit = true
|
|
||||||
|
|
||||||
tp.Flags.Set(Confused)
|
|
||||||
p.Flags.Clear(CanConfuse)
|
|
||||||
g.endmsg()
|
|
||||||
g.HasHit = false
|
|
||||||
g.msg("your hands stop glowing %s", g.pickColor("red"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if tp.Stats.HP <= 0 {
|
|
||||||
g.killed(tp, true)
|
|
||||||
} else if didHit && !p.On(Blind) {
|
|
||||||
g.msg("%s appears confused", mname)
|
|
||||||
}
|
|
||||||
|
|
||||||
didHit = true
|
|
||||||
} else {
|
|
||||||
if thrown {
|
|
||||||
g.bounce(weap, mname, g.Options.Terse)
|
|
||||||
} else {
|
|
||||||
g.miss("", mname, g.Options.Terse)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return didHit
|
if thrown {
|
||||||
|
g.bounce(weap, mname, g.Options.Terse)
|
||||||
|
} else {
|
||||||
|
g.miss("", mname, g.Options.Terse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// attack has the monster attack the player (fight.c attack). removed
|
// revealXeroc lets him know it was really a xeroc (if it was one); it
|
||||||
|
// reports whether one was unmasked (the X block of fight.c fight).
|
||||||
|
func (g *RogueGame) revealXeroc(tp *Monster) bool {
|
||||||
|
p := &g.Player
|
||||||
|
if tp.Type != 'X' || tp.Disguise == 'X' || p.On(Blind) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tp.Disguise = 'X'
|
||||||
|
if p.On(Hallucinating) {
|
||||||
|
g.mvaddch(tp.Pos.Y, tp.Pos.X, g.randomMonsterLetter())
|
||||||
|
}
|
||||||
|
|
||||||
|
g.msg("%s", g.chooseStr("heavy! That's a nasty critter!",
|
||||||
|
"wait! That's a xeroc!"))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// heroHits lands the hero's blow on a monster: messages, the confusing
|
||||||
|
// touch, and the kill check (the hit arm of fight.c fight).
|
||||||
|
func (g *RogueGame) heroHits(tp *Monster, mname string, weap *Object, thrown bool) {
|
||||||
|
p := &g.Player
|
||||||
|
confused := false
|
||||||
|
|
||||||
|
if thrown {
|
||||||
|
g.thunk(weap, mname, g.Options.Terse)
|
||||||
|
} else {
|
||||||
|
g.hit("", mname, g.Options.Terse)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.On(CanConfuse) {
|
||||||
|
confused = true
|
||||||
|
|
||||||
|
tp.Flags.Set(Confused)
|
||||||
|
p.Flags.Clear(CanConfuse)
|
||||||
|
g.endmsg()
|
||||||
|
g.HasHit = false
|
||||||
|
g.msg("your hands stop glowing %s", g.pickColor("red"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tp.Stats.HP <= 0 {
|
||||||
|
g.killed(tp, true)
|
||||||
|
} else if confused && !p.On(Blind) {
|
||||||
|
g.msg("%s appears confused", mname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attack has the monster attack the player (fight.c attack). The result
|
||||||
// reports that the monster took itself off the level during its own
|
// reports that the monster took itself off the level during its own
|
||||||
// attack (the C -1 return).
|
// attack (the C -1 return).
|
||||||
func (g *RogueGame) attack(mp *Monster) (removed bool) {
|
func (g *RogueGame) attack(mp *Monster) bool {
|
||||||
p := &g.Player
|
p := &g.Player
|
||||||
// Since this is an attack, stop running and any healing that was
|
// Since this is an attack, stop running and any healing that was
|
||||||
// going on at the time.
|
// going on at the time.
|
||||||
@@ -129,185 +144,12 @@ func (g *RogueGame) attack(mp *Monster) (removed bool) {
|
|||||||
|
|
||||||
mname := g.setMname(mp)
|
mname := g.setMname(mp)
|
||||||
oldhp := p.Stats.HP
|
oldhp := p.Stats.HP
|
||||||
|
removed := false
|
||||||
|
|
||||||
if g.rollAttacks(&mp.Creature, &p.Creature, nil, false) {
|
if g.rollAttacks(&mp.Creature, &p.Creature, nil, false) {
|
||||||
if mp.Type != 'I' {
|
removed = g.monsterHit(mp, mname, oldhp)
|
||||||
if g.HasHit {
|
} else {
|
||||||
g.addmsgf(". ")
|
g.monsterMiss(mp, mname)
|
||||||
}
|
|
||||||
|
|
||||||
g.hit(mname, "", false)
|
|
||||||
} else if g.HasHit {
|
|
||||||
g.endmsg()
|
|
||||||
}
|
|
||||||
|
|
||||||
g.HasHit = false
|
|
||||||
if p.Stats.HP <= 0 {
|
|
||||||
g.death(mp.Type) // Bye bye life ...
|
|
||||||
} else if !g.Kamikaze {
|
|
||||||
oldhp -= p.Stats.HP
|
|
||||||
if oldhp > g.MaxHit {
|
|
||||||
g.MaxHit = oldhp
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Stats.HP <= g.MaxHit {
|
|
||||||
g.ToDeath = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mp.On(Cancelled) {
|
|
||||||
switch mp.Type {
|
|
||||||
case 'A':
|
|
||||||
// If an aquator hits, you can lose armor class.
|
|
||||||
g.rustArmor(p.CurArmor)
|
|
||||||
case 'I':
|
|
||||||
// The ice monster freezes you
|
|
||||||
p.Flags.Clear(Awake)
|
|
||||||
|
|
||||||
if g.NoCommand == 0 {
|
|
||||||
g.addmsgf("you are frozen")
|
|
||||||
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.addmsgf(" by the %s", mname)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.endmsg()
|
|
||||||
}
|
|
||||||
|
|
||||||
g.NoCommand += g.rnd(2) + 2
|
|
||||||
if g.NoCommand > BoreLevel {
|
|
||||||
g.death('h')
|
|
||||||
}
|
|
||||||
case 'R':
|
|
||||||
// Rattlesnakes have poisonous bites
|
|
||||||
if !g.save(VsPoison) {
|
|
||||||
if !p.IsWearing(RingSustainStrength) {
|
|
||||||
g.changeStrength(-1)
|
|
||||||
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.msg("you feel a bite in your leg and now feel weaker")
|
|
||||||
} else {
|
|
||||||
g.msg("a bite has weakened you")
|
|
||||||
}
|
|
||||||
} else if !g.ToDeath {
|
|
||||||
if !g.Options.Terse {
|
|
||||||
g.msg("a bite momentarily weakens you")
|
|
||||||
} else {
|
|
||||||
g.msg("bite has no effect")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'W', 'V':
|
|
||||||
// Wraiths might drain energy levels, and Vampires can
|
|
||||||
// steal max_hp
|
|
||||||
chance := 30
|
|
||||||
if mp.Type == 'W' {
|
|
||||||
chance = 15
|
|
||||||
}
|
|
||||||
|
|
||||||
if g.rnd(100) < chance {
|
|
||||||
var fewer int
|
|
||||||
|
|
||||||
if mp.Type == 'W' {
|
|
||||||
if p.Stats.Exp == 0 {
|
|
||||||
g.death('W') // All levels gone
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Stats.Lvl--; p.Stats.Lvl == 0 {
|
|
||||||
p.Stats.Exp = 0
|
|
||||||
p.Stats.Lvl = 1
|
|
||||||
} else {
|
|
||||||
p.Stats.Exp = g.data.eLevels[p.Stats.Lvl-1] + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
fewer = g.roll(1, 10)
|
|
||||||
} else {
|
|
||||||
fewer = g.roll(1, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Stats.HP -= fewer
|
|
||||||
|
|
||||||
p.Stats.MaxHP -= fewer
|
|
||||||
if p.Stats.HP <= 0 {
|
|
||||||
p.Stats.HP = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Stats.MaxHP <= 0 {
|
|
||||||
g.death(mp.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.msg("you suddenly feel weaker")
|
|
||||||
}
|
|
||||||
case 'F':
|
|
||||||
// Venus Flytrap stops the poor guy from moving
|
|
||||||
p.Flags.Set(Held)
|
|
||||||
p.VfHit++
|
|
||||||
|
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = DiceSpec{{Count: p.VfHit, Sides: 1}}
|
|
||||||
if p.Stats.HP--; p.Stats.HP <= 0 {
|
|
||||||
g.death('F')
|
|
||||||
}
|
|
||||||
case 'L':
|
|
||||||
// Leprechaun steals some gold
|
|
||||||
lastpurse := p.Purse
|
|
||||||
|
|
||||||
p.Purse -= g.goldCalc()
|
|
||||||
if !g.save(VsMagic) {
|
|
||||||
p.Purse -= g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Purse < 0 {
|
|
||||||
p.Purse = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
g.removeMon(mp.Pos, mp, false)
|
|
||||||
|
|
||||||
removed = true
|
|
||||||
|
|
||||||
if p.Purse != lastpurse {
|
|
||||||
g.msg("your purse feels lighter")
|
|
||||||
}
|
|
||||||
case 'N':
|
|
||||||
// Nymphs steal a magic item; look through the pack and
|
|
||||||
// pick out one we like.
|
|
||||||
var steal *Object
|
|
||||||
|
|
||||||
nobj := 0
|
|
||||||
|
|
||||||
for _, obj := range p.Pack {
|
|
||||||
if obj != p.CurArmor && obj != p.CurWeapon &&
|
|
||||||
obj != p.CurRing[Left] && obj != p.CurRing[Right] &&
|
|
||||||
g.isMagic(obj) {
|
|
||||||
if nobj++; g.rnd(nobj) == 0 {
|
|
||||||
steal = obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if steal != nil {
|
|
||||||
g.removeMon(mp.Pos, g.Level.MonsterAt(mp.Pos.Y, mp.Pos.X), false)
|
|
||||||
|
|
||||||
removed = true
|
|
||||||
|
|
||||||
g.leavePack(steal, false, false)
|
|
||||||
g.msg("she stole %s!", g.inventoryName(steal, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if mp.Type != 'I' {
|
|
||||||
if g.HasHit {
|
|
||||||
g.addmsgf(". ")
|
|
||||||
g.HasHit = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if mp.Type == 'F' {
|
|
||||||
p.Stats.HP -= p.VfHit
|
|
||||||
if p.Stats.HP <= 0 {
|
|
||||||
g.death(mp.Type) // Bye bye life ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g.miss(mname, "", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.Options.FightFlush && !g.ToDeath {
|
if g.Options.FightFlush && !g.ToDeath {
|
||||||
@@ -320,6 +162,235 @@ func (g *RogueGame) attack(mp *Monster) (removed bool) {
|
|||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// monsterHit lands a monster's blow on the hero: messages, death and
|
||||||
|
// to-death bookkeeping, then the monster's special power (the hit arm
|
||||||
|
// of fight.c attack). It reports whether the monster removed itself.
|
||||||
|
func (g *RogueGame) monsterHit(mp *Monster, mname string, oldhp int) bool {
|
||||||
|
p := &g.Player
|
||||||
|
if mp.Type != 'I' {
|
||||||
|
if g.HasHit {
|
||||||
|
g.addmsgf(". ")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.hit(mname, "", false)
|
||||||
|
} else if g.HasHit {
|
||||||
|
g.endmsg()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.HasHit = false
|
||||||
|
if p.Stats.HP <= 0 {
|
||||||
|
g.death(mp.Type) // Bye bye life ...
|
||||||
|
} else if !g.Kamikaze {
|
||||||
|
oldhp -= p.Stats.HP
|
||||||
|
if oldhp > g.MaxHit {
|
||||||
|
g.MaxHit = oldhp
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Stats.HP <= g.MaxHit {
|
||||||
|
g.ToDeath = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mp.On(Cancelled) {
|
||||||
|
if h := g.data.hitHandlers[mp.Type-'A']; h != nil {
|
||||||
|
return h(g, mp, mname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// monsterMiss handles a monster's whiffed swing (the miss arm of
|
||||||
|
// fight.c attack); ice monsters miss silently.
|
||||||
|
func (g *RogueGame) monsterMiss(mp *Monster, mname string) {
|
||||||
|
if mp.Type == 'I' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
if g.HasHit {
|
||||||
|
g.addmsgf(". ")
|
||||||
|
g.HasHit = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.Type == 'F' {
|
||||||
|
p.Stats.HP -= p.VfHit
|
||||||
|
if p.Stats.HP <= 0 {
|
||||||
|
g.death(mp.Type) // Bye bye life ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.miss(mname, "", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The monster special-power handlers, dispatched through
|
||||||
|
// gameData.hitHandlers when an uncancelled monster's hit lands. Each is
|
||||||
|
// one case of the C attack switch; a true return means the monster
|
||||||
|
// removed itself from the level.
|
||||||
|
|
||||||
|
func (g *RogueGame) hitAquator(*Monster, string) bool {
|
||||||
|
// If an aquator hits, you can lose armor class.
|
||||||
|
g.rustArmor(g.Player.CurArmor)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitIceMonster(_ *Monster, mname string) bool {
|
||||||
|
// The ice monster freezes you
|
||||||
|
g.Player.Flags.Clear(Awake)
|
||||||
|
|
||||||
|
if g.NoCommand == 0 {
|
||||||
|
g.addmsgf("you are frozen")
|
||||||
|
|
||||||
|
if !g.Options.Terse {
|
||||||
|
g.addmsgf(" by the %s", mname)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.endmsg()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.NoCommand += g.rnd(2) + 2
|
||||||
|
if g.NoCommand > BoreLevel {
|
||||||
|
g.death('h')
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitRattlesnake(*Monster, string) bool {
|
||||||
|
// Rattlesnakes have poisonous bites
|
||||||
|
if g.save(VsPoison) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !g.Player.IsWearing(RingSustainStrength) {
|
||||||
|
g.changeStrength(-1)
|
||||||
|
g.msg("%s", g.chooseTerse("a bite has weakened you",
|
||||||
|
"you feel a bite in your leg and now feel weaker"))
|
||||||
|
} else if !g.ToDeath {
|
||||||
|
g.msg("%s", g.chooseTerse("bite has no effect",
|
||||||
|
"a bite momentarily weakens you"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitLifeDrainer(mp *Monster, _ string) bool {
|
||||||
|
// Wraiths might drain energy levels, and Vampires can steal max_hp
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
chance := 30
|
||||||
|
if mp.Type == 'W' {
|
||||||
|
chance = 15
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.rnd(100) >= chance {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var fewer int
|
||||||
|
|
||||||
|
if mp.Type == 'W' {
|
||||||
|
if p.Stats.Exp == 0 {
|
||||||
|
g.death('W') // All levels gone
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Stats.Lvl--; p.Stats.Lvl == 0 {
|
||||||
|
p.Stats.Exp = 0
|
||||||
|
p.Stats.Lvl = 1
|
||||||
|
} else {
|
||||||
|
p.Stats.Exp = g.data.eLevels[p.Stats.Lvl-1] + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fewer = g.roll(1, 10)
|
||||||
|
} else {
|
||||||
|
fewer = g.roll(1, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Stats.HP -= fewer
|
||||||
|
|
||||||
|
p.Stats.MaxHP -= fewer
|
||||||
|
if p.Stats.HP <= 0 {
|
||||||
|
p.Stats.HP = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Stats.MaxHP <= 0 {
|
||||||
|
g.death(mp.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.msg("you suddenly feel weaker")
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitFlytrap(*Monster, string) bool {
|
||||||
|
// Venus Flytrap stops the poor guy from moving
|
||||||
|
p := &g.Player
|
||||||
|
p.Flags.Set(Held)
|
||||||
|
p.VfHit++
|
||||||
|
|
||||||
|
g.Monsters['F'-'A'].Stats.Dmg = DiceSpec{{Count: p.VfHit, Sides: 1}}
|
||||||
|
if p.Stats.HP--; p.Stats.HP <= 0 {
|
||||||
|
g.death('F')
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitLeprechaun(mp *Monster, _ string) bool {
|
||||||
|
// Leprechaun steals some gold
|
||||||
|
p := &g.Player
|
||||||
|
lastpurse := p.Purse
|
||||||
|
|
||||||
|
p.Purse -= g.goldCalc()
|
||||||
|
if !g.save(VsMagic) {
|
||||||
|
p.Purse -= g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Purse < 0 {
|
||||||
|
p.Purse = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
g.removeMon(mp.Pos, mp, false)
|
||||||
|
|
||||||
|
if p.Purse != lastpurse {
|
||||||
|
g.msg("your purse feels lighter")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RogueGame) hitNymph(mp *Monster, _ string) bool {
|
||||||
|
// Nymphs steal a magic item; look through the pack and pick out one
|
||||||
|
// we like.
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
var steal *Object
|
||||||
|
|
||||||
|
nobj := 0
|
||||||
|
|
||||||
|
for _, obj := range p.Pack {
|
||||||
|
if obj != p.CurArmor && obj != p.CurWeapon &&
|
||||||
|
obj != p.CurRing[Left] && obj != p.CurRing[Right] &&
|
||||||
|
g.isMagic(obj) {
|
||||||
|
if nobj++; g.rnd(nobj) == 0 {
|
||||||
|
steal = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if steal == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
g.removeMon(mp.Pos, g.Level.MonsterAt(mp.Pos.Y, mp.Pos.X), false)
|
||||||
|
g.leavePack(steal, false, false)
|
||||||
|
g.msg("she stole %s!", g.inventoryName(steal, true))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// swing returns true if the swing hits (fight.c swing).
|
// swing returns true if the swing hits (fight.c swing).
|
||||||
func (g *RogueGame) swing(atLvl, opArm, wplus int) bool {
|
func (g *RogueGame) swing(atLvl, opArm, wplus int) bool {
|
||||||
res := g.rnd(20)
|
res := g.rnd(20)
|
||||||
@@ -330,7 +401,6 @@ func (g *RogueGame) swing(atLvl, opArm, wplus int) bool {
|
|||||||
|
|
||||||
// rollAttacks rolls several attacks (fight.c roll_em).
|
// rollAttacks rolls several attacks (fight.c roll_em).
|
||||||
func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool) bool {
|
func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool) bool {
|
||||||
p := &g.Player
|
|
||||||
att := &thatt.Stats
|
att := &thatt.Stats
|
||||||
def := &thdef.Stats
|
def := &thdef.Stats
|
||||||
|
|
||||||
@@ -342,34 +412,7 @@ func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool)
|
|||||||
if weap == nil {
|
if weap == nil {
|
||||||
attacks = att.Dmg
|
attacks = att.Dmg
|
||||||
} else {
|
} else {
|
||||||
hplus = weap.HPlus
|
attacks, hplus, dplus = g.weaponAttack(weap, hurl)
|
||||||
|
|
||||||
dplus = weap.DPlus
|
|
||||||
if weap == p.CurWeapon {
|
|
||||||
if p.IsRing(Left, RingIncreaseDamage) {
|
|
||||||
dplus += p.CurRing[Left].Bonus
|
|
||||||
} else if p.IsRing(Left, RingDexterity) {
|
|
||||||
hplus += p.CurRing[Left].Bonus
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.IsRing(Right, RingIncreaseDamage) {
|
|
||||||
dplus += p.CurRing[Right].Bonus
|
|
||||||
} else if p.IsRing(Right, RingDexterity) {
|
|
||||||
hplus += p.CurRing[Right].Bonus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attacks = weap.Damage
|
|
||||||
if hurl {
|
|
||||||
if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
|
|
||||||
WeaponKind(p.CurWeapon.Which) == weap.Launch {
|
|
||||||
attacks = weap.HurlDmg
|
|
||||||
hplus += p.CurWeapon.HPlus
|
|
||||||
dplus += p.CurWeapon.DPlus
|
|
||||||
} else if weap.Launch < 0 {
|
|
||||||
attacks = weap.HurlDmg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the creature being attacked is not running (asleep or held) then
|
// If the creature being attacked is not running (asleep or held) then
|
||||||
// the attacker gets a plus four bonus to hit.
|
// the attacker gets a plus four bonus to hit.
|
||||||
@@ -377,21 +420,7 @@ func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool)
|
|||||||
hplus += 4
|
hplus += 4
|
||||||
}
|
}
|
||||||
|
|
||||||
defArm := def.ArmorClass
|
defArm := g.defenderArmor(thdef)
|
||||||
if def == &p.Stats {
|
|
||||||
if p.CurArmor != nil {
|
|
||||||
defArm = p.CurArmor.ArmorClass
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.IsRing(Left, RingProtection) {
|
|
||||||
defArm -= p.CurRing[Left].Bonus
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.IsRing(Right, RingProtection) {
|
|
||||||
defArm -= p.CurRing[Right].Bonus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
didHit := false
|
didHit := false
|
||||||
|
|
||||||
for _, atk := range attacks {
|
for _, atk := range attacks {
|
||||||
@@ -410,6 +439,78 @@ func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool)
|
|||||||
return didHit
|
return didHit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// weaponAttack picks the dice and to-hit/damage bonuses a weapon swings
|
||||||
|
// with: ring bonuses when wielded, and launcher pairing for hurled
|
||||||
|
// missiles (the weapon preamble of fight.c roll_em).
|
||||||
|
func (g *RogueGame) weaponAttack(weap *Object, hurl bool) (DiceSpec, int, int) {
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
hplus := weap.HPlus
|
||||||
|
|
||||||
|
dplus := weap.DPlus
|
||||||
|
if weap == p.CurWeapon {
|
||||||
|
hplus, dplus = g.wieldedRingBonus(hplus, dplus)
|
||||||
|
}
|
||||||
|
|
||||||
|
attacks := weap.Damage
|
||||||
|
if hurl {
|
||||||
|
if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
|
||||||
|
WeaponKind(p.CurWeapon.Which) == weap.Launch {
|
||||||
|
attacks = weap.HurlDmg
|
||||||
|
hplus += p.CurWeapon.HPlus
|
||||||
|
dplus += p.CurWeapon.DPlus
|
||||||
|
} else if weap.Launch < 0 {
|
||||||
|
attacks = weap.HurlDmg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attacks, hplus, dplus
|
||||||
|
}
|
||||||
|
|
||||||
|
// wieldedRingBonus folds damage and dexterity ring bonuses into the
|
||||||
|
// wielded weapon's to-hit/damage pluses (fight.c roll_em).
|
||||||
|
func (g *RogueGame) wieldedRingBonus(hplus, dplus int) (int, int) {
|
||||||
|
p := &g.Player
|
||||||
|
if p.IsRing(Left, RingIncreaseDamage) {
|
||||||
|
dplus += p.CurRing[Left].Bonus
|
||||||
|
} else if p.IsRing(Left, RingDexterity) {
|
||||||
|
hplus += p.CurRing[Left].Bonus
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.IsRing(Right, RingIncreaseDamage) {
|
||||||
|
dplus += p.CurRing[Right].Bonus
|
||||||
|
} else if p.IsRing(Right, RingDexterity) {
|
||||||
|
hplus += p.CurRing[Right].Bonus
|
||||||
|
}
|
||||||
|
|
||||||
|
return hplus, dplus
|
||||||
|
}
|
||||||
|
|
||||||
|
// defenderArmor computes the defender's effective armor class: worn
|
||||||
|
// armor and protection rings when the hero defends (the def_arm
|
||||||
|
// computation of fight.c roll_em).
|
||||||
|
func (g *RogueGame) defenderArmor(thdef *Creature) int {
|
||||||
|
p := &g.Player
|
||||||
|
def := &thdef.Stats
|
||||||
|
|
||||||
|
defArm := def.ArmorClass
|
||||||
|
if def == &p.Stats {
|
||||||
|
if p.CurArmor != nil {
|
||||||
|
defArm = p.CurArmor.ArmorClass
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.IsRing(Left, RingProtection) {
|
||||||
|
defArm -= p.CurRing[Left].Bonus
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.IsRing(Right, RingProtection) {
|
||||||
|
defArm -= p.CurRing[Right].Bonus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defArm
|
||||||
|
}
|
||||||
|
|
||||||
// cAtoi parses a leading integer like C atoi: trailing non-digits are
|
// cAtoi parses a leading integer like C atoi: trailing non-digits are
|
||||||
// ignored rather than an error.
|
// ignored rather than an error.
|
||||||
func cAtoi(s string) int {
|
func cAtoi(s string) int {
|
||||||
@@ -566,30 +667,7 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
|
|||||||
p := &g.Player
|
p := &g.Player
|
||||||
p.Stats.Exp += tp.Stats.Exp
|
p.Stats.Exp += tp.Stats.Exp
|
||||||
|
|
||||||
// If the monster was a venus flytrap, un-hold him
|
g.killedSpecial(tp)
|
||||||
switch tp.Type {
|
|
||||||
case 'F':
|
|
||||||
p.Flags.Clear(Held)
|
|
||||||
p.VfHit = 0
|
|
||||||
g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
|
|
||||||
case 'L':
|
|
||||||
pos, ok := g.fallpos(tp.Pos)
|
|
||||||
if ok {
|
|
||||||
tp.Room.Gold = pos
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok && g.Depth >= g.MaxDepth {
|
|
||||||
gold := newObject()
|
|
||||||
gold.Kind = KindGold
|
|
||||||
|
|
||||||
gold.GoldValue = g.goldCalc()
|
|
||||||
if g.save(VsMagic) {
|
|
||||||
gold.GoldValue += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
|
||||||
}
|
|
||||||
|
|
||||||
attachObj(&tp.Pack, gold)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get rid of the monster.
|
// Get rid of the monster.
|
||||||
mname := g.setMname(tp)
|
mname := g.setMname(tp)
|
||||||
g.removeMon(tp.Pos, tp, true)
|
g.removeMon(tp.Pos, tp, true)
|
||||||
@@ -616,6 +694,37 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// killedSpecial handles deaths with side effects: a flytrap releases its
|
||||||
|
// grip and a leprechaun drops its gold (the switch of fight.c killed).
|
||||||
|
func (g *RogueGame) killedSpecial(tp *Monster) {
|
||||||
|
p := &g.Player
|
||||||
|
|
||||||
|
// If the monster was a venus flytrap, un-hold him
|
||||||
|
switch tp.Type {
|
||||||
|
case 'F':
|
||||||
|
p.Flags.Clear(Held)
|
||||||
|
p.VfHit = 0
|
||||||
|
g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
|
||||||
|
case 'L':
|
||||||
|
pos, ok := g.fallpos(tp.Pos)
|
||||||
|
if ok {
|
||||||
|
tp.Room.Gold = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok && g.Depth >= g.MaxDepth {
|
||||||
|
gold := newObject()
|
||||||
|
gold.Kind = KindGold
|
||||||
|
|
||||||
|
gold.GoldValue = g.goldCalc()
|
||||||
|
if g.save(VsMagic) {
|
||||||
|
gold.GoldValue += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
|
||||||
|
}
|
||||||
|
|
||||||
|
attachObj(&tp.Pack, gold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// flushType flushes typeahead for the fight_flush option (mach_dep.c
|
// flushType flushes typeahead for the fight_flush option (mach_dep.c
|
||||||
// flush_type / curses flushinp).
|
// flush_type / curses flushinp).
|
||||||
func (g *RogueGame) flushType() {
|
func (g *RogueGame) flushType() {
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ type gameData struct {
|
|||||||
// cases of the sticks.c do_zap switch. A false return aborts 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).
|
// zap without using a charge (drain life on a too-weak hero).
|
||||||
zapHandlers [NumWandTypes]func(g *RogueGame, obj *Object) bool
|
zapHandlers [NumWandTypes]func(g *RogueGame, obj *Object) bool
|
||||||
|
|
||||||
|
// hitHandlers dispatches a monster's special power when its hit
|
||||||
|
// lands, indexed by monster letter - 'A': the cases of the fight.c
|
||||||
|
// attack switch. A true return means the monster removed itself.
|
||||||
|
hitHandlers [26]func(g *RogueGame, mp *Monster, mname string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ripWall is the repeated blank wall line of the tombstone art.
|
// ripWall is the repeated blank wall line of the tombstone art.
|
||||||
@@ -656,6 +661,17 @@ func newGameData() *gameData {
|
|||||||
WandTeleportTo: (*RogueGame).zapTeleport,
|
WandTeleportTo: (*RogueGame).zapTeleport,
|
||||||
WandCancellation: (*RogueGame).zapCancellation,
|
WandCancellation: (*RogueGame).zapCancellation,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hitHandlers: [26]func(*RogueGame, *Monster, string) bool{
|
||||||
|
0: (*RogueGame).hitAquator, // 'A' - 'A'
|
||||||
|
'F' - 'A': (*RogueGame).hitFlytrap,
|
||||||
|
'I' - 'A': (*RogueGame).hitIceMonster,
|
||||||
|
'L' - 'A': (*RogueGame).hitLeprechaun,
|
||||||
|
'N' - 'A': (*RogueGame).hitNymph,
|
||||||
|
'R' - 'A': (*RogueGame).hitRattlesnake,
|
||||||
|
'V' - 'A': (*RogueGame).hitLifeDrainer,
|
||||||
|
'W' - 'A': (*RogueGame).hitLifeDrainer,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user