155
|
1 The New Dicing System: A Proposal for OpenRPG
|
|
2 ----------------------------------------------
|
|
3
|
|
4 The current dice system for OpenRPG has several limitations. Foremost
|
|
5 among these are the fact that adding a new, non-standard dicing mechanism
|
|
6 requires editing of the basic dice code. There are several secondary
|
|
7 limitations, such as the fact that while the dice system can handle math,
|
|
8 it cannot be used as a calculator -- it will not allow expressions that do
|
|
9 not involve dice.
|
|
10
|
|
11 This proposal is for a new dicing system to replace the current one in
|
|
12 OpenRPG. Since the dicing system is something that users will interact
|
|
13 with frequently, a new system needs to be considered carefully. This
|
|
14 document attempts to describe the new dicing system so that such
|
|
15 consideration can be given to it. It is expected that this document will
|
|
16 grow and change as it is scrutinized.
|
|
17
|
|
18
|
|
19 Design goals for this dicing system:
|
|
20
|
|
21 1. Should be easy for new users to get started with, based on knowing
|
|
22 standard RPG dice notation (NdX) and basic math.
|
|
23
|
|
24 2. Should, as far as practical, maintain compatibility with existing
|
|
25 character sheets, etc., that use the current dice system.
|
|
26
|
|
27 3. Should allow users to create new dice types and new ways of
|
|
28 counting dice. Ideally, this should not require programming, except
|
|
29 in exceptional cases.
|
|
30
|
|
31 4. The dice system should be usable for doing basic math that does
|
|
32 not involve dice.
|
|
33
|
|
34 5. The dice system should be able to handle most current RPG dicing
|
|
35 systems.
|
|
36
|
|
37 Things this dicing system is designed to NOT do:
|
|
38
|
|
39 1. Be a programming language. There are no facilities in it for
|
|
40 user input, output formatting, loops, if-then-else, or similar
|
|
41 things. If these are desired for something involving dice, an
|
|
42 appropriate node and nodehandler can be created.
|
|
43
|
|
44 2. Handle all theoretically possible dicing systems without the
|
|
45 need for programming plugins. First off, this is impossible.
|
|
46 Second, even making an attempt to would require supporting
|
|
47 dicing methods that don't actually turn up in any real game.
|
|
48
|
|
49 3. Handle floating-point math. I don't know of any systems that
|
|
50 use it in their dice schemes right now. If there are some,
|
|
51 we might have to consider adding it.
|
|
52
|
|
53
|
|
54 Syntax Specification
|
|
55
|
|
56 What follows is a BNF specification for the proposed dicing system, with
|
|
57 explanatory text interleaved. At the end of this document is a copy of
|
|
58 the BNF with no explanations, for those who would like to look at it "all
|
|
59 together". Note that BNF describes only syntax, and not semantics; thus,
|
|
60 while anything generated with this grammar should be syntactically correct,
|
|
61 that doesn't mean it will make sense or be allowed.
|
|
62
|
|
63
|
|
64 dice string ::= <expression>
|
|
65 <expression> of <comparison> | <comparison>
|
|
66
|
|
67 This is the top level. The major thing of note here is that comparisons
|
|
68 only occur at this level. This is intentional; the result of a comparison
|
|
69 is a boolean true/false flag rather than a number. Thus, it makes no
|
|
70 sense to allow people to perform further numerical operations on the
|
|
71 result of a comparison. Systems where dice are triggered by the results
|
|
72 of other dice are left for the realm of plugins.
|
|
73
|
|
74
|
|
75 comparison ::= <expression> <relation> <expression>
|
|
76
|
|
77 expression ::= <factor> | <factor> <low-op> <factor>
|
|
78
|
|
79 The separation into "low-op" and "high-op" of the operators is to allow
|
|
80 order of operations to be handled more easily. Syntactically, it's not
|
|
81 really necessary, but it should be helpful in implementation.
|
|
82
|
|
83
|
|
84 factor :: = <term> | <term> <high-op> <term> |
|
|
85 <multi-dice> | <multi-dice> <high-op> <term> |
|
|
86 <term> <high-op> <multi-dice>
|
|
87
|
|
88 Here we start to hit some complication. The intent of the different
|
|
89 entries for multi-dice is that we don't want to allow things like
|
|
90 [3d6 each * 2d6 each]. We are *not* doing vector multiplication!
|
|
91 The "expression" level doesn't have any such limitation on syntax;
|
|
92 things like [3d6 each + 2d6 each] we'll have to either think of a
|
|
93 logical way to handle, or disallow on a semantic level. (Well... I
|
|
94 suppose it could be handled in the BNF, but I think it would get
|
|
95 kind of messy.)
|
|
96
|
|
97
|
|
98 term ::= <dice> | <unit>
|
|
99
|
|
100 unit ::= <number> | ( <expression> )
|
|
101
|
|
102 Dice are not considered a unit. This means that things like [3d6d10] can't
|
|
103 be done without using parentheses. I consider that to be a win for
|
|
104 clarity.
|
|
105
|
|
106
|
|
107 dice ::= <unit>d<unit> | <unit>d<name> | <dice> <flag> | lastroll
|
|
108
|
|
109 The <name> entry here allows for user-created dice (in the syntax, at
|
|
110 least...).
|
|
111
|
|
112
|
|
113 multi-dice ::= ( <dice>, <dice>+ ) | # (1d6,1d8)
|
|
114 <dice> each | # 3d6 each
|
|
115 ( <expression> of <expression> ) | # (3 of 2d6)
|
|
116 lastroll | # lastroll
|
|
117 <multi-dice> <flag> # (3 of 2d6) best 2
|
|
118
|
|
119 "lastroll" by itself can be either dice or multi-dice. I'm thinking that it
|
|
120
|
|
121 should be whatever type the last roll was.
|
|
122
|
|
123
|
|
124 flag ::= reroll <condition> | # repeats
|
|
125 reroll <slice> | # once only
|
|
126 grow <condition> | # reroll and add
|
|
127 shrink <condition> | # reroll and subtract
|
|
128 drop <condition> |
|
|
129 drop <slice> |
|
|
130 take <condition> |
|
|
131 take <slice> |
|
|
132 <slice> | # implied "take"
|
|
133 <name> <condition> | # user-created
|
|
134 <name> # user-created
|
|
135
|
|
136 Technically, we don't need both "drop" and "take" -- one implies the
|
|
137 other. However, having both should make the language easier to use.
|
|
138
|
|
139 "reroll" will work differently depending on whether a condition or a
|
|
140 slice is given. If a condition is given, it will reroll until none of
|
|
141 the dice in the set meet the reroll condition (or until it hits a maximum
|
|
142 allowed number of rerolls). If a slice is given, it will reroll those
|
|
143 dice once. IMHO, this behavior makes the most sense.
|
|
144
|
|
145 The <name> entries here are to allow for user-created flags. Note that
|
|
146 as I've specified things right now, a user-created flag can have a
|
|
147 condition,
|
|
148 but not a slice. That's mostly because I couldn't think of a case where
|
|
149 a slice would be useful... should we add it anyways?
|
|
150
|
|
151
|
|
152 slice ::= highest | lowest | highest <number> | lowest <number>
|
|
153
|
|
154 "highest" and "lowest" without a number are equivalent to doing them with
|
|
155 1 as the number. This is to simplify things like [4d6 drop lowest].
|
|
156
|
|
157
|
|
158 condition ::= <relation> <unit>
|
|
159
|
|
160 This is for conditions on flags. Note that it can take a unit, so you could
|
|
161
|
|
162 use dice in a condition; however, I think the unit should only be evaluated
|
|
163
|
|
164 once, to make things faster. Anyone for repetitive evaluation?
|
|
165
|
|
166
|
|
167 low-op ::= + | - | min | max
|
|
168
|
|
169 "min" takes two values and returns the highest of them, and "max"
|
|
170 returns the lowest of them. This might seem counterintuitive, but
|
|
171 it's meant to be used with dice, like so:
|
|
172
|
|
173 3d6 min 8 - always returns 8 or higher
|
|
174
|
|
175 3d6 max 15 - always returns 15 or lower
|
|
176
|
|
177 I decided to put min and max as having the same precedence as + and -,
|
|
178 because if they had higher precedence, then:
|
|
179
|
|
180 3d6+2 min 10
|
|
181
|
|
182 would be equivalent to 3d6+10. (It would take the max of 2 and 10, then
|
|
183 add that to 3d6). One problem that does arise here is with multiplication
|
|
184 and division: [1d6 min 5 * 2] will be equivalent to [1d6 min 10], since
|
|
185 multiplication has higher precedence. We may just want to warn people
|
|
186 that min and max can be screwy unless you parenthesize, unless someone can
|
|
187 think of a better way to handle them.
|
|
188
|
|
189
|
|
190 high-op ::= * | / | mod
|
|
191
|
|
192 The / is integer division, of course, since we're doing integer math.
|
|
193
|
|
194
|
|
195 number ::= <digit>+ | -<digit>+
|
|
196
|
|
197 Positive and negative numbers are allowed. This means that, syntactically,
|
|
198 [-2d-4] is legal. Do we want to modify the BNF to disallow this, or handle
|
|
199 it on a semantic level?
|
|
200
|
|
201
|
|
202 name ::= <letter>[<letter>|<digit>]*
|
|
203
|
|
204 We may want to expand to allow underscores and dashes in user-created names.
|
|
205
|
|
206
|
|
207
|
|
208 letter ::= A-Z | a-z
|
|
209
|
|
210 digit ::= 0-9
|
|
211
|
|
212 relation ::= < | > | <= | >= | => | =< | = | ==
|
|
213
|
|
214
|
|
215
|
|
216 Well, that's the BNF. Again, at the end is a copy without all the running
|
|
217 commentary.
|
|
218
|
|
219
|
|
220 Thoughts on Implementation:
|
|
221
|
|
222 First, I think a sort of "dice library" of common functions needs to be
|
|
223 created. This would include rolling a set of dice, getting the highest
|
|
224 of a group of dice, growing and shrinking dice from a set based on
|
|
225 conditions, and so on. These functions should be available for use by
|
|
226 custom-written dice types.
|
|
227
|
|
228 Next, that library should be used as a tool in implementing a dice-string
|
|
229 interpreter. That will require creating a parser for the dice-string
|
|
230 'language'. This could be either a custom-written parser, or possibly
|
|
231 one created with some of the Python parser generators. A custom-written
|
|
232 parser may take longer to do and be a bit more finicky to maintain, but
|
|
233 it would remove a dependency from the code.
|
|
234
|
|
235 User-created flags and dice types could be supported in two ways:
|
|
236
|
|
237 - First, by allowing users to specify strings in the dice language
|
|
238 that the flags/expressions would expand to -- basically, allowing
|
|
239 dice macros.
|
|
240
|
|
241 - Second, by adding hooks for python modules to be associated with
|
|
242 user-created dice or flag types. This is likely to be the more
|
|
243 complicated of the two solutions, but it would also be more
|
|
244 flexible.
|
|
245
|
|
246 Personally, I think both are desirable -- the first, so that
|
|
247 non-programming users can create simple die and flag types. The
|
|
248 second, because by design, there are some things this dice system
|
|
249 just won't do.
|
|
250
|
|
251
|
|
252 Further work needed:
|
|
253
|
|
254 - specs for the "dice library"
|
|
255
|
|
256 - specs on an interface for python modules meant to be dice and
|
|
257 flag types.
|
|
258
|
|
259 ----------------------------------------------------------------
|
|
260
|
|
261 start ::= <expression> |
|
|
262 <comparison>
|
|
263
|
|
264 comparison ::= <expression> <condition>
|
|
265
|
|
266 expression ::= <term> | <term> <low-op> <factor>
|
|
267
|
|
268 term ::= <factor> | <factor> <high-op> <unit>
|
|
269
|
|
270 factor ::= <atom> | <dice_set>
|
|
271
|
|
272 atom ::= <number> | ( <expression> )
|
|
273
|
|
274 dice_set ::= <dice> <dice_set>, <dice> | <expr> of <dice> | <dice> each |
|
|
275 lastroll
|
|
276
|
|
277 dice ::= <atom>d<atom> | <atom>d<name> | <dice> <flag>
|
|
278
|
|
279 flag ::= reroll <condition> |
|
|
280 reroll <slice> |
|
|
281 grow <condition> |
|
|
282 shrink <condition> |
|
|
283 drop <condition> |
|
|
284 drop <slice> |
|
|
285 take <condition> |
|
|
286 take <slice> |
|
|
287 <slice> |
|
|
288 <name> <condition> |
|
|
289 <name> <slice> |
|
|
290 <name>
|
|
291
|
|
292 slice ::= highest | lowest | highest <number> | lowest <number>
|
|
293
|
|
294 condition ::= <relation> <unit>
|
|
295
|
|
296 low-op ::= + | -
|
|
297
|
|
298 high-op ::= * | / | mod| max | min
|
|
299
|
|
300 number ::= <digit>+ | -<digit>+
|
|
301
|
|
302 name ::= <letter>[<letter>|<digit>]*
|
|
303
|
|
304 letter ::= A-Z | a-z
|
|
305
|
|
306 digit ::= 0-9
|
|
307
|
|
308 relation ::= < | > | <= | >= | => | =< | = | ==
|
|
309
|