File Coverage

blib/lib/Games/RolePlay/MapGen/Tools.pm
Criterion Covered Total %
statement 231 245 94.2
branch 55 82 67.0
condition 4 5 80.0
subroutine 37 38 97.3
pod 0 6 0.0
total 327 376 86.9


line stmt bran cond sub pod time code
1             # vi:tw=0 syntax=perl:
2              
3             # package ::_interconnected_map {{{
4             package Games::RolePlay::MapGen::_disallow_autoviv;
5              
6 17     17   30135 use common::sense;
  17         61  
  17         117  
7 17     17   29616 use Tie::Array;
  17         26801  
  17         1181  
8 17     17   2308 use parent -norequire => 'Tie::StdArray';
  17         2019  
  17         193  
9 17     17   835 use Carp;
  17         48  
  17         4334  
10              
11             1;
12              
13             sub TIEARRAY {
14 500     500   656 my $class = shift;
15 500         1426 my $this = bless [], $class;
16 500         621 my $that = shift;
17              
18 500         2466 @$this = @$that;
19              
20 500         4431 $this;
21             }
22              
23             sub FETCH {
24 440144     440144   2346326 my $this = shift;
25            
26 440144 50       1104603 croak "autovivifing new rows and columns is disabled ($_[0]>$#$this)" if $_[0] > $#$this;
27              
28 440144         1379041 $this->SUPER::FETCH(@_);
29             }
30              
31             package Games::RolePlay::MapGen::_interconnected_map;
32              
33 17     17   257 use common::sense;
  17         50  
  17         135  
34 17     17   742 use Carp;
  17         47  
  17         20457  
35              
36             1;
37              
38             # interconnect_map {{{
39             sub interconnect_map {
40 20     20   63 my $map = shift;
41              
42             # This interconnected array stuff is _REALLY_ handy, but it needs to be cleaned up, so it gets it's own class
43              
44 20         158 for($map, @$map) {
45 500         651 my @a;
46 500         1815 tie @a, "Games::RolePlay::MapGen::_disallow_autoviv", $_;
47 500         1175 $_ = \@a;
48             }
49              
50 20         177 for my $i (0 .. $#$map) {
51 480         4034 my $jend = $#{ $map->[$i] };
  480         1253  
52              
53 480         3494 for my $j (0 .. $jend) {
54 11850         88115 $map->[$i][$j]->{nb} = {}; # clear it all out
55 11850 100       86534 $map->[$i][$j]->{nb}{s} = $map->[$i+1][$j] unless $i == $#$map;
56 11850 100       110186 $map->[$i][$j]->{nb}{n} = $map->[$i-1][$j] unless $i == 0;
57 11850 100       94794 $map->[$i][$j]->{nb}{e} = $map->[$i][$j+1] unless $j == $jend;
58 11850 100       92914 $map->[$i][$j]->{nb}{w} = $map->[$i][$j-1] unless $j == 0;
59             }
60             }
61              
62             # check
63 20         224 for my $y (0 .. $#$map) {
64 480         913 for my $x (0 .. $#{ $map->[$y] }) {
  480         1408  
65 11850         21902 for my $d (qw(n e s w)) {
66 47400         154629 my $o = {n=>"s", s=>"n", e=>"w", w=>"e"}->{$d};
67              
68 47400         160629 my $t = $map->[$y][$x];
69 47400         250299 my $n = $t->{nb}{$d};
70              
71 47400 100       97632 next unless $n;
72              
73 45480 50       157826 warn "od issues with ($x, $y):$d-$o" unless $t->{od}{$d} == $n->{od}{$o};
74 45480 50       175188 warn "nb issues with ($x, $y):$d-$o" unless $n->{nb}{$o} == $t;
75             }
76             }
77             }
78             }
79             # }}}
80             # disconnect_map {{{
81             sub disconnect_map {
82 16     16   39 my $map = shift;
83              
84 16         38 local $@;
85              
86 16         40 eval {
87 16         53 untie @$_ for grep {tied $_} @$map;
  400         496  
88 16 50       66 untie @$map if tied $map;
89              
90 16         67 for my $i (0 .. $#$map) {
91 400         2749 my $jend = $#{ $map->[$i] };
  400         1347  
92              
93 400         1818 for my $j (0 .. $jend) {
94             # Destroying the map wouldn't destroy the tiles if they're self
95             # referencing like this. That's not a problem because of the
96             # global destructor, *whew*; except that each new map generated,
97             # until perl exits, would eat up more memory.
98              
99 10150         81020 delete $map->[$i][$j]{nb}; # So we have to break the self-refs here.
100             }
101             }
102             };
103              
104 16 50       338 if( $@ ) {
105             # NOTE: The above emits a fatal under global destruction for some
106             # reason, probably the bless+tie gets cleaned up in the wrong order or
107             # something. It doesn't really matter since we're already exiting perl
108             # anyway. This assumption may be false under win32, where it may
109             # create a memory leak. Does windows clean up when a process exits?
110             # It really aught to, but I have my doubts.
111              
112 0 0       0 die $@ unless $@ =~ m/global destruction/;
113             }
114              
115             # You can test to make sure the tiles are dying when a map goes out of
116             # scope by setting the VERBOSE_TILE_DEATH environment variable to a true
117             # value. If they fail to die when they go out of scope, it would say so on
118             # the warning line. If you'd really really like to see that, change the
119             # {nb} above to {nb_borked} and you'll see what I mean.
120              
121             # Lastly, if you'd like to read a lengthy dissertation on this subject,
122             # search for "Two-Phased" in the perlobj man page.
123             }
124             # }}}
125             # new {{{
126             sub new {
127 8     8   20 my $class = shift;
128 8         22 my $arg = shift;
129 8         78 my $map = bless $arg, $class;
130              
131 8         110 $map->interconnect_map; # also used by save_map()
132              
133 8         653 return $map;
134             }
135             # }}}
136             # DESTROY {{{
137             sub DESTROY {
138 11     11   2721 my $map = shift;
139              
140 11         64 $map->disconnect_map; # also used by save_map()
141             }
142             # }}}
143              
144             # }}}
145             # package ::_group; {{{
146             package Games::RolePlay::MapGen::_group;
147              
148 17     17   124 use common::sense;
  17         39  
  17         89  
