tabletop 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/README.markdown +59 -37
- data/Rakefile +6 -2
- data/lib/fixnum.rb +4 -8
- data/lib/tabletop/condition.rb +20 -0
- data/lib/tabletop/pool.rb +61 -30
- data/lib/tabletop/randomizers.rb +37 -22
- data/lib/tabletop/roll.rb +37 -29
- data/lib/tabletop/token.rb +57 -19
- data/lib/tabletop/version.rb +1 -1
- data/lib/tabletop.rb +1 -0
- data/spec/condition_spec.rb +15 -0
- data/spec/fixnum_spec.rb +4 -0
- data/spec/pool_spec.rb +141 -117
- data/spec/randomizers_spec.rb +79 -59
- data/spec/roll_spec.rb +19 -18
- data/spec/spec_helper.rb +1 -5
- data/spec/token_spec.rb +72 -13
- data/tabletop.gemspec +1 -1
- metadata +7 -4
data/.gitignore
CHANGED
data/README.markdown
CHANGED
@@ -2,17 +2,19 @@
|
|
2
2
|
|
3
3
|
Tabletop aims to provide a simple way of describing, automating and tracking the tools and tasks involved in "analog" games, determining results from the motions and properties of various dice and chips.
|
4
4
|
|
5
|
-
Currently, you can create pools of dice, and rolls that compare them to different possible results.
|
6
|
-
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
gem install tabletop
|
10
8
|
|
11
9
|
require 'tabletop'
|
12
10
|
|
13
|
-
##
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
For detailed information, I recommend the [full documentation](http://rubydoc.info/gems/tabletop/), but I want this file to give you a good primer, so let's get started.
|
14
14
|
|
15
|
-
Dice
|
15
|
+
### Dice
|
16
|
+
|
17
|
+
Dice are pretty straightforward. They've got a number of sides, set on instantiation (defaulting to 6), and a current value between that number and 1 inclusive, which can be set on instantiation, or set directly. Finally, they can be rolled, which gives them a new random value.
|
16
18
|
|
17
19
|
d6 = Die.new
|
18
20
|
d6.sides #=> 6
|
@@ -22,7 +24,7 @@ Dice are pretty straightforward. They've got a number of sides, set on instanti
|
|
22
24
|
|
23
25
|
d8 = Die.new(8, 4)
|
24
26
|
d8.sides #=> 8
|
25
|
-
d8.to_s #=> "[
|
27
|
+
d8.to_s #=> "[4]/d8"
|
26
28
|
|
27
29
|
One fun special kind of die is a "Fudge Die". They are a special kind of three-sided die that have three possible values: -1, 0, or 1. Those are usually expressed as '-', ' ' and '+', though.
|
28
30
|
|
@@ -30,19 +32,27 @@ One fun special kind of die is a "Fudge Die". They are a special kind of three-
|
|
30
32
|
f.sides #=> 3
|
31
33
|
f.value #=> 0
|
32
34
|
f.to_s #=> "[ ]"
|
35
|
+
|
36
|
+
You may not believe this, but coins are also a special case of die. Coins have two sides, and have a value of either 1 or 0, aka heads or tails, aka "+" or " ". They can be rolled if you really want, but you'd normally call that "flipping," right?
|
37
|
+
|
38
|
+
c = Coin.new
|
39
|
+
c.sides #=> 2
|
40
|
+
c.value #=> 1
|
41
|
+
c.to_s #=> "(+)"
|
42
|
+
c.flip.to_s #=> "( )"
|
33
43
|
|
34
44
|
### Pools
|
35
45
|
|
36
|
-
Pools are
|
46
|
+
Pools are arrays of Dice objects with some extra helpful methods.
|
37
47
|
|
38
48
|
The _best_ way to create one is exactly the way you'd expect: the same old d-notation we've been using for decades. You can even combine them with `+`.
|
39
49
|
|
40
50
|
3.d6 #=> [[3]/d6, [3]/d6, [4]/d6]
|
41
51
|
2.d10 + 1.d8 #=> [[2]/d10, [6]/d10, [8]/d8]
|
42
|
-
|
52
|
+
|
43
53
|
You can also create them by passing Pool.new a literal array of dice, or (slightly more interesting) a string in die notation.
|
44
54
|
|
45
|
-
|
55
|
+
Pool's instance methods are common operations you might do on a pool of dice: summing, counting sets, dropping the lowest or highest valued dice, dropping all _but_ the lowest or highest valued dice, even dropping any dice a specified list of values.
|
46
56
|
|
47
57
|
d&d = 3.d6.sum #=> 13
|
48
58
|
ore = 10.d10.sets #=> ["3x2", "2x8", "1x7", "1x6", "1x4", "1x3", "1x1"]
|
@@ -53,33 +63,33 @@ You can also #roll an entire pool, or you can interact with individual dice in t
|
|
53
63
|
|
54
64
|
When pools are compared to each other or to numbers with <=>, it's assumed you're actually interested in their sum. The same thing happens if you try to add a number to them.
|
55
65
|
|
56
|
-
|
57
|
-
1.d20 > 2.d10
|
66
|
+
4.d4 + 4 #=> 17
|
67
|
+
1.d20 > 2.d10 #=> false
|
58
68
|
|
59
69
|
### Rolls
|
60
70
|
|
61
|
-
Rolls are very much under construction, but they allow you to automate randomly determining results in a variety of ways.
|
71
|
+
Rolls are very much under construction and their API is in flux, but they allow you to automate randomly determining results in a variety of ways.
|
62
72
|
|
63
|
-
Rolls have a lot of options.
|
73
|
+
Rolls have a lot of options, described in detail in the documentation. But let's take a simple example from one of my favorite games, Apocalypse World.
|
64
74
|
|
65
75
|
>When you open your brain to the world’s psychic maelstrom, roll+weird. On a hit, the MC will tell you something new and interesting about the current situation, and might ask you a question or two; answer them. On a 10+, the MC will give you good detail. On a 7–9, the MC will give you an impression.
|
66
76
|
|
67
|
-
(In the parlance of the game, a "hit" is getting a 7 or higher
|
77
|
+
(In the parlance of the game, a "hit" is getting a 7 or higher. "Roll+weird" means to roll 2d6 and add the character's "weird" stat, which is an integer from -1 to 3.)
|
68
78
|
|
69
|
-
Here's how I'd write
|
79
|
+
Here's how I'd write that out in Tabletop:
|
70
80
|
|
71
|
-
|
81
|
+
weird = [-1, 0, 1, 2, 3].sample #=> get a random stat
|
72
82
|
|
73
83
|
open_brain = Roll.new(2.d6) {
|
74
|
-
|
75
|
-
at_least 7, "the MC will tell you something new and interesting about the current situation"
|
84
|
+
add weird
|
85
|
+
at_least 7, "the MC will tell you something new and interesting about the current situation..."
|
76
86
|
equals (7..9), "...but it's just an impression"
|
77
87
|
at_least 10, "...and it's a good detail"
|
78
88
|
}
|
79
89
|
|
80
|
-
Simple, right? `add` sets a value to be permanently added for the purposes of determining results. `at_least` and `equals` take an integer (or a range, in the case of `equals`) as their first parameter, and then one or more
|
90
|
+
Simple, right? `add` sets a value to be permanently added for the purposes of determining results. `at_least` and `equals` take an integer (or a range, in the case of `equals`) as their first parameter, and then one or more values to return if the pool's result meets the stated condition.
|
81
91
|
|
82
|
-
Once they've been instantiated, Rolls have
|
92
|
+
Once they've been instantiated, Rolls have three important methods.
|
83
93
|
|
84
94
|
#### #roll
|
85
95
|
|
@@ -88,25 +98,38 @@ This method which re-rolls all the dice in the pool, and returns the Roll object
|
|
88
98
|
bad_luck = -1
|
89
99
|
open_brain.roll(:modifier => bad_luck)
|
90
100
|
|
91
|
-
#### #
|
101
|
+
#### #result
|
92
102
|
|
93
|
-
This method returns
|
103
|
+
This method, by default, returns the sum of the values of the Roll's dice, plus any static modifiers from `add` or per-roll modifiers from `roll(:modifier)`.
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
105
|
+
But! When instantiating a roll, you can call `set_result` with an appropriate symbol to make `result` mean something else.
|
106
|
+
|
107
|
+
For example, in Exalted, you principally care about how many dice in your pool came up 7 or higher, counting 10s twice:
|
108
|
+
|
109
|
+
exalted = Roll.new(8.d10) do
|
110
|
+
set_result :count, :at_least=>7, :doubles=>10
|
111
|
+
end
|
112
|
+
|
113
|
+
exalted.result #=> 2
|
114
|
+
exalted.sum #=> 30
|
115
|
+
|
116
|
+
#### #effects
|
117
|
+
|
118
|
+
This method returns either an array containing the results passed to any `at_least` and `equals` calls whose conditions were met, or nil if no such conditions were met.
|
98
119
|
|
99
120
|
So, possible results for our cool AW roll:
|
100
121
|
|
101
|
-
open_brain.roll.effects #=>
|
102
|
-
open_brain.roll.effects #=> [
|
103
|
-
|
104
|
-
puts
|
105
|
-
puts effects
|
122
|
+
open_brain.roll.effects #=> nil
|
123
|
+
open_brain.roll.effects #=> ["the MC will tell you something new and interesting about the current situation", "...but it's just an impression"]
|
124
|
+
open_brain.roll
|
125
|
+
puts open_brain.result #=> 10
|
126
|
+
puts open_brain.effects #=> ["the MC will tell you something new and interesting about the current situation", "...and it's a good detail"]
|
106
127
|
|
107
|
-
|
128
|
+
### Coming Soon
|
129
|
+
|
130
|
+
That's already enough functionality to do many different kinds of rolls, but there's a lot more in store.
|
108
131
|
|
109
|
-
One
|
132
|
+
One bonus thing I'll briefly note is that Rolls can be nested, and `effects` returns them as such.
|
110
133
|
|
111
134
|
rps = Roll.new(1.d3) {
|
112
135
|
equals 1, "rock"
|
@@ -122,21 +145,20 @@ One last thing I'll briefly note is that Rolls can be nested.
|
|
122
145
|
equals 1, "Rock Paper Scissors", rps
|
123
146
|
equals 2, "JanKenPon", jkp
|
124
147
|
}
|
125
|
-
fist_game.roll.effects #=> [
|
148
|
+
fist_game.roll.effects #=> ["JanKenPon", ["guu"]]
|
126
149
|
|
127
150
|
This can lead to some surprisingly sophisticated constructions. Remember that "tables" are really just a special case of a roll!
|
128
151
|
|
129
152
|
## How to contribute
|
130
153
|
|
131
|
-
*First and most importantly*, any complaints or suggestions,
|
154
|
+
*First and most importantly*, if you're reading this and you make games, please tell me about them! Second, any complaints or suggestions are always welcome, _regardless of coding knowledge_. Feel free to communicate your opinions by [creating an issue on github](https://github.com/njay/tabletop/issues), or just dropping me a line at <nick.novitski@gmail.com>.
|
132
155
|
|
133
|
-
|
156
|
+
If you have clear ideas about what more the project should do, and you think you can do something about it, then make it so! Don't even bother asking me about it, you know the drill:
|
134
157
|
|
135
158
|
* Fork the project.
|
136
|
-
* Create a topic branch
|
159
|
+
* Create a topic branch.
|
137
160
|
* Make tests that describe your feature addition or bug fix.
|
138
|
-
* Write code that passes those tests
|
139
|
-
* Commit, without altering the version.
|
161
|
+
* Write code that passes those tests.
|
140
162
|
* Send me a pull request.
|
141
163
|
|
142
164
|
## Copyright
|
data/Rakefile
CHANGED
@@ -55,8 +55,12 @@ task :clean do
|
|
55
55
|
FileUtils.rm_rf "pkg"
|
56
56
|
end
|
57
57
|
|
58
|
+
desc "Update master and develop branches on github"
|
59
|
+
task :github do
|
60
|
+
system "git push origin : --tags"
|
61
|
+
end
|
62
|
+
|
58
63
|
desc "Push changes to github and rubygems"
|
59
|
-
task :publish do
|
60
|
-
system "git push origin master --tags"
|
64
|
+
task :publish => :github do
|
61
65
|
system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
62
66
|
end
|
data/lib/fixnum.rb
CHANGED
@@ -4,21 +4,17 @@ class Fixnum
|
|
4
4
|
|
5
5
|
# Returns a pool of dice of the given sides and size self
|
6
6
|
def dX(sides)
|
7
|
-
|
8
|
-
times { dice << Tabletop::Die.new(sides) }
|
9
|
-
Tabletop::Pool.new(dice)
|
7
|
+
Tabletop::Pool.new("#{self}d#{sides}")
|
10
8
|
end
|
11
9
|
|
12
10
|
# Returns a pool of fudge dice of size self
|
13
11
|
def dF
|
14
|
-
|
15
|
-
times {dice << Tabletop::FudgeDie.new}
|
16
|
-
Tabletop::Pool.new(dice)
|
12
|
+
Tabletop::Pool.new("#{self}dF")
|
17
13
|
end
|
18
14
|
|
19
|
-
# Matches any methods of the form
|
15
|
+
# Matches any methods of the form dN, where N > 0, and calls #dX(N)
|
20
16
|
def method_missing(symbol, *args, &block)
|
21
|
-
if symbol =~ /^d(.*)$/
|
17
|
+
if symbol =~ /^d(.*)$/ and $1.to_i > 0
|
22
18
|
dX($1.to_i)
|
23
19
|
else
|
24
20
|
super
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Tabletop
|
2
|
+
|
3
|
+
# Stores a block to evaluate against a Pool
|
4
|
+
class Condition
|
5
|
+
def initialize(&block)
|
6
|
+
@test = block
|
7
|
+
end
|
8
|
+
|
9
|
+
# returns false if the stored block evaluates to false or nil,
|
10
|
+
# otherwise returns true
|
11
|
+
def met_by?(pool)
|
12
|
+
if @test.call(pool)
|
13
|
+
true
|
14
|
+
else
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/tabletop/pool.rb
CHANGED
@@ -1,42 +1,59 @@
|
|
1
1
|
require_relative 'randomizers'
|
2
|
-
require 'delegate'
|
3
2
|
|
4
3
|
module Tabletop
|
5
|
-
class Pool <
|
4
|
+
class Pool < Array
|
6
5
|
include Comparable
|
7
6
|
|
8
|
-
#
|
7
|
+
# Requires one parameter, which can be either of
|
9
8
|
# - an array of Die objects
|
10
|
-
# - a string of
|
9
|
+
# - a string of elements separated by spaces which can be in two different formats:
|
10
|
+
# + d-notation (ie, "d20", "3dF", etc) denoting dice that will be given random values
|
11
|
+
# + a value, a slash, then a number of sides (ie, "2/6", "47/100", etc)
|
11
12
|
def initialize(init_dice)
|
12
|
-
return super(init_dice) if init_dice.
|
13
|
+
return super(init_dice) if init_dice.kind_of?(Array)
|
13
14
|
d_groups = init_dice.split
|
14
15
|
dice = []
|
15
|
-
d_groups.each do |
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
d_groups.each do |d|
|
17
|
+
if d =~ /d/ # d_notation
|
18
|
+
number, sides = d.split('d')
|
19
|
+
number = number.to_i
|
20
|
+
number += 1 if number == 0
|
21
|
+
if sides.to_i > 0
|
22
|
+
number.times { dice << Die.new(sides: sides.to_i)}
|
23
|
+
elsif sides == "F"
|
24
|
+
number.times {dice << FudgeDie.new}
|
25
|
+
end
|
26
|
+
else
|
27
|
+
dice << Die.new_from_string(d)
|
23
28
|
end
|
24
29
|
end
|
25
30
|
super(dice)
|
26
31
|
end
|
27
32
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
33
|
+
# If adding a pool or array of dice objects, returns the the union of these pools.
|
34
|
+
#
|
35
|
+
# If adding a number, returns the sum of that number and all die values in the pool.
|
36
|
+
#
|
37
|
+
# Otherwise, raises an ArgumentError.
|
32
38
|
def +(operand)
|
33
|
-
# if the
|
34
|
-
if operand.
|
39
|
+
# if the parameter seems to be an array of dice (this includes pools)
|
40
|
+
if operand.respond_to?(:all?) and operand.all?{|obj| obj.respond_to?(:roll)}
|
35
41
|
new_union(operand)
|
36
|
-
|
42
|
+
# if the parameter seems to be a randomizer
|
43
|
+
elsif operand.respond_to?(:sides)
|
44
|
+
new_union([operand])
|
45
|
+
elsif operand.respond_to?(:to_int)
|
37
46
|
sum + operand
|
38
47
|
else
|
39
|
-
|
48
|
+
raise ArgumentError, "Only numbers and other pools can be added to pools"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def -(operand)
|
53
|
+
if operand.respond_to?(:to_a)
|
54
|
+
super
|
55
|
+
else
|
56
|
+
sum - operand
|
40
57
|
end
|
41
58
|
end
|
42
59
|
|
@@ -89,6 +106,12 @@ module Tabletop
|
|
89
106
|
end
|
90
107
|
self
|
91
108
|
end
|
109
|
+
|
110
|
+
def roll_if(&block)
|
111
|
+
each do |die|
|
112
|
+
die.roll if block.call(die)
|
113
|
+
end
|
114
|
+
end
|
92
115
|
|
93
116
|
# Returns the sum of all values of dice in the pool
|
94
117
|
def sum
|
@@ -109,12 +132,24 @@ module Tabletop
|
|
109
132
|
|
110
133
|
# Returns a Pool containing copies of the n highest dice
|
111
134
|
def highest(n=1)
|
112
|
-
|
135
|
+
if n < length
|
136
|
+
drop_lowest(length-n)
|
137
|
+
else
|
138
|
+
self
|
139
|
+
end
|
113
140
|
end
|
114
141
|
|
115
142
|
# Returns a Pool containing copies of the n lowest dice
|
116
143
|
def lowest(n=1)
|
117
|
-
|
144
|
+
sorted = sort.first(n)
|
145
|
+
in_order = []
|
146
|
+
each do |d|
|
147
|
+
if sorted.include?(d)
|
148
|
+
in_order << d
|
149
|
+
sorted -= [d]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
Pool.new(in_order)
|
118
153
|
end
|
119
154
|
|
120
155
|
# Returns a copy of the Pool, minus the n highest-value dice
|
@@ -133,19 +168,15 @@ module Tabletop
|
|
133
168
|
def drop(to_drop)
|
134
169
|
to_drop = [to_drop].flatten #turn it into an array if it isn't one.
|
135
170
|
kept = reject{|die| to_drop.any?{|drop_value| die.value == drop_value }}
|
136
|
-
|
171
|
+
Pool.new(kept)
|
137
172
|
end
|
138
173
|
|
139
174
|
private
|
140
175
|
def new_union(array)
|
141
176
|
union = [self, array].flatten
|
142
177
|
new_pool =[]
|
143
|
-
union.each do |die|
|
144
|
-
|
145
|
-
new_pool << FudgeDie.new(die.value)
|
146
|
-
else
|
147
|
-
new_pool << Die.new(die.sides, die.value)
|
148
|
-
end
|
178
|
+
union.each do |die|
|
179
|
+
new_pool << die.class.new(sides:die.sides, value:die.value)
|
149
180
|
end
|
150
181
|
Pool.new(new_pool)
|
151
182
|
end
|
data/lib/tabletop/randomizers.rb
CHANGED
@@ -3,23 +3,29 @@ module Tabletop
|
|
3
3
|
include Comparable
|
4
4
|
|
5
5
|
attr_reader :sides, :value
|
6
|
-
|
7
|
-
#
|
8
|
-
# If
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
|
7
|
+
# :sides must be greater then or equal to 1. By default it is 6.
|
8
|
+
# If :value is nil, then #roll is called.
|
9
|
+
def initialize(params={})
|
10
|
+
|
11
|
+
if params[:sides].nil?
|
12
|
+
@sides = 6
|
13
|
+
else
|
14
|
+
@sides = Integer(params[:sides])
|
15
|
+
raise ArgumentError if @sides < 2
|
15
16
|
end
|
16
|
-
|
17
|
-
if
|
18
|
-
|
17
|
+
|
18
|
+
if params[:value].nil?
|
19
|
+
roll
|
19
20
|
else
|
20
|
-
|
21
|
+
self.value = params[:value]
|
21
22
|
end
|
22
|
-
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.new_from_string(string)
|
26
|
+
raise ArgumentError unless string.respond_to?(:split)
|
27
|
+
v, s = string.split('/')
|
28
|
+
Die.new(sides: s.to_i, value: v.to_i)
|
23
29
|
end
|
24
30
|
|
25
31
|
# Sets @value to a random number n, where 1 <= n <= @sides
|
@@ -34,8 +40,9 @@ module Tabletop
|
|
34
40
|
|
35
41
|
# Raises ArgumentError if new_value isn't between 1 and @sides inclusive
|
36
42
|
def value=(new_value)
|
37
|
-
|
38
|
-
|
43
|
+
integer_value = Integer(new_value)
|
44
|
+
raise ArgumentError unless valid_value?(integer_value)
|
45
|
+
@value = integer_value
|
39
46
|
end
|
40
47
|
|
41
48
|
# Compares based on value of the die
|
@@ -50,7 +57,7 @@ module Tabletop
|
|
50
57
|
|
51
58
|
protected
|
52
59
|
def valid_value?(val)
|
53
|
-
|
60
|
+
0 < val and @sides >= val
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
@@ -58,8 +65,8 @@ module Tabletop
|
|
58
65
|
# A FudgeDie is a kind of three-sided Die that has a value
|
59
66
|
# of either 0, 1, or -1.
|
60
67
|
class FudgeDie < Die
|
61
|
-
def initialize(
|
62
|
-
super(3,
|
68
|
+
def initialize(params = {})
|
69
|
+
super(sides: 3, value: params[:value])
|
63
70
|
end
|
64
71
|
def roll
|
65
72
|
@value = rand(sides)-1
|
@@ -80,8 +87,8 @@ module Tabletop
|
|
80
87
|
# A coin is a kind of two-sided Die that has a value of
|
81
88
|
# either 0 or 1
|
82
89
|
class Coin < Die
|
83
|
-
def initialize(
|
84
|
-
super(2, value)
|
90
|
+
def initialize(params={})
|
91
|
+
super(sides: 2, value: params[:value])
|
85
92
|
end
|
86
93
|
|
87
94
|
def roll #:nodoc:
|
@@ -93,7 +100,15 @@ module Tabletop
|
|
93
100
|
roll
|
94
101
|
self
|
95
102
|
end
|
96
|
-
|
103
|
+
|
104
|
+
def heads?
|
105
|
+
@value == 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def tails?
|
109
|
+
@value == 0
|
110
|
+
end
|
111
|
+
|
97
112
|
# Returns either "( )" or "(+)" depending on @value
|
98
113
|
def to_s
|
99
114
|
"(#{[' ', '+'][value]})"
|
data/lib/tabletop/roll.rb
CHANGED
@@ -10,6 +10,7 @@ module Tabletop
|
|
10
10
|
def initialize(outcomes, conditions)
|
11
11
|
@outcomes, @conditions = outcomes, conditions
|
12
12
|
end
|
13
|
+
|
13
14
|
end
|
14
15
|
|
15
16
|
class Roll
|
@@ -38,11 +39,10 @@ module Tabletop
|
|
38
39
|
end
|
39
40
|
|
40
41
|
|
41
|
-
# Returns an array.
|
42
|
-
# +
|
43
|
-
# + If
|
44
|
-
# +
|
45
|
-
# + if none of the above conditions are met, the second and final element is nil.
|
42
|
+
# Returns either an array or nil.
|
43
|
+
# + If a "difficulty" was set in the most recent call of #roll, and #result meets or exceeds it, then the first element will be "Success".
|
44
|
+
# + If the conditions of any of the roll's @possibilities are met (see #meets?), then their outcomes will be all following elements.
|
45
|
+
# + If none of these conditions are met, returns nil
|
46
46
|
def effects
|
47
47
|
results = []
|
48
48
|
|
@@ -51,22 +51,15 @@ module Tabletop
|
|
51
51
|
end
|
52
52
|
|
53
53
|
@possibilities.each do |poss|
|
54
|
-
|
55
|
-
poss.outcomes.each do |outcome|
|
56
|
-
if outcome.instance_of?(Roll)
|
57
|
-
results << outcome.roll.effects
|
58
|
-
else
|
59
|
-
results << outcome
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
54
|
+
results.concat(check(poss))
|
63
55
|
end
|
64
|
-
|
56
|
+
|
57
|
+
results.compact!
|
58
|
+
|
65
59
|
if results.empty?
|
66
|
-
results
|
60
|
+
results = nil
|
67
61
|
end
|
68
|
-
|
69
|
-
results.unshift(result)
|
62
|
+
results
|
70
63
|
end
|
71
64
|
|
72
65
|
# Without any options passed, calls Pool#roll on the roll's pool. Returns the Roll.
|
@@ -94,21 +87,36 @@ module Tabletop
|
|
94
87
|
self
|
95
88
|
end
|
96
89
|
|
97
|
-
# Takes
|
98
|
-
#
|
90
|
+
# Takes a Possibility, returns an Array containing nil if any of it's conditions
|
91
|
+
# aren't met. Otherwise, returns an Array containing all the Possibility's
|
92
|
+
# outcomes. If any of those outcomes are Roll objects, they are rolled and their
|
93
|
+
# #effects are returned as an outcome.
|
99
94
|
#--
|
100
95
|
# TODO: checks #result, not #sum
|
101
|
-
def
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
96
|
+
def check(p) #:nodoc:
|
97
|
+
conditions_met = true
|
98
|
+
|
99
|
+
if p.conditions[:>=] and sum < p.conditions[:>=]
|
100
|
+
conditions_met = false
|
101
|
+
end
|
102
|
+
|
103
|
+
if p.conditions[:==] and p.conditions[:==] != sum
|
104
|
+
conditions_met = false
|
107
105
|
end
|
108
|
-
|
109
|
-
|
106
|
+
|
107
|
+
if conditions_met
|
108
|
+
results = []
|
109
|
+
p.outcomes.each do |outcome|
|
110
|
+
if outcome.instance_of?(Roll)
|
111
|
+
results << outcome.roll.effects
|
112
|
+
else
|
113
|
+
results << outcome
|
114
|
+
end
|
115
|
+
end
|
116
|
+
results
|
117
|
+
else
|
118
|
+
[nil]
|
110
119
|
end
|
111
|
-
answer
|
112
120
|
end
|
113
121
|
|
114
122
|
# The sum of the values of dice in the pool, and any modifier set in
|