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() }