line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Games::Dice::Roller;
|
2
|
|
|
|
|
|
|
|
3
|
10
|
|
|
10
|
|
80432
|
use 5.010;
|
|
10
|
|
|
|
|
39
|
|
4
|
10
|
|
|
10
|
|
51
|
use strict;
|
|
10
|
|
|
|
|
24
|
|
|
10
|
|
|
|
|
225
|
|
5
|
10
|
|
|
10
|
|
51
|
use warnings;
|
|
10
|
|
|
|
|
19
|
|
|
10
|
|
|
|
|
265
|
|
6
|
|
|
|
|
|
|
|
7
|
10
|
|
|
10
|
|
52
|
use Carp;
|
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
24911
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.01';
|
10
|
|
|
|
|
|
|
our $debug = $ENV{DICE_ROLLER_DEBUG} // 0;
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub new{
|
13
|
12
|
|
|
12
|
1
|
9155
|
my $class = shift;
|
14
|
12
|
|
|
|
|
33
|
my %opts = @_;
|
15
|
12
|
100
|
|
|
|
49
|
if ( defined $opts{sub_rand} ){
|
16
|
|
|
|
|
|
|
croak "sub_rand must be a code reference meant to replace core rand function"
|
17
|
7
|
100
|
|
|
|
41
|
unless ref $opts{sub_rand} eq 'CODE';
|
18
|
|
|
|
|
|
|
}
|
19
|
|
|
|
|
|
|
return bless {
|
20
|
10013
|
|
|
10013
|
|
25767
|
sub_rand => $opts{sub_rand} // sub{ rand($_[0]) },
|
21
|
11
|
|
100
|
|
|
95
|
}, $class;
|
22
|
|
|
|
|
|
|
}
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub roll{
|
26
|
37
|
|
|
37
|
1
|
13430
|
my $self = shift;
|
27
|
37
|
|
|
|
|
64
|
my $arg = shift;
|
28
|
37
|
100
|
|
|
|
98
|
croak "roll method expects one argument" unless $arg;
|
29
|
36
|
100
|
|
|
|
90
|
croak "roll method expects a single string argument" if @_;
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# trim spaces
|
32
|
35
|
|
|
|
|
111
|
$arg =~ s/^\s+//;
|
33
|
35
|
|
|
|
|
90
|
$arg =~ s/\s+$//;
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# check if we received a dice pool
|
36
|
35
|
|
|
|
|
118
|
my @args = split /\s+/, $arg;
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# a dice pool
|
39
|
35
|
100
|
|
|
|
91
|
if ( scalar @args > 1 ){
|
40
|
|
|
|
|
|
|
# transform each one in resulting hashref returned by _identify_type
|
41
|
6
|
|
|
|
|
12
|
@args = map { _identify_type($_) } @args;
|
|
23
|
|
|
|
|
43
|
|
42
|
6
|
|
|
|
|
13
|
@args = _validate_pool( @args );
|
43
|
|
|
|
|
|
|
# transform each dice expression in its resulting format
|
44
|
4
|
|
|
|
|
7
|
foreach my $ele( @args ){
|
45
|
17
|
100
|
|
|
|
40
|
next unless $ele->{type} eq 'dice_expression';
|
46
|
9
|
|
|
|
|
46
|
my ($res, $descr) = $self->roll( $ele->{original} );
|
47
|
9
|
|
|
|
|
54
|
$ele = { result => $res, result_description => $descr, original => $ele->{original}};
|
48
|
|
|
|
|
|
|
}
|
49
|
|
|
|
|
|
|
# is the last element
|
50
|
4
|
|
|
|
|
7
|
my $global_modifier = pop @args;
|
51
|
4
|
|
|
|
|
15
|
my @sorted = sort{ $a->{result} <=> $b->{result} }@args;
|
|
12
|
|
|
|
|
28
|
|
52
|
4
|
100
|
|
|
|
12
|
@sorted = reverse @sorted if $global_modifier->{value} eq 'kh';
|
53
|
4
|
|
|
|
|
7
|
my $global_result = $sorted[0]->{result};
|
54
|
|
|
|
|
|
|
my @global_descr = (
|
55
|
|
|
|
|
|
|
($sorted[0]->{original} ? $sorted[0]->{original} : $sorted[0]->{result}).
|
56
|
4
|
50
|
|
|
|
16
|
($sorted[0]->{result_description} ? " = $sorted[0]->{result_description}": '')
|
|
|
50
|
|
|
|
|
|
57
|
|
|
|
|
|
|
);
|
58
|
4
|
|
|
|
|
7
|
shift @sorted;
|
59
|
|
|
|
|
|
|
push @global_descr, "( ".
|
60
|
|
|
|
|
|
|
($_->{original} ? $_->{original} : '').
|
61
|
|
|
|
|
|
|
($_->{result_description}?" = $_->{result_description} = ":'').
|
62
|
|
|
|
|
|
|
($_->{result}?"$_->{result} ":'').
|
63
|
4
|
100
|
|
|
|
34
|
")" for @sorted;
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
4
|
|
|
|
|
32
|
return ($global_result, join ', ',@global_descr);
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
}
|
68
|
|
|
|
|
|
|
# a single dice expression
|
69
|
|
|
|
|
|
|
else{
|
70
|
|
|
|
|
|
|
# transform it in a hashref as returned by _identify_type
|
71
|
|
|
|
|
|
|
# this will be returned as third element
|
72
|
29
|
|
|
|
|
63
|
my $ref = _identify_type( shift @args );
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# used to accumulate partial results and descriptive string elements
|
75
|
28
|
|
|
|
|
59
|
my ( @partial, @descr );
|
76
|
|
|
|
|
|
|
|
77
|
28
|
|
|
|
|
95
|
my ($times, $sides) = split 'd', $ref->{dice_exp};
|
78
|
28
|
|
|
|
|
88
|
while( $times > 0 ){
|
79
|
|
|
|
|
|
|
|
80
|
256
|
|
|
|
|
319
|
my $single_res;
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# BARE DICE EXPRESSION
|
83
|
256
|
100
|
|
|
|
488
|
unless ( $ref->{die_mod} ){
|
84
|
122
|
|
|
|
|
206
|
$single_res = $self->single_die( $sides );
|
85
|
122
|
|
|
|
|
902
|
push @partial, $single_res;
|
86
|
122
|
|
|
|
|
169
|
push @descr, $single_res;
|
87
|
122
|
|
|
|
|
154
|
$times--;
|
88
|
122
|
|
|
|
|
232
|
next;
|
89
|
|
|
|
|
|
|
}
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# DIE MODIFIERS #
|
92
|
|
|
|
|
|
|
# avg does not require further processing
|
93
|
134
|
100
|
66
|
|
|
360
|
if ( $ref->{die_mod} and $ref->{die_mod} eq 'avg' ){
|
94
|
5
|
|
|
|
|
10
|
$single_res = (1 + $sides) / 2;
|
95
|
5
|
|
|
|
|
8
|
push @partial, $single_res;
|
96
|
5
|
|
|
|
|
14
|
push @descr, $single_res;
|
97
|
5
|
|
|
|
|
10
|
$times--;
|
98
|
5
|
|
|
|
|
11
|
next;
|
99
|
|
|
|
|
|
|
}
|
100
|
|
|
|
|
|
|
# if r x cs roll the die
|
101
|
|
|
|
|
|
|
else{
|
102
|
129
|
|
|
|
|
219
|
$single_res = $self->single_die( $sides );
|
103
|
|
|
|
|
|
|
}
|
104
|
|
|
|
|
|
|
# process r x cs die modifiers
|
105
|
|
|
|
|
|
|
# if r
|
106
|
129
|
100
|
66
|
|
|
991
|
if ( $ref->{die_mod} and $ref->{die_mod} eq 'r' ){
|
107
|
42
|
|
|
|
|
80
|
my $comp_num = $ref->{die_mod_val};
|
108
|
42
|
|
|
|
|
56
|
my $comp_op = $ref->{comp_mod};
|
109
|
|
|
|
|
|
|
# check if it must be rerolled
|
110
|
42
|
100
|
100
|
|
|
352
|
if(
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
111
|
|
|
|
|
|
|
(not defined $ref->{comp_mod} and $single_res == $comp_num) or
|
112
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'lt' and $single_res < $comp_num ) or
|
113
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'gt' and $single_res > $comp_num )
|
114
|
|
|
|
|
|
|
){
|
115
|
|
|
|
|
|
|
# REROLL
|
116
|
10
|
|
|
|
|
22
|
push @descr,"($single_res"."r)";
|
117
|
10
|
|
|
|
|
22
|
next;
|
118
|
|
|
|
|
|
|
}
|
119
|
|
|
|
|
|
|
else{
|
120
|
32
|
|
|
|
|
71
|
push @descr, $single_res;
|
121
|
32
|
|
|
|
|
44
|
push @partial, $single_res;
|
122
|
32
|
|
|
|
|
37
|
$times--;
|
123
|
32
|
|
|
|
|
70
|
next;
|
124
|
|
|
|
|
|
|
}
|
125
|
|
|
|
|
|
|
} # end of r check
|
126
|
|
|
|
|
|
|
# if x
|
127
|
87
|
100
|
66
|
|
|
267
|
if ( $ref->{die_mod} and $ref->{die_mod} eq 'x' ){
|
128
|
51
|
|
|
|
|
76
|
my $comp_num = $ref->{die_mod_val};
|
129
|
51
|
|
|
|
|
70
|
my $comp_op = $ref->{comp_mod};
|
130
|
|
|
|
|
|
|
# check if it must be exploded
|
131
|
51
|
100
|
100
|
|
|
507
|
if(
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
132
|
|
|
|
|
|
|
(not defined $ref->{comp_mod} and $single_res == $comp_num) or
|
133
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'lt' and $single_res < $comp_num ) or
|
134
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'gt' and $single_res > $comp_num )
|
135
|
|
|
|
|
|
|
){
|
136
|
|
|
|
|
|
|
# EXPLODE
|
137
|
10
|
|
|
|
|
23
|
push @descr,$single_res."x";
|
138
|
10
|
|
|
|
|
17
|
push @partial, $single_res;
|
139
|
10
|
|
|
|
|
20
|
next;
|
140
|
|
|
|
|
|
|
}
|
141
|
|
|
|
|
|
|
else{
|
142
|
41
|
|
|
|
|
89
|
push @descr, $single_res;
|
143
|
41
|
|
|
|
|
84
|
push @partial, $single_res;
|
144
|
41
|
|
|
|
|
59
|
$times--;
|
145
|
41
|
|
|
|
|
78
|
next;
|
146
|
|
|
|
|
|
|
}
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
} # end of x check
|
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# if cs
|
151
|
36
|
50
|
33
|
|
|
105
|
if ( $ref->{die_mod} and $ref->{die_mod} eq 'cs' ){
|
152
|
36
|
|
|
|
|
56
|
my $comp_num = $ref->{die_mod_val};
|
153
|
36
|
|
|
|
|
44
|
my $comp_op = $ref->{comp_mod};
|
154
|
|
|
|
|
|
|
# initialize partial with zero succes
|
155
|
36
|
|
|
|
|
48
|
push @partial, 0;
|
156
|
|
|
|
|
|
|
# check if it is success
|
157
|
36
|
100
|
100
|
|
|
252
|
if(
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
158
|
|
|
|
|
|
|
(not defined $ref->{comp_mod} and $single_res == $comp_num) or
|
159
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'lt' and $single_res < $comp_num ) or
|
160
|
|
|
|
|
|
|
(defined $ref->{comp_mod} and $ref->{comp_mod} eq 'gt' and $single_res > $comp_num )
|
161
|
|
|
|
|
|
|
){
|
162
|
|
|
|
|
|
|
# SUCCESS
|
163
|
10
|
|
|
|
|
15
|
push @descr,$single_res;
|
164
|
10
|
|
|
|
|
16
|
push @partial, 1;
|
165
|
10
|
|
|
|
|
13
|
$times--;
|
166
|
10
|
|
|
|
|
20
|
next;
|
167
|
|
|
|
|
|
|
}
|
168
|
|
|
|
|
|
|
else{
|
169
|
26
|
|
|
|
|
48
|
push @descr, "($single_res)";
|
170
|
26
|
|
|
|
|
39
|
$times--;
|
171
|
26
|
|
|
|
|
47
|
next;
|
172
|
|
|
|
|
|
|
}
|
173
|
|
|
|
|
|
|
} # end of cs check
|
174
|
|
|
|
|
|
|
} # end of while loop
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# RESULT MODIFIERS kh kl dh dl #
|
177
|
28
|
100
|
66
|
|
|
93
|
if ( $ref->{res_mod} and $ref->{res_mod} =~/^(?:kh|kl|dh|dl)$/ ){
|
178
|
6
|
|
|
|
|
12
|
my @wanted;
|
179
|
|
|
|
|
|
|
my @dropped;
|
180
|
|
|
|
|
|
|
# sort from lowest to highest partial, temporary results
|
181
|
6
|
|
|
|
|
27
|
my @sorted = sort{ $a <=> $b }@partial;
|
|
120
|
|
|
|
|
171
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# kh and kl
|
184
|
6
|
100
|
100
|
|
|
43
|
if ( $ref->{res_mod} eq 'kh' or $ref->{res_mod} eq 'kl'){
|
185
|
|
|
|
|
|
|
# reverse if highest are needed
|
186
|
3
|
100
|
|
|
|
8
|
@sorted = reverse @sorted if $ref->{res_mod} eq 'kh';
|
187
|
|
|
|
|
|
|
# reset partial result array
|
188
|
3
|
|
|
|
|
16
|
undef @partial;
|
189
|
|
|
|
|
|
|
# unshift n highest values shortening @sorted
|
190
|
3
|
|
|
|
|
17
|
unshift @partial, shift @sorted for 1..$ref->{res_mod_val};
|
191
|
|
|
|
|
|
|
# consume what left in sorted to modify description
|
192
|
3
|
|
|
|
|
10
|
while ( my $tobedropped = shift @sorted ){
|
193
|
24
|
|
|
|
|
39
|
foreach my $ele( @descr ){
|
194
|
172
|
100
|
|
|
|
290
|
if ( $ele eq $tobedropped ){
|
195
|
24
|
|
|
|
|
34
|
$ele = "($ele)";
|
196
|
24
|
|
|
|
|
48
|
last;
|
197
|
|
|
|
|
|
|
}
|
198
|
|
|
|
|
|
|
}
|
199
|
|
|
|
|
|
|
}
|
200
|
3
|
100
|
|
|
|
8
|
@descr = reverse @descr if $ref->{res_mod} eq 'kl';
|
201
|
|
|
|
|
|
|
} # end kh kl check
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# dh and dl
|
204
|
6
|
100
|
100
|
|
|
25
|
if ( $ref->{res_mod} eq 'dh' or $ref->{res_mod} eq 'dl'){
|
205
|
|
|
|
|
|
|
# reverse if lowest are needed
|
206
|
3
|
100
|
|
|
|
9
|
@sorted = reverse @sorted if $ref->{res_mod} eq 'dl';
|
207
|
|
|
|
|
|
|
# reset partial result array
|
208
|
3
|
|
|
|
|
6
|
undef @partial;
|
209
|
|
|
|
|
|
|
# unshift n highest values shortening @sorted
|
210
|
3
|
|
|
|
|
18
|
unshift @partial, shift @sorted for 1 .. ( scalar @sorted - $ref->{res_mod_val} );
|
211
|
|
|
|
|
|
|
# consume what left in sorted to modify description
|
212
|
3
|
|
|
|
|
7
|
while ( my $tobedropped = shift @sorted ){
|
213
|
20
|
100
|
|
|
|
40
|
foreach my $ele( $ref->{res_mod} eq 'dl' ? reverse @descr : @descr ){
|
214
|
79
|
100
|
|
|
|
138
|
if ( $ele eq $tobedropped ){
|
215
|
20
|
|
|
|
|
26
|
$ele = "($ele)";
|
216
|
20
|
|
|
|
|
40
|
last;
|
217
|
|
|
|
|
|
|
}
|
218
|
|
|
|
|
|
|
}
|
219
|
|
|
|
|
|
|
}
|
220
|
3
|
100
|
|
|
|
8
|
@descr = reverse @descr if $ref->{res_mod} eq 'dh';
|
221
|
|
|
|
|
|
|
} # end dh dl check
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
} # end of result modifiers processing
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# RESULT SUMMATION
|
226
|
28
|
100
|
|
|
|
54
|
if ( $ref->{res_sum} ){
|
227
|
12
|
|
|
|
|
32
|
push @descr, $ref->{res_sum};
|
228
|
12
|
|
|
|
|
22
|
push @partial, $ref->{res_sum};
|
229
|
|
|
|
|
|
|
}
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# COMPUTE RESULT AND DESCRIPTION
|
232
|
|
|
|
|
|
|
# add them to the $ref detailed result hasref
|
233
|
28
|
|
|
|
|
120
|
$ref->{result} += $_ for @partial;
|
234
|
28
|
|
|
|
|
139
|
$ref->{result_description} = join ' ', @descr;
|
235
|
|
|
|
|
|
|
|
236
|
28
|
50
|
|
|
|
61
|
print "Description: $ref->{result_description}\nResult : $ref->{result}\n\n" if $debug;
|
237
|
|
|
|
|
|
|
|
238
|
28
|
|
|
|
|
144
|
return ($ref->{result}, $ref->{result_description}, $ref);
|
239
|
|
|
|
|
|
|
} # end of single dice expression evaluation
|
240
|
|
|
|
|
|
|
}
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub single_die{
|
243
|
10261
|
|
|
10261
|
0
|
24714
|
my $self = shift;
|
244
|
10261
|
|
|
|
|
12639
|
my $sides = shift;
|
245
|
10261
|
100
|
|
|
|
16684
|
croak "single_die expect one argument" unless $sides;
|
246
|
10260
|
100
|
|
|
|
26007
|
croak "Invalid side [$sides]" unless $sides =~/^(\d+)$/;
|
247
|
10256
|
|
|
|
|
17871
|
$sides = $1;
|
248
|
10256
|
|
|
|
|
16450
|
return 1 + int( $self->{sub_rand}($sides) );
|
249
|
|
|
|
|
|
|
}
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub _validate_expr{
|
252
|
98
|
|
|
98
|
|
146
|
my $result = shift;
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# NB: see ./t/04-validate-expr.t
|
255
|
|
|
|
|
|
|
# many of the following check are never reached
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# die_mod = avg
|
258
|
98
|
100
|
100
|
|
|
286
|
if ( $result->{die_mod} and $result->{die_mod} eq 'avg' ){
|
259
|
7
|
50
|
|
|
|
16
|
croak "with avg no result modification (k|d) are admitted. OK: 3d8avg NO: 3d8avgkh" if $result->{res_mod};
|
260
|
7
|
100
|
|
|
|
25
|
croak "with avg no comparison modifiers (gt|lt) are admitted. OK: 3d8avg NO: 3d8avglt" if $result->{comp_mod};
|
261
|
6
|
100
|
|
|
|
40
|
croak "with avg no modification value (number) is admitted. OK: 3d8avg NO: 3d8avg3" if $result->{die_mod_val};
|
262
|
|
|
|
|
|
|
}
|
263
|
|
|
|
|
|
|
# die_mod = cs
|
264
|
96
|
100
|
100
|
|
|
251
|
if ( $result->{die_mod} and $result->{die_mod} eq 'cs' ){
|
265
|
9
|
100
|
|
|
|
31
|
croak "with cs no result modification (k|d) are admitted. OK: 3d8cs3 NO: 3d8cs3kl" if $result->{res_mod};
|
266
|
8
|
100
|
|
|
|
51
|
croak "with cs a number must be also specified. OK: 3d8cs2 NO: 3d8cs" unless $result->{die_mod_val};
|
267
|
6
|
100
|
|
|
|
26
|
croak "with cs no sum are permitted. OK: 3d8cs2 NO: 3d8cs2+12" if $result->{res_sum};
|
268
|
|
|
|
|
|
|
}
|
269
|
|
|
|
|
|
|
# die_mod = x
|
270
|
92
|
100
|
100
|
|
|
258
|
if ( $result->{die_mod} and $result->{die_mod} eq 'x' ){
|
271
|
12
|
100
|
|
|
|
42
|
croak "with x no result modification (k|d) are admitted. OK: 3d8x8 NO: 3d8x8kl" if $result->{res_mod};
|
272
|
11
|
100
|
|
|
|
37
|
croak "with x a number must be also specified. OK: 3d8x8 NO: 3d8x" unless $result->{die_mod_val};
|
273
|
|
|
|
|
|
|
}
|
274
|
|
|
|
|
|
|
# die_mod = r
|
275
|
90
|
100
|
100
|
|
|
238
|
if ( $result->{die_mod} and $result->{die_mod} eq 'r' ){
|
276
|
9
|
50
|
|
|
|
23
|
croak "with r a number must be also specified. OK: 3d8r1 NO: 3d8r" unless $result->{die_mod_val};
|
277
|
|
|
|
|
|
|
}
|
278
|
|
|
|
|
|
|
# comp_mod = gt|lt
|
279
|
90
|
100
|
66
|
|
|
226
|
if ( $result->{comp_mod} and $result->{comp_mod} =~/^(?:gt|lt)$/ ){
|
280
|
10
|
50
|
|
|
|
33
|
croak "a comparison modifier (lt or gt) can only be used with r x and cs. OK: 3d8rlt2 NO: 3d8avglt4" unless $result->{die_mod} =~ /^(?:r|x|cs)$/;
|
281
|
|
|
|
|
|
|
}
|
282
|
|
|
|
|
|
|
# res_mod = kh|kl|dh|dl
|
283
|
90
|
100
|
66
|
|
|
291
|
if ( $result->{res_mod} and $result->{res_mod} =~/^(?:kh|kl|dh|dl)$/ ){
|
284
|
27
|
50
|
|
|
|
66
|
croak "a result modifier (kh, kl, dh and dl) can only be used with number after it. OK: 3d8kh2 NO: 3d8kl" unless $result->{res_mod_val};
|
285
|
27
|
50
|
|
|
|
51
|
croak "a result modifier (kh, kl, dh and dl) cannot be used with a die modifier (r, x, cs or avg) OK: 3d8kh2 NO: 3d8x7kh3" if $result->{die_mod};
|
286
|
27
|
50
|
|
|
|
62
|
croak "a result modifier (kh, kl, dh and dl) cannot be used with a comparison modifier (lt or gt). OK: 3d8kh2 NO: 3d8khlt2" if $result->{comp_mod};
|
287
|
27
|
50
|
|
|
|
128
|
my $dice_num = $1 if $result->{dice_exp}=~ /^(\d+)d/;
|
288
|
27
|
100
|
|
|
|
108
|
croak "too many dice to keep or drop ($dice_num) in $result->{dice_exp}" if $result->{res_mod_val} >= $dice_num;
|
289
|
|
|
|
|
|
|
}
|
290
|
|
|
|
|
|
|
# res_sum = +3|-3
|
291
|
89
|
100
|
66
|
|
|
349
|
if ( $result->{res_sum} and $result->{res_sum} =~ /^[+-]\d+$/){
|
292
|
49
|
50
|
66
|
|
|
153
|
croak "a result sum cannot be used when cs is used" if defined $result->{die_mod} and $result->{die_mod} eq 'cs';
|
293
|
|
|
|
|
|
|
}
|
294
|
|
|
|
|
|
|
}
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub _validate_pool{
|
297
|
6
|
|
|
6
|
|
12
|
my @args = @_;
|
298
|
|
|
|
|
|
|
# type => 'number'
|
299
|
|
|
|
|
|
|
# type => 'global_modifier'
|
300
|
|
|
|
|
|
|
# type => 'dice_expression'
|
301
|
|
|
|
|
|
|
|
302
|
6
|
100
|
|
|
|
13
|
croak "too many bare number in dice pool" if 1 < grep{ $_->{type} eq 'number' }@args;
|
|
23
|
|
|
|
|
69
|
|
303
|
5
|
100
|
|
|
|
7
|
croak "too many global modifiers (kh or kl) in dice pool" if 1 < grep{ $_->{type} eq 'global_modifier' }@args;
|
|
19
|
|
|
|
|
52
|
|
304
|
|
|
|
|
|
|
# deafult to kh
|
305
|
4
|
100
|
|
|
|
6
|
push @args, { type => 'global_modifier', value => 'kh' } if 0 == grep{ $_->{type} eq 'global_modifier' }@args;
|
|
15
|
|
|
|
|
32
|
|
306
|
4
|
50
|
|
|
|
9
|
croak "global modifiers (kh or kl) must be the last element in a dice pool" unless $args[-1]->{type} eq 'global_modifier';
|
307
|
4
|
|
|
|
|
11
|
return @args;
|
308
|
|
|
|
|
|
|
}
|
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub _identify_type{
|
311
|
122
|
|
|
122
|
|
29892
|
my $exp = shift;
|
312
|
122
|
100
|
|
|
|
291
|
croak "_validate_type expects one argument" unless $exp;
|
313
|
|
|
|
|
|
|
|
314
|
121
|
50
|
|
|
|
247
|
print "\nEvaluating [$exp]\n" if $debug;
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# we got a dice expression, complex at will
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# dice_exp 1d6
|
319
|
|
|
|
|
|
|
# res_mod kh kl dh dl
|
320
|
|
|
|
|
|
|
# res_mod_val \d+
|
321
|
|
|
|
|
|
|
# die_mod r x cs avg
|
322
|
|
|
|
|
|
|
# comp_mod gt lt (null stands for eq)
|
323
|
|
|
|
|
|
|
# die_mod_val \d+
|
324
|
|
|
|
|
|
|
# res_sum +3 -13
|
325
|
|
|
|
|
|
|
|
326
|
121
|
100
|
|
|
|
917
|
if( $exp =~ /
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
327
|
|
|
|
|
|
|
^
|
328
|
|
|
|
|
|
|
(?\d+d\d+) # a mandatory dice expression as start 1d6
|
329
|
|
|
|
|
|
|
( # an optional res_mod group
|
330
|
|
|
|
|
|
|
(?(?:kh|kl|dh|dl)) # with a res_mod kh|kl|dh|dl
|
331
|
|
|
|
|
|
|
(?\d+) # and with a mod_val 3
|
332
|
|
|
|
|
|
|
)?
|
333
|
|
|
|
|
|
|
( # an optional die_mod
|
334
|
|
|
|
|
|
|
(?(?:r|x|cs|avg)) # with a die_mod r|x|cs|avg
|
335
|
|
|
|
|
|
|
(?(?:gt|lt))? # an optional comp_mod gt|lt
|
336
|
|
|
|
|
|
|
(?\d{0,}) # and an optional die_mod_val 3
|
337
|
|
|
|
|
|
|
)?
|
338
|
|
|
|
|
|
|
( # an optional res_sum
|
339
|
|
|
|
|
|
|
(?[+-]{1}\d+) # with a res_mod +|-3
|
340
|
|
|
|
|
|
|
)?
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
/x
|
343
|
|
|
|
|
|
|
){
|
344
|
106
|
50
|
|
|
|
256
|
if ( $debug ){
|
345
|
0
|
|
|
|
|
0
|
print "\toriginal : [$exp]\n";
|
346
|
0
|
|
|
|
|
0
|
print "\ttype : [dice_expression]\n";
|
347
|
10
|
|
|
10
|
|
5224
|
print "\tdice expression : [$+{dice_exp}]\n";
|
|
10
|
|
|
|
|
3889
|
|
|
10
|
|
|
|
|
5219
|
|
|
0
|
|
|
|
|
0
|
|
348
|
0
|
0
|
|
|
|
0
|
print "\tresult modifier : [$+{res_mod}]\n" if $+{res_mod};
|
349
|
0
|
0
|
|
|
|
0
|
print "\tresult val modifier: [$+{res_mod_val}]\n" if $+{res_mod_val};
|
350
|
0
|
0
|
|
|
|
0
|
print "\tdie modifier : [$+{die_mod}]\n" if $+{die_mod};
|
351
|
0
|
0
|
|
|
|
0
|
print "\tdie comp modifier : [$+{comp_mod}]\n" if $+{comp_mod};
|
352
|
0
|
0
|
|
|
|
0
|
print "\tdie val modifier : [$+{die_mod_val}]\n" if $+{die_mod_val};
|
353
|
0
|
0
|
|
|
|
0
|
print "\tresult sum : [$+{res_sum}]\n" if $+{res_sum};
|
354
|
|
|
|
|
|
|
}
|
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# save the hashref output ( $+{KEY} cannot be reused inside a later s/// )
|
357
|
|
|
|
|
|
|
my $result = {
|
358
|
|
|
|
|
|
|
type => 'dice_expression',
|
359
|
|
|
|
|
|
|
original => $exp,
|
360
|
|
|
|
|
|
|
dice_exp => $+{dice_exp},
|
361
|
|
|
|
|
|
|
res_mod => $+{res_mod},
|
362
|
|
|
|
|
|
|
res_mod_val => $+{res_mod_val},
|
363
|
|
|
|
|
|
|
die_mod => $+{die_mod},
|
364
|
|
|
|
|
|
|
comp_mod => $+{comp_mod},
|
365
|
|
|
|
|
|
|
die_mod_val => $+{die_mod_val},
|
366
|
|
|
|
|
|
|
res_sum => $+{res_sum},
|
367
|
106
|
|
|
|
|
1831
|
};
|
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# remove everything matched from original expression..
|
370
|
106
|
|
|
|
|
418
|
my $tobenull = $exp;
|
371
|
106
|
50
|
|
|
|
215
|
print "Cleaning the expression to spot garbage:\n" if $debug;
|
372
|
|
|
|
|
|
|
# 'type' key unuseful, dice_exp must be the first to be removed or a lone number can modify it
|
373
|
106
|
|
|
|
|
195
|
foreach my $key ( qw( dice_exp res_mod res_mod_val die_mod comp_mod die_mod_val res_sum) ){
|
374
|
742
|
50
|
66
|
|
|
2016
|
print "\tremoving: $result->{$key}\n" if defined $result->{$key} and $debug;
|
375
|
742
|
100
|
|
|
|
3773
|
$tobenull =~ s/\Q$result->{$key}\E// if defined $result->{$key};
|
376
|
|
|
|
|
|
|
}
|
377
|
106
|
50
|
|
|
|
219
|
print "Left in the expression: [$tobenull]\n" if $debug;
|
378
|
|
|
|
|
|
|
# ..to spot unwanted remaining crumbles
|
379
|
106
|
100
|
|
|
|
290
|
croak "unexpected string [$tobenull] in expression [$exp]" if length $tobenull;
|
380
|
|
|
|
|
|
|
|
381
|
98
|
|
|
|
|
246
|
_validate_expr( $result );
|
382
|
89
|
|
|
|
|
275
|
return $result;
|
383
|
|
|
|
|
|
|
}
|
384
|
|
|
|
|
|
|
# we got a bare number (can be used in dice pool)
|
385
|
|
|
|
|
|
|
elsif ( $exp =~ /^\d+$/ ){
|
386
|
8
|
50
|
|
|
|
17
|
print "received a bare number [$exp] used in dice pools\n" if $debug;
|
387
|
8
|
|
|
|
|
37
|
return { type => 'number', result => $exp };
|
388
|
|
|
|
|
|
|
}
|
389
|
|
|
|
|
|
|
# we got a global dice pool modifier
|
390
|
|
|
|
|
|
|
elsif( $exp =~ /^kh|kl$/){
|
391
|
4
|
50
|
|
|
|
8
|
print "received a global dice modifier [$exp] used in dice pools\n" if $debug;
|
392
|
4
|
|
|
|
|
15
|
return { type => 'global_modifier', value => $exp };
|
393
|
|
|
|
|
|
|
}
|
394
|
|
|
|
|
|
|
else{
|
395
|
3
|
|
|
|
|
29
|
croak "unrecognized expression [$exp]";
|
396
|
|
|
|
|
|
|
}
|
397
|
|
|
|
|
|
|
}
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
my $wanted=<<'EOT';
|
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#########################
|
402
|
|
|
|
|
|
|
# multiple dice expression:
|
403
|
|
|
|
|
|
|
#########################
|
404
|
|
|
|
|
|
|
Dice pools; {2d8, 1d6} (qw( 2d8 1d6 )) -> sum
|
405
|
|
|
|
|
|
|
Dice pools with modifiers; {1d20+7, 10}kh1 (qw( 2d8+7 1d20 ))
|
406
|
|
|
|
|
|
|
(qw( 2d8+7 33 ))
|
407
|
|
|
|
|
|
|
(qw( 2d8 1d6 kh ))
|
408
|
|
|
|
|
|
|
(qw( 2d8 1d6 kl ))
|
409
|
|
|
|
|
|
|
(qw( 2d8 33 kh ))
|
410
|
|
|
|
|
|
|
(qw( 2d8 33 kl ))
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Rounding; floor(1.5), ceil(1.5), round(1.5)
|
414
|
|
|
|
|
|
|
Average; avg(8d6)
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
EOT
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=head1 NAME
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
Games::Dice::Roller - a full featured dice roller system
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head1 VERSION
|
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Version 0.01
|
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=cut
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
use Games::Dice::Roller;
|
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
my $dice = Games::Dice::Roller->new();
|
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# simple dice expressions
|
438
|
|
|
|
|
|
|
my @simple = (qw( 3d6 4d8+4 1d100-5 ));
|
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# average results
|
441
|
|
|
|
|
|
|
my @average = (qw(4d4avg 4d8avg+2 4d12avg-7));
|
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# reroll if equal (default), lesser than or greater than N
|
444
|
|
|
|
|
|
|
my @reroll = (qw(6d4r1 5d6rlt3 5d6rgt4 6d4r1+10 6d4r1-5));
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# explode if equal (default), lesser than or greater than N
|
447
|
|
|
|
|
|
|
my @explode = (qw( 3d6x6 3d6xlt3 3d6xgt4 3d6x6+3 3d6x6-4 ));
|
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# just count succesful rolls
|
450
|
|
|
|
|
|
|
my @succes = (qw( 3d6cs1 3d6cslt3 3d6csgt4 ));
|
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# keep and drop dice from final results
|
453
|
|
|
|
|
|
|
my @keep_and_drop = (qw( 4d6kh3 4d6kh2 4d6kl2+3 4d6kl2-3 4d12dh1 4d12dl3 4d12dl3+3 4d12dl1-4 ));
|
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
foreach my $dice_expression ( @simple , @average, @reroll, @explode, @succes, @keep_and_drop ){
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
my ($res, $descr) = $dice->roll( $dice_expression );
|
459
|
|
|
|
|
|
|
print "$res [$dice_expression] $descr\n";
|
460
|
|
|
|
|
|
|
}
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# 10 [3d6] 5 2 3
|
465
|
|
|
|
|
|
|
# 22 [4d8+4] 7 7 1 3 +4
|
466
|
|
|
|
|
|
|
# 14 [1d100-5] 19 -5
|
467
|
|
|
|
|
|
|
# 10 [4d4avg] 2.5 2.5 2.5 2.5
|
468
|
|
|
|
|
|
|
# 20 [4d8avg+2] 4.5 4.5 4.5 4.5 +2
|
469
|
|
|
|
|
|
|
# 19 [4d12avg-7] 6.5 6.5 6.5 6.5 -7
|
470
|
|
|
|
|
|
|
# 18 [6d4r1] 4 (1r) 3 2 3 (1r) 2 4
|
471
|
|
|
|
|
|
|
# 19 [5d6rlt3] 3 4 3 4 (2r) (2r) (2r) 5
|
472
|
|
|
|
|
|
|
# 11 [5d6rgt4] 2 4 1 (5r) 2 (5r) 2
|
473
|
|
|
|
|
|
|
# 25 [6d4r1+10] (1r) 2 (1r) 2 2 4 3 2 +10
|
474
|
|
|
|
|
|
|
# 13 [6d4r1-5] (1r) (1r) 2 (1r) 2 4 4 (1r) 4 2 -5
|
475
|
|
|
|
|
|
|
# 7 [3d6x6] 1 1 5
|
476
|
|
|
|
|
|
|
# 17 [3d6xlt3] 6 5 1x 1x 1x 3
|
477
|
|
|
|
|
|
|
# 11 [3d6xgt4] 4 3 4
|
478
|
|
|
|
|
|
|
# 11 [3d6x6+3] 2 2 4 +3
|
479
|
|
|
|
|
|
|
# 8 [3d6x6-4] 5 3 4 -4
|
480
|
|
|
|
|
|
|
# 1 [3d6cs1] (5) (3) 1
|
481
|
|
|
|
|
|
|
# 1 [3d6cslt3] 2 (6) (6)
|
482
|
|
|
|
|
|
|
# 2 [3d6csgt4] 6 (3) 5
|
483
|
|
|
|
|
|
|
# 14 [4d6kh3] (2) 6 4 4
|
484
|
|
|
|
|
|
|
# 9 [4d6kh2] 3 6 (2) (2)
|
485
|
|
|
|
|
|
|
# 8 [4d6kl2+3] (6) 4 1 (6) +3
|
486
|
|
|
|
|
|
|
# 1 [4d6kl2-3] (5) 1 3 (3) -3
|
487
|
|
|
|
|
|
|
# 13 [4d12dh1] 2 6 5 (6)
|
488
|
|
|
|
|
|
|
# 12 [4d12dl3] (9) (10) (9) 12
|
489
|
|
|
|
|
|
|
# 9 [4d12dl3+3] (1) 6 (3) (6) +3
|
490
|
|
|
|
|
|
|
# 26 [4d12dl1-4] 9 (1) 9 12 -4
|
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=head1 METHODS
|
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=head2 new
|
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
The constructor accept only one option, an anonymous hash and the only valid key is C holding as value an anonymous sub to be invoked instead of the core function L
|
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head2 roll
|
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
This method expects a single string to be passed as argument. This string can be a C or a C (see below).
|
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
It returns the final result and a string representing the roll.
|
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
my $result = $dice->roll('3d6+3');
|
510
|
|
|
|
|
|
|
print "result of the dice roll was: $result";
|
511
|
|
|
|
|
|
|
# result of the dice roll was: 16
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
my ($res, $descr) = $dice->roll('3d6+3');
|
514
|
|
|
|
|
|
|
print "$descr\nResult: $res";
|
515
|
|
|
|
|
|
|
# 5 2 6 +3
|
516
|
|
|
|
|
|
|
# Result: 16
|
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
In the descriptive string some die result can be modified by modifiers: dropped ones will be inside parens, rerolled dice result will be inside parens and with a C following them and exploded dice results will be marked by a C
|
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
A third element is returned too: a hash reference intended to be used mainly internally and for debug purposes, with the internal carateristics of the dice expression. Dont rely on this because it can be changed or removed in future releases.
|
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head3 die mofiers
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head4 avg - average
|
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
No dice are rolled, but the die average will be used instead. For C<1d6> the average will be C<3.5> so C<4d6avg> will always result in C<14>
|
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=head4 r - reroll
|
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
Reroll dice equal, lesser than (C) or greater than (C) C as in C<3d6r1 3d6rlt3 3d6rgt4>
|
531
|
|
|
|
|
|
|
Each die rerolled will be not part of the final result and in the descriptive string will be inside parens and followed by C as in C<(1r)>
|
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=head4 x - explode
|
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
Each die roll equal, lesser than (C) or greater than (C) C (as in C<3d6x6 3d6xlt3 3d6xgt4>) will add another die of the same type.
|
536
|
|
|
|
|
|
|
An exploded die will be added to final result and will be marked with C as in C<6x> in the descriptive string.
|
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
For example C<8d6xgt4> can lead to a result of C<42> and a description like: C<6x 4 6x 4 5x 3 5x 3 2 2 1 1>
|
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head4 cs - count successes
|
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
If a die roll is equal, lesser than (C) or greater than (C) C (as in C<3d6cs1 3d6cslt3 3d6gt4>) then it will count as a success.
|
544
|
|
|
|
|
|
|
The final result will be the succes count.
|
545
|
|
|
|
|
|
|
In the decription string unsuccesfull rolls will be inside parens.
|
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head3 result modifiers
|
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=head4 keep and drop
|
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
With the result modifiers C you can choose how many dice will be used to compute the final result, keeping or dropping highest or lowest C dice.
|
555
|
|
|
|
|
|
|
For example C<4d6kh3> will roll C<4d6> but only best three ones will be used.
|
556
|
|
|
|
|
|
|
The descriptive string in this case will be always ordered in ascending or descending order, without representing the real occurence of numbers.
|
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=head4 result sum
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
An optional sum C can be added to the final result as positive or negative modifier. This must be the last element of the dice expression like in: C<3d8+4>
|
562
|
|
|
|
|
|
|
This option cannot be used with C
|
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head3 dice pools
|
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
If to the C method is passed a string containing different things (separated by spaces) this string will be treated as a C
|
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
A C must contain at least two elements. It can contains one or more C (explained above), no or one and only one C and no, one and only one C ( C for keep highest or C for keep lowest).
|
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
All results of C are computed and compared each other and with an eventual C and the result of the C will be the highest (if no C then C will be the default) or lowest one (if C is specified) roll among them.
|
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
For example: C<$dice-Eroll('4d4+6 3d6+2 2d8+1 12')> can lead to the following results (default C is C):
|
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# Result: 14
|
580
|
|
|
|
|
|
|
# Description: 4d4+6 = 1 2 1 4 +6, ( 3d6+2 = 4 3 4 +2 = 13 ), ( 12 ), ( 2d8+1 = 1 8 +1 = 10 )
|
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
As you can see descriptions of discarded C or eventual C (C<12> in the example) lower than the higher result are represented inside parens.
|
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=head2 about rand
|
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
Some ancient perl on some unfortunate OS has proven to have problem with the core C even if nowadays this is very rare to appear. In this case you can provide your own C function during the constructor, for example using L as in the following example:
|
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
my $gen = Math::Random::MT->new();
|
590
|
|
|
|
|
|
|
my $mt_dicer = Games::Dice::Roller->new(
|
591
|
|
|
|
|
|
|
sub_rand => sub{
|
592
|
|
|
|
|
|
|
my $sides = shift;
|
593
|
|
|
|
|
|
|
return $gen->rand( $sides );
|
594
|
|
|
|
|
|
|
},
|
595
|
|
|
|
|
|
|
);
|
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
See the thread at L where this argument was discussed.
|
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head1 DEBUG
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
This module can produce debug informations if C environment variable is set to C<1>
|
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Under debug rolling a dice expression will produce something like:
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
Evaluating [12d6kh4+3]
|
606
|
|
|
|
|
|
|
original : [12d6kh4+3]
|
607
|
|
|
|
|
|
|
type : [dice_expression]
|
608
|
|
|
|
|
|
|
dice expression : [12d6]
|
609
|
|
|
|
|
|
|
result modifier : [kh]
|
610
|
|
|
|
|
|
|
result val modifier: [4]
|
611
|
|
|
|
|
|
|
result sum : [+3]
|
612
|
|
|
|
|
|
|
Cleaning the expression to spot garbage:
|
613
|
|
|
|
|
|
|
removing: 12d6
|
614
|
|
|
|
|
|
|
removing: kh
|
615
|
|
|
|
|
|
|
removing: 4
|
616
|
|
|
|
|
|
|
removing: +3
|
617
|
|
|
|
|
|
|
Left in the expression: []
|
618
|
|
|
|
|
|
|
Description: 6 6 5 5 (4) (4) (3) (3) (2) (2) (1) (1) +3
|
619
|
|
|
|
|
|
|
Result : 25
|
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head1 AUTHOR
|
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
LorenzoTa, C<< >>
|
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=head1 BUGS
|
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through
|
630
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll
|
631
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes.
|
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head1 SUPPORT
|
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
The main support site for the present module is L where I can be found as Discipulus
|
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command.
|
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
perldoc Games::Dice::Roller
|
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
You can also look for information at:
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=over 4
|
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here)
|
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
L
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation
|
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
L
|
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=item * CPAN Ratings
|
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
L
|
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=item * Search CPAN
|
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
L
|
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=back
|
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS
|
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Copyright 2021 LorenzoTa.
|
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
675
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a
|
676
|
|
|
|
|
|
|
copy of the full license at:
|
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
L
|
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified
|
681
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or
|
682
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify,
|
683
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license.
|
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made
|
686
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that
|
687
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license.
|
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service
|
690
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder.
|
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge
|
693
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and
|
694
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims
|
695
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the
|
696
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or
|
697
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes
|
698
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License
|
699
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed.
|
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
|
702
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
|
703
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
704
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
|
705
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
|
706
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
|
707
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
|
708
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=cut
|
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
1; # End of Module |