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:
58
game/dice.go
Normal file
58
game/dice.go
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user