149              
150             1;
151              
152             # new {{{
153             sub new {
154 51     51   126 my $class = shift;
155 51         1834 my $this = bless {name=>"?", type=>"?", loc=>[], size=>[], loc_size=>"n/a"}, $class;
156              
157 51 50       211 if( @_ ) {
158 0         0 my $h = {@_};
159 0 0       0 $this->{name} = $h->{name} if exists $h->{name};
160 0 0       0 $this->{type} = $h->{type} if exists $h->{type};
161             }
162              
163             $this
164 51         201 }
165             # }}}
166             # name {{{
167             sub name {
168 30     30   66 my $this = shift;
169 30 50       183 $this->{name} = $_[0] if @_;
170              
171 30         103 $this->{name};
172             }
173             # }}}
174             # type {{{
175             sub type {
176 50     50   113 my $this = shift;
177 50 50       284 $this->{type} = $_[0] if @_;
178              
179 50         156 $this->{type};
180             }
181             # }}}
182             # desc {{{
183             sub desc {
184 0     0   0 my $this = shift;
185              
186 0         0 $this->{loc_size};
187             }
188             # }}}
189             # add_rectangle {{{
190             sub add_rectangle {
191 61     61   120 my $this = shift;
192 61         125 my $loc = shift;
193 61         104 my $size = shift;
194 61         115 my $mapo = shift;
195              
196 61 100 66     399 if( $loc and $size ) {
197 50         92 push @{$this->{loc}}, $loc;
  50         166  
198 50         99 push @{$this->{size}}, $size;
  50         243  
199             }
200              
201 61         226 my @i = map { $_->[0] }
  0         0  
202 61         1512 sort { $b->[1]<=>$a->[2] }
203 61         359 map { my $t = $this->{size}[$_]; [$_, $t->[0]*$t->[1]] }
  61         492  
204 61         159 0 .. $#{$this->{loc}};
205              
206 61         253 my @to_kill; # remove these, they don't say anything
207             my %points; # don't count the same tiles over and over
208 61         628 my $sloc = [0,0];
209 61         122 my $mloc = [@{$this->{loc}[0]}];
  61         309  
210 61         110 my $Mloc = [@{$this->{loc}[0]}];
  61         299  
211 61         121 my $nloc = 0;
212 61         145 for my $i (@i) {
213 61         140 my $l = $this->{loc}[$i];
214 61         136 my $s = $this->{size}[$i];
215              
216 61         127 my $x = $l->[0];
217 61         278 my $y = $l->[1];
218              
219 61         108 my $i_count = 0;
220              
221 61         276 for my $xi (0 .. $s->[0]-1) {
222 277         564 for my $yi (0 .. $s->[1]-1) {
223 1648         2003 my $xc = $x + $xi;
224 1648         1921 my $yc = $y + $yi;
225              
226 1648 50       4646 unless( $points{$xc}{$yc} ) {
227 1648         3070 $points{$xc}{$yc} = 1;
228 1648         1700 $i_count ++;
229              
230 1648         2007 $sloc->[0] += $xc;
231 1648         1891 $sloc->[1] += $yc;
232 1648         2072 $nloc ++;
233              
234 1648 50       3247 $mloc->[0] = $xc if $xc < $mloc->[0];
235 1648 50       3281 $mloc->[1] = $yc if $yc < $mloc->[1];
236 1648 100       3361 $Mloc->[0] = $xc if $xc > $Mloc->[0];
237 1648 100       3199 $Mloc->[1] = $yc if $yc > $Mloc->[1];
238              
239 1648 50       4382 $mapo->[ $yc ][ $xc ]{group} = $this if $mapo;
240             }
241              
242             }}
243              
244 61 50       274 push @to_kill, $i unless $i_count>0;
245             }
246              
247 61         214 for my $kill (sort {$b<=>$a} @to_kill) {
  0         0  
248 0         0 splice @{$this->{loc}}, $kill, 0;
  0         0  
249 0         0 splice @{$this->{size}}, $kill, 0;
  0         0  
250             }
251              
252 61         169 my $cloc = [0,0];
253 61 50       695 $cloc = [ int($sloc->[0]/$nloc), int($sloc->[1]/$nloc) ] if $nloc > 0;
254              
255 61         259 my $extent = [ $Mloc->[0]-$mloc->[0]+1, $Mloc->[1]-$mloc->[1]+1 ];
256              
257 61         431 $this->{loc_size} = "($cloc->[0], $cloc->[1]) $extent->[0]x$extent->[1]";
258 61         829 $this->{extents} = [ @$mloc, @$Mloc ];
259             }
260             # }}}
261             # enumerate_tiles {{{
262             sub enumerate_tiles {
263 224     224   332 my $this = shift;
264              
265 224         1983 my @i = map { $_->[0] }
  0         0  
266 224         441 sort { $b->[1]<=>$a->[2] }
267 224         805 map { my $t = $this->{size}[$_]; [$_, $t->[0]*$t->[1]] }
  224         535  
268 224         330 0 .. $#{$this->{loc}};
269              
270 224         406 my @ret;
271             my %points; # don't count the same tiles over and over
272 224         436 for my $i (@i) {
273 224         412 my $l = $this->{loc}[$i];
274 224         520 my $s = $this->{size}[$i];
275              
276 224         313 my $x = $l->[0];
277 224         281 my $y = $l->[1];
278              
279 224         1567 for my $xi (0 .. $s->[0]-1) {
280 619         1413 for my $yi (0 .. $s->[1]-1) {
281 1700         2262 my $xc = $x + $xi;
282 1700         3098 my $yc = $y + $yi;
283              
284 1700 50       5020 unless( $points{$xc}{$yc} ) {
285 1700         2948 $points{$xc}{$yc} = 1;
286              
287 1700         6009 push @ret, [$xc,$yc];
288             }
289              
290             }}
291             }
292              
293 224         1896 @ret;
294             }
295             # }}}
296             # enumerate_extents {{{
297             sub enumerate_extents {
298 127     127   204 my $this = shift;
299              
300 127         176 @{ $this->{extents} };
  127         601  
301             }
302             # }}}
303              
304             # }}}
305             # package ::_tile; {{{
306             package Games::RolePlay::MapGen::_tile;
307              
308 17     17   36043 use common::sense;
  17         47  
  17         103  
