dicebag 3.2.2 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +65 -40
- 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 +43 -20
- data/lib/dicebag.rb +15 -171
- metadata +17 -8
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
|