nerd_dice 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/main.yml +67 -0
- data/.rubocop.yml +114 -0
- data/CHANGELOG.md +76 -2
- data/Gemfile +2 -2
- data/Gemfile.lock +54 -32
- data/README.md +372 -5
- data/bin/generate_checksums +13 -0
- data/bin/nerd_dice_benchmark +322 -0
- data/certs/msducheminjr.pem +26 -0
- data/checksum/nerd_dice-0.1.0.gem.sha256 +1 -0
- data/checksum/nerd_dice-0.1.0.gem.sha512 +1 -0
- data/checksum/nerd_dice-0.1.1.gem.sha256 +1 -0
- data/checksum/nerd_dice-0.1.1.gem.sha512 +1 -0
- data/checksum/nerd_dice-0.2.0.gem.sha256 +1 -0
- data/checksum/nerd_dice-0.2.0.gem.sha512 +1 -0
- data/checksum/nerd_dice-0.3.0.gem.sha256 +1 -0
- data/checksum/nerd_dice-0.3.0.gem.sha512 +1 -0
- data/lib/nerd_dice/class_methods/configure.rb +50 -0
- data/lib/nerd_dice/class_methods/execute_die_roll.rb +47 -0
- data/lib/nerd_dice/class_methods/harvest_totals.rb +40 -0
- data/lib/nerd_dice/class_methods/refresh_seed.rb +83 -0
- data/lib/nerd_dice/class_methods/roll_ability_scores.rb +73 -0
- data/lib/nerd_dice/class_methods/roll_dice.rb +45 -0
- data/lib/nerd_dice/class_methods/total_ability_scores.rb +52 -0
- data/lib/nerd_dice/class_methods/total_dice.rb +44 -0
- data/lib/nerd_dice/class_methods.rb +30 -0
- data/lib/nerd_dice/configuration.rb +91 -0
- data/lib/nerd_dice/convenience_methods.rb +279 -0
- data/lib/nerd_dice/dice_set.rb +166 -0
- data/lib/nerd_dice/die.rb +51 -0
- data/lib/nerd_dice/sets_randomization_technique.rb +19 -0
- data/lib/nerd_dice/version.rb +1 -1
- data/lib/nerd_dice.rb +15 -33
- data/nerd_dice.gemspec +12 -7
- data.tar.gz.sig +0 -0
- metadata +97 -21
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -6
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
[![Coverage Status](https://coveralls.io/repos/github/statelesscode/nerd_dice/badge.svg?branch=master)](https://coveralls.io/github/statelesscode/nerd_dice?branch=master)
|
2
|
+
![Build](https://github.com/statelesscode/nerd_dice/actions/workflows/main.yml/badge.svg)
|
3
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/721f587b792d583065be/maintainability)](https://codeclimate.com/github/statelesscode/nerd_dice/maintainability)
|
1
4
|
# NerdDice
|
2
5
|
Nerd dice allows you to roll polyhedral dice and add bonuses as you would in a tabletop roleplaying game. You can choose to roll multiple dice and keep a specified number of dice such as rolling 4d6 and dropping the lowest for ability scores or rolling with advantage and disadvantage if those mechanics exist in your game.
|
3
6
|
|
7
|
+
## Educational Videos By Stateless Code
|
8
|
+
The end-to-end process of developing this gem has been captured as [instructional videos](https://www.youtube.com/playlist?list=PL9kkbu1kLUeOnUtMpAnJOCtHdThx1Efkt). The videos are in a one-take style so that the mistakes along the way have troubleshooting and the concepts used to develop the gem are explained as they are covered.
|
9
|
+
|
4
10
|
## Installation
|
5
11
|
|
6
12
|
Add this line to your application's Gemfile:
|
@@ -18,18 +24,379 @@ Or install it yourself as:
|
|
18
24
|
$ gem install nerd_dice
|
19
25
|
|
20
26
|
## Usage
|
27
|
+
After the gem is installed, you can require it as you would any other gem.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'nerd_dice'
|
31
|
+
```
|
32
|
+
|
33
|
+
### Module methods or a dynamic method_missing DSL
|
34
|
+
There are two main patterns for using NerdDice in your project. You can invoke the module-level methods like `NerdDice.total_dice` or you can include the `NerdDice::ConvenienceMethods` module to your class \(or IRB \). Once mixed in, you can dynamically invoke methods like `roll_d20_with_advantage` or `total_3d8_plus5`. See the [Convenience Methods Mixin](#convenience-methods-mixin) section for usage details.
|
35
|
+
|
36
|
+
### Configuration
|
37
|
+
You can customize the behavior of NerdDice via a configuration block as below or by assigning an individual property via the ```NerdDice.configuration.property = value``` syntax \(where ```property``` is the config property and ```value``` is the value you want to assign\)\. The available configuration options as well as their defaults, if applicable, are listed in the example configuration block below:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
NerdDice.configure do | config|
|
41
|
+
|
42
|
+
# number of ability scores to place in an ability score array
|
43
|
+
config.ability_score_array_size = 6 # must duck-type to positive Integer
|
44
|
+
|
45
|
+
# number of sides for each ability score Die
|
46
|
+
config.ability_score_number_of_sides = 6 # must duck-type to positive Integer
|
47
|
+
|
48
|
+
# total number of dice rolled for each ability score
|
49
|
+
config.ability_score_dice_rolled = 4 # must duck-type to positive Integer
|
50
|
+
|
51
|
+
# highest(n) dice from the total number of dice rolled
|
52
|
+
# that are included in the ability scoretotal
|
53
|
+
#
|
54
|
+
# CANNOT EXCEED ability_score_dice_rolled see Note below
|
55
|
+
config.ability_score_dice_kept = 3 # must duck-type to positive Integer
|
56
|
+
|
57
|
+
# randomization technique options are:
|
58
|
+
# :securerandom => Uses SecureRandom.rand(). Good entropy, medium speed.
|
59
|
+
# :random_rand => Uses Random.rand(). Class method. Poor entropy, fastest speed.
|
60
|
+
# (Seed is shared with other processes. Too predictable)
|
61
|
+
# :random_object => Uses Random.new() and calls rand()
|
62
|
+
# Medium entropy, fastest speed. (Performs the best under speed benchmark)
|
63
|
+
# :randomized =>
|
64
|
+
# Uses a random choice of the :securerandom, :rand, and :random_new_interval options above
|
65
|
+
config.randomization_technique = :random_object # fast with independent seed
|
66
|
+
|
67
|
+
# Number of iterations to use on a generator before refreshing the seed
|
68
|
+
# 1 very slow and heavy pressure on processor and memory but very high entropy
|
69
|
+
# 1000 would refresh the object every 1000 times you call rand()
|
70
|
+
config.refresh_seed_interval = nil # don't refresh the seed
|
71
|
+
# Background and foreground die colors are string values.
|
72
|
+
# By default these correspond to the constants in the class
|
73
|
+
# Defaults: DEFAULT_BACKGROUND_COLOR = "#0000DD" DEFAULT_FOREGROUND_COLOR = "#DDDDDD"
|
74
|
+
# It is recommended but not enforced that these should be valid CSS color property attributes
|
75
|
+
config.die_background_color = "red"
|
76
|
+
config.die_foreground_color = "#000"
|
77
|
+
end
|
78
|
+
```
|
79
|
+
**Note:** You cannot set `ability_score_dice_kept` greater than `ability_score_dice_rolled`. If you try to set `ability_score_dice_kept` higher than `ability_score_dice_rolled`, an error will be raised. If you set `ability_score_dice_rolled` _lower_ than the existing value of `ability_score_dice_kept`, no error will be thrown, but `ability_score_dice_kept` will be _**modified**_ to match `ability_score_dice_rolled` and a warning will be printed.
|
80
|
+
|
21
81
|
### Rolling a number of dice and adding a bonus
|
82
|
+
You can use two different methods to roll dice. The `total_dice` method returns an `Integer` representing the total of the dice plus any applicable bonuses. The `total_dice` method does not support chaining additional methods like `highest`, `lowest`, `with_advantage`, `with_disadvantage`. The `roll_dice` method returns a `DiceSet` collection object, and allows for chaining the methods mentioned above and iterating over the individual `Die` objects. `NerdDice.roll_dice.total` and `NerdDice.total_dice` are roughly equivalent.
|
83
|
+
|
22
84
|
```ruby
|
23
85
|
# roll a single d4
|
24
|
-
NerdDice.total_dice(4) # =>
|
86
|
+
NerdDice.total_dice(4) # => Integer: between 1-4
|
87
|
+
NerdDice.roll_dice(4) # => DiceSet: with one 4-sided Die with a value between 1-4
|
88
|
+
NerdDice.roll_dice(4).total # => Integer: between 1-4
|
25
89
|
|
26
90
|
# roll 3d6
|
27
|
-
NerdDice.total_dice(6, 3) =>
|
91
|
+
NerdDice.total_dice(6, 3) # => Integer: total of three 6-sided dice
|
92
|
+
NerdDice.roll_dice(6, 3) # => DiceSet: three 6-sided Die objects, each with values between 1-6
|
93
|
+
NerdDice.roll_dice(6, 3).total # => Integer: total of three 6-sided dice
|
28
94
|
|
29
95
|
# roll a d20 and add 5 to the value
|
30
|
-
NerdDice.total_dice(20,
|
96
|
+
NerdDice.total_dice(20, bonus: 5) # => Integer: roll a d20 and add the bonus to the total
|
97
|
+
NerdDice.roll_dice(20, bonus: 5) # => DiceSet: one 20-sided Die and bonus of 5
|
98
|
+
NerdDice.roll_dice(20, bonus: 5).total # => Integer: roll a d20 and add the bonus to the total
|
99
|
+
|
100
|
+
# without changing the config at the module level
|
101
|
+
# roll a d20 and overide the configured randomization_technique one time
|
102
|
+
NerdDice.total_dice(20, randomization_technique: :randomized) # => Integer
|
103
|
+
# roll a d20 and overide the configured randomization_technique for the DiceSet
|
104
|
+
# object will persist on the DiceSet object for subsequent rerolls
|
105
|
+
NerdDice.roll_dice(20, randomization_technique: :randomized) # => DiceSet with :randomized
|
106
|
+
```
|
107
|
+
__NOTE:__ If provided, the bonus must respond to `:to_i` or an `ArgumentError` will be raised
|
108
|
+
|
109
|
+
### Taking actions on the dice as objects using the DiceSet object
|
110
|
+
The `NerdDice.roll_dice` method or the `NerdDice::DiceSet.new` methods return a collection object with an array of one or more `Die` objects. There are properties on both the `DiceSet` object and the `Die` object. Applicable properties are cascaded from the `DiceSet` to the `Die` objects in the collection by default.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# These are equivalent. Both return a NerdDice::DiceSet
|
114
|
+
dice_set = NerdDice.roll_dice(6, 3, bonus: 2, randomization_technique: :randomized,
|
115
|
+
damage_type: 'psychic', foreground_color: '#FFF', background_color: '#0FF')
|
116
|
+
|
117
|
+
dice_set = NerdDice::DiceSet.new(6, 3, bonus: 2, randomization_technique: :randomized,
|
118
|
+
damage_type: 'psychic', foreground_color: '#FFF', background_color: '#0FF')
|
119
|
+
|
120
|
+
```
|
121
|
+
#### Available options for NerdDice::DiceSet objects
|
122
|
+
There are a number of options that can be provided when initializing a `NerdDice::DiceSet` object after specifying the mandatory number of sides and the optional number of dice \(default: 1\). The list below provides the options and indicates whether they are cascaded to the Die objects in the collection.
|
123
|
+
* `bonus` \(Duck-type Integer, _default: 0_\): Bonus or penalty to apply to the total after all dice are rolled. _**Not applied** to Die objects_
|
124
|
+
* `randomization_technique` \(Symbol, _default: nil_\): Randomization technique override to use for the `DiceSet`. If `nil` it will use the value in `NerdDice.configuration`. _**Applied** to Die objects by default with ability to modify_
|
125
|
+
* `damage_type` \(String, _default: nil_\): Optional string indicating the damage type associated with the dice for systems where it is relevant. _**Applied** to Die objects by default with ability to modify_
|
126
|
+
* `foreground_color` \(String, _default: `NerdDice.configuration.die_foreground_color`_\): Intended foreground color to apply to the dice in the `DiceSet`. Should be a valid CSS color but is not validated or enforced and doesn\'t currently have any real functionality associated with it. _**Applied** to Die objects by default with ability to modify_
|
127
|
+
* `background_color` \(String, _default: `NerdDice.configuration.die_background_color`_\): Intended background color to apply to the dice in the `DiceSet`. Should be a valid CSS color but is not validated or enforced and doesn\'t currently have any real functionality associated with it. _**Applied** to Die objects by default with ability to modify_
|
128
|
+
|
129
|
+
#### Properties of individual Die objects
|
130
|
+
When initialized from a `DiceSet` object most of the properties of the `Die` object are inherited from the `DiceSet` object. In addition, there is an `is_included_in_total` public attribute that can be set to indicate whether the value of that particular die should be included in the total for its parent `DiceSet`. This property always starts out as true when the `Die` is initialized, but can be set to false.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# six sided die
|
134
|
+
die = NerdDice::Die.new(6, randomization_technique: :randomized, damage_type: 'psychic',
|
135
|
+
foreground_color: '#FFF', background_color: '#0FF')
|
136
|
+
die.is_included_in_total # => true
|
137
|
+
die.included_in_total? # => true
|
138
|
+
die.is_included_in_total = false
|
139
|
+
die.included_in_total? # => false
|
140
|
+
|
141
|
+
# value property
|
142
|
+
die.value # => Integer between 1 and number_of_sides
|
143
|
+
|
144
|
+
# Rolls/rerolls the Die, sets value to the result of the roll, and returns the new value
|
145
|
+
die.roll # => Integer.
|
146
|
+
```
|
147
|
+
#### Iterating through dice in a DiceSet
|
148
|
+
The `DiceSet` class mixes in the `Enumerable` module and the `Die` object mixes in the `Comparable` module. This allows you to iterate over the dice in the collection. The `sort` method on the dice will return the die objects in ascending value from lowest to highest.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
dice_set = NerdDice.roll_dice(6, 3) # => NerdDice::DiceSet
|
152
|
+
dice_set.dice => Array of Die objects
|
153
|
+
dice_set.length # => 3. (dice_set.dice.length)
|
154
|
+
dice_set[0] # => NerdDice::Die (first element of dice array)
|
155
|
+
# take actions on each die
|
156
|
+
dice_set.each do |die|
|
157
|
+
# print the current value
|
158
|
+
puts "Die value before reroll is #{die.value}"
|
159
|
+
# set the foreground_color of the die
|
160
|
+
die.foreground_color = ["gray", "#FF0000#", "#d9d9d9", "green"].shuffle.first
|
161
|
+
# reroll the die
|
162
|
+
die.roll
|
163
|
+
# print the new value
|
164
|
+
puts "Die value after reroll is #{die.value}"
|
165
|
+
# do other things
|
166
|
+
end
|
167
|
+
```
|
168
|
+
#### Methods and method chaining on the DiceSet
|
169
|
+
Since the DiceSet is an object, you can call methods that operate on the result returned and allow for things like the 5e advantage/disadvantage mechanic, the ability to re-roll all of the dice in the `DiceSet`, or to mark them all as included in the total.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
##############################################
|
173
|
+
# highest/with_advantage and lowest/with_disadvantage methods
|
174
|
+
# assuming 4d6 with values of [1, 3, 4, 6]
|
175
|
+
##############################################
|
176
|
+
dice_set = NerdDice.roll_dice(6, 4)
|
177
|
+
|
178
|
+
# the 6, 4, and 3 will have is_included_in_total true while the 1 has it false
|
179
|
+
# Returns the existing DiceSet object with the changes made to dice inclusion
|
180
|
+
dice_set.highest(3) # => DiceSet
|
181
|
+
dice_set.with_advantage(3) # => DiceSet (Alias of highest method)
|
182
|
+
|
183
|
+
# calling total after highest/with_advantage for this DiceSet
|
184
|
+
dice_set.total # => 13
|
185
|
+
|
186
|
+
# same DiceSet using lowest.
|
187
|
+
# The 1, 3, and 4 will have is_included_in_total true while the 6 has it false
|
188
|
+
dice_set.lowest(3) # => DiceSet
|
189
|
+
dice_set.with_disadvantage(3) # => DiceSet (Alias of lowest method)
|
190
|
+
|
191
|
+
# calling total after lowest/with_disadvantage for this DiceSet
|
192
|
+
dice_set.total # => 8
|
193
|
+
|
194
|
+
# you can chain these methods (assumes the same seed as the above examples)
|
195
|
+
NerdDice.roll_dice(6, 4).with_advantage(3).total # => 13
|
196
|
+
NerdDice.roll_dice(6, 4).lowest(3).total # => 8
|
197
|
+
|
198
|
+
# reroll_all! method
|
199
|
+
dice_set = NerdDice.roll_dice(6, 4)
|
200
|
+
# rerolls each of the Die objects in the collection and re-includes them in the total
|
201
|
+
dice_set.reroll_all!
|
202
|
+
|
203
|
+
# include_all_dice! method
|
204
|
+
dice_set.include_all_dice! # resets is_included_in_total to true for all Die objects
|
205
|
+
```
|
206
|
+
|
207
|
+
### Rolling Ability Scores
|
208
|
+
You can call `roll_ability_scores` or `total_ability_scores` to get back an array of `DiceSet` objects or `Integer` objects, respectively. The `total_ability_scores` method calls `total` on each `DiceSet` and returns those numbers with one value per ability score. The `Configuration` object defaults to 6 ability scores using a methodology of __4d6 drop the lowest__ by default.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
# return an array of DiceSet objects including info about the discarded dice
|
212
|
+
#
|
213
|
+
NerdDice.roll_ability_scores
|
214
|
+
#=> [DiceSet0, DiceSet1, ...]
|
215
|
+
# => DiceSet0 hash representation { total: 12, dice: [
|
216
|
+
# {value: 2, is_included_in_total: true},
|
217
|
+
# {value: 6, is_included_in_total: true},
|
218
|
+
# {value: 4, is_included_in_total: true},
|
219
|
+
# {value: 1, is_included_in_total: false}
|
220
|
+
# ]}
|
221
|
+
# if you want to get back DiceSet objects that you can interact with
|
222
|
+
|
223
|
+
# just return an array of totaled ability scores
|
224
|
+
NerdDice.total_ability_scores
|
225
|
+
#=> [12, 14, 13, 15, 10, 8]
|
226
|
+
```
|
227
|
+
|
228
|
+
Both methods can be called without arguments to use the values specified in `NerdDice.configuration` or passed a set of options.
|
229
|
+
```ruby
|
230
|
+
|
231
|
+
# total_dice and roll_dice take the same set of options
|
232
|
+
NerdDice.roll_ability_scores(
|
233
|
+
ability_score_array_size: 7,
|
234
|
+
ability_score_number_of_sides: 8,
|
235
|
+
ability_score_dice_rolled: 5,
|
236
|
+
ability_score_dice_kept: 4,
|
237
|
+
randomization_technique: :randomized,
|
238
|
+
foreground_color: "#FF0000",
|
239
|
+
background_color: "#FFFFFF"
|
240
|
+
)
|
241
|
+
# => [DiceSet0, DiceSet1, ...] with 7 ability scores that each roll 5d8 dropping the lowest
|
242
|
+
# or if called with total_ability_scores
|
243
|
+
# => [27, 17, 21, 17, 23, 13, 27]
|
244
|
+
```
|
245
|
+
**Note:** If you try to call this method with `ability_score_dice_kept` greater than `ability_score_dice_rolled` an error will be raised.
|
246
|
+
|
247
|
+
### Manually setting or refreshing the random generator seed
|
248
|
+
For randomization techniques other than `:securerandom` you can manually set or refresh the generator's seed by calling the `refresh_seed!` method. This is automatically called at the interval specified in `NerdDice.configuration.refresh_seed_interval` if it is not nil.
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
# no arguments, will refresh the seed for the configured generator(s) only
|
252
|
+
NerdDice.refresh_seed! # => hash with old seed(s) or nil if :securerandom
|
253
|
+
|
254
|
+
# OPTIONS:
|
255
|
+
# randomization_technique (Symbol) => NerdDice::RANDOMIZATION_TECHNIQUES
|
256
|
+
# random_rand_seed (Integer) => Seed to set for Random
|
257
|
+
# random_object_seed (Integer) => Seed to set for new Random object
|
258
|
+
NerdDice.refresh_seed!(randomization_technique: :randomized,
|
259
|
+
random_rand_seed: 1337,
|
260
|
+
random_object_seed: 24601)
|
261
|
+
```
|
262
|
+
__NOTE:__ Ability to specify a seed is primarily provided for testing purposes. This makes all random numbers generated _transparently deterministic_ and should not be used if you want behavior approximating randomness.
|
263
|
+
|
264
|
+
### Utility Methods
|
265
|
+
|
266
|
+
#### Harvesting Totals from DiceSets
|
267
|
+
The `harvest_totals` method take any collection of objects where each element responds to `total` and return an array of the results of the total method.
|
268
|
+
```ruby
|
269
|
+
ability_score_array = NerdDice.roll_ability_scores
|
270
|
+
# => Array of 6 DiceSet objects
|
271
|
+
|
272
|
+
# Arguments:
|
273
|
+
# collection (Enumerable) a collection where each element responds to total
|
274
|
+
#
|
275
|
+
# Return (Array) => Data type of each element will be whatever is returned by total method
|
276
|
+
totals_array = NerdDice.harvest_totals(totals_array)
|
277
|
+
# => [15, 14, 13, 12, 10, 8]
|
278
|
+
# yes, it just happened to be the standard array by amazing coincidence
|
279
|
+
```
|
280
|
+
<a name="convenience-methods-mixin"></a>
|
281
|
+
### Convenience Methods Mixin
|
282
|
+
NerdDice provides an optional mixin `NerdDice::ConvenienceMethods` that uses Ruby\'s `method_missing` metaprogramming pattern to allow you to roll any number of dice with bonuses and/or the advantage/disadvantage mechanic by dynamically responding to methods that you type that match the `roll_` or `total_` pattern.
|
283
|
+
|
284
|
+
#### Considerations for ConvenienceMethods
|
285
|
+
Before mixing in this method with a class, be aware of other `method_missing` gems that you are also mixing into your project and be sure to write robust tests. We have sought to use `method_missing` in a responsible manner that delegates back to the default implementation using `super` if the method does not match the `ConvenienceMethods` pattern, but there is no guarantee that other gems included in your project are doing the same. If you run into problems with the `ConvenienceMethods` module interacting with other `method_missing` gems, everything that the `ConvenienceMethods` module does can be replicated using the module\-level methods described above or by calling the convenience method on `NerdDice`.
|
286
|
+
|
287
|
+
Once a particular method has been called, it will define that method so that the next time it will invoke the method directly instead of traversing up the call stack for `method_missing`, which improves performance. The method will remain defined for the duration of the Ruby interpreter process.
|
288
|
+
|
289
|
+
#### Calling ConvenienceMethods as NerdDice class methods
|
290
|
+
NerdDice extends the `ConvenienceMethods` module into the top-level module as class methods, so you can call the methods on the NerdDice module without needing to worry about the implications of extending it into your own class.
|
291
|
+
```ruby
|
292
|
+
require 'nerd_dice'
|
293
|
+
# works with all the examples and patterns below
|
294
|
+
NerdDice.roll_3d6_lowest2_minus1
|
295
|
+
NerdDice.total_d20_with_advantage_p6
|
296
|
+
```
|
297
|
+
|
298
|
+
#### Mixing in the ConvenienceMethods module
|
299
|
+
To mix the NerdDice DSL into your class, make sure the gem is required if not already and then use `include NerdDice::ConvenienceMethods` as shown below:
|
300
|
+
```ruby
|
301
|
+
# example of a class that mixes in NerdDice::ConvenienceMethods
|
302
|
+
require 'nerd_dice'
|
303
|
+
class Monster
|
304
|
+
include NerdDice::ConvenienceMethods
|
305
|
+
|
306
|
+
# hard-coding probably not the best solution
|
307
|
+
# but gives you an idea how to mix in to a simple class
|
308
|
+
def hits_the_monster
|
309
|
+
# using the ConvenienceMethods version
|
310
|
+
total_d20_plus5 >= @armor_class ? "hit" : "miss"
|
311
|
+
end
|
312
|
+
|
313
|
+
def initialize(armor_class=16)
|
314
|
+
@armor_class = armor_class
|
315
|
+
end
|
316
|
+
end
|
317
|
+
```
|
318
|
+
To mix in the module as class methods, you can use `extend NerdDice::ConvenienceMethods`
|
319
|
+
```ruby
|
320
|
+
# example of a class that mixes in NerdDice::ConvenienceMethods
|
321
|
+
require 'nerd_dice'
|
322
|
+
class OtherClass
|
323
|
+
extend NerdDice::ConvenienceMethods
|
324
|
+
end
|
325
|
+
OtherClass.roll_3d6_lowest2_minus1 # returns NerdDice::DiceSet
|
326
|
+
```
|
327
|
+
|
328
|
+
#### ConvenienceMethods usage examples
|
329
|
+
Any invocation of `NerdDice.roll_dice` and `NerdDice.total_dice` can be duplicated using the `NerdDice::ConvenienceMethods` mixin. Here are some examples of what you can do with the return types and equivalent methods in the comments:
|
330
|
+
|
331
|
+
* `roll_dNN` and `total_dNN` roll one die
|
332
|
+
```ruby
|
333
|
+
roll_d20 # => DiceSet: NerdDice.roll_dice(20)
|
334
|
+
roll_d8 # => DiceSet: NerdDice.roll_dice(8)
|
335
|
+
roll_d1000 # => DiceSet: NerdDice.roll_dice(1000)
|
336
|
+
total_d20 # => Integer NerdDice.total_dice(20)
|
337
|
+
total_d8 # => Integer NerdDice.total_dice(8)
|
338
|
+
total_d1000 # => Integer NerdDice.total_dice(1000)
|
339
|
+
```
|
340
|
+
* `roll_NNdNN` and `total_NNdNN` roll specified quantity of dice
|
341
|
+
```ruby
|
342
|
+
roll_2d20 # => DiceSet: NerdDice.roll_dice(20, 2)
|
343
|
+
roll_3d8 # => DiceSet: NerdDice.roll_dice(8, 3)
|
344
|
+
roll_22d1000 # => DiceSet: NerdDice.roll_dice(1000, 22)
|
345
|
+
total_2d20 # => Integer NerdDice.total_dice(20, 2)
|
346
|
+
total_3d8 # => Integer NerdDice.total_dice(8, 3)
|
347
|
+
total_22d1000 # => Integer NerdDice.total_dice(1000, 22)
|
348
|
+
```
|
349
|
+
* Keyword arguments are passed on to `roll_dice`/`total_dice` method
|
350
|
+
```ruby
|
351
|
+
roll_2d20 foreground_color: "blue" # => DiceSet: NerdDice.roll_dice(20, 2, foreground_color: "blue")
|
352
|
+
|
353
|
+
total_d12 randomization_technique: :randomized
|
354
|
+
# => Integer NerdDice.total_dice(12, randomization_technique: :randomized)
|
355
|
+
total_22d1000 randomization_technique: :random_rand
|
356
|
+
# => Integer NerdDice.total_dice(1000, 22, randomization_technique: :random_rand)
|
357
|
+
|
358
|
+
roll_4d6_with_advantage3 background_color: 'blue'
|
359
|
+
# => DiceSet: NerdDice.roll_dice(4, 3, background_color: 'blue').highest(3)
|
360
|
+
total_4d6_with_advantage3 randomization_technique: :random_rand
|
361
|
+
# => Integer: NerdDice.roll_dice(4, 3, randomization_technique: :random_rand).highest(3).total
|
362
|
+
```
|
363
|
+
* Positive and negative bonuses can be used with `plus` (alias `p`) or `minus` (alias `m`)
|
364
|
+
```ruby
|
365
|
+
roll_d20_plus6 # => DiceSet: NerdDice.roll_dice(20, bonus: 6)
|
366
|
+
total_3d8_p2 # => Integer: NerdDice.total_dice(8, 3, bonus: 2)
|
367
|
+
total_d20_minus5 # => Integer: NerdDice.total_dice(20, bonus: -6)
|
368
|
+
roll_3d8_m3 # => DiceSet: NerdDice.roll_dice(8, 3, bonus: -3)
|
369
|
+
```
|
370
|
+
* `_with_advantageN` or `highestN` roll with advantage
|
371
|
+
* `_with_disadvantageN` or `lowestN` roll with disadvantage
|
372
|
+
* Calling `roll_dNN_with_advantage` \(and variants\) rolls 2 dice and keeps one
|
373
|
+
```ruby
|
374
|
+
# equivalent
|
375
|
+
roll_3d8_with_advantage1
|
376
|
+
roll_3d8_highest1
|
377
|
+
# => DiceSet: NerdDice.roll_dice(8, 3).with_advantage(1)
|
378
|
+
|
379
|
+
# calls roll_dice and total to return an integer
|
380
|
+
total_3d8_with_advantage1
|
381
|
+
total_3d8_highest1
|
382
|
+
# => Integer: NerdDice.roll_dice(8, 3).with_advantage(1).total
|
383
|
+
|
384
|
+
# rolls two dice in this case
|
385
|
+
# equal to roll_2d20_with_advantage but more natural
|
386
|
+
roll_d20_with_advantage # => DiceSet: NerdDice.roll_dice(20, 2).with_advantage(1)
|
387
|
+
# equal to total_2d20_with_advantage but more natural
|
388
|
+
total_d20_with_advantage # => Integer: NerdDice.roll_dice(20, 2).with_advantage(1).total
|
389
|
+
```
|
390
|
+
#### ConvenienceMethods error handling
|
391
|
+
* If you try to call with a plus and a minus, an Exception is raised
|
392
|
+
* If you call with a bonus and a keyword argument and they don't match, an Exception is raised
|
393
|
+
* Any combination not expressly allowed or matched will call `super` on `method_missing`
|
394
|
+
```ruby
|
395
|
+
roll_3d8_plus3_m2 # will raise NameError using super method_missing
|
396
|
+
roll_3d8_plus3 bonus: 1 # will raise NerdDice::Error with message about "Bonus integrity failure"
|
397
|
+
roll_d20_with_advantage_lowest # will raise NameError using super method_missing
|
398
|
+
total_4d6_lowest3_highest2 # will raise NameError using super method_missing
|
31
399
|
```
|
32
|
-
__NOTE:__ If provided, the bonus must be an ```Integer``` or it will be ignored
|
33
400
|
|
34
401
|
## Development
|
35
402
|
|
@@ -44,4 +411,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/statel
|
|
44
411
|
|
45
412
|
## Unlicense, License, and Copyright
|
46
413
|
|
47
|
-
The
|
414
|
+
The project is dual-licensed under the [MIT](https://opensource.org/licenses/MIT) license and the [UNLICENSE](https://unlicense.org/) \(with strong preference toward the UNLICENSE\)\. The content is released under [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) \(no rights reserved\). You are free to include it in its original form or modified with or without additional modification in your own project\.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "digest/sha2"
|
5
|
+
|
6
|
+
version = ARGV[0]
|
7
|
+
built_gem_path = "pkg/nerd_dice-#{version}.gem"
|
8
|
+
checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
|
9
|
+
checksum_path = "checksum/nerd_dice-#{version}.gem.sha512"
|
10
|
+
File.open(checksum_path, "w") { |f| f.write(checksum) }
|
11
|
+
sha256 = Digest::SHA256.new.hexdigest(File.read(built_gem_path))
|
12
|
+
checksum_256_path = "checksum/nerd_dice-#{version}.gem.sha256"
|
13
|
+
File.open(checksum_256_path, "w") { |f| f.write(sha256) }
|