309              
310             1;
311              
312             sub dup {
313 1350     1350   1865 my $that = shift;
314 1350         2453 my $class = $that->{__c};
315 1350         6996 my $this = bless {od=>{n=>1, s=>1, e=>1, w=>1}}, $class;
316              
317 1350         5275 $this->{$_} = $that->{$_} for grep {not ref $that->{$_}} keys %$that;
  11192         30302  
318 1350         2497 $this->{od}{$_} = $that->{od}{$_} for keys %{ $that->{od} };
  1350         8800  
319 1350         3633 $this->{group} = $that->{group};
320 1350         2185 $this->{_dup} = 1;
321              
322 1350         3749 return $this;
323             }
324              
325 3801     3801   4632 sub new { my $class = shift; bless { @_, __c=>$class, v=>0, od=>{n=>0, s=>0, e=>0, w=>0} }, $class }
  3801         36716  
326 7026 50   7026   36953 sub DESTROY { warn "tile verbosely dying" if $ENV{VERBOSE_TILE_DEATH} } # search for VERBOSE above...
327             # }}}
328             # package ::_door; {{{
329             package Games::RolePlay::MapGen::_door;
330              
331 17     17   6497 use common::sense;
  17         38  
  17         88  
332              
333             1;
334              
335             sub new {
336 53     53   83 my $class = shift;
337 53         394 my $this = bless { @_ }, $class;
338              
339 53 100       224 $this->{locked} = 0 unless $this->{locked};
340 53 100       150 $this->{stuck} = 0 unless $this->{stuck};
341 53 100       158 $this->{secret} = 0 unless $this->{secret};
342 53 50       170 $this->{open_dir} = { major=>undef, minor=>undef } unless ref($this->{open_dir});
343 53 50       167 $this->{'open'} = 0 unless $this->{'open'};
344              
345 53         421 return $this;
346             }
347              
348             # }}}
349              
350             package Games::RolePlay::MapGen::Tools;
351              
352 17     17   3822 use common::sense;
  17         46  
  17         91  
