dicebag 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/bin/dicebag +47 -0
- data/lib/dicebag/label_part.rb +4 -2
- data/lib/dicebag/normalize.rb +165 -0
- data/lib/dicebag/parser.rb +67 -41
- data/lib/dicebag/result.rb +14 -7
- data/lib/dicebag/roll.rb +18 -14
- data/lib/dicebag/roll_part.rb +111 -41
- data/lib/dicebag/roll_part_string.rb +25 -10
- data/lib/dicebag/roll_string.rb +11 -10
- data/lib/dicebag/simple_part.rb +7 -4
- data/lib/dicebag/static_part.rb +9 -4
- data/lib/dicebag/systems/dnd.rb +65 -0
- data/lib/dicebag/systems/fudge.rb +80 -0
- data/lib/dicebag/systems/gurps.rb +52 -0
- data/lib/dicebag/systems/savage_worlds.rb +31 -0
- data/lib/dicebag/systems/standard.rb +19 -0
- data/lib/dicebag/systems/storyteller.rb +45 -0
- data/lib/dicebag/transform.rb +58 -47
- data/lib/dicebag.rb +16 -166
- metadata +18 -9
data/lib/dicebag/roll_part.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# DiceBag Module
|
3
2
|
module DiceBag
|
4
3
|
# This represents the xDx part of the dice string.
|
5
4
|
class RollPart < SimplePart
|
@@ -10,42 +9,49 @@ module DiceBag
|
|
10
9
|
attr_reader :parts
|
11
10
|
attr_reader :options
|
12
11
|
attr_reader :tally
|
12
|
+
attr_reader :reroll_count
|
13
13
|
|
14
14
|
def initialize(part)
|
15
|
-
|
16
|
-
@tally = []
|
17
|
-
@value = part
|
18
|
-
@count = part[:count]
|
19
|
-
@sides = part[:sides]
|
20
|
-
@notes = part[:notes] || []
|
15
|
+
super part
|
21
16
|
|
22
|
-
|
23
|
-
@
|
17
|
+
@total = nil
|
18
|
+
@tally = []
|
19
|
+
@count = part[:count]
|
20
|
+
@sides = part[:sides]
|
21
|
+
@notes = part[:notes]
|
22
|
+
@options = default_options
|
24
23
|
|
25
24
|
@options.update(part[:options]) if part.key?(:options)
|
26
25
|
end
|
27
26
|
|
27
|
+
# Our Default Options
|
28
|
+
#
|
29
|
+
# Note the absence of :explode, that is handled below.
|
30
|
+
def default_options
|
31
|
+
{
|
32
|
+
drop: 0,
|
33
|
+
keep: 0,
|
34
|
+
keeplowest: 0,
|
35
|
+
reroll: 0,
|
36
|
+
target: 0,
|
37
|
+
failure: 0
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
28
41
|
def notes
|
29
42
|
@notes.empty? ? '' : @notes.join("\n")
|
30
43
|
end
|
31
44
|
|
32
|
-
# Checks to see if this instance has rolled yet
|
33
|
-
# or not.
|
45
|
+
# Checks to see if this instance has rolled yet or not.
|
34
46
|
def rolled?
|
35
47
|
@total.nil? ? false : true
|
36
48
|
end
|
37
49
|
|
38
|
-
# Rolls a single die from the xDx string.
|
39
|
-
def roll_die
|
40
|
-
num = 0
|
41
|
-
num = rand(sides) + 1 while num <= @options[:reroll]
|
42
|
-
|
43
|
-
num
|
44
|
-
end
|
45
|
-
|
46
50
|
def roll
|
47
51
|
generate_results
|
48
52
|
|
53
|
+
return __roll_for_keep_lowest if @options[:keeplowest].positive?
|
54
|
+
|
49
55
|
@results.sort!
|
50
56
|
@results.reverse!
|
51
57
|
|
@@ -60,12 +66,21 @@ module DiceBag
|
|
60
66
|
|
61
67
|
# Set the total.
|
62
68
|
handle_total
|
69
|
+
end
|
63
70
|
|
64
|
-
|
71
|
+
def __roll_for_keep_lowest
|
72
|
+
@tally = @results.dup
|
73
|
+
|
74
|
+
@tally.sort!
|
75
|
+
@tally.reverse!
|
76
|
+
@results.sort!
|
77
|
+
|
78
|
+
handle_keeplowest
|
79
|
+
handle_total
|
65
80
|
end
|
66
81
|
|
67
|
-
# Gets the total of the last roll; if there is no
|
68
|
-
#
|
82
|
+
# Gets the total of the last roll; if there is no last roll, it
|
83
|
+
# calls roll() first.
|
69
84
|
def total
|
70
85
|
roll if @total.nil?
|
71
86
|
|
@@ -81,25 +96,43 @@ module DiceBag
|
|
81
96
|
def generate_results
|
82
97
|
@results = []
|
83
98
|
|
99
|
+
explode = @options.key?(:explode)
|
100
|
+
|
84
101
|
count.times do
|
85
|
-
|
102
|
+
roll = roll_die
|
86
103
|
|
87
|
-
@results.push(
|
104
|
+
@results.push(roll)
|
88
105
|
|
89
|
-
handle_explode(
|
106
|
+
handle_explode(roll) if explode
|
90
107
|
end
|
91
108
|
end
|
92
109
|
|
93
|
-
|
94
|
-
|
95
|
-
|
110
|
+
# Rolls a single die from the xDx string.
|
111
|
+
def roll_die
|
112
|
+
num = __roll_die
|
96
113
|
|
97
|
-
|
114
|
+
# Handle Reroll
|
115
|
+
if options[:reroll].positive?
|
116
|
+
num = __roll_die while num <= @options[:reroll]
|
117
|
+
end
|
118
|
+
|
119
|
+
num
|
120
|
+
end
|
121
|
+
|
122
|
+
def handle_explode(roll)
|
123
|
+
# If the explode value is nil (allowed!) then default to the
|
124
|
+
# number of sides.
|
125
|
+
val = @options[:explode] || sides
|
126
|
+
|
127
|
+
while roll >= val
|
128
|
+
roll = roll_die
|
129
|
+
|
130
|
+
@results.push(roll)
|
98
131
|
end
|
99
132
|
end
|
100
133
|
|
101
134
|
def handle_drop
|
102
|
-
return unless @options[:drop]
|
135
|
+
return unless @options[:drop].positive?
|
103
136
|
|
104
137
|
# Note that we invert the drop value here.
|
105
138
|
range = 0...-(@options[:drop])
|
@@ -108,23 +141,60 @@ module DiceBag
|
|
108
141
|
end
|
109
142
|
|
110
143
|
def handle_keep
|
111
|
-
return unless @options[:keep]
|
144
|
+
return unless @options[:keep].positive?
|
112
145
|
|
113
146
|
range = 0...@options[:keep]
|
114
147
|
|
115
148
|
@results = @results.slice range
|
116
149
|
end
|
117
150
|
|
151
|
+
def handle_keeplowest
|
152
|
+
return unless @options[:keeplowest].positive?
|
153
|
+
|
154
|
+
range = 0...@options[:keeplowest]
|
155
|
+
|
156
|
+
@results = @results.slice range
|
157
|
+
end
|
158
|
+
|
159
|
+
# If we have a target number, count how many rolls in the results
|
160
|
+
# are >= than this number and subtract the number <= the failure
|
161
|
+
# threshold, otherwise we just add up all the numbers.
|
118
162
|
def handle_total
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
163
|
+
tpos = @options[:target].positive?
|
164
|
+
fpos = @options[:failure].positive?
|
165
|
+
|
166
|
+
# Just add up the results.
|
167
|
+
return __simple_total unless tpos || fpos
|
168
|
+
|
169
|
+
# Add up successes and subtract failures.
|
170
|
+
return __target_and_failure_total if tpos
|
171
|
+
|
172
|
+
# Just tally failures.
|
173
|
+
@total = 0 - __failure_total
|
174
|
+
end
|
175
|
+
|
176
|
+
def __roll_die
|
177
|
+
rand(sides) + 1
|
178
|
+
end
|
179
|
+
|
180
|
+
def __simple_total
|
181
|
+
# I think reduce(:+) is ugly, but it's very fast.
|
182
|
+
@total = @results.reduce(:+)
|
183
|
+
end
|
184
|
+
|
185
|
+
def __target_and_failure_total
|
186
|
+
tcount = __target_total
|
187
|
+
fcount = __failure_total
|
188
|
+
|
189
|
+
@total = tcount - fcount
|
190
|
+
end
|
191
|
+
|
192
|
+
def __target_total
|
193
|
+
@results.count { |r| r >= @options[:target] }
|
194
|
+
end
|
195
|
+
|
196
|
+
def __failure_total
|
197
|
+
@results.count { |r| r <= @options[:failure] }
|
128
198
|
end
|
129
199
|
end
|
130
200
|
end
|
@@ -1,11 +1,8 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# This encapsulates the RollPart string
|
4
|
-
# generation methods.
|
1
|
+
# This encapsulates the RollPart string generation methods.
|
5
2
|
module RollPartString
|
6
|
-
# This takes the @parts hash and recreates the xDx
|
7
|
-
#
|
8
|
-
#
|
3
|
+
# This takes the @parts hash and recreates the xDx string. Optionally,
|
4
|
+
# passing true to the method will remove spaces from the finished
|
5
|
+
# string.
|
9
6
|
def to_s(no_spaces = false)
|
10
7
|
@parts = []
|
11
8
|
|
@@ -13,14 +10,20 @@ module RollPartString
|
|
13
10
|
to_s_explode
|
14
11
|
to_s_drop
|
15
12
|
to_s_keep
|
13
|
+
to_s_keeplowest
|
16
14
|
to_s_reroll
|
17
15
|
to_s_target
|
16
|
+
to_s_failure
|
18
17
|
|
19
18
|
join_str = no_spaces ? '' : ' '
|
20
19
|
|
21
20
|
@parts.join join_str
|
22
21
|
end
|
23
22
|
|
23
|
+
def inspect
|
24
|
+
"<#{self.class.name} #{self}>"
|
25
|
+
end
|
26
|
+
|
24
27
|
private
|
25
28
|
|
26
29
|
def to_s_xdx
|
@@ -31,11 +34,11 @@ module RollPartString
|
|
31
34
|
end
|
32
35
|
|
33
36
|
def to_s_explode
|
34
|
-
return
|
37
|
+
return unless @options.key?(:explode)
|
35
38
|
|
36
|
-
e =
|
39
|
+
e = @options[:explode].nil? ? 'e' : format('e%s', @options[:explode])
|
37
40
|
|
38
|
-
@parts.push
|
41
|
+
@parts.push e
|
39
42
|
end
|
40
43
|
|
41
44
|
def to_s_drop
|
@@ -50,6 +53,12 @@ module RollPartString
|
|
50
53
|
@parts.push format('k%s', @options[:keep])
|
51
54
|
end
|
52
55
|
|
56
|
+
def to_s_keeplowest
|
57
|
+
return if @options[:keeplowest].zero?
|
58
|
+
|
59
|
+
@parts.push format('kl%s', @options[:keeplowest])
|
60
|
+
end
|
61
|
+
|
53
62
|
def to_s_reroll
|
54
63
|
return if @options[:reroll].zero?
|
55
64
|
|
@@ -61,4 +70,10 @@ module RollPartString
|
|
61
70
|
|
62
71
|
@parts.push format('t%s', @options[:target])
|
63
72
|
end
|
73
|
+
|
74
|
+
def to_s_failure
|
75
|
+
return if @options[:failure].zero?
|
76
|
+
|
77
|
+
@parts.push format('f%s', @options[:failure])
|
78
|
+
end
|
64
79
|
end
|
data/lib/dicebag/roll_string.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# This encapsulates the Roll class' string
|
4
|
-
# generation methods.
|
1
|
+
# This encapsulates the Roll class' string generation methods.
|
5
2
|
module RollString
|
6
3
|
def to_s(no_spaces = false)
|
7
4
|
@parts = []
|
@@ -13,6 +10,10 @@ module RollString
|
|
13
10
|
no_spaces ? str.tr(' ', '') : str
|
14
11
|
end
|
15
12
|
|
13
|
+
def inspect
|
14
|
+
"<#{self.class.name} #{self}>"
|
15
|
+
end
|
16
|
+
|
16
17
|
private
|
17
18
|
|
18
19
|
def to_s_tree
|
@@ -30,22 +31,22 @@ module RollString
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def to_s_add(value)
|
33
|
-
|
34
|
+
__op_value '+', value
|
34
35
|
end
|
35
36
|
|
36
37
|
def to_s_sub(value)
|
37
|
-
|
38
|
+
__op_value '-', value
|
38
39
|
end
|
39
40
|
|
40
41
|
def to_s_mul(value)
|
41
|
-
|
42
|
+
__op_value '*', value
|
42
43
|
end
|
43
44
|
|
44
45
|
def to_s_div(value)
|
45
|
-
|
46
|
+
__op_value '/', value
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
-
"#{
|
49
|
+
def __op_value(oper, value)
|
50
|
+
"#{oper}#{value}"
|
50
51
|
end
|
51
52
|
end
|
data/lib/dicebag/simple_part.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module DiceBag
|
2
|
-
# The most simplest of a part. If a given part of
|
3
|
-
# a
|
4
|
-
#
|
5
|
-
# returns the value given to it.
|
2
|
+
# The most simplest of a part. If a given part of a dice string is not
|
3
|
+
# a Label, Fixnum, or a xDx part it will be an instance of this class,
|
4
|
+
# which simply returns the value given to it.
|
6
5
|
class SimplePart
|
7
6
|
attr_reader :value
|
8
7
|
|
@@ -17,5 +16,9 @@ module DiceBag
|
|
17
16
|
def to_s
|
18
17
|
value
|
19
18
|
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"<#{self.class.name} #{self}>"
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
data/lib/dicebag/static_part.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# DiceBag module
|
1
2
|
module DiceBag
|
2
|
-
# This represents a static, non-random number part
|
3
|
-
# of the dice string.
|
3
|
+
# This represents a static, non-random number part of the dice string.
|
4
4
|
class StaticPart < SimplePart
|
5
5
|
def initialize(num)
|
6
|
-
num
|
7
|
-
|
6
|
+
num = num.to_i if num.is_a?(String)
|
7
|
+
|
8
|
+
super num
|
8
9
|
end
|
9
10
|
|
10
11
|
def total
|
@@ -14,5 +15,9 @@ module DiceBag
|
|
14
15
|
def to_s
|
15
16
|
value.to_s
|
16
17
|
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"<#{self.class.name} #{self}>"
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# This models a D20 roll used in various D&D versions for rolling a d20
|
2
|
+
# +/-mod to equal or exceed a DC value.
|
3
|
+
#
|
4
|
+
# This is a very simple version, but could easily be expanded on.
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# die = D20.new
|
9
|
+
#
|
10
|
+
# die.roll 5, 15 => [:success, 17]
|
11
|
+
# die.roll -3, 12 => [:fail, 9]
|
12
|
+
class D20
|
13
|
+
attr_reader :mod
|
14
|
+
attr_reader :dc
|
15
|
+
|
16
|
+
def self.roll(mod = 0, difficulty = 10)
|
17
|
+
new(mod, difficulty).roll
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(mod = 0, difficulty = 10)
|
21
|
+
@mod = mod.to_i
|
22
|
+
@dc = difficulty.to_i
|
23
|
+
@dstr = "1d20 #{stringify_mod}"
|
24
|
+
@roll = DiceBag::Roll.new @dstr
|
25
|
+
end
|
26
|
+
|
27
|
+
def roll
|
28
|
+
total = roll_for_result.total
|
29
|
+
sym = total >= dc ? :success : :failure
|
30
|
+
|
31
|
+
[sym, total]
|
32
|
+
end
|
33
|
+
|
34
|
+
def stringify_mod
|
35
|
+
return "+#{@mod}" if @mod.positive?
|
36
|
+
|
37
|
+
return @mod.to_s if @mod.negative?
|
38
|
+
|
39
|
+
''
|
40
|
+
end
|
41
|
+
|
42
|
+
def roll_for_result
|
43
|
+
@roll.roll
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Roll a d20 with Advantage
|
48
|
+
class D20Advantage < D20
|
49
|
+
def roll_for_result
|
50
|
+
r1 = @roll.roll
|
51
|
+
r2 = @roll.roll
|
52
|
+
|
53
|
+
r1 > r2 ? r1 : r2
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Roll a d20 with Disadvantage
|
58
|
+
class D20Disadvantage < D20
|
59
|
+
def roll_for_result
|
60
|
+
r1 = @roll.roll
|
61
|
+
r2 = @roll.roll
|
62
|
+
|
63
|
+
r1 < r2 ? r1 : r2
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'dicebag'
|
2
|
+
|
3
|
+
# This handles Fudge RPG type of rolls.
|
4
|
+
#
|
5
|
+
# This probably isn't (it *totally* isn't!) the most effecient way to do
|
6
|
+
# this, but shows how to examine DiceBag objects to get what you need
|
7
|
+
# for wonky dice systems.
|
8
|
+
module Fudge
|
9
|
+
# This models a standard Fudge RPG dice pool.
|
10
|
+
class Roll < DiceBag::Roll
|
11
|
+
def initialize(number = 4)
|
12
|
+
@number = number
|
13
|
+
@total = nil
|
14
|
+
@tally = nil
|
15
|
+
|
16
|
+
# This is a very silly way to do this, since there is no need to
|
17
|
+
# actually add the dice together here. But we need all of the d6's
|
18
|
+
# together in the same roll.
|
19
|
+
dstr = (['1d6'] * number).join(' + ')
|
20
|
+
|
21
|
+
super(dstr)
|
22
|
+
end
|
23
|
+
|
24
|
+
def roll
|
25
|
+
super
|
26
|
+
|
27
|
+
generate_tally
|
28
|
+
|
29
|
+
@total = @tally.count('+') - @tally.count('-')
|
30
|
+
|
31
|
+
[@total, tally_to_s]
|
32
|
+
end
|
33
|
+
|
34
|
+
def total
|
35
|
+
roll unless @total
|
36
|
+
|
37
|
+
@total
|
38
|
+
end
|
39
|
+
|
40
|
+
def tally
|
41
|
+
roll unless @tally
|
42
|
+
|
43
|
+
@tally
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
base = "#{@number}dF"
|
48
|
+
|
49
|
+
"#{base} #{tally_to_s} => #{@total}" if @total
|
50
|
+
|
51
|
+
base
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def generate_tally
|
57
|
+
@tally = @sections.map { |s| gen_symbol s.total }.sort.reverse
|
58
|
+
end
|
59
|
+
|
60
|
+
def gen_symbol(total)
|
61
|
+
case total
|
62
|
+
when 1, 2 then '-'
|
63
|
+
when 3, 4 then ' '
|
64
|
+
when 5, 6 then '+'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def tally_to_s
|
69
|
+
return '[]' unless @tally
|
70
|
+
|
71
|
+
"[#{@tally.join('][')}]"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
DF = Roll.new
|
76
|
+
|
77
|
+
def self.roll(num = 4)
|
78
|
+
Roll.new(num).roll
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'dicebag'
|
2
|
+
|
3
|
+
# This models the standard GURPS 3d6 dice pool used for attribute/skill
|
4
|
+
# tests.
|
5
|
+
#
|
6
|
+
# This will return an array of [status, result] where:
|
7
|
+
# - status is one of:
|
8
|
+
# :success, :failure, :critical_success, or :critical_failure
|
9
|
+
# - result is the actual dice roll total.
|
10
|
+
class GURPS < DiceBag::Roll
|
11
|
+
def self.roll(target, mod = 0)
|
12
|
+
new.roll(target, mod)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super('3d6')
|
17
|
+
end
|
18
|
+
|
19
|
+
def roll(target, mod = 0)
|
20
|
+
mod = 0 unless mod.is_a?(Integer)
|
21
|
+
|
22
|
+
@total_target = target + mod
|
23
|
+
@total = super().total
|
24
|
+
|
25
|
+
figure_success
|
26
|
+
figure_failure
|
27
|
+
|
28
|
+
[figure_result, @total]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def figure_success
|
34
|
+
@crit_success = [3, 4]
|
35
|
+
|
36
|
+
@crit_success.push(5) if @total_target >= 15
|
37
|
+
@crit_success.push(6) if @total_target >= 16
|
38
|
+
end
|
39
|
+
|
40
|
+
def figure_failure
|
41
|
+
@crit_failure = [18]
|
42
|
+
|
43
|
+
@crit_failure.push(17) if @total_target <= 15
|
44
|
+
end
|
45
|
+
|
46
|
+
def figure_result
|
47
|
+
return :critical_success if @crit_success.include?(@total)
|
48
|
+
return :critical_failure if @crit_failure.include?(@total)
|
49
|
+
|
50
|
+
@total <= @total_target ? :success : :failure
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'dicebag'
|
2
|
+
|
3
|
+
module SavageWorlds
|
4
|
+
# Models a single Savage World die, with attributes for max and half
|
5
|
+
# values.
|
6
|
+
#
|
7
|
+
# Since all Savage Worlds Trait (Attributes + Skills) dice can
|
8
|
+
# explode, that option is included automatically.
|
9
|
+
class SWDie < DiceBag::Roll
|
10
|
+
attr_reader :maximum
|
11
|
+
attr_reader :half
|
12
|
+
|
13
|
+
def initialize(sides, mod = 0)
|
14
|
+
@maximum = sides
|
15
|
+
@half = (sides / 2) + (mod / 2)
|
16
|
+
|
17
|
+
dstr = mod.zero? ? "1d#{sides}e" : "1d#{sides}e #{mod}"
|
18
|
+
|
19
|
+
super(dstr)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
D4 = SWDie.new(4)
|
24
|
+
D6 = SWDie.new(6)
|
25
|
+
D8 = SWDie.new(8)
|
26
|
+
D10 = SWDie.new(10)
|
27
|
+
D12 = SWDie.new(12)
|
28
|
+
WildDie = SWDie.new(6)
|
29
|
+
NoTrait = SWDie.new(4, -2)
|
30
|
+
NoTraitWildDie = SWDie.new(6, -2)
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'dicebag'
|
2
|
+
|
3
|
+
# Module for standard polyhedral dice types.
|
4
|
+
module Standard
|
5
|
+
# Models a single, simple die.
|
6
|
+
class Die < DiceBag::Roll
|
7
|
+
def initialize(sides)
|
8
|
+
super("1d#{sides}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
D4 = Die.new(4)
|
13
|
+
D6 = Die.new(6)
|
14
|
+
D8 = Die.new(8)
|
15
|
+
D10 = Die.new(10)
|
16
|
+
D12 = Die.new(12)
|
17
|
+
D20 = Die.new(20)
|
18
|
+
D100 = Die.new(100)
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'dicebag'
|
2
|
+
|
3
|
+
# This is actually modeling the "Storytelling" system dice, not the
|
4
|
+
# older "Storyteller" system dice, but I personally find "Storytelling"
|
5
|
+
# kind of a silly name, so I prefer the older name. :D
|
6
|
+
module Storyteller
|
7
|
+
# This is a models a pool of Storyteller dice.
|
8
|
+
class Pool < DiceBag::Roll
|
9
|
+
def initialize(number = 1, success = 8)
|
10
|
+
@number = number
|
11
|
+
@success = success
|
12
|
+
@result = nil
|
13
|
+
|
14
|
+
super("#{number}d10e t#{success}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def roll
|
18
|
+
@result = super
|
19
|
+
end
|
20
|
+
|
21
|
+
def successes
|
22
|
+
roll unless @result
|
23
|
+
|
24
|
+
@result.total
|
25
|
+
end
|
26
|
+
|
27
|
+
def tally
|
28
|
+
roll unless @result
|
29
|
+
|
30
|
+
@result.sections[0].tally
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"#{@number}d10/#{@success}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.roll(number = 1, success = 8)
|
39
|
+
Pool.new(number, success).roll
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.chance
|
43
|
+
Pool.new(1, 10).roll
|
44
|
+
end
|
45
|
+
end
|