Un-overload Object fields and pre-parse damage dice (refactor step 3)

Object.Arm carried four meanings in C (o_arm/o_charges/o_goldval plus
ring bonuses); it is now four fields: ArmorClass, Charges, GoldValue,
and Bonus. Stats.Arm becomes Stats.ArmorClass. The Charges()/GoldVal()
accessor pair is gone.

Damage dice strings ("1x4/1x2") are parsed once into DiceSpec — at
table definition for the bestiary and weapon tables, at creation for
items — instead of re-parsed with atoi on every swing as fight.c
roll_em did. ParseDice preserves the C parse semantics exactly,
including the junk-tolerant "%%%x0" bestiary placeholder and the
"000x0" flytrap reset (regression-tested in dice_test.go); the
flytrap's growing grip becomes DiceSpec{{VfHit, 1}}.

Save format bumps to 5.4.4-go3 (field renames and retypes would
silently zero under gob's match-by-name decoding).

No behavior change; full suite green.
This commit is contained in:
2026-07-06 23:07:57 +02:00
parent c75af2ec22
commit 2eff377a73
28 changed files with 268 additions and 186 deletions

12
TODO.md
View File

@@ -29,14 +29,16 @@ Refactor ground rules:
# Next Step # Next Step
Refactor step 3 (branch refactor/object-fields): un-overload Object Update the module base path to git.eeqj.de/sneak/rgoue (user request
fields — split Object.Arm into ArmorClass/Charges/GoldValue; parse 2026-07-06): go.mod, all imports in term/ and cmd/, and doc references.
damage dice ("1x4/1x2") once into a DiceSpec type at table definition
instead of per swing; save-format update with round-trip test
migration.
# Completed Steps # Completed Steps
- 2026-07-06 Refactor step 3 (refactor/object-fields): Object.Arm split
into ArmorClass/Charges/GoldValue/Bonus (rings); Stats.Arm →
ArmorClass; damage strings parsed once into DiceSpec at table
definition (ParseDice keeps C roll_em parse semantics, incl. "%%%x0"
and "000x0" edge cases, regression-tested); save format 5.4.4-go3.
- 2026-07-06 Refactor step 2 (refactor/typed-kinds, b940cfc): - 2026-07-06 Refactor step 2 (refactor/typed-kinds, b940cfc):
ObjectKind separates item category from map glyph (Object.Type byte ObjectKind separates item category from map glyph (Object.Type byte
→ Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/ → Kind ObjectKind with Glyph()); PotionKind/ScrollKind/RingKind/

View File

@@ -410,7 +410,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
g.turnSee(p.On(SenseMonsters)) g.turnSee(p.On(SenseMonsters))
case CTRL('~'): case CTRL('~'):
if item := g.getItem("charge", KindWand); item != nil { if item := g.getItem("charge", KindWand); item != nil {
item.SetCharges(10000) item.Charges = 10000
} }
case CTRL('I'): case CTRL('I'):
for range 9 { for range 9 {
@@ -427,7 +427,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
obj = newObject() obj = newObject()
obj.Kind = KindArmor obj.Kind = KindArmor
obj.Which = int(ArmorPlateMail) obj.Which = int(ArmorPlateMail)
obj.Arm = -5 obj.ArmorClass = -5
obj.Flags.Set(Known) obj.Flags.Set(Known)
obj.Count = 1 obj.Count = 1
p.CurArmor = obj p.CurArmor = obj

58
game/dice.go Normal file
View File

@@ -0,0 +1,58 @@
package game
import (
"fmt"
"strings"
)
// Rogue expresses damage as strings like "1x4/3x6": one attack rolling
// 1d4 and a second rolling 3d6. The C code re-parsed these with atoi on
// every swing (fight.c roll_em); the port parses them once, at table
// definition or item creation, into a DiceSpec.
// DiceRoll is one attack's dice: Count rolls of a Sides-sided die.
type DiceRoll struct {
Count int
Sides int
}
// DiceSpec is the sequence of attacks a damage string described.
type DiceSpec []DiceRoll
// ParseDice parses a C damage string with the exact semantics of the
// roll_em loop: leading digits (C atoi) before and after each 'x', attacks
// separated by '/'. Junk like the bestiary's "%%%x0" placeholder parses as
// a single 0x0 attack, as it did in C.
func ParseDice(s string) DiceSpec {
var spec DiceSpec
for s != "" {
count := cAtoi(s)
xi := strings.IndexByte(s, 'x')
if xi < 0 {
break
}
s = s[xi+1:]
spec = append(spec, DiceRoll{Count: count, Sides: cAtoi(s)})
si := strings.IndexByte(s, '/')
if si < 0 {
break
}
s = s[si+1:]
}
return spec
}
// dice is the table-definition shorthand for ParseDice.
func dice(s string) DiceSpec { return ParseDice(s) }
// String renders the spec back in the classic "NxM/NxM" form.
func (d DiceSpec) String() string {
var sb strings.Builder
for i, r := range d {
if i > 0 {
sb.WriteByte('/')
}
fmt.Fprintf(&sb, "%dx%d", r.Count, r.Sides)
}
return sb.String()
}

45
game/dice_test.go Normal file
View File

@@ -0,0 +1,45 @@
package game
import "testing"
// ParseDice must keep the exact semantics of the C roll_em parse loop,
// including its junk-tolerant edges: the bestiary placeholder "%%%x0" and
// the flytrap reset "000x0" both mean a single 0x0 attack.
func TestParseDice(t *testing.T) {
cases := []struct {
in string
want string
len int
}{
{"1x4", "1x4", 1},
{"1x2/1x5/1x5", "1x2/1x5/1x5", 3},
{"2x12/2x4", "2x12/2x4", 2},
{"0x0", "0x0", 1},
{"000x0", "0x0", 1},
{"%%%x0", "0x0", 1},
{"", "", 0},
{"3", "", 0}, // no 'x': C parses nothing
}
for _, c := range cases {
got := ParseDice(c.in)
if len(got) != c.len || got.String() != c.want {
t.Errorf("ParseDice(%q) = %v (len %d), want %q (len %d)",
c.in, got, len(got), c.want, c.len)
}
}
}
// The bestiary and weapon tables must parse to at least one attack each so
// every creature and weapon actually swings.
func TestTablesHaveDice(t *testing.T) {
for i, m := range monsterTable {
if len(m.Stats.Dmg) == 0 {
t.Errorf("monster %c (%s) has no attacks", 'A'+i, m.Name)
}
}
for w, iw := range initWeaps {
if len(iw.dam) == 0 || len(iw.hrl) == 0 {
t.Errorf("weapon %v has empty dice", WeaponKind(w))
}
}
}

View File

@@ -72,11 +72,11 @@ func TestReadEnchantArmor(t *testing.T) {
ch := give(g, scr) ch := give(g, scr)
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
before := g.Player.CurArmor.Arm before := g.Player.CurArmor.ArmorClass
g.readScroll() g.readScroll()
if g.Player.CurArmor.Arm != before-1 { if g.Player.CurArmor.ArmorClass != before-1 {
t.Errorf("enchant armor: AC %d -> %d, want %d", t.Errorf("enchant armor: AC %d -> %d, want %d",
before, g.Player.CurArmor.Arm, before-1) before, g.Player.CurArmor.ArmorClass, before-1)
} }
} }
@@ -139,12 +139,12 @@ func TestZapSlowMonster(t *testing.T) {
g.scr.term.(*testTerm).input = []byte{ch} g.scr.term.(*testTerm).input = []byte{ch}
g.Delta = Coord{X: 1, Y: 0} // aim at the monster g.Delta = Coord{X: 1, Y: 0} // aim at the monster
charges := stick.Charges() charges := stick.Charges
g.doZap() g.doZap()
if !tp.On(Slowed) { if !tp.On(Slowed) {
t.Error("slow monster wand did not slow") t.Error("slow monster wand did not slow")
} }
if stick.Charges() != charges-1 { if stick.Charges != charges-1 {
t.Error("zap did not use a charge") t.Error("zap did not use a charge")
} }
} }

View File

@@ -1,10 +1,6 @@
package game package game
import ( import "strconv"
"fmt"
"strconv"
"strings"
)
// fight.c — all the fighting gets done here. // fight.c — all the fighting gets done here.
@@ -245,7 +241,7 @@ func (g *RogueGame) attack(mp *Monster) int {
// Venus Flytrap stops the poor guy from moving // Venus Flytrap stops the poor guy from moving
p.Flags.Set(Held) p.Flags.Set(Held)
p.VfHit++ p.VfHit++
g.Monsters['F'-'A'].Stats.Dmg = fmt.Sprintf("%dx1", p.VfHit) g.Monsters['F'-'A'].Stats.Dmg = DiceSpec{{Count: p.VfHit, Sides: 1}}
if p.Stats.HP--; p.Stats.HP <= 0 { if p.Stats.HP--; p.Stats.HP <= 0 {
g.death('F') g.death('F')
} }
@@ -322,34 +318,34 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
p := &g.Player p := &g.Player
att := &thatt.Stats att := &thatt.Stats
def := &thdef.Stats def := &thdef.Stats
var cp string var attacks DiceSpec
var hplus, dplus int var hplus, dplus int
if weap == nil { if weap == nil {
cp = att.Dmg attacks = att.Dmg
} else { } else {
hplus = weap.HPlus hplus = weap.HPlus
dplus = weap.DPlus dplus = weap.DPlus
if weap == p.CurWeapon { if weap == p.CurWeapon {
if p.IsRing(Left, RingIncreaseDamage) { if p.IsRing(Left, RingIncreaseDamage) {
dplus += p.CurRing[Left].Arm dplus += p.CurRing[Left].Bonus
} else if p.IsRing(Left, RingDexterity) { } else if p.IsRing(Left, RingDexterity) {
hplus += p.CurRing[Left].Arm hplus += p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RingIncreaseDamage) { if p.IsRing(Right, RingIncreaseDamage) {
dplus += p.CurRing[Right].Arm dplus += p.CurRing[Right].Bonus
} else if p.IsRing(Right, RingDexterity) { } else if p.IsRing(Right, RingDexterity) {
hplus += p.CurRing[Right].Arm hplus += p.CurRing[Right].Bonus
} }
} }
cp = weap.Damage attacks = weap.Damage
if hurl { if hurl {
if weap.Flags.Has(Missile) && p.CurWeapon != nil && if weap.Flags.Has(Missile) && p.CurWeapon != nil &&
WeaponKind(p.CurWeapon.Which) == weap.Launch { WeaponKind(p.CurWeapon.Which) == weap.Launch {
cp = weap.HurlDmg attacks = weap.HurlDmg
hplus += p.CurWeapon.HPlus hplus += p.CurWeapon.HPlus
dplus += p.CurWeapon.DPlus dplus += p.CurWeapon.DPlus
} else if weap.Launch < 0 { } else if weap.Launch < 0 {
cp = weap.HurlDmg attacks = weap.HurlDmg
} }
} }
} }
@@ -358,40 +354,28 @@ func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool
if !thdef.Flags.Has(Awake) { if !thdef.Flags.Has(Awake) {
hplus += 4 hplus += 4
} }
defArm := def.Arm defArm := def.ArmorClass
if def == &p.Stats { if def == &p.Stats {
if p.CurArmor != nil { if p.CurArmor != nil {
defArm = p.CurArmor.Arm defArm = p.CurArmor.ArmorClass
} }
if p.IsRing(Left, RingProtection) { if p.IsRing(Left, RingProtection) {
defArm -= p.CurRing[Left].Arm defArm -= p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RingProtection) { if p.IsRing(Right, RingProtection) {
defArm -= p.CurRing[Right].Arm defArm -= p.CurRing[Right].Bonus
} }
} }
didHit := false didHit := false
for cp != "" { for _, atk := range attacks {
ndice := cAtoi(cp)
xi := strings.IndexByte(cp, 'x')
if xi < 0 {
break
}
cp = cp[xi+1:]
nsides := cAtoi(cp)
if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) { if g.swing(att.Lvl, defArm, hplus+strPlus[att.Str]) {
proll := g.roll(ndice, nsides) proll := g.roll(atk.Count, atk.Sides)
damage := dplus + proll + addDam[att.Str] damage := dplus + proll + addDam[att.Str]
if damage > 0 { if damage > 0 {
def.HP -= damage def.HP -= damage
} }
didHit = true didHit = true
} }
si := strings.IndexByte(cp, '/')
if si < 0 {
break
}
cp = cp[si+1:]
} }
return didHit return didHit
} }
@@ -531,7 +515,7 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
case 'F': case 'F':
p.Flags.Clear(Held) p.Flags.Clear(Held)
p.VfHit = 0 p.VfHit = 0
g.Monsters['F'-'A'].Stats.Dmg = "000x0" g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
case 'L': case 'L':
pos, ok := g.fallpos(tp.Pos) pos, ok := g.fallpos(tp.Pos)
if ok { if ok {
@@ -540,9 +524,9 @@ func (g *RogueGame) killed(tp *Monster, pr bool) {
if ok && g.Depth >= g.MaxDepth { if ok && g.Depth >= g.MaxDepth {
gold := newObject() gold := newObject()
gold.Kind = KindGold gold.Kind = KindGold
gold.SetGoldVal(g.goldCalc()) gold.GoldValue = g.goldCalc()
if g.save(VsMagic) { if g.save(VsMagic) {
gold.Arm += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc() gold.GoldValue += g.goldCalc() + g.goldCalc() + g.goldCalc() + g.goldCalc()
} }
attachObj(&tp.Pack, gold) attachObj(&tp.Pack, gold)
} }

View File

@@ -23,8 +23,8 @@ func spawnAdjacent(g *RogueGame, typ byte) *Monster {
func TestRollEmParsesMultiAttackDice(t *testing.T) { func TestRollEmParsesMultiAttackDice(t *testing.T) {
g := mkGame(t, 42) g := mkGame(t, 42)
att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: "1x4/1x4/1x4"}} att := &Creature{Stats: Stats{Str: 16, Lvl: 20, Dmg: dice("1x4/1x4/1x4")}}
def := &Creature{Stats: Stats{Arm: 10, HP: 1000}} def := &Creature{Stats: Stats{ArmorClass: 10, HP: 1000}}
def.Flags.Set(Awake) def.Flags.Set(Awake)
// With attacker level 20 vs armor 10, swing always hits // With attacker level 20 vs armor 10, swing always hits
// (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of // (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of

View File

@@ -20,7 +20,7 @@ func (g *RogueGame) initPlayer() {
obj = newObject() obj = newObject()
obj.Kind = KindArmor obj.Kind = KindArmor
obj.Which = int(ArmorRingMail) obj.Which = int(ArmorRingMail)
obj.Arm = aClass[ArmorRingMail] - 1 obj.ArmorClass = aClass[ArmorRingMail] - 1
obj.Flags.Set(Known) obj.Flags.Set(Known)
obj.Count = 1 obj.Count = 1
p.CurArmor = obj p.CurArmor = obj

View File

@@ -142,9 +142,9 @@ func (g *RogueGame) status() {
p := &g.Player p := &g.Player
// If nothing has changed since the last status, don't bother. // If nothing has changed since the last status, don't bother.
temp := p.Stats.Arm temp := p.Stats.ArmorClass
if p.CurArmor != nil { if p.CurArmor != nil {
temp = p.CurArmor.Arm temp = p.CurArmor.ArmorClass
} }
if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp && if s.init && s.hp == p.Stats.HP && s.exp == p.Stats.Exp &&
s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str && s.pur == p.Purse && s.arm == temp && s.str == p.Stats.Str &&

View File

@@ -279,10 +279,10 @@ func (g *RogueGame) chgStr(amt int) {
addStr(&p.Stats.Str, amt) addStr(&p.Stats.Str, amt)
comp := p.Stats.Str comp := p.Stats.Str
if p.IsRing(Left, RingAddStrength) { if p.IsRing(Left, RingAddStrength) {
addStr(&comp, -p.CurRing[Left].Arm) addStr(&comp, -p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RingAddStrength) { if p.IsRing(Right, RingAddStrength) {
addStr(&comp, -p.CurRing[Right].Arm) addStr(&comp, -p.CurRing[Right].Bonus)
} }
if comp > p.MaxStats.Str { if comp > p.MaxStats.Str {
p.MaxStats.Str = comp p.MaxStats.Str = comp

View File

@@ -54,7 +54,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
tp.Stats.Lvl = mp.Stats.Lvl + levAdd tp.Stats.Lvl = mp.Stats.Lvl + levAdd
tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8) tp.Stats.MaxHP = g.roll(tp.Stats.Lvl, 8)
tp.Stats.HP = tp.Stats.MaxHP tp.Stats.HP = tp.Stats.MaxHP
tp.Stats.Arm = mp.Stats.Arm - levAdd tp.Stats.ArmorClass = mp.Stats.ArmorClass - levAdd
tp.Stats.Dmg = mp.Stats.Dmg tp.Stats.Dmg = mp.Stats.Dmg
tp.Stats.Str = mp.Stats.Str tp.Stats.Str = mp.Stats.Str
tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp) tp.Stats.Exp = mp.Stats.Exp + levAdd*10 + expAdd(tp)
@@ -183,10 +183,10 @@ func (g *RogueGame) save(which int) bool {
p := &g.Player p := &g.Player
if which == VsMagic { if which == VsMagic {
if p.IsRing(Left, RingProtection) { if p.IsRing(Left, RingProtection) {
which -= p.CurRing[Left].Arm which -= p.CurRing[Left].Bonus
} }
if p.IsRing(Right, RingProtection) { if p.IsRing(Right, RingProtection) {
which -= p.CurRing[Right].Arm which -= p.CurRing[Right].Bonus
} }
} }
return g.saveThrow(which, &p.Stats) return g.saveThrow(which, &p.Stats)

View File

@@ -241,7 +241,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
p.Flags.Clear(Awake) p.Flags.Clear(Awake)
g.msg("a strange white mist envelops you and you fall asleep") g.msg("a strange white mist envelops you and you fall asleep")
case TrapArrow: case TrapArrow:
if g.swing(p.Stats.Lvl-1, p.Stats.Arm, 1) { if g.swing(p.Stats.Lvl-1, p.Stats.ArmorClass, 1) {
p.Stats.HP -= g.roll(1, 6) p.Stats.HP -= g.roll(1, 6)
if p.Stats.HP <= 0 { if p.Stats.HP <= 0 {
g.msg("an arrow killed you") g.msg("an arrow killed you")
@@ -263,7 +263,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
g.teleport() g.teleport()
g.mvaddch(tc.Y, tc.X, Trap) g.mvaddch(tc.Y, tc.X, Trap)
case TrapDart: case TrapDart:
if !g.swing(p.Stats.Lvl+1, p.Stats.Arm, 1) { if !g.swing(p.Stats.Lvl+1, p.Stats.ArmorClass, 1) {
g.msg("a small dart whizzes by your ear and vanishes") g.msg("a small dart whizzes by your ear and vanishes")
} else { } else {
p.Stats.HP -= g.roll(1, 4) p.Stats.HP -= g.roll(1, 4)
@@ -322,7 +322,7 @@ func (g *RogueGame) rndmove(who *Creature) Coord {
// aren't wearing a magic ring (move.c rust_armor). // aren't wearing a magic ring (move.c rust_armor).
func (g *RogueGame) rustArmor(arm *Object) { func (g *RogueGame) rustArmor(arm *Object) {
if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather || if arm == nil || arm.Kind != KindArmor || arm.ArmorKind() == ArmorLeather ||
arm.Arm >= 9 { arm.ArmorClass >= 9 {
return return
} }
@@ -331,7 +331,7 @@ func (g *RogueGame) rustArmor(arm *Object) {
g.msg("the rust vanishes instantly") g.msg("the rust vanishes instantly")
} }
} else { } else {
arm.Arm++ arm.ArmorClass++
if !g.Options.Terse { if !g.Options.Terse {
g.msg("your armor appears to be weaker now. Oh my!") g.msg("your armor appears to be weaker now. Oh my!")
} else { } else {

View File

@@ -111,9 +111,9 @@ func (g *RogueGame) putThings() {
if g.Depth >= AmuletLevel && !g.HasAmulet { if g.Depth >= AmuletLevel && !g.HasAmulet {
obj := newObject() obj := newObject()
attachObj(&g.Level.Objects, obj) attachObj(&g.Level.Objects, obj)
obj.Damage = "0x0" obj.Damage = dice("0x0")
obj.HurlDmg = "0x0" obj.HurlDmg = dice("0x0")
obj.Arm = 11 obj.ArmorClass = 11
obj.Kind = KindAmulet obj.Kind = KindAmulet
// Put it somewhere // Put it somewhere
obj.Pos, _ = g.findFloor(nil, 0, false) obj.Pos, _ = g.findFloor(nil, 0, false)

View File

@@ -99,13 +99,16 @@ type Object struct {
Text string // what it says if you read it Text string // what it says if you read it
Launch WeaponKind // what you need to launch it (noWeapon if none) Launch WeaponKind // what you need to launch it (noWeapon if none)
PackCh byte // what character it is in the pack PackCh byte // what character it is in the pack
Damage string // damage if used like sword Damage DiceSpec // damage if used like sword
HurlDmg string // damage if thrown HurlDmg DiceSpec // damage if thrown
Count int // count for plural objects Count int // count for plural objects
Which int // which object of a type it is (index for the Kind's table) Which int // which object of a type it is (index for the Kind's table)
HPlus int // plusses to hit HPlus int // plusses to hit
DPlus int // plusses to damage DPlus int // plusses to damage
Arm int // armor protection — overloaded as in C (see Charges/GoldVal) ArmorClass int // armor protection (armor only)
Charges int // charges remaining (wands and staffs only)
GoldValue int // worth (gold piles only)
Bonus int // magic bonus (rings only: protection, add strength, ...)
Flags ObjFlags Flags ObjFlags
Group int // group number for this object Group int // group number for this object
Label string // label for object Label string // label for object
@@ -117,18 +120,6 @@ func newObject() *Object {
return &Object{Launch: noWeapon} return &Object{Launch: noWeapon}
} }
// Charges is the o_charges alias for Arm on wands and staffs.
func (o *Object) Charges() int { return o.Arm }
// SetCharges stores a charge count in the overloaded Arm field.
func (o *Object) SetCharges(n int) { o.Arm = n }
// GoldVal is the o_goldval alias for Arm on gold piles.
func (o *Object) GoldVal() int { return o.Arm }
// SetGoldVal stores a gold value in the overloaded Arm field.
func (o *Object) SetGoldVal(n int) { o.Arm = n }
// PotionKind returns Which as a potion kind; valid only for KindPotion. // PotionKind returns Which as a potion kind; valid only for KindPotion.
func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) } func (o *Object) PotionKind() PotionKind { return PotionKind(o.Which) }

View File

@@ -250,7 +250,7 @@ func (g *RogueGame) pickUp(ch byte) {
if obj == nil { if obj == nil {
return return
} }
g.money(obj.GoldVal()) g.money(obj.GoldValue)
detachObj(&g.Level.Objects, obj) detachObj(&g.Level.Objects, obj)
p.Room.GoldVal = 0 p.Room.GoldVal = 0
default: default:

View File

@@ -153,19 +153,19 @@ func (g *RogueGame) quaff() {
} }
case PotionRestoreStrength: case PotionRestoreStrength:
if p.IsRing(Left, RingAddStrength) { if p.IsRing(Left, RingAddStrength) {
addStr(&p.Stats.Str, -p.CurRing[Left].Arm) addStr(&p.Stats.Str, -p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RingAddStrength) { if p.IsRing(Right, RingAddStrength) {
addStr(&p.Stats.Str, -p.CurRing[Right].Arm) addStr(&p.Stats.Str, -p.CurRing[Right].Bonus)
} }
if p.Stats.Str < p.MaxStats.Str { if p.Stats.Str < p.MaxStats.Str {
p.Stats.Str = p.MaxStats.Str p.Stats.Str = p.MaxStats.Str
} }
if p.IsRing(Left, RingAddStrength) { if p.IsRing(Left, RingAddStrength) {
addStr(&p.Stats.Str, p.CurRing[Left].Arm) addStr(&p.Stats.Str, p.CurRing[Left].Bonus)
} }
if p.IsRing(Right, RingAddStrength) { if p.IsRing(Right, RingAddStrength) {
addStr(&p.Stats.Str, p.CurRing[Right].Arm) addStr(&p.Stats.Str, p.CurRing[Right].Bonus)
} }
g.msg("hey, this tastes great. It make you feel warm all over") g.msg("hey, this tastes great. It make you feel warm all over")
case PotionBlindness: case PotionBlindness:
@@ -212,7 +212,7 @@ func (g *RogueGame) doPot(kind PotionKind, knowit bool) {
func (o *Object) isMagic() bool { func (o *Object) isMagic() bool {
switch o.Kind { switch o.Kind {
case KindArmor: case KindArmor:
return o.Flags.Has(Protected) || o.Arm != aClass[o.Which] return o.Flags.Has(Protected) || o.ArmorClass != aClass[o.Which]
case KindWeapon: case KindWeapon:
return o.HPlus != 0 || o.DPlus != 0 return o.HPlus != 0 || o.DPlus != 0
case KindPotion, KindScroll, KindWand, KindRing, KindAmulet: case KindPotion, KindScroll, KindWand, KindRing, KindAmulet:

View File

@@ -49,7 +49,7 @@ func (g *RogueGame) ringOn() {
// Calculate the effect it has on the poor guy. // Calculate the effect it has on the poor guy.
switch obj.RingKind() { switch obj.RingKind() {
case RingAddStrength: case RingAddStrength:
g.chgStr(obj.Arm) g.chgStr(obj.Bonus)
case RingSeeInvisible: case RingSeeInvisible:
g.invisOn() g.invisOn()
case RingAggravateMonsters: case RingAggravateMonsters:
@@ -168,7 +168,7 @@ func ringNum(g *RogueGame, obj *Object) string {
} }
switch obj.RingKind() { switch obj.RingKind() {
case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity: case RingProtection, RingAddStrength, RingIncreaseDamage, RingDexterity:
return fmt.Sprintf(" [%s]", num(obj.Arm, 0, Ring)) return fmt.Sprintf(" [%s]", num(obj.Bonus, 0, Ring))
} }
return "" return ""
} }

View File

@@ -124,8 +124,8 @@ func (g *RogueGame) totalWinner() {
obj.Flags.Set(Known) obj.Flags.Set(Known)
case KindArmor: case KindArmor:
worth = it.Armors[obj.Which].Worth worth = it.Armors[obj.Which].Worth
worth += (9 - obj.Arm) * 100 worth += (9 - obj.ArmorClass) * 100
worth += 10 * (aClass[obj.Which] - obj.Arm) worth += 10 * (aClass[obj.Which] - obj.ArmorClass)
obj.Flags.Set(Known) obj.Flags.Set(Known)
case KindScroll: case KindScroll:
op := &it.Scrolls[obj.Which] op := &it.Scrolls[obj.Which]
@@ -146,8 +146,8 @@ func (g *RogueGame) totalWinner() {
worth = op.Worth worth = op.Worth
if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage || if obj.RingKind() == RingAddStrength || obj.RingKind() == RingIncreaseDamage ||
obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity { obj.RingKind() == RingProtection || obj.RingKind() == RingDexterity {
if obj.Arm > 0 { if obj.Bonus > 0 {
worth += obj.Arm * 100 worth += obj.Bonus * 100
} else { } else {
worth = 10 worth = 10
} }
@@ -160,7 +160,7 @@ func (g *RogueGame) totalWinner() {
case KindWand: case KindWand:
op := &it.Sticks[obj.Which] op := &it.Sticks[obj.Which]
worth = op.Worth worth = op.Worth
worth += 20 * obj.Charges() worth += 20 * obj.Charges
if !obj.Flags.Has(Known) { if !obj.Flags.Has(Known) {
worth /= 2 worth /= 2
} }

View File

@@ -88,7 +88,7 @@ func (g *RogueGame) doRooms() {
if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) { if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) {
gold := newObject() gold := newObject()
rp.GoldVal = g.goldCalc() rp.GoldVal = g.goldCalc()
gold.SetGoldVal(rp.GoldVal) gold.GoldValue = rp.GoldVal
rp.Gold, _ = g.findFloorIn(rp, 0, false) rp.Gold, _ = g.findFloorIn(rp, 0, false)
gold.Pos = rp.Gold gold.Pos = rp.Gold
g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold) g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold)

View File

@@ -13,8 +13,10 @@ import (
// saveFormatVersion identifies the snapshot layout; it changes whenever a // saveFormatVersion identifies the snapshot layout; it changes whenever a
// field rename or retype would make gob silently mis-decode an older // field rename or retype would make gob silently mis-decode an older
// save ("go2": Object.Type became Kind ObjectKind). // save ("go2": Object.Type became Kind ObjectKind; "go3": Object.Arm split
const saveFormatVersion = Release + "-go2" // into ArmorClass/Charges/GoldValue/Bonus, damage strings became
// DiceSpec).
const saveFormatVersion = Release + "-go3"
// destRef encodes a monster's chase target (state.c rs_write_thing's // destRef encodes a monster's chase target (state.c rs_write_thing's
// (kind, index) pairs). // (kind, index) pairs).

View File

@@ -15,7 +15,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
g.HasAmulet = true g.HasAmulet = true
g.Items.Potions[PotionHealing].Know = true g.Items.Potions[PotionHealing].Know = true
g.Items.Scrolls[ScrollMagicMapping].Guess = "map???" g.Items.Scrolls[ScrollMagicMapping].Guess = "map???"
g.Monsters['F'-'A'].Stats.Dmg = "3x1" // mutated bestiary must survive g.Monsters['F'-'A'].Stats.Dmg = dice("3x1") // mutated bestiary must survive
if len(g.Level.Monsters) > 0 { if len(g.Level.Monsters) > 0 {
g.Level.Monsters[0].Flags.Set(Awake) g.Level.Monsters[0].Flags.Set(Awake)
g.Level.Monsters[0].Dest = &g.Player.Pos g.Level.Monsters[0].Dest = &g.Player.Pos
@@ -48,7 +48,7 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" { if h.Items.Scrolls[ScrollMagicMapping].Guess != "map???" {
t.Error("scroll guess lost") t.Error("scroll guess lost")
} }
if h.Monsters['F'-'A'].Stats.Dmg != "3x1" { if h.Monsters['F'-'A'].Stats.Dmg.String() != "3x1" {
t.Error("mutated bestiary lost") t.Error("mutated bestiary lost")
} }
if h.Rng.Seed != g.Rng.Seed { if h.Rng.Seed != g.Rng.Seed {

View File

@@ -42,7 +42,7 @@ func (g *RogueGame) readScroll() {
g.msg("your hands begin to glow %s", g.pickColor("red")) g.msg("your hands begin to glow %s", g.pickColor("red"))
case ScrollEnchantArmor: case ScrollEnchantArmor:
if p.CurArmor != nil { if p.CurArmor != nil {
p.CurArmor.Arm-- p.CurArmor.ArmorClass--
p.CurArmor.Flags.Clear(Cursed) p.CurArmor.Flags.Clear(Cursed)
g.msg("your armor glows %s for a moment", g.pickColor("silver")) g.msg("your armor glows %s for a moment", g.pickColor("silver"))
} }

View File

@@ -16,7 +16,7 @@ func (g *RogueGame) doZap() {
g.msg("you can't zap with that!") g.msg("you can't zap with that!")
return return
} }
if obj.Charges() == 0 { if obj.Charges == 0 {
g.msg("nothing happens") g.msg("nothing happens")
return return
} }
@@ -111,7 +111,7 @@ func (g *RogueGame) doZap() {
g.Items.Sticks[WandMagicMissile].Know = true g.Items.Sticks[WandMagicMissile].Know = true
bolt := newObject() bolt := newObject()
bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon bolt.Kind = KindGold // C set o_type='*': draws a '*' and is not a weapon
bolt.HurlDmg = "1x4" bolt.HurlDmg = dice("1x4")
bolt.HPlus = 100 bolt.HPlus = 100
bolt.DPlus = 1 bolt.DPlus = 1
bolt.Flags = Missile bolt.Flags = Missile
@@ -167,7 +167,7 @@ func (g *RogueGame) doZap() {
g.Items.Sticks[obj.Which].Know = true g.Items.Sticks[obj.Which].Know = true
case WandNothing: case WandNothing:
} }
obj.SetCharges(obj.Charges() - 1) obj.Charges--
} }
// drain does the drain-hit-points-from-player schtick (sticks.c drain). // drain does the drain-hit-points-from-player schtick (sticks.c drain).
@@ -213,7 +213,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
bolt := newObject() bolt := newObject()
bolt.Kind = KindWeapon bolt.Kind = KindWeapon
bolt.Which = int(WeaponFlame) bolt.Which = int(WeaponFlame)
bolt.HurlDmg = "6x6" bolt.HurlDmg = dice("6x6")
bolt.HPlus = 100 bolt.HPlus = 100
bolt.DPlus = 0 bolt.DPlus = 0
g.Items.Weapons[WeaponFlame].Name = name g.Items.Weapons[WeaponFlame].Name = name
@@ -322,17 +322,17 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) {
// fixStick sets up a new wand or staff (sticks.c fix_stick). // fixStick sets up a new wand or staff (sticks.c fix_stick).
func (g *RogueGame) fixStick(cur *Object) { func (g *RogueGame) fixStick(cur *Object) {
if g.Items.WandType[cur.Which] == "staff" { if g.Items.WandType[cur.Which] == "staff" {
cur.Damage = "2x3" cur.Damage = dice("2x3")
} else { } else {
cur.Damage = "1x1" cur.Damage = dice("1x1")
} }
cur.HurlDmg = "1x1" cur.HurlDmg = dice("1x1")
switch cur.WandKind() { switch cur.WandKind() {
case WandLight: case WandLight:
cur.SetCharges(g.rnd(10) + 10) cur.Charges = g.rnd(10) + 10
default: default:
cur.SetCharges(g.rnd(5) + 3) cur.Charges = g.rnd(5) + 3
} }
} }
@@ -343,7 +343,7 @@ func chargeStr(g *RogueGame, obj *Object) string {
return "" return ""
} }
if g.Options.Terse { if g.Options.Terse {
return fmt.Sprintf(" [%d]", obj.Charges()) return fmt.Sprintf(" [%d]", obj.Charges)
} }
return fmt.Sprintf(" [%d charges]", obj.Charges()) return fmt.Sprintf(" [%d charges]", obj.Charges)
} }

View File

@@ -6,7 +6,7 @@ package game
// change during play) are cloned into RogueGame.Items by NewGame. // change during play) are cloned into RogueGame.Items by NewGame.
// initStats is the C INIT_STATS: the player's starting statistics. // initStats is the C INIT_STATS: the player's starting statistics.
var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, Arm: 10, HP: 12, Dmg: "1x4", MaxHP: 12} var initStats = Stats{Str: 16, Exp: 0, Lvl: 1, ArmorClass: 10, HP: 12, Dmg: dice("1x4"), MaxHP: 12}
// aClass is extern.c a_class[]: armor class for each armor type. // aClass is extern.c a_class[]: armor class for each armor type.
var aClass = [NumArmorTypes]int{ var aClass = [NumArmorTypes]int{
@@ -47,32 +47,32 @@ var invTName = []string{"Overwrite", "Slow", "Clear"}
// rolled from the level at creation time. // rolled from the level at creation time.
var monsterTable = [26]MonsterKind{ var monsterTable = [26]MonsterKind{
/* Name CARRY FLAGS str exp lvl arm hp dmg */ /* Name CARRY FLAGS str exp lvl arm hp dmg */
{"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, "0x0/0x0", 0}}, {"aquator", 0, Mean, Stats{10, 20, 5, 2, 1, dice("0x0/0x0"), 0}},
{"bat", 0, Flying, Stats{10, 1, 1, 3, 1, "1x2", 0}}, {"bat", 0, Flying, Stats{10, 1, 1, 3, 1, dice("1x2"), 0}},
{"centaur", 15, 0, Stats{10, 17, 4, 4, 1, "1x2/1x5/1x5", 0}}, {"centaur", 15, 0, Stats{10, 17, 4, 4, 1, dice("1x2/1x5/1x5"), 0}},
{"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, "1x8/1x8/3x10", 0}}, {"dragon", 100, Mean, Stats{10, 5000, 10, -1, 1, dice("1x8/1x8/3x10"), 0}},
{"emu", 0, Mean, Stats{10, 2, 1, 7, 1, "1x2", 0}}, {"emu", 0, Mean, Stats{10, 2, 1, 7, 1, dice("1x2"), 0}},
{"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, "%%%x0", 0}}, {"venus flytrap", 0, Mean, Stats{10, 80, 8, 3, 1, dice("%%%x0"), 0}},
{"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, "4x3/3x5", 0}}, {"griffin", 20, Mean | Flying | Regenerates, Stats{10, 2000, 13, 2, 1, dice("4x3/3x5"), 0}},
{"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, "1x8", 0}}, {"hobgoblin", 0, Mean, Stats{10, 3, 1, 5, 1, dice("1x8"), 0}},
{"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, "0x0", 0}}, {"ice monster", 0, 0, Stats{10, 5, 1, 9, 1, dice("0x0"), 0}},
{"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, "2x12/2x4", 0}}, {"jabberwock", 70, 0, Stats{10, 3000, 15, 6, 1, dice("2x12/2x4"), 0}},
{"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, "1x4", 0}}, {"kestrel", 0, Mean | Flying, Stats{10, 1, 1, 7, 1, dice("1x4"), 0}},
{"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, "1x1", 0}}, {"leprechaun", 0, 0, Stats{10, 10, 3, 8, 1, dice("1x1"), 0}},
{"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, "3x4/3x4/2x5", 0}}, {"medusa", 40, Mean, Stats{10, 200, 8, 2, 1, dice("3x4/3x4/2x5"), 0}},
{"nymph", 100, 0, Stats{10, 37, 3, 9, 1, "0x0", 0}}, {"nymph", 100, 0, Stats{10, 37, 3, 9, 1, dice("0x0"), 0}},
{"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, "1x8", 0}}, {"orc", 15, Greedy, Stats{10, 5, 1, 6, 1, dice("1x8"), 0}},
{"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, "4x4", 0}}, {"phantom", 0, Invisible, Stats{10, 120, 8, 3, 1, dice("4x4"), 0}},
{"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, "1x5/1x5", 0}}, {"quagga", 0, Mean, Stats{10, 15, 3, 3, 1, dice("1x5/1x5"), 0}},
{"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, "1x6", 0}}, {"rattlesnake", 0, Mean, Stats{10, 9, 2, 3, 1, dice("1x6"), 0}},
{"snake", 0, Mean, Stats{10, 2, 1, 5, 1, "1x3", 0}}, {"snake", 0, Mean, Stats{10, 2, 1, 5, 1, dice("1x3"), 0}},
{"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, "1x8/1x8/2x6", 0}}, {"troll", 50, Regenerates | Mean, Stats{10, 120, 6, 4, 1, dice("1x8/1x8/2x6"), 0}},
{"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, "1x9/1x9/2x9", 0}}, {"black unicorn", 0, Mean, Stats{10, 190, 7, -2, 1, dice("1x9/1x9/2x9"), 0}},
{"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, "1x10", 0}}, {"vampire", 20, Regenerates | Mean, Stats{10, 350, 8, 1, 1, dice("1x10"), 0}},
{"wraith", 0, 0, Stats{10, 55, 5, 4, 1, "1x6", 0}}, {"wraith", 0, 0, Stats{10, 55, 5, 4, 1, dice("1x6"), 0}},
{"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, "4x4", 0}}, {"xeroc", 30, 0, Stats{10, 100, 7, 7, 1, dice("4x4"), 0}},
{"yeti", 30, 0, Stats{10, 50, 4, 6, 1, "1x6/1x6", 0}}, {"yeti", 30, 0, Stats{10, 50, 4, 6, 1, dice("1x6/1x6"), 0}},
{"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, "1x8", 0}}, {"zombie", 0, Mean, Stats{10, 6, 2, 8, 1, dice("1x8"), 0}},
} }
// Base ObjInfo tables (extern.c). These are templates: NewGame copies them // Base ObjInfo tables (extern.c). These are templates: NewGame copies them

View File

@@ -69,11 +69,11 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
case KindArmor: case KindArmor:
sp := it.Armors[which].Name sp := it.Armors[which].Name
if obj.Flags.Has(Known) { if obj.Flags.Has(Known) {
fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.Arm, 0, Armor), sp) fmt.Fprintf(&pb, "%s %s [", num(aClass[which]-obj.ArmorClass, 0, Armor), sp)
if !g.Options.Terse { if !g.Options.Terse {
pb.WriteString("protection ") pb.WriteString("protection ")
} }
fmt.Fprintf(&pb, "%d]", 10-obj.Arm) fmt.Fprintf(&pb, "%d]", 10-obj.ArmorClass)
} else { } else {
pb.WriteString(sp) pb.WriteString(sp)
} }
@@ -83,7 +83,7 @@ func (g *RogueGame) invName(obj *Object, drop bool) string {
case KindAmulet: case KindAmulet:
pb.WriteString("The Amulet of Yendor") pb.WriteString("The Amulet of Yendor")
case KindGold: case KindGold:
fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldVal()) fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldValue)
} }
out := pb.String() out := pb.String()
@@ -168,7 +168,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
p.CurRing[hand] = nil p.CurRing[hand] = nil
switch obj.RingKind() { switch obj.RingKind() {
case RingAddStrength: case RingAddStrength:
g.chgStr(-obj.Arm) g.chgStr(-obj.Bonus)
case RingSeeInvisible: case RingSeeInvisible:
g.unsee(0) g.unsee(0)
g.Extinguish(DUnsee) g.Extinguish(DUnsee)
@@ -180,9 +180,9 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
// newThing returns a new random thing for the dungeon (things.c new_thing). // newThing returns a new random thing for the dungeon (things.c new_thing).
func (g *RogueGame) newThing() *Object { func (g *RogueGame) newThing() *Object {
cur := newObject() cur := newObject()
cur.Damage = "0x0" cur.Damage = dice("0x0")
cur.HurlDmg = "0x0" cur.HurlDmg = dice("0x0")
cur.Arm = 11 cur.ArmorClass = 11
cur.Count = 1 cur.Count = 1
// Decide what kind of object it will be; if we haven't had food for a // Decide what kind of object it will be; if we haven't had food for a
@@ -219,20 +219,20 @@ func (g *RogueGame) newThing() *Object {
case 4: case 4:
cur.Kind = KindArmor cur.Kind = KindArmor
cur.Which = pickOne(g, g.Items.Armors[:]) cur.Which = pickOne(g, g.Items.Armors[:])
cur.Arm = aClass[cur.Which] cur.ArmorClass = aClass[cur.Which]
if r := g.rnd(100); r < 20 { if r := g.rnd(100); r < 20 {
cur.Flags.Set(Cursed) cur.Flags.Set(Cursed)
cur.Arm += g.rnd(3) + 1 cur.ArmorClass += g.rnd(3) + 1
} else if r < 28 { } else if r < 28 {
cur.Arm -= g.rnd(3) + 1 cur.ArmorClass -= g.rnd(3) + 1
} }
case 5: case 5:
cur.Kind = KindRing cur.Kind = KindRing
cur.Which = pickOne(g, g.Items.Rings[:]) cur.Which = pickOne(g, g.Items.Rings[:])
switch cur.RingKind() { switch cur.RingKind() {
case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage: case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
if cur.Arm = g.rnd(3); cur.Arm == 0 { if cur.Bonus = g.rnd(3); cur.Bonus == 0 {
cur.Arm = -1 cur.Bonus = -1
cur.Flags.Set(Cursed) cur.Flags.Set(Cursed)
} }
case RingAggravateMonsters, RingTeleportation: case RingAggravateMonsters, RingTeleportation:

View File

@@ -377,9 +377,9 @@ type Stats struct {
Str int // strength (s_str; 3..31) Str int // strength (s_str; 3..31)
Exp int // experience Exp int // experience
Lvl int // level of mastery Lvl int // level of mastery
Arm int // armor class ArmorClass int // armor class (s_arm)
HP int // hit points (s_hpt) HP int // hit points (s_hpt)
Dmg string // damage dice, e.g. "1x4/1x2" Dmg DiceSpec // damage dice, e.g. "1x4/1x2"
MaxHP int MaxHP int
} }

View File

@@ -123,20 +123,20 @@ func (g *RogueGame) wield() {
// initWeaps is the weapons.c init_dam[] table. // initWeaps is the weapons.c init_dam[] table.
var initWeaps = [NumWeaponTypes]struct { var initWeaps = [NumWeaponTypes]struct {
dam string // damage when wielded dam DiceSpec // damage when wielded
hrl string // damage when thrown hrl DiceSpec // damage when thrown
launch WeaponKind // launching weapon launch WeaponKind // launching weapon
flags ObjFlags flags ObjFlags
}{ }{
{"2x4", "1x3", noWeapon, 0}, // WeaponMace {dice("2x4"), dice("1x3"), noWeapon, 0}, // WeaponMace
{"3x4", "1x2", noWeapon, 0}, // Long sword {dice("3x4"), dice("1x2"), noWeapon, 0}, // Long sword
{"1x1", "1x1", noWeapon, 0}, // WeaponBow {dice("1x1"), dice("1x1"), noWeapon, 0}, // WeaponBow
{"1x1", "2x3", WeaponBow, Stackable | Missile}, // WeaponArrow {dice("1x1"), dice("2x3"), WeaponBow, Stackable | Missile}, // WeaponArrow
{"1x6", "1x4", noWeapon, Missile}, // WeaponDagger {dice("1x6"), dice("1x4"), noWeapon, Missile}, // WeaponDagger
{"4x4", "1x2", noWeapon, 0}, // 2h sword {dice("4x4"), dice("1x2"), noWeapon, 0}, // 2h sword
{"1x1", "1x3", noWeapon, Stackable | Missile}, // WeaponDart {dice("1x1"), dice("1x3"), noWeapon, Stackable | Missile}, // WeaponDart
{"1x2", "2x4", noWeapon, Stackable | Missile}, // Shuriken {dice("1x2"), dice("2x4"), noWeapon, Stackable | Missile}, // Shuriken
{"2x3", "1x6", noWeapon, Missile}, // WeaponSpear {dice("2x3"), dice("1x6"), noWeapon, Missile}, // WeaponSpear
} }
// initWeapon sets up a new weapon (weapons.c init_weapon). // initWeapon sets up a new weapon (weapons.c init_weapon).

View File

@@ -38,12 +38,12 @@ func (g *RogueGame) createObj() {
obj.HPlus += g.rnd(3) + 1 obj.HPlus += g.rnd(3) + 1
} }
} else { } else {
obj.Arm = aClass[obj.Which] obj.ArmorClass = aClass[obj.Which]
if bless == '-' { if bless == '-' {
obj.Arm += g.rnd(3) + 1 obj.ArmorClass += g.rnd(3) + 1
} }
if bless == '+' { if bless == '+' {
obj.Arm -= g.rnd(3) + 1 obj.ArmorClass -= g.rnd(3) + 1
} }
} }
case obj.Kind == KindRing: case obj.Kind == KindRing:
@@ -54,9 +54,9 @@ func (g *RogueGame) createObj() {
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
if bless == '-' { if bless == '-' {
obj.Flags.Set(Cursed) obj.Flags.Set(Cursed)
obj.Arm = -1 obj.Bonus = -1
} else { } else {
obj.Arm = g.rnd(2) + 1 obj.Bonus = g.rnd(2) + 1
} }
case RingAggravateMonsters, RingTeleportation: case RingAggravateMonsters, RingTeleportation:
obj.Flags.Set(Cursed) obj.Flags.Set(Cursed)
@@ -67,7 +67,7 @@ func (g *RogueGame) createObj() {
g.msg("how much?") g.msg("how much?")
buf := "" buf := ""
if g.getStr(&buf, g.scr.Std) == Norm { if g.getStr(&buf, g.scr.Std) == Norm {
obj.SetGoldVal(cAtoi(buf)) obj.GoldValue = cAtoi(buf)
} }
} }
g.addPack(obj, false) g.addPack(obj, false)
@@ -169,7 +169,7 @@ func (g *RogueGame) teleport() {
if p.On(Held) { if p.On(Held) {
p.Flags.Clear(Held) p.Flags.Clear(Held)
p.VfHit = 0 p.VfHit = 0
g.Monsters['F'-'A'].Stats.Dmg = "000x0" g.Monsters['F'-'A'].Stats.Dmg = dice("000x0")
} }
g.NoMove = 0 g.NoMove = 0
g.Count = 0 g.Count = 0