353 17     17   637 use Carp;
  17         37  
  17         1561  
354 17     17   97 use parent q(Exporter);
  17         53  
  17         108  
355              
356             our @EXPORT_OK = qw(choice roll random irange range str_eval _group _tile _door);
357              
358             1;
359              
360             # helper functions
361             # choice {{{
362             sub choice {
363 21212   100 21212 0 53896 return $_[&random(int @_)] || "";
364             }
365             # }}}
366             # roll {{{
367             sub roll {
368 13541     13541 0 23290 my ($num, $sides) = @_;
369 13541         35819 my $roll = 0;
370            
371 13541         72859 $roll += int rand $sides for 1 .. $num;
372 13541         20980 $roll += $num;
373              
374 13541         76321 return $roll;
375             }
376             # }}}
377             # random {{{
378             sub random {
379 21229     21229 0 175703 return int rand shift;
380             }
381             # }}}
382             # range {{{
383             sub range {
384 383027     383027 0 1096333 my $lhs = shift;
385 383027         443800 my $rhs = shift;
386 383027         445749 my $correlation = shift;
387              
388 383027 50       842721 ($lhs, $rhs) = ($rhs, $lhs) if $rhs < $lhs;
389              
390 383027         448589 my $rand;
391 383027 100       662901 if( $correlation ) {
392 400 50       714 croak "correlated range without previous value!!" unless defined $global::last_rand;
393              
394 400 100       702 if( $correlation == 1 ) {
    50          
395 200         251 $rand = $global::last_rand;
396              
397             } elsif( $correlation == -1 ) {
398 200         267 $rand = 1000000.0 - $global::last_rand;
399              
400             } else {
401 0         0 croak "unsupported correlation value";
402             }
403              
404             } else {
405 382627         442762 $rand = rand 1000000.0;
406 382627         519771 $global::last_rand = $rand;
407              
408             }
409              
410 383027         474614 $rand /= 1000000.0;
411              
412 383027         503294 my $diff = $rhs - $lhs;
413 383027         462981 $rand = $rand * $diff;
414              
415 383027         1977259 return $lhs + $rand;
416             }
417             # }}}
418             # irange {{{
419             sub irange {
420 100064     100064 0 298942 my $il = shift;
421 100064         118446 my $ir = shift;
422              
423 100064         128210 $il -= 0.4999999999;
424 100064         118143 $ir += 0.4999999999;
425              
426 100064         173521 my $s = sprintf('%0.0f', range($il, $ir, @_));
427              
428 100064 100       219205 $s = 0 if $s eq "-0";
429              
430 100064         273635 return $s;
431             }
432             # }}}
433             # str_eval {{{
434             sub str_eval {
435 51     51 0 3795 my $str = shift;
436              
437 51 100       286 return int $str if $str =~ m/^\d+$/;
438              
439 50         218 $str =~ s/^\s*(\d+)d(\d+)\s*$/&roll($1, $2)/eg;
  34         73  
440 50 100       157 $str =~ s/^\s*(\d+)d(\d+)\s*([\+\-])\s*(\d+)$/&roll($1, $2) + ($3 eq "+" ? $4 : 0-$4)/eg;
  15         75  
441              
442 50 100       163 return undef if $str =~ m/\D/;
443 49         161 return int $str;
444             }
445             # }}}
446              
447 51     51   515 sub _group { return new Games::RolePlay::MapGen::_group(@_) }
448 3801     3801   9841 sub _tile { return new Games::RolePlay::MapGen::_tile(@_) }
449 53     53   232 sub _door { return new Games::RolePlay::MapGen::_door(@_) }
450              
451             __END__