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.
59 lines
1.4 KiB
Go
59 lines
1.4 KiB
Go
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()
|
|
}
|