line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# vi:filetype=perl: |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Games::RolePlay::MapGen::MapQueue; |
4
|
|
|
|
|
|
|
|
5
|
6
|
|
|
6
|
|
7338
|
use common::sense; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
43
|
|
6
|
6
|
|
|
6
|
|
577
|
use Carp; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
650
|
|
7
|
6
|
|
|
6
|
|
38
|
use Exporter; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
319
|
|
8
|
6
|
|
|
6
|
|
16481
|
use Math::Trig; |
|
6
|
|
|
|
|
156575
|
|
|
6
|
|
|
|
|
1668
|
|
9
|
6
|
|
|
6
|
|
7250
|
use Math::Round; |
|
6
|
|
|
|
|
96407
|
|
|
6
|
|
|
|
|
588
|
|
10
|
6
|
|
|
6
|
|
69
|
use List::Util qw(min max); |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
761
|
|
11
|
6
|
|
|
6
|
|
36
|
use Storable qw(freeze thaw); |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
498
|
|
12
|
|
|
|
|
|
|
use constant { |
13
|
6
|
|
|
|
|
1859
|
LOS_NO => 0, |
14
|
|
|
|
|
|
|
LOS_YES => 1, |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
LOS_NO_COVER => 0, |
17
|
|
|
|
|
|
|
LOS_COVER => 1, |
18
|
|
|
|
|
|
|
LOS_DOUBLE_COVER => 2, |
19
|
6
|
|
|
6
|
|
35
|
}; |
|
6
|
|
|
|
|
10
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
22
|
|
|
|
|
|
|
our @EXPORT = qw(LOS_NO LOS_YES LOS_NO_COVER LOS_IGNORABLE_COVER LOS_COVER LOS_DOUBLE_COVER); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our $LOS_CREATURE_RADIUS = 0.19; # used for double-cover check |
25
|
|
|
|
|
|
|
our $LOS_LHS_BONUS = 0.05_777; # slight advantage for being closer to obstruction |
26
|
|
|
|
|
|
|
our $EXTRUDE_POINTS = 4; |
27
|
|
|
|
|
|
|
our $CLOS_MIN_ANGLE = deg2rad(9); # the minimum angle between our LOS and the closure where we can still tell if there's a door on that wall |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
*_line_of_sight = *_line_of_sight_xs; |
30
|
|
|
|
|
|
|
*_tight_line_of_sight = *_tight_line_of_sight_xs; |
31
|
|
|
|
|
|
|
*_ranged_cover = *_ranged_cover_xs; |
32
|
|
|
|
|
|
|
*_melee_cover = *_melee_cover_xs; |
33
|
|
|
|
|
|
|
*_closure_line_of_sight = *_closure_line_of_sight_xs; |
34
|
|
|
|
|
|
|
|
35
|
6
|
|
|
6
|
|
15630
|
use Memoize qw(memoize flush_cache); |
|
6
|
|
|
|
|
18373
|
|
|
6
|
|
|
|
|
5723
|
|
36
|
|
|
|
|
|
|
memoize( _line_of_sight => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
37
|
|
|
|
|
|
|
memoize( _tight_line_of_sight => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
38
|
|
|
|
|
|
|
memoize( _ranged_cover => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
39
|
|
|
|
|
|
|
memoize( _melee_cover => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
40
|
|
|
|
|
|
|
memoize( _ignorable_cover => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
41
|
|
|
|
|
|
|
memoize( _locations_in_line_of_sight => NORMALIZER => sub { "$_[0] @{$_[1]}" } ); |
42
|
|
|
|
|
|
|
memoize( _locations_in_range_and_line_of_sight => NORMALIZER => sub { "$_[0] @{$_[1]} $_[2]" } ); |
43
|
|
|
|
|
|
|
memoize( _locations_in_path => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
44
|
|
|
|
|
|
|
memoize( _closure_line_of_sight => NORMALIZER => sub { "$_[0] @{$_[1]} @{$_[2]}" } ); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
our @toflush = qw( _line_of_sight _tight_line_of_sight _ranged_cover _melee_cover _ignorable_cover |
47
|
|
|
|
|
|
|
_locations_in_line_of_sight _locations_in_range_and_line_of_sight |
48
|
|
|
|
|
|
|
_locations_in_path _closure_line_of_sight ); |
49
|
|
|
|
|
|
|
|
50
|
6
|
|
|
6
|
|
65
|
use Games::RolePlay::MapGen; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
144498
|
|
51
|
|
|
|
|
|
|
require XSLoader; XSLoader::load('Games::RolePlay::MapGen', $Games::RolePlay::MapGen::VERSION); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# new {{{ |
54
|
|
|
|
|
|
|
sub new { |
55
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
56
|
0
|
|
|
|
|
|
my $the_m = shift; |
57
|
0
|
|
|
|
|
|
my $this = bless { o=>{}, c=>[] }, $class; |
58
|
|
|
|
|
|
|
|
59
|
0
|
0
|
|
|
|
|
croak "where is _the_map?" unless ref $the_m; |
60
|
0
|
|
|
|
|
|
$the_m = $the_m->{_the_map}; |
61
|
0
|
|
|
|
|
|
$this->{_the_map} = $the_m; |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
|
|
|
$this->{ym} = $#{ $the_m }; |
|
0
|
|
|
|
|
|
|
64
|
0
|
|
|
|
|
|
$this->{xm} = $#{ $the_m->[0] }; |
|
0
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
return $this; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
# }}} |
69
|
|
|
|
|
|
|
# retag {{{ |
70
|
|
|
|
|
|
|
sub retag { |
71
|
0
|
|
|
0
|
0
|
|
my $this = shift; |
72
|
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
|
my $tags = {}; |
74
|
0
|
|
|
|
|
|
for my $row ( 0 .. $this->{ym} ) { |
75
|
0
|
|
|
|
|
|
for my $col ( 0 .. $this->{xm} ) { |
76
|
0
|
|
|
|
|
|
my $rhs = [ $col, $row ]; |
77
|
|
|
|
|
|
|
|
78
|
0
|
0
|
|
|
|
|
for my $o (@{ $this->{c}[ $rhs->[1] ][ $rhs->[0] ] || [] }) { |
|
0
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
$tags->{"$o"} = $rhs; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
0
|
|
|
|
|
|
$this->{l} = $tags; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
# }}} |
87
|
|
|
|
|
|
|
# flush {{{ |
88
|
|
|
|
|
|
|
sub flush { |
89
|
0
|
|
|
0
|
1
|
|
flush_cache($_) for @toflush |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
# }}} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# _check_loc {{{ |
94
|
|
|
|
|
|
|
sub _check_loc { |
95
|
0
|
|
|
0
|
|
|
my $this = shift; |
96
|
0
|
|
|
|
|
|
my $loc = shift; |
97
|
|
|
|
|
|
|
|
98
|
0
|
0
|
|
|
|
|
return 0 if @$loc != 2; |
99
|
0
|
0
|
|
|
|
|
return 0 if $loc->[0] < 0; |
100
|
0
|
0
|
|
|
|
|
return 0 if $loc->[1] < 0; |
101
|
0
|
0
|
|
|
|
|
return 0 if $loc->[0] > $this->{xm}; |
102
|
0
|
0
|
|
|
|
|
return 0 if $loc->[1] > $this->{ym}; |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
my $type = $this->{_the_map}[ $loc->[1] ][ $loc->[0] ]{type}; |
105
|
0
|
0
|
|
|
|
|
return 0 unless $type; # the wall type is |
106
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
|
return $loc; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
# }}} |
110
|
|
|
|
|
|
|
# _od_segments {{{ |
111
|
|
|
|
|
|
|
sub _od_segments { |
112
|
0
|
|
|
0
|
|
|
my $this = shift; |
113
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
## DEBUG ## warn "SET\n<@$lhs> <@$rhs>\n"; |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
|
my @X = sort {$a<=>$b} ($lhs->[0], $rhs->[0]); @X = ($X[0] .. $X[1]); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
|
my @Y = sort {$a<=>$b} ($lhs->[1], $rhs->[1]); @Y = ($Y[0] .. $Y[1]); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
0
|
0
|
|
|
|
|
my $x_dir = ($lhs->[0] < $rhs->[0] ? "e" : "w"); |
121
|
0
|
0
|
|
|
|
|
my $y_dir = ($lhs->[1] < $rhs->[1] ? "s" : "n"); |
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
my @od_segments = (); # the solid line segments we might have to pass through |
124
|
0
|
|
|
|
|
|
for my $x (@X[0 .. $#X]) { |
125
|
0
|
|
|
|
|
|
for my $y (@Y[0 .. $#Y]) { |
126
|
0
|
|
|
|
|
|
my $x_od = $this->{_the_map}[ $y ][ $x ]{od}{ $x_dir }; |
127
|
0
|
|
|
|
|
|
my $y_od = $this->{_the_map}[ $y ][ $x ]{od}{ $y_dir }; |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
for( $x_od, $y_od ) { |
130
|
0
|
0
|
|
|
|
|
$_ = $_->{'open'} if ref $_; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
0
|
|
|
|
unless( $x_od or $x == ($x_dir eq "e" ? $X[$#X]:$X[0]) ) { |
|
|
0
|
|
|
|
|
|
134
|
0
|
0
|
|
|
|
|
if( $x_dir eq "e" ) { push @od_segments, [[ $x+1, $y ] => [$x+1, $y+1]] } |
|
0
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
else { push @od_segments, [[ $x, $y ] => [$x, $y+1]] } |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
0
|
0
|
0
|
|
|
|
unless( $y_od or $y == ($y_dir eq "s" ? $Y[$#Y]:$Y[0]) ) { |
|
|
0
|
|
|
|
|
|
139
|
0
|
0
|
|
|
|
|
if( $y_dir eq "s" ) { push @od_segments, [[ $x, $y+1 ] => [$x+1, $y+1]] } |
|
0
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
else { push @od_segments, [[ $x, $y ] => [$x+1, $y ]] } |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
## DEBUG ## warn "(@{$_->[0]})->(@{$_->[1]})\n" for @od_segments; |
146
|
|
|
|
|
|
|
## DEBUG ## warn "DONE\n"; |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
|
return @od_segments; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
# }}} |
151
|
|
|
|
|
|
|
# _extrude_point {{{ |
152
|
|
|
|
|
|
|
sub _extrude_point { |
153
|
|
|
|
|
|
|
# extrude a point into a tile or a sub-tile |
154
|
0
|
|
|
0
|
|
|
my $this = shift; |
155
|
0
|
|
|
|
|
|
my $point = shift; |
156
|
0
|
|
|
|
|
|
my $use_ocr = shift; # use our creature radius |
157
|
0
|
|
|
|
|
|
my $use_lhs = shift; # use our lhs bonus |
158
|
|
|
|
|
|
|
|
159
|
0
|
0
|
0
|
|
|
|
die "EXTRUDE_POINTS=$EXTRUDE_POINTS must be an even integer" unless $EXTRUDE_POINTS >= 2 and not $EXTRUDE_POINTS =~ m/\./ |
|
|
|
0
|
|
|
|
|
160
|
|
|
|
|
|
|
and not $EXTRUDE_POINTS & 1; # needed for closure_line_of_sight |
161
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
|
my $s = ($use_ocr ? 0.50-$LOS_CREATURE_RADIUS-($use_lhs ? $LOS_LHS_BONUS : 0) : 0.0001); |
|
|
0
|
|
|
|
|
|
163
|
0
|
0
|
|
|
|
|
my $e = ($use_ocr ? 0.50+$LOS_CREATURE_RADIUS+($use_lhs ? $LOS_LHS_BONUS : 0) : 0.9999); |
|
|
0
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
my $i = ( abs($s-$e) / ($EXTRUDE_POINTS-1) ); |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
my @r = ( |
167
|
|
|
|
|
|
|
[$point->[0] + $s, $point->[1] + $s], |
168
|
|
|
|
|
|
|
[$point->[0] + $e, $point->[1] + $s], |
169
|
|
|
|
|
|
|
[$point->[0] + $s, $point->[1] + $e], |
170
|
|
|
|
|
|
|
[$point->[0] + $e, $point->[1] + $e], |
171
|
|
|
|
|
|
|
); |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
## DEBUG ## return @r; # psh> require "MapGen/MapQueue.pm"; d[ Games::RolePlay::MapGen::MapQueue->_extrude_point([5,5]) ] |
174
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
|
my $c = $s+$i; |
176
|
0
|
|
|
|
|
|
while( $c < $e ) { |
177
|
0
|
|
|
|
|
|
push @r, |
178
|
|
|
|
|
|
|
[$point->[0] + $c, $point->[1] + $s], |
179
|
|
|
|
|
|
|
[$point->[0] + $s, $point->[1] + $c], |
180
|
|
|
|
|
|
|
[$point->[0] + $c, $point->[1] + $e], |
181
|
|
|
|
|
|
|
[$point->[0] + $e, $point->[1] + $c], |
182
|
0
|
|
|
|
|
|
;$c += $i; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# use Data::Dumper; $Data::Dumper::Indent = $Data::Dumper::Sortkeys = 0; |
186
|
|
|
|
|
|
|
# warn Dumper([$s, $e, $i, \@r]); |
187
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
my %h; |
189
|
0
|
|
|
|
|
|
return grep {my $x = not $h{"@$_"}; $h{"@$_"}=1; $x} @r; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
# }}} |
192
|
|
|
|
|
|
|
# _tight_line_of_sight_xs {{{ |
193
|
|
|
|
|
|
|
sub _tight_line_of_sight_xs { |
194
|
|
|
|
|
|
|
my $this = shift; |
195
|
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
return LOS_YES if "@$lhs" eq "@$rhs"; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
my @ods = $this->_od_segments(@_); |
200
|
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 1,1 ); # ocr,lhs |
201
|
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 1,0 ); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
return LOS_YES if &Games::RolePlay::MapGen::MapQueue::any_any_los_loop(\@lhs, \@rhs, \@ods); |
204
|
|
|
|
|
|
|
return LOS_NO; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
# }}} |
207
|
|
|
|
|
|
|
# _tight_line_of_sight_pl {{{ |
208
|
|
|
|
|
|
|
sub _tight_line_of_sight_pl { |
209
|
0
|
|
|
0
|
|
|
my $this = shift; |
210
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
211
|
|
|
|
|
|
|
|
212
|
0
|
0
|
|
|
|
|
return LOS_YES if "@$lhs" eq "@$rhs"; |
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments(@_); |
215
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 1,1 ); # ocr,lhs |
217
|
0
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 1,0 ); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
##---------------- LOS CALC |
220
|
0
|
|
|
|
|
|
my $line = 0; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
## DEBUG ## warn "SET\n"; |
223
|
|
|
|
|
|
|
## DEBUG ## warn "\@target: <@$rhs>\n"; |
224
|
|
|
|
|
|
|
## DEBUG ## warn "wall: (@{$_->[0]})->(@{$_->[1]})\n" for @od_segments; |
225
|
|
|
|
|
|
|
LOS_CHECK: |
226
|
0
|
|
|
|
|
|
for my $l (@lhs) { |
227
|
0
|
|
|
|
|
|
for my $r (@rhs) { |
228
|
0
|
|
|
|
|
|
my $this_line = 1; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
OD_CHECK: |
231
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
232
|
0
|
0
|
|
|
|
|
if( $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
$this_line = 0; |
234
|
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
|
last OD_CHECK; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
0
|
0
|
|
|
|
|
if( $this_line ) { |
240
|
|
|
|
|
|
|
## DEBUG ## warn "LOS: (@$l)->(@$r)\n"; |
241
|
0
|
|
|
|
|
|
$line = 1; |
242
|
0
|
|
|
|
|
|
last LOS_CHECK; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
}} |
245
|
|
|
|
|
|
|
## DEBUG ## warn "DONE\n"; |
246
|
|
|
|
|
|
|
|
247
|
0
|
0
|
|
|
|
|
return LOS_NO unless $line; |
248
|
0
|
|
|
|
|
|
return LOS_YES; # cover needs to be double checked |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
# }}} |
251
|
|
|
|
|
|
|
# _line_of_sight_xs {{{ |
252
|
|
|
|
|
|
|
sub _line_of_sight_xs { |
253
|
|
|
|
|
|
|
my $this = shift; |
254
|
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
return LOS_YES if "@$lhs" eq "@$rhs"; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
my @ods = $this->_od_segments(@_); |
259
|
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
260
|
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
return LOS_YES if &Games::RolePlay::MapGen::MapQueue::any_any_los_loop(\@lhs, \@rhs, \@ods); |
263
|
|
|
|
|
|
|
return LOS_NO; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
# }}} |
266
|
|
|
|
|
|
|
# _line_of_sight_pl {{{ |
267
|
|
|
|
|
|
|
sub _line_of_sight_pl { |
268
|
0
|
|
|
0
|
|
|
my $this = shift; |
269
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
270
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
|
return LOS_YES if "@$lhs" eq "@$rhs"; |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments(@_); |
274
|
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
276
|
0
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# warn "LHS: " . join(" ", map(sprintf('<%9.6f, %9.6f>', @$_), @lhs)); |
279
|
|
|
|
|
|
|
# warn "RHS: " . join(" ", map(sprintf('[%9.6f, %9.6f]', @$_), @rhs)); |
280
|
|
|
|
|
|
|
# warn "ODS: " . join(" ", map(sprintf('(%9.6f, %9.6f)->(%9.6f, %9.6f)', @{$_->[0]}, @{$_->[1]}), @od_segments)); |
281
|
|
|
|
|
|
|
|
282
|
0
|
|
|
|
|
|
my $line = 0; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
## DEBUG ## warn "---------- LOS @$lhs => @$rhs\n"; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
LOS_CHECK: |
287
|
0
|
|
|
|
|
|
for my $l (@lhs) { |
288
|
0
|
|
|
|
|
|
for my $r (@rhs) { |
289
|
0
|
|
|
|
|
|
my $this_line = 1; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
OD_CHECK: |
292
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
293
|
0
|
0
|
|
|
|
|
if( $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
294
|
0
|
|
|
|
|
|
$this_line = 0; |
295
|
|
|
|
|
|
|
|
296
|
0
|
|
|
|
|
|
last OD_CHECK; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
0
|
0
|
|
|
|
|
if( $this_line ) { |
301
|
|
|
|
|
|
|
## DEBUG ## warn "\tfound: (@$l)->(@$r)\n"; |
302
|
0
|
|
|
|
|
|
$line = 1; |
303
|
0
|
|
|
|
|
|
last LOS_CHECK; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
## DEBUG ## else { warn "\treject: (@$l)->(@$r)\n"; } |
306
|
|
|
|
|
|
|
}} |
307
|
|
|
|
|
|
|
|
308
|
0
|
0
|
|
|
|
|
return LOS_NO unless $line; |
309
|
0
|
|
|
|
|
|
return LOS_YES; # cover needs to be double checked |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
# }}} |
312
|
|
|
|
|
|
|
# _ranged_cover_pl {{{ |
313
|
|
|
|
|
|
|
sub _ranged_cover_pl { |
314
|
0
|
|
|
0
|
|
|
my $this = shift; |
315
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
316
|
|
|
|
|
|
|
|
317
|
0
|
0
|
|
|
|
|
return LOS_NO_COVER if "@$lhs" eq "@$rhs"; |
318
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments(@_); |
320
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
322
|
0
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
323
|
|
|
|
|
|
|
|
324
|
0
|
|
|
|
|
|
for my $l (@lhs) { |
325
|
0
|
|
|
|
|
|
my $cover = 0; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
## DEBUG ## warn "SET\n"; |
328
|
|
|
|
|
|
|
## DEBUG ## warn "<@$lhs> <@$rhs>\n"; |
329
|
|
|
|
|
|
|
|
330
|
0
|
|
|
|
|
|
RCRHS: for my $r (@rhs) { |
331
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
332
|
0
|
0
|
|
|
|
|
if( $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
## DEBUG ## warn "SET\n<@$lhs> <@$rhs>\n"; |
334
|
|
|
|
|
|
|
## DEBUG ## warn "(@{$od_segment->[0]})->(@{$od_segment->[1]}) (@$l)->(@$r)\n"; |
335
|
|
|
|
|
|
|
## DEBUG ## warn "DONE\n"; |
336
|
0
|
|
|
|
|
|
$cover = 1; |
337
|
0
|
|
|
|
|
|
last RCRHS; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
## DEBUG ## warn "DONE\n"; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# for ranged cover, if we can find even one lhs corner that can see all the rhs corners |
345
|
|
|
|
|
|
|
# then we return LOS_NO_COVER; |
346
|
0
|
0
|
|
|
|
|
unless( $cover ) { |
347
|
|
|
|
|
|
|
## DEBUG ## warn "\e[32m here(@$l) \e[m"; |
348
|
|
|
|
|
|
|
# NOTE: this cover-upgrade _not_ d20 rules: |
349
|
0
|
0
|
|
|
|
|
return LOS_COVER unless $this->_tight_line_of_sight( $lhs => $rhs ); |
350
|
0
|
|
|
|
|
|
return LOS_NO_COVER; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
## DEBUG ## warn "\e[32m here(---) \e[m"; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# NOTE: this cover-upgrade is _not_ d20 rules: |
357
|
0
|
0
|
|
|
|
|
return LOS_DOUBLE_COVER unless $this->_tight_line_of_sight( $lhs => $rhs ); |
358
|
0
|
|
|
|
|
|
return LOS_COVER; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
# }}} |
361
|
|
|
|
|
|
|
# _ranged_cover_xs {{{ |
362
|
|
|
|
|
|
|
sub _ranged_cover_xs { |
363
|
|
|
|
|
|
|
my $this = shift; |
364
|
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
return LOS_NO_COVER if "@$lhs" eq "@$rhs"; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
my @ods = $this->_od_segments(@_); |
369
|
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
370
|
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
if( &Games::RolePlay::MapGen::MapQueue::any_all_los_loop(\@lhs, \@rhs, \@ods) ) { |
373
|
|
|
|
|
|
|
## DEBUG ## warn "\e[31m here(@@@) \e[m"; |
374
|
|
|
|
|
|
|
return LOS_COVER unless $this->_tight_line_of_sight( $lhs => $rhs ); |
375
|
|
|
|
|
|
|
return LOS_NO_COVER; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
## DEBUG ## warn "\e[31m here(---) \e[m"; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
return LOS_DOUBLE_COVER unless $this->_tight_line_of_sight( $lhs => $rhs ); |
381
|
|
|
|
|
|
|
return LOS_COVER; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
# }}} |
384
|
|
|
|
|
|
|
# _melee_cover_pl {{{ |
385
|
|
|
|
|
|
|
sub _melee_cover_pl { |
386
|
0
|
|
|
0
|
|
|
my $this = shift; |
387
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# NOTE: Let the caller figure this out? Different creatures have different |
390
|
|
|
|
|
|
|
# reach and reach weapons should be using ranged_cover() anyway. On the |
391
|
|
|
|
|
|
|
# other hand, this map-logic doesn't even begin to consider creatures that |
392
|
|
|
|
|
|
|
# take up more than one tile... |
393
|
|
|
|
|
|
|
|
394
|
0
|
0
|
|
|
|
|
return LOS_NO_COVER if abs($lhs->[0]-$rhs->[0]) > 1; |
395
|
0
|
0
|
|
|
|
|
return LOS_NO_COVER if abs($lhs->[1]-$rhs->[1]) > 1; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# end_NOTE |
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments(@_); |
400
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
402
|
0
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
403
|
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
for my $l (@lhs) { |
405
|
0
|
|
|
|
|
|
for my $r (@rhs) { |
406
|
0
|
|
|
|
|
|
my $cover = 0; |
407
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
409
|
0
|
0
|
|
|
|
|
if( $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# This short circuits quickly half the time (on average). If |
411
|
|
|
|
|
|
|
# there's cover from any corner it counds as melee cover! |
412
|
0
|
|
|
|
|
|
return LOS_COVER; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
}} |
416
|
|
|
|
|
|
|
|
417
|
0
|
|
|
|
|
|
return LOS_NO_COVER; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
# }}} |
420
|
|
|
|
|
|
|
# _melee_cover_xs {{{ |
421
|
|
|
|
|
|
|
sub _melee_cover_xs { |
422
|
|
|
|
|
|
|
my $this = shift; |
423
|
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
return LOS_NO_COVER if abs($lhs->[0]-$rhs->[0]) > 1; |
426
|
|
|
|
|
|
|
return LOS_NO_COVER if abs($lhs->[1]-$rhs->[1]) > 1; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
my @ods = $this->_od_segments(@_); |
429
|
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lhs |
430
|
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
return LOS_COVER |
433
|
|
|
|
|
|
|
if &Games::RolePlay::MapGen::MapQueue::any_any_intersect_loop(\@lhs, \@rhs, \@ods); |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
return LOS_NO_COVER; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
# }}} |
438
|
|
|
|
|
|
|
# _closure_line_of_sight_pl {{{ |
439
|
|
|
|
|
|
|
sub _closure_line_of_sight_pl { |
440
|
0
|
|
|
0
|
|
|
my $this = shift; |
441
|
0
|
|
|
|
|
|
my $lhs = shift; |
442
|
0
|
|
|
|
|
|
my $rhsd = shift; |
443
|
|
|
|
|
|
|
|
444
|
0
|
|
|
|
|
|
my $s = (0.0001); |
445
|
0
|
|
|
|
|
|
my $e = (0.9999); |
446
|
0
|
|
|
|
|
|
my $i = (abs($s-$e) / ($EXTRUDE_POINTS-1)); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# NOTE: We build a row of points just "this side" of the door using (@c,$b) |
449
|
|
|
|
|
|
|
# for n/s doors or ($b,@c) for e/w ones. When we're done, there's a row of |
450
|
|
|
|
|
|
|
# points in the @rhs, built from @c and $b. |
451
|
|
|
|
|
|
|
|
452
|
0
|
|
|
|
|
|
my @c = ($s); $c[@c] = $c[$#c] + $i while $c[$#c] < $e; |
|
0
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
|
my $b; |
454
|
|
|
|
|
|
|
|
455
|
0
|
0
|
|
|
|
|
if( $rhsd->[2] eq "n" ) { $b = $rhsd->[1] + ($lhs->[1]>=$rhsd->[1] ? 0.01 : -0.01) } # slightly more or less than 0 |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
456
|
0
|
0
|
|
|
|
|
elsif( $rhsd->[2] eq "s" ) { $b = $rhsd->[1] + ($lhs->[1]<=$rhsd->[1] ? 0.99 : 1.01) } # slightly more or less than 1 |
457
|
0
|
0
|
|
|
|
|
elsif( $rhsd->[2] eq "e" ) { $b = $rhsd->[0] + ($lhs->[0]<=$rhsd->[0] ? 0.99 : 1.01) } |
458
|
0
|
0
|
|
|
|
|
elsif( $rhsd->[2] eq "w" ) { $b = $rhsd->[0] + ($lhs->[0]>=$rhsd->[0] ? 0.01 : -0.01) } |
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
|
|
|
my @rhs; |
461
|
0
|
0
|
|
|
|
|
if( $rhsd->[2] eq "n" ) { @rhs = map {[ $rhsd->[0]+$_, $b ]} @c } |
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
462
|
0
|
|
|
|
|
|
elsif( $rhsd->[2] eq "s" ) { @rhs = map {[ $rhsd->[0]+$_, $b ]} @c } |
|
0
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
|
elsif( $rhsd->[2] eq "e" ) { @rhs = map {[ $b, $rhsd->[1]+$_ ]} @c } |
|
0
|
|
|
|
|
|
|
464
|
0
|
|
|
|
|
|
elsif( $rhsd->[2] eq "w" ) { @rhs = map {[ $b, $rhsd->[1]+$_ ]} @c } |
|
0
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
466
|
0
|
|
|
|
|
|
my $v = [ $rhs[-1][0]-$rhs[0][0], $rhs[-1][1]-$rhs[0][1] ]; # vector @origin describing the line-segment named @rhs |
467
|
0
|
|
|
|
|
|
my $mv = sqrt( $v->[0]**2 + $v->[1]**2 ); |
468
|
0
|
|
|
|
|
|
$v = [ map { $_/$mv } @$v ]; # unit vector describing the line-segment named @rhs |
|
0
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
470
|
0
|
|
|
|
|
|
my $c = [ $rhsd->[0] + $v->[0]/2, $rhsd->[1] + $v->[1]/2 ]; # center of the line-segment named $rhsd |
471
|
0
|
0
|
|
|
|
|
$c->[0] ++ if $rhsd->[2] eq "e"; # which does require some minor correction |
472
|
0
|
0
|
|
|
|
|
$c->[1] ++ if $rhsd->[2] eq "s"; |
473
|
|
|
|
|
|
|
|
474
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments($lhs, [$rhs[0][0],$rhs[0][1]]); # line segments possibly in the way |
475
|
|
|
|
|
|
|
|
476
|
0
|
|
|
|
|
|
my @lhs = |
477
|
|
|
|
|
|
|
grep { |
478
|
0
|
|
|
|
|
|
my $l = $_; |
479
|
0
|
|
|
|
|
|
my $ok = 1; |
480
|
0
|
|
|
|
|
|
for my $r (@rhs) { |
481
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
482
|
0
|
0
|
|
|
|
|
if( my @i = $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
483
|
0
|
|
|
|
|
|
$ok = 0; |
484
|
0
|
|
|
|
|
|
last; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
$ok |
490
|
|
|
|
|
|
|
|
491
|
0
|
|
|
|
|
|
} grep { |
492
|
0
|
|
|
|
|
|
my $ab; |
493
|
0
|
|
|
|
|
|
my $od = $this->{_the_map}[ $rhsd->[1] ][ $rhsd->[0] ]{od}{ $rhsd->[2] }; |
494
|
0
|
|
|
|
|
|
my $rf = ref $od; |
495
|
0
|
0
|
0
|
|
|
|
if( ($od and not $rf) or ($rf and $od->{'open'}) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
496
|
0
|
|
|
|
|
|
$ab = 360; |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
} else { |
499
|
0
|
|
|
|
|
|
my $u = [ $c->[0]-$_->[0], $c->[1]-$_->[1] ]; # the line-segment from the $_ to the center of the closure |
500
|
0
|
|
|
|
|
|
my $mu = sqrt( $u->[0]**2 + $u->[1]**2 ); |
501
|
0
|
|
|
|
|
|
$u = [ map { abs $_/$mu } @$u ]; # unit vector of $u -- er, the totally positive version anyway |
|
0
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# We wish to exclude points that are within a certain arc. |
504
|
|
|
|
|
|
|
# Anything within $CLOS_MIN_ANGLE degrees of the wall plane |
505
|
|
|
|
|
|
|
# we're searching is defined to be an akward search angle |
506
|
|
|
|
|
|
|
|
507
|
0
|
|
|
|
|
|
my $cab = $v->[0]*$u->[0] + $v->[1]*$u->[1]; |
508
|
|
|
|
|
|
|
|
509
|
0
|
|
|
|
|
|
$ab = acos( $cab ); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# $ab, hopefully, contains the angle between the vectors |
513
|
0
|
|
|
|
|
|
$ab >= $CLOS_MIN_ANGLE; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
# All the points around the edge of the source tile. We do not need to |
517
|
|
|
|
|
|
|
# worry about any lhs being in the same line segment as the rhs since |
518
|
|
|
|
|
|
|
# none of them should be $c and all of them will have too small of an |
519
|
|
|
|
|
|
|
# angle between -- this assumes EXTRUDE_POINTS is even, which is now |
520
|
|
|
|
|
|
|
# enforced in _ex_p |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
($this->_extrude_point( $lhs, 0,0 ), [$lhs->[0]+0.5,$lhs->[1]+0.5]); |
523
|
|
|
|
|
|
|
|
524
|
0
|
0
|
|
|
|
|
my $min = (@lhs ? min map { my $l = $_; (max map { sqrt(($l->[0]-$_->[0])**2 + ($l->[1]-$_->[1])**2) } @rhs) } @lhs : 0); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
525
|
0
|
|
|
|
|
|
return $min; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
# }}} |
528
|
|
|
|
|
|
|
# _closure_line_of_sight_xs {{{ |
529
|
|
|
|
|
|
|
sub _closure_line_of_sight_xs { |
530
|
|
|
|
|
|
|
my $this = shift; |
531
|
|
|
|
|
|
|
my $lhs = shift; |
532
|
|
|
|
|
|
|
my $rhsd = shift; |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
my $s = (0.0001); |
535
|
|
|
|
|
|
|
my $e = (0.9999); |
536
|
|
|
|
|
|
|
my $i = (abs($s-$e) / ($EXTRUDE_POINTS-1)); |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# NOTE: We build a row of points just "this side" of the door using (@c,$b) |
539
|
|
|
|
|
|
|
# for n/s doors or ($b,@c) for e/w ones. When we're done, there's a row of |
540
|
|
|
|
|
|
|
# points in the @rhs, built from @c and $b. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
my @c = ($s); $c[@c] = $c[$#c] + $i while $c[$#c] < $e; |
543
|
|
|
|
|
|
|
my $b; |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
if( $rhsd->[2] eq "n" ) { $b = $rhsd->[1] + ($lhs->[1]>=$rhsd->[1] ? 0.01 : -0.01) } # slightly more or less than 0 |
546
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "s" ) { $b = $rhsd->[1] + ($lhs->[1]<=$rhsd->[1] ? 0.99 : 1.01) } # slightly more or less than 1 |
547
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "e" ) { $b = $rhsd->[0] + ($lhs->[0]<=$rhsd->[0] ? 0.99 : 1.01) } |
548
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "w" ) { $b = $rhsd->[0] + ($lhs->[0]>=$rhsd->[0] ? 0.01 : -0.01) } |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
my @rhs; # we don't know what the rhs is until we figure out where the door is in relation to the $lhs |
551
|
|
|
|
|
|
|
if( $rhsd->[2] eq "n" ) { @rhs = map {[ $rhsd->[0]+$_, $b ]} @c } |
552
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "s" ) { @rhs = map {[ $rhsd->[0]+$_, $b ]} @c } |
553
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "e" ) { @rhs = map {[ $b, $rhsd->[1]+$_ ]} @c } |
554
|
|
|
|
|
|
|
elsif( $rhsd->[2] eq "w" ) { @rhs = map {[ $b, $rhsd->[1]+$_ ]} @c } |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
my $v = [ $rhs[-1][0]-$rhs[0][0], $rhs[-1][1]-$rhs[0][1] ]; # vector @origin describing the line-segment named @rhs |
557
|
|
|
|
|
|
|
my $mv = sqrt( $v->[0]**2 + $v->[1]**2 ); |
558
|
|
|
|
|
|
|
$v = [ map { $_/$mv } @$v ]; # unit vector describing the line-segment named @rhs |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
my $c = [ $rhsd->[0] + $v->[0]/2, $rhsd->[1] + $v->[1]/2 ]; # center of the line-segment named $rhsd |
561
|
|
|
|
|
|
|
$c->[0] ++ if $rhsd->[2] eq "e"; # which does require some minor correction |
562
|
|
|
|
|
|
|
$c->[1] ++ if $rhsd->[2] eq "s"; |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
my @ods = $this->_od_segments($lhs, [$rhs[0][0],$rhs[0][1]]); # line segments possibly in the way |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
my @lhs = grep { &Games::RolePlay::MapGen::MapQueue::any_all_los_loop([$_], \@rhs, \@ods) } |
567
|
|
|
|
|
|
|
grep { |
568
|
|
|
|
|
|
|
my $ab; |
569
|
|
|
|
|
|
|
my $od = $this->{_the_map}[ $rhsd->[1] ][ $rhsd->[0] ]{od}{ $rhsd->[2] }; |
570
|
|
|
|
|
|
|
my $rf = ref $od; |
571
|
|
|
|
|
|
|
if( ($od and not $rf) or ($rf and $od->{'open'}) ) { |
572
|
|
|
|
|
|
|
$ab = 360; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
} else { |
575
|
|
|
|
|
|
|
my $u = [ $c->[0]-$_->[0], $c->[1]-$_->[1] ]; # the line-segment from the $_ to the center of the closure |
576
|
|
|
|
|
|
|
my $mu = sqrt( $u->[0]**2 + $u->[1]**2 ); |
577
|
|
|
|
|
|
|
$u = [ map { abs $_/$mu } @$u ]; # unit vector of $u -- er, the totally positive version anyway |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# We wish to exclude points that are within a certain arc. |
580
|
|
|
|
|
|
|
# Anything within $CLOS_MIN_ANGLE degrees of the wall plane |
581
|
|
|
|
|
|
|
# we're searching is defined to be an akward search angle |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
my $cab = $v->[0]*$u->[0] + $v->[1]*$u->[1]; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
$ab = acos( $cab ); |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# $ab, hopefully, contains the angle between the vectors |
589
|
|
|
|
|
|
|
$ab >= $CLOS_MIN_ANGLE; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# All the points around the edge of the source tile. We do not need to |
593
|
|
|
|
|
|
|
# worry about any lhs being in the same line segment as the rhs since |
594
|
|
|
|
|
|
|
# none of them should be $c and all of them will have too small of an |
595
|
|
|
|
|
|
|
# angle between -- this assumes EXTRUDE_POINTS is even, which is now |
596
|
|
|
|
|
|
|
# enforced in _ex_p |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
($this->_extrude_point( $lhs, 0,0 ), [$lhs->[0]+0.5,$lhs->[1]+0.5]); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my $min = (@lhs ? min map { my $l = $_; (max map { sqrt(($l->[0]-$_->[0])**2 + ($l->[1]-$_->[1])**2) } @rhs) } @lhs : 0); |
601
|
|
|
|
|
|
|
return $min; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
# }}} |
604
|
|
|
|
|
|
|
# _mxb_of_sight (returns m and b of y=mx+b fame) {{{ |
605
|
|
|
|
|
|
|
sub _mxb_of_sight { |
606
|
0
|
|
|
0
|
|
|
my $this = shift; |
607
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
608
|
|
|
|
|
|
|
|
609
|
0
|
0
|
|
|
|
|
return if "@$lhs" eq "@$rhs"; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
## DEBUG ## warn "---------- MXB @$lhs => @$rhs\n"; |
612
|
|
|
|
|
|
|
|
613
|
0
|
|
|
|
|
|
my @od_segments = $this->_od_segments(@_); |
614
|
|
|
|
|
|
|
|
615
|
0
|
|
|
|
|
|
my @lhs = $this->_extrude_point( $lhs, 0,0 ); # ocr,lh |
616
|
0
|
|
|
|
|
|
my @rhs = $this->_extrude_point( $rhs, 0,0 ); |
617
|
|
|
|
|
|
|
|
618
|
0
|
|
|
|
|
|
for my $l (sort { $this->_ldistance($a=>$rhs) <=> $this->_ldistance($b=>$rhs) } @lhs) { |
|
0
|
|
|
|
|
|
|
619
|
0
|
|
|
|
|
|
for my $r (sort { $this->_ldistance($a=>$l) <=> $this->_ldistance($b=>$l) } @rhs) { |
|
0
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
|
my $this_line = 1; |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
OD_CHECK: |
623
|
0
|
|
|
|
|
|
for my $od_segment (@od_segments) { |
624
|
0
|
0
|
|
|
|
|
if( $this->_line_segments_intersect( (map {@$_} @$od_segment) => (@$l=>@$r) ) ) { |
|
0
|
|
|
|
|
|
|
625
|
0
|
|
|
|
|
|
$this_line = 0; |
626
|
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
|
last OD_CHECK; |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
0
|
0
|
|
|
|
|
if( $this_line ) { |
632
|
0
|
|
|
|
|
|
my $d = ($r->[0]-$l->[0]); |
633
|
0
|
0
|
|
|
|
|
my $m = ($d != 0 ? ( ($r->[1]-$l->[1]) / $d ) : undef ); |
634
|
0
|
0
|
|
|
|
|
my $b = (defined $m ? ($l->[1] - ($m*$l->[0])) : 0); |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
## DEBUG ## warn "\tfound: (@$l)->(@$r)\n"; |
637
|
|
|
|
|
|
|
|
638
|
0
|
|
|
|
|
|
return ($m, $b, $l, $r); |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
## DEBUG ## else { warn "\treject: (@$l)->(@$r)\n"; } |
641
|
|
|
|
|
|
|
}} |
642
|
|
|
|
|
|
|
|
643
|
0
|
|
|
|
|
|
return; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
# }}} |
646
|
|
|
|
|
|
|
# _ignorable_cover {{{ |
647
|
|
|
|
|
|
|
sub _ignorable_cover { |
648
|
|
|
|
|
|
|
my $this = shift; |
649
|
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
warn "ignorable cover isn't actually calculated"; |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
return 0; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
# }}} |
656
|
|
|
|
|
|
|
# _ldistance {{{ |
657
|
|
|
|
|
|
|
sub _ldistance { |
658
|
0
|
|
|
0
|
|
|
my $this = shift; |
659
|
0
|
|
|
|
|
|
my ($lhs, $rhs) = @_; |
660
|
|
|
|
|
|
|
|
661
|
0
|
|
|
|
|
|
return sqrt ( (($lhs->[0]-$rhs->[0]) ** 2) + (($lhs->[1]-$rhs->[1]) ** 2) ); |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
# }}} |
664
|
|
|
|
|
|
|
# _locations_in_line_of_sight {{{ |
665
|
|
|
|
|
|
|
sub _locations_in_line_of_sight { |
666
|
|
|
|
|
|
|
my $this = shift; |
667
|
|
|
|
|
|
|
my $init = shift; |
668
|
|
|
|
|
|
|
my @loc = (); |
669
|
|
|
|
|
|
|
my @new = ($init); |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
my %checked = ( "@$init" => 1 ); |
672
|
|
|
|
|
|
|
while( @new ) { |
673
|
|
|
|
|
|
|
my @very_new = (); |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
for my $i (@new) { |
676
|
|
|
|
|
|
|
for my $j ( [$i->[0]+1, $i->[1]], [$i->[0]-1, $i->[1]], [$i->[0], $i->[1]+1], [$i->[0], $i->[1]-1] ) { |
677
|
|
|
|
|
|
|
next if $checked{"@$j"}; $checked{"@$j"} = 1; |
678
|
|
|
|
|
|
|
next unless $this->_check_loc($j); |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
push @very_new, $j if $this->_line_of_sight( $init => $j ); |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
push @loc, @new; |
685
|
|
|
|
|
|
|
@new = @very_new; |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
return @loc; |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
# }}} |
691
|
|
|
|
|
|
|
# _locations_in_range_and_line_of_sight {{{ |
692
|
|
|
|
|
|
|
sub _locations_in_range_and_line_of_sight { |
693
|
|
|
|
|
|
|
my $this = shift; |
694
|
|
|
|
|
|
|
my $init = shift; |
695
|
|
|
|
|
|
|
my $range = shift; |
696
|
|
|
|
|
|
|
my @loc = (); |
697
|
|
|
|
|
|
|
my @new = ($init); |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
my %checked = ( "@$init" => 1 ); |
700
|
|
|
|
|
|
|
while( @new ) { |
701
|
|
|
|
|
|
|
my @very_new = (); |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
for my $i (@new) { |
704
|
|
|
|
|
|
|
for my $j ( [$i->[0]+1, $i->[1]], [$i->[0]-1, $i->[1]], [$i->[0], $i->[1]+1], [$i->[0], $i->[1]-1] ) { |
705
|
|
|
|
|
|
|
next if $checked{"@$j"}; $checked{"@$j"} = 1; |
706
|
|
|
|
|
|
|
next unless $this->_check_loc($j); |
707
|
|
|
|
|
|
|
next unless sqrt( ($init->[0]-$j->[0])**2 + ($init->[1]-$j->[1])**2) <= $range; |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
push @very_new, $j if $this->_line_of_sight( $init => $j ); |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
push @loc, @new; |
714
|
|
|
|
|
|
|
@new = @very_new; |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
return @loc; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
# }}} |
720
|
|
|
|
|
|
|
# _objs_at_location {{{ |
721
|
|
|
|
|
|
|
sub _objs_at_location { |
722
|
0
|
|
|
0
|
|
|
my $this = shift; |
723
|
0
|
|
|
|
|
|
my $loc = shift; |
724
|
0
|
0
|
|
|
|
|
my @itm = @{ $this->{c}[ $loc->[1] ][ $loc->[0] ] || [] }; |
|
0
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
|
726
|
0
|
|
|
|
|
|
return @itm; # this is a copy, so it's silly to use wantarray... |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
# }}} |
729
|
|
|
|
|
|
|
# _locations_in_path {{{ |
730
|
|
|
|
|
|
|
sub _locations_in_path { |
731
|
|
|
|
|
|
|
my $this = shift; |
732
|
|
|
|
|
|
|
my $lhs = shift; |
733
|
|
|
|
|
|
|
my $rhs = shift; |
734
|
|
|
|
|
|
|
my @path = (); |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
return ([@$lhs],[@$rhs]) if "@$lhs" eq "@$rhs"; |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
my ($m, $b, $p0, $p1) = $this->_mxb_of_sight($lhs => $rhs); |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
## DEBUG ## warn "m=$m; b=$b; p0=(@$p0); p1=(@$p1)"; |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
my $ranger = sub { |
743
|
|
|
|
|
|
|
my ($l, $r) = @_; |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
return ( $l<$r ? ($l+1 .. $r-1) : (reverse ($r+1 .. $l-1)) ); |
746
|
|
|
|
|
|
|
}; |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
push @path, [@$lhs]; |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
if( not defined $m ) { |
751
|
|
|
|
|
|
|
for my $y ( $ranger->($lhs->[1] => $rhs->[1]) ) { |
752
|
|
|
|
|
|
|
my $x = $lhs->[0]; # == $rhs->[0] |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
push @path, [$x,$y]; |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
} elsif( (abs $m) > 1 ) { |
758
|
|
|
|
|
|
|
for my $y ( $ranger->($lhs->[1] => $rhs->[1]) ) { |
759
|
|
|
|
|
|
|
my $z = (($y+0.5)-$b)/$m; |
760
|
|
|
|
|
|
|
my $x = round($z-0.5); |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
push @path, [$x,$y]; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
} elsif( $m == 0 ) { |
765
|
|
|
|
|
|
|
for my $x ( $ranger->($lhs->[0] => $rhs->[0]) ) { |
766
|
|
|
|
|
|
|
my $y = round($b); |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
push @path, [$x,$y]; |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
} else { |
772
|
|
|
|
|
|
|
for my $x ( $ranger->($lhs->[0] => $rhs->[0]) ) { |
773
|
|
|
|
|
|
|
my $z = ($m * ($x+0.5)) + $b; |
774
|
|
|
|
|
|
|
my $y = round($z-0.5); |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
push @path, [$x,$y]; |
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
push @path, [@$rhs]; |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
for my $list ([+1, reverse 0 .. $#path-1], [-1, 1 .. $#path]) { my $ni = shift @$list; |
783
|
|
|
|
|
|
|
for my $i (@$list) { |
784
|
|
|
|
|
|
|
my $changes = 0; |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
for my $j (0,1) { |
787
|
|
|
|
|
|
|
my $A = $path[$i][$j]; |
788
|
|
|
|
|
|
|
my $d = $path[$i+$ni][$j] - $A; |
789
|
|
|
|
|
|
|
my $md = abs $d; |
790
|
|
|
|
|
|
|
if( $md > 1 ) { |
791
|
|
|
|
|
|
|
$A += $d/$md; |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
## DEBUG ## warn (($j==0 ? "X":"Y") . "-CHANGE($i,$j)::(@{$path[$i]})[$j] = $A\n"); |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
$path[$i][$j] = $A; |
796
|
|
|
|
|
|
|
$changes ++; |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
## DEBUG ## else { warn (($j==0 ? "X":"Y") . "-!NO!CHANGE($i,$j)::(@{$path[$i]})[$j] = $A; md=$md; d=$d\n"); } |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
last unless $changes; |
803
|
|
|
|
|
|
|
}} |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
DIAG_ORDEAL: { |
806
|
|
|
|
|
|
|
my $map = $this->{_the_map}; |
807
|
|
|
|
|
|
|
for my $i ( 0 .. $#path-1 ) { |
808
|
|
|
|
|
|
|
my $j = $i + 1; |
809
|
|
|
|
|
|
|
my ($lhs, $rhs) = ($path[$i], $path[$j]); |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
if( $lhs->[0] != $rhs->[0] and $lhs->[1] != $rhs->[1] ) { |
812
|
|
|
|
|
|
|
# NOTE: a diagonal move is illegal if there's a "corner" in the way phb p. 147 |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
LHS_DIAG_VIOLATION: { |
815
|
|
|
|
|
|
|
my $lod = $map->[ $lhs->[1] ][ $lhs->[0] ]{od}; |
816
|
|
|
|
|
|
|
my $xdir = ($lhs->[0]<$rhs->[0] ? 'e':'w'); my $xo = $lod->{$xdir}; $xo = 1 if ref $xo and $xo->{'open'}; |
817
|
|
|
|
|
|
|
my $ydir = ($lhs->[1]<$rhs->[1] ? 's':'n'); my $yo = $lod->{$ydir}; $yo = 1 if ref $yo and $yo->{'open'}; |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
if( not $yo ) { |
820
|
|
|
|
|
|
|
if( $i == 0 or ($path[$i-1][0] != $lhs->[0]) ) { |
821
|
|
|
|
|
|
|
splice @path, $j, 0, [ $rhs->[0], $lhs->[1] ]; # 0-width inserts at $j |
822
|
|
|
|
|
|
|
redo DIAG_ORDEAL; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
} else { |
825
|
|
|
|
|
|
|
$lhs->[0] = $rhs->[0]; |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
} elsif( not $xo ) { |
829
|
|
|
|
|
|
|
if( $i == 0 or ($path[$i-1][1] != $lhs->[1]) ) { |
830
|
|
|
|
|
|
|
splice @path, $j, 0, [ $lhs->[0], $rhs->[1] ]; # 0-width inserts at $j |
831
|
|
|
|
|
|
|
redo DIAG_ORDEAL; |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
} else { |
834
|
|
|
|
|
|
|
$lhs->[1] = $rhs->[1]; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
RHS_DIAG_VIOLATION: { |
840
|
|
|
|
|
|
|
my $lod = $map->[ $rhs->[1] ][ $rhs->[0] ]{od}; |
841
|
|
|
|
|
|
|
my $xdir = ($lhs->[0]<$rhs->[0] ? 'w':'e'); my $xo = $lod->{$xdir}; $xo = 1 if ref $xo and $xo->{'open'}; |
842
|
|
|
|
|
|
|
my $ydir = ($lhs->[1]<$rhs->[1] ? 'n':'s'); my $yo = $lod->{$ydir}; $yo = 1 if ref $yo and $yo->{'open'}; |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
if( not $yo ) { |
845
|
|
|
|
|
|
|
if( $j == $#path or ($path[$j+1][0] != $rhs->[0] ) ) { |
846
|
|
|
|
|
|
|
splice @path, $j, 0, [ $lhs->[0], $rhs->[1] ]; # 0-width inserts at $j |
847
|
|
|
|
|
|
|
redo DIAG_ORDEAL; |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
} else { |
850
|
|
|
|
|
|
|
$rhs->[0] = $lhs->[0]; |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
} elsif( not $xo ) { |
854
|
|
|
|
|
|
|
if( $j == $#path or ($path[$j+1][1] != $rhs->[1] ) ) { |
855
|
|
|
|
|
|
|
splice @path, $j, 0, [ $rhs->[0], $lhs->[1] ]; # 0-width inserts at $j |
856
|
|
|
|
|
|
|
redo DIAG_ORDEAL; |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
} else { |
859
|
|
|
|
|
|
|
$rhs->[1] = $lhs->[1]; |
860
|
|
|
|
|
|
|
} |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
} |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
return @path; |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
# }}} |
870
|
|
|
|
|
|
|
# _door {{{ |
871
|
|
|
|
|
|
|
sub _door { |
872
|
0
|
|
|
0
|
|
|
my $this = shift; |
873
|
0
|
0
|
|
|
|
|
my $door = shift; return unless ref $door; |
|
0
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
|
875
|
0
|
|
|
|
|
|
for my $y ( 0 .. $this->{ym} ) { |
876
|
0
|
|
|
|
|
|
for my $x ( 0 .. $this->{xm} ) { |
877
|
0
|
|
|
|
|
|
my $tile = $this->{_the_map}[$y][$x]; |
878
|
|
|
|
|
|
|
|
879
|
0
|
|
|
|
|
|
for my $d (qw(n e s w)) { |
880
|
0
|
0
|
|
|
|
|
if( $door == $tile->{od}{$d} ) { |
881
|
0
|
|
|
|
|
|
my $nb = $tile->{nb}{$d}; |
882
|
|
|
|
|
|
|
|
883
|
0
|
|
|
|
|
|
return [$x,$y,$d]; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
|
889
|
0
|
|
|
|
|
|
return; |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
# }}} |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
# _line_segments_intersect {{{ |
894
|
|
|
|
|
|
|
sub _line_segments_intersect { |
895
|
0
|
|
|
0
|
|
|
my $this = shift; |
896
|
|
|
|
|
|
|
# this is http://perlmonks.org/?node_id=253983 |
897
|
|
|
|
|
|
|
|
898
|
0
|
|
|
|
|
|
my ( $ax,$ay, $bx,$by, $cx,$cy, $dx,$dy ) = @_; |
899
|
|
|
|
|
|
|
# printf STDERR "[pl] A(%9.6f,%9.6f) B(%9.6f,%9.6f) C(%9.6f,%9.6f) D(%9.6f,%9.6f)", $ax,$ay, $bx,$by, $cx,$cy, $dx,$dy; |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# P = p*A + (1-p)*B |
902
|
|
|
|
|
|
|
# Q = q*C + (1-q)*D |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
# for p=0, P=A, and for p=1, P=B |
905
|
|
|
|
|
|
|
# for 0<=p<=1, P is on the line segment between A and B |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
# find p,q such than P=Q |
908
|
|
|
|
|
|
|
# (... lengthy derivation ...) |
909
|
|
|
|
|
|
|
|
910
|
0
|
|
|
|
|
|
my $d = ($ax-$bx)*($cy-$dy) - ($ay-$by)*($cx-$dx); |
911
|
|
|
|
|
|
|
# printf STDERR " d=$d"; |
912
|
|
|
|
|
|
|
|
913
|
0
|
0
|
0
|
|
|
|
if( $cx == $dx and $cy == $dy ) { |
914
|
|
|
|
|
|
|
# 6/25/7 we're a point on the rhs ... apparently this happens when you remove the extrude shortcutting |
915
|
|
|
|
|
|
|
|
916
|
0
|
0
|
0
|
|
|
|
if( $ay == $by and $cy == $ay ) { |
|
|
0
|
0
|
|
|
|
|
917
|
0
|
0
|
0
|
|
|
|
return ($cx, $cy) if $ax <= $cx and $cx <= $bx; |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
} elsif( $ax == $bx and $cx == $ax ) { |
920
|
0
|
0
|
0
|
|
|
|
return ($cx, $cy) if $ay <= $cy and $cy <= $by; |
921
|
|
|
|
|
|
|
} |
922
|
|
|
|
|
|
|
|
923
|
0
|
|
|
|
|
|
die "probably a bug"; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
0
|
0
|
|
|
|
|
if( $d == 0 ) { |
927
|
|
|
|
|
|
|
# d=0 when len(C->D)==0 !! |
928
|
0
|
|
|
|
|
|
for my $l ([$ax,$ay], [$bx, $by]) { |
929
|
0
|
|
|
|
|
|
for my $r ([$cx,$cy], [$dx, $dy]) { |
930
|
0
|
0
|
0
|
|
|
|
return (@$l) if $l->[0] == $r->[0] and $l->[1] == $r->[1]; |
931
|
|
|
|
|
|
|
}} |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
# NOTE: another huge bug from 6/23/7 !! This vertical overlap was totally overlooked before. |
934
|
|
|
|
|
|
|
# This is arguably not the most efficient way to check it, but it's literally better than *nothing* |
935
|
0
|
0
|
0
|
|
|
|
if( abs($ax-$bx)<0.0001 and abs($bx-$cx)<0.0001 and abs($cx-$dx)<0.0001 ) { |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
936
|
0
|
0
|
0
|
|
|
|
return ($cx,$cy) if $ay <= $cy and $cy <= $by; |
937
|
0
|
0
|
0
|
|
|
|
return ($dx,$dy) if $ay <= $dy and $dy <= $by; |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
# 6/25/7 -- sorta the same deal as above, but horizontal |
940
|
|
|
|
|
|
|
} elsif( abs($ay-$by)<0.0001 and abs($by-$cy)<0.0001 and abs($cy-$dy)<0.0001 ) { |
941
|
0
|
0
|
0
|
|
|
|
return ($cx,$cy) if $ax <= $cx and $cx <= $bx; |
942
|
0
|
0
|
0
|
|
|
|
return ($dx,$dy) if $ax <= $dx and $dx <= $bx; |
943
|
|
|
|
|
|
|
} |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
## DEBUG ## warn "\t\tlsi p=||\n"; |
946
|
0
|
|
|
|
|
|
return; # probably parallel |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
0
|
|
|
|
|
|
my $p = ( ($by-$dy)*($cx-$dx) - ($bx-$dx)*($cy-$dy) ) / $d; |
950
|
|
|
|
|
|
|
# printf STDERR " p=$p"; |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
## NOTE: this was an effin hard bug to find... |
953
|
|
|
|
|
|
|
## my @w = ( ( ($p <= 1) ? 1:0 ), ( ($p == 1) ? 1:0 ), ( ($p != 1) ? 1:0 ), ( ($p - 1) ),); |
954
|
|
|
|
|
|
|
## warn "\t\tlsi p=$p (@w)\n"; |
955
|
|
|
|
|
|
|
## lsi p-1 = 2.22044604925031e-16 = 1? No, not actually, sometimes... |
956
|
|
|
|
|
|
|
|
957
|
0
|
0
|
|
|
|
|
$p = 0 if abs($p) < 0.00001; # fixed 6/23/7 |
958
|
0
|
0
|
|
|
|
|
$p = 1 if abs($p-1) < 0.00001; |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# printf STDERR " p=$p\n"; |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
## DEBUG ## warn "\t\tlsi p=$p\n"; |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
# we probably don't need to find q because we already restricted the domain/range above |
965
|
0
|
0
|
0
|
|
|
|
return unless $p >= 0 and $p <= 1; |
966
|
|
|
|
|
|
|
|
967
|
0
|
|
|
|
|
|
my $px = $p*$ax + (1-$p)*$bx; |
968
|
0
|
|
|
|
|
|
my $py = $p*$ay + (1-$p)*$by; |
969
|
|
|
|
|
|
|
|
970
|
0
|
|
|
|
|
|
return ($px, $py); |
971
|
|
|
|
|
|
|
} |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
# NOTE: simply uncomment these to get verbose LSI results |
974
|
|
|
|
|
|
|
## DEBUG ## *debug_lsi = *_line_segments_intersect; |
975
|
|
|
|
|
|
|
## DEBUG ## sub replacer { my @ret = &debug_lsi(@_); warn "\t\tLSI(@ret)\n"; return @ret; } |
976
|
|
|
|
|
|
|
## DEBUG ## *_line_segments_intersect = *replacer; |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# }}} |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# location {{{ |
981
|
|
|
|
|
|
|
sub location { |
982
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
983
|
0
|
|
|
|
|
|
my $that = shift; |
984
|
|
|
|
|
|
|
|
985
|
0
|
0
|
|
|
|
|
croak "that object/tag ($that) isn't on the map" unless exists $this->{l}{$that}; |
986
|
|
|
|
|
|
|
|
987
|
0
|
|
|
|
|
|
my $l = $this->{l}{$that}; |
988
|
0
|
0
|
|
|
|
|
return (wantarray ? @$l : $l); |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
# }}} |
991
|
|
|
|
|
|
|
# lline_of_sight {{{ |
992
|
|
|
|
|
|
|
sub lline_of_sight { |
993
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
994
|
|
|
|
|
|
|
|
995
|
0
|
0
|
|
|
|
|
croak "you should provide 4 arguments to line_of_sight()" unless @_ == 4; |
996
|
|
|
|
|
|
|
|
997
|
0
|
|
|
|
|
|
my @lhs = @_[0 .. 1]; |
998
|
0
|
|
|
|
|
|
my @rhs = @_[2 .. 3]; |
999
|
|
|
|
|
|
|
|
1000
|
0
|
0
|
|
|
|
|
croak "the first two arguments to lline_of_sight() do not appear to form a sane map location" unless $this->_check_loc(\@lhs); |
1001
|
0
|
0
|
|
|
|
|
croak "the last two arguments to lline_of_sight() do not appear to form a sane map location" unless $this->_check_loc(\@rhs); |
1002
|
|
|
|
|
|
|
|
1003
|
0
|
|
|
|
|
|
return $this->_line_of_sight(\@lhs, \@rhs); |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
# }}} |
1006
|
|
|
|
|
|
|
# ldistance {{{ |
1007
|
|
|
|
|
|
|
sub ldistance { |
1008
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1009
|
|
|
|
|
|
|
|
1010
|
0
|
0
|
|
|
|
|
croak "you should provide 4 arguments to ldistance()" unless @_ == 4; |
1011
|
|
|
|
|
|
|
|
1012
|
0
|
|
|
|
|
|
my @lhs = @_[0 .. 1]; |
1013
|
0
|
|
|
|
|
|
my @rhs = @_[2 .. 3]; |
1014
|
|
|
|
|
|
|
|
1015
|
0
|
0
|
|
|
|
|
croak "the first two arguments to ldistance() do not appear to form a sane map location" unless $this->_check_loc(\@lhs); |
1016
|
0
|
0
|
|
|
|
|
croak "the last two arguments to ldistance() do not appear to form a sane map location" unless $this->_check_loc(\@rhs); |
1017
|
|
|
|
|
|
|
|
1018
|
0
|
0
|
|
|
|
|
if( $_[4] ) { |
1019
|
0
|
|
|
|
|
|
my @r = ($this->_ldistance(\@lhs, \@rhs), $this->_line_of_sight(\@lhs, \@rhs)); |
1020
|
0
|
0
|
|
|
|
|
return (wantarray ? @r : \@r); |
1021
|
|
|
|
|
|
|
} |
1022
|
|
|
|
|
|
|
|
1023
|
0
|
0
|
|
|
|
|
return undef unless $this->_line_of_sight(\@lhs => \@rhs); |
1024
|
0
|
|
|
|
|
|
return $this->_ldistance(\@lhs => \@rhs); |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
# }}} |
1027
|
|
|
|
|
|
|
# distance {{{ |
1028
|
|
|
|
|
|
|
sub distance { |
1029
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1030
|
0
|
0
|
|
|
|
|
my $lhs = shift; croak "the lhs=$lhs isn't on the map" unless exists $this->{l}{$lhs}; |
|
0
|
|
|
|
|
|
|
1031
|
0
|
0
|
|
|
|
|
my $rhs = shift; croak "the rhs=$rhs isn't on the map" unless exists $this->{l}{$rhs}; |
|
0
|
|
|
|
|
|
|
1032
|
0
|
|
|
|
|
|
my $los = shift; |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
|
|
|
|
|
$lhs = $this->{l}{$lhs}; |
1035
|
0
|
|
|
|
|
|
$rhs = $this->{l}{$rhs}; |
1036
|
|
|
|
|
|
|
|
1037
|
0
|
0
|
|
|
|
|
if( $los ) { |
1038
|
0
|
|
|
|
|
|
my @r = ($this->_ldistance($lhs, $rhs), $this->_line_of_sight($lhs, $rhs)); |
1039
|
0
|
0
|
|
|
|
|
return (wantarray ? @r : \@r); |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
|
1042
|
0
|
0
|
|
|
|
|
return undef unless $this->_line_of_sight($lhs, $rhs); |
1043
|
0
|
|
|
|
|
|
return $this->_ldistance($lhs, $rhs); |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
# }}} |
1046
|
|
|
|
|
|
|
# line_of_sight {{{ |
1047
|
|
|
|
|
|
|
sub line_of_sight { |
1048
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1049
|
|
|
|
|
|
|
|
1050
|
0
|
0
|
|
|
|
|
croak "you should provide 2 arguments to line_of_sight()" unless @_ == 2; |
1051
|
|
|
|
|
|
|
|
1052
|
0
|
|
|
|
|
|
my $lhs = shift; $lhs = "$lhs"; |
|
0
|
|
|
|
|
|
|
1053
|
0
|
|
|
|
|
|
my $rhs = shift; $rhs = "$rhs"; |
|
0
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
|
1055
|
0
|
0
|
|
|
|
|
croak "the first argument to line_of_sight() does not appear to be on the map" unless ($lhs = $this->{l}{$lhs}); |
1056
|
0
|
0
|
|
|
|
|
croak "the last argument to line_of_sight() does not appear to be on the map" unless ($rhs = $this->{l}{$rhs}); |
1057
|
|
|
|
|
|
|
|
1058
|
0
|
|
|
|
|
|
return $this->_line_of_sight($lhs, $rhs); |
1059
|
|
|
|
|
|
|
} |
1060
|
|
|
|
|
|
|
# }}} |
1061
|
|
|
|
|
|
|
# closure_line_of_sight {{{ |
1062
|
|
|
|
|
|
|
sub closure_line_of_sight { |
1063
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
0
|
|
|
|
|
croak "you should provide 2 arguments to closure_line_of_sight()" unless @_ == 2; |
1066
|
|
|
|
|
|
|
|
1067
|
0
|
|
|
|
|
|
my $lhs = shift; $lhs = "$lhs"; |
|
0
|
|
|
|
|
|
|
1068
|
0
|
|
|
|
|
|
my $rhs = shift; |
1069
|
|
|
|
|
|
|
|
1070
|
0
|
0
|
|
|
|
|
croak "the first argument to closure_line_of_sight() does not appear to be on the map" unless ($lhs = $this->{l}{$lhs}); |
1071
|
0
|
0
|
|
|
|
|
croak "the last argument to closure_line_of_sight() does not appear to be a door" unless ($rhs = $this->_door($rhs)); |
1072
|
|
|
|
|
|
|
# it definitely does have to be a door so we can get the direction! ... for arbitrary closures you must use |
1073
|
|
|
|
|
|
|
# closure_lline_of_sight. :( |
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
|
|
|
|
return $this->_closure_line_of_sight($lhs, $rhs); |
1076
|
|
|
|
|
|
|
} |
1077
|
|
|
|
|
|
|
# }}} |
1078
|
|
|
|
|
|
|
# closure_lline_of_sight {{{ |
1079
|
|
|
|
|
|
|
sub closure_lline_of_sight { |
1080
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1081
|
|
|
|
|
|
|
|
1082
|
0
|
0
|
|
|
|
|
croak "you should provide 5 arguments to closure_lline_of_sight()" unless @_ == 5; |
1083
|
|
|
|
|
|
|
|
1084
|
0
|
|
|
|
|
|
my @lhs = @_[0 .. 1]; |
1085
|
0
|
|
|
|
|
|
my @rhs = @_[2 .. 3]; |
1086
|
0
|
|
|
|
|
|
my $dir = $_[4]; |
1087
|
|
|
|
|
|
|
|
1088
|
0
|
0
|
|
|
|
|
croak "the first two arguments to closeure_lline_of_sight() do not appear to form a sane map location" unless $this->_check_loc(\@lhs); |
1089
|
0
|
0
|
|
|
|
|
croak "the second two arguments to closeure_lline_of_sight() do not appear to form a sane map location" unless $this->_check_loc(\@rhs); |
1090
|
0
|
0
|
|
|
|
|
croak "the fifth argument to closure_lline_of_sight() should be a map direction (ie, n s e w)" unless $dir =~ m/^[nsew]\z/; |
1091
|
|
|
|
|
|
|
|
1092
|
0
|
|
|
|
|
|
return $this->_closure_line_of_sight(\@lhs, [@rhs, $dir]); |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
# }}} |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
# {{{ sub build_queue_from_hash |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
################## |
1099
|
|
|
|
|
|
|
# XXX: experimental, undocumented, crazy thing, do not use, may change |
1100
|
|
|
|
|
|
|
###### |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
sub build_queue_from_hash { |
1103
|
0
|
|
|
0
|
0
|
|
my $this = shift; |
1104
|
0
|
0
|
0
|
|
|
|
my $that = @_==1 && ref($_[0])eq"HASH" ? $_[0] : { @_ }; |
1105
|
|
|
|
|
|
|
|
1106
|
0
|
|
|
|
|
|
delete $this->{l}; |
1107
|
0
|
|
|
|
|
|
delete $this->{c}; |
1108
|
|
|
|
|
|
|
|
1109
|
0
|
|
|
|
|
|
for my $k (keys %$that) { |
1110
|
0
|
|
|
|
|
|
$this->{l} = $k; |
1111
|
0
|
|
|
|
|
|
my $loc = $that->{$k}{l}; |
1112
|
0
|
|
|
|
|
|
my $itm = $that->{$k}{i}; |
1113
|
|
|
|
|
|
|
|
1114
|
0
|
|
|
|
|
|
push @{$this->{c}[ $loc->[1] ][ $loc->[0] ]}, $itm; |
|
0
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
# }}} |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
# add {{{ |
1121
|
|
|
|
|
|
|
sub add { |
1122
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1123
|
0
|
0
|
|
|
|
|
my $that = shift or croak "place what?"; my $tag = "$that"; |
|
0
|
|
|
|
|
|
|
1124
|
0
|
|
|
|
|
|
my @loc = @_; |
1125
|
|
|
|
|
|
|
|
1126
|
0
|
0
|
|
|
|
|
croak "that object/tag ($tag) appears to already be on the map" if exists $this->{l}{$tag}; |
1127
|
0
|
0
|
|
|
|
|
croak "that location (@loc) makes no sense" unless $this->_check_loc(\@loc); |
1128
|
|
|
|
|
|
|
|
1129
|
0
|
|
|
|
|
|
$this->{l}{$tag} = \@loc; |
1130
|
0
|
|
|
|
|
|
push @{ $this->{c}[ $loc[1] ][ $loc[0] ] }, $that; |
|
0
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
} |
1132
|
|
|
|
|
|
|
# }}} |
1133
|
|
|
|
|
|
|
# remove {{{ |
1134
|
|
|
|
|
|
|
sub remove { |
1135
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1136
|
0
|
|
|
|
|
|
my $that = shift; my $tag = "$that"; |
|
0
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
|
1138
|
0
|
0
|
|
|
|
|
croak "that object/tag ($tag) isn't on the map" unless exists $this->{l}{$tag}; |
1139
|
|
|
|
|
|
|
|
1140
|
0
|
|
|
|
|
|
my @loc = @{ delete $this->{l}{$tag} }; |
|
0
|
|
|
|
|
|
|
1141
|
0
|
|
|
|
|
|
my $itm = $this->{c}[ $loc[1] ][ $loc[0] ]; |
1142
|
|
|
|
|
|
|
|
1143
|
0
|
|
|
|
|
|
@$itm = ( grep {$_ ne $tag} @$itm ); |
|
0
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
} |
1145
|
|
|
|
|
|
|
# }}} |
1146
|
|
|
|
|
|
|
# replace {{{ |
1147
|
|
|
|
|
|
|
sub replace { |
1148
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1149
|
0
|
|
|
|
|
|
my $that = shift; my $tag = "$that"; |
|
0
|
|
|
|
|
|
|
1150
|
0
|
|
|
|
|
|
my @loc = @_; |
1151
|
|
|
|
|
|
|
|
1152
|
0
|
0
|
|
|
|
|
croak "that location (@loc) makes no sense" unless $this->_check_loc(\@loc); |
1153
|
|
|
|
|
|
|
|
1154
|
0
|
0
|
|
|
|
|
$this->remove($tag) if exists $this->{l}{$tag}; |
1155
|
0
|
|
|
|
|
|
$this->add($that => @loc); |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
# }}} |
1158
|
|
|
|
|
|
|
# {{{ is_on_map |
1159
|
|
|
|
|
|
|
sub is_on_map { |
1160
|
0
|
|
|
0
|
0
|
|
my $this = shift; |
1161
|
0
|
|
|
|
|
|
my $that = shift; |
1162
|
|
|
|
|
|
|
|
1163
|
0
|
0
|
|
|
|
|
return exists($this->{l}{$that}) ? 1:0; |
1164
|
|
|
|
|
|
|
} |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
# }}} |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# objs_at_location {{{ |
1169
|
|
|
|
|
|
|
sub objs_at_location { |
1170
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1171
|
0
|
0
|
|
|
|
|
my $loc = $this->_check_loc(\@_) or croak "that location (@_) makes no sense"; |
1172
|
|
|
|
|
|
|
|
1173
|
0
|
|
|
|
|
|
return $this->_objs_at_location( $loc ); |
1174
|
|
|
|
|
|
|
} |
1175
|
|
|
|
|
|
|
*objects_at_location = *objs_at_location; |
1176
|
|
|
|
|
|
|
# }}} |
1177
|
|
|
|
|
|
|
# objs_in_line_of_sight {{{ |
1178
|
|
|
|
|
|
|
sub objs_in_line_of_sight { |
1179
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1180
|
0
|
0
|
|
|
|
|
my $loc = $this->_check_loc(\@_) or croak "that location (@_) makes no sense"; |
1181
|
0
|
|
|
|
|
|
my @ret = (); |
1182
|
|
|
|
|
|
|
|
1183
|
0
|
|
|
|
|
|
for my $l ($this->_locations_in_line_of_sight($loc)) { |
1184
|
0
|
0
|
|
|
|
|
push @ret, @{ $this->{c}[ $l->[1] ][ $l->[0] ] || [] }; |
|
0
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
|
1187
|
0
|
|
|
|
|
|
return @ret; |
1188
|
|
|
|
|
|
|
} |
1189
|
|
|
|
|
|
|
*objects_in_line_of_sight = *objs_in_line_of_sight; |
1190
|
|
|
|
|
|
|
# }}} |
1191
|
|
|
|
|
|
|
# objs {{{ |
1192
|
|
|
|
|
|
|
sub objs { |
1193
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1194
|
0
|
|
|
|
|
|
my @ret = (); |
1195
|
|
|
|
|
|
|
|
1196
|
0
|
|
|
|
|
|
for my $row ( 0 .. $this->{ym} ) { |
1197
|
0
|
|
|
|
|
|
for my $col ( 0 .. $this->{xm} ) { |
1198
|
|
|
|
|
|
|
|
1199
|
0
|
0
|
|
|
|
|
push @ret, @{ $this->{c}[ $row ][ $col ] || [] }; |
|
0
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
|
1203
|
0
|
|
|
|
|
|
return @ret; |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
*objects = *objs; |
1206
|
|
|
|
|
|
|
# }}} |
1207
|
|
|
|
|
|
|
# objs_with_locations {{{ |
1208
|
|
|
|
|
|
|
sub objs_with_locations { |
1209
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1210
|
0
|
|
|
|
|
|
my @ret = (); |
1211
|
|
|
|
|
|
|
|
1212
|
0
|
|
|
|
|
|
for my $row ( 0 .. $this->{ym} ) { |
1213
|
0
|
|
|
|
|
|
for my $col ( 0 .. $this->{xm} ) { |
1214
|
0
|
|
|
|
|
|
my $loc = [ $col, $row ]; |
1215
|
|
|
|
|
|
|
|
1216
|
0
|
0
|
|
|
|
|
my @junk = @{ $this->{c}[ $loc->[1] ][ $loc->[0] ] || [] }; |
|
0
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
|
1218
|
0
|
0
|
|
|
|
|
push @ret, [ $loc => \@junk ] if @junk; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
|
1222
|
0
|
|
|
|
|
|
return @ret; |
1223
|
|
|
|
|
|
|
} |
1224
|
|
|
|
|
|
|
*objects_with_locations = *objs_with_locations; |
1225
|
|
|
|
|
|
|
# }}} |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
# random_open_location {{{ |
1228
|
|
|
|
|
|
|
sub random_open_location { |
1229
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1230
|
0
|
|
|
|
|
|
my @l = $this->all_open_locations; |
1231
|
0
|
|
|
|
|
|
my $i = int rand int @l; |
1232
|
|
|
|
|
|
|
|
1233
|
0
|
0
|
|
|
|
|
return unless @l; |
1234
|
0
|
0
|
|
|
|
|
return (wantarray ? @{$l[$i]}:$l[$i]); |
|
0
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
} |
1236
|
|
|
|
|
|
|
# }}} |
1237
|
|
|
|
|
|
|
# all_open_locations {{{ |
1238
|
|
|
|
|
|
|
sub all_open_locations { |
1239
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1240
|
0
|
|
|
|
|
|
my ($X, $Y) = ($this->{xm}+1, $this->{ym}+1); |
1241
|
0
|
|
|
|
|
|
my @ret = (); |
1242
|
|
|
|
|
|
|
|
1243
|
0
|
|
|
|
|
|
for my $x ( 0 .. $this->{xm} ) { |
1244
|
0
|
|
|
|
|
|
for my $y ( 0 .. $this->{ym} ) { |
1245
|
0
|
0
|
|
|
|
|
push @ret, [$x, $y] if defined $this->{_the_map}[ $y ][ $x ]{type}; # the wall type is |
1246
|
|
|
|
|
|
|
}} |
1247
|
|
|
|
|
|
|
|
1248
|
0
|
0
|
|
|
|
|
return (wantarray ? @ret:\@ret); |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
# }}} |
1251
|
|
|
|
|
|
|
# locations_in_line_of_sight {{{ |
1252
|
|
|
|
|
|
|
sub locations_in_line_of_sight { |
1253
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1254
|
0
|
0
|
|
|
|
|
my @init = @_; $this->_check_loc(\@init) or croak "that location (@_) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
|
1256
|
0
|
|
|
|
|
|
return $this->_locations_in_line_of_sight(\@init); |
1257
|
|
|
|
|
|
|
} |
1258
|
|
|
|
|
|
|
# }}} |
1259
|
|
|
|
|
|
|
# locations_in_range_and_line_of_sight {{{ |
1260
|
|
|
|
|
|
|
sub locations_in_range_and_line_of_sight { |
1261
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1262
|
0
|
0
|
|
|
|
|
my @init = splice @_,0,2; $this->_check_loc(\@init) or croak "that location (@_) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1263
|
0
|
|
0
|
|
|
|
my $range = shift || 0; |
1264
|
|
|
|
|
|
|
|
1265
|
0
|
0
|
|
|
|
|
croak "range should be greater than 0" unless $range > 0; |
1266
|
|
|
|
|
|
|
|
1267
|
0
|
|
|
|
|
|
return $this->_locations_in_range_and_line_of_sight(\@init, $range); |
1268
|
|
|
|
|
|
|
} |
1269
|
|
|
|
|
|
|
# }}} |
1270
|
|
|
|
|
|
|
# locations_in_path {{{ |
1271
|
|
|
|
|
|
|
sub locations_in_path { |
1272
|
0
|
0
|
|
0
|
1
|
|
my $this = shift; croak "you should provide 4 arguments to locations_in_path()" unless @_ == 4; |
|
0
|
|
|
|
|
|
|
1273
|
0
|
0
|
|
|
|
|
my @lhs = @_[0 .. 1]; $this->_check_loc(\@lhs) or croak "the first two arguments to locations_in_path() (@_) don't make any sense"; |
|
0
|
|
|
|
|
|
|
1274
|
0
|
0
|
|
|
|
|
my @rhs = @_[2 .. 3]; $this->_check_loc(\@rhs) or croak "the second two arguments to locations_in_path() (@_) don't make any sense"; |
|
0
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
|
1276
|
0
|
0
|
|
|
|
|
croak "the target location doesn't appear to be visible from the source" |
1277
|
|
|
|
|
|
|
unless $this->_line_of_sight(\@lhs => \@rhs); |
1278
|
|
|
|
|
|
|
|
1279
|
0
|
|
|
|
|
|
return $this->_locations_in_path(\@lhs => \@rhs); |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
# }}} |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
# ranged_cover {{{ |
1284
|
|
|
|
|
|
|
sub ranged_cover { |
1285
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1286
|
0
|
0
|
|
|
|
|
my @l = @_[0 .. 1]; $this->_check_loc(\@l) or croak "the left location (@l) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1287
|
0
|
0
|
|
|
|
|
my @r = @_[2 .. 3]; $this->_check_loc(\@r) or croak "the right location (@r) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
|
1289
|
0
|
|
|
|
|
|
return $this->_ranged_cover(\@l=>\@r); |
1290
|
|
|
|
|
|
|
} |
1291
|
|
|
|
|
|
|
# }}} |
1292
|
|
|
|
|
|
|
# melee_cover {{{ |
1293
|
|
|
|
|
|
|
sub melee_cover { |
1294
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1295
|
0
|
0
|
|
|
|
|
my @l = @_[0 .. 1]; $this->_check_loc(\@l) or croak "the left location (@l) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1296
|
0
|
0
|
|
|
|
|
my @r = @_[2 .. 3]; $this->_check_loc(\@r) or croak "the right location (@r) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
|
1298
|
0
|
|
|
|
|
|
return $this->_melee_cover(\@r=>\@l); |
1299
|
|
|
|
|
|
|
} |
1300
|
|
|
|
|
|
|
# }}} |
1301
|
|
|
|
|
|
|
# ignorable_cover {{{ |
1302
|
|
|
|
|
|
|
sub ignorable_cover { |
1303
|
0
|
|
|
0
|
0
|
|
my $this = shift; |
1304
|
0
|
0
|
|
|
|
|
my @l = @_[0 .. 1]; $this->_check_loc(\@l) or croak "the left location (@l) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1305
|
0
|
0
|
|
|
|
|
my @r = @_[2 .. 3]; $this->_check_loc(\@r) or croak "the right location (@r) doesn't make any sense"; |
|
0
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
|
1307
|
0
|
|
|
|
|
|
return $this->_ignorable_cover(\@r=>\@l); |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
# }}} |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
# is_open {{{ |
1312
|
|
|
|
|
|
|
sub is_open { |
1313
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1314
|
0
|
|
|
|
|
|
my @loc = @_[0 .. 1]; |
1315
|
|
|
|
|
|
|
|
1316
|
0
|
|
|
|
|
|
return $this->_check_loc(\@loc); |
1317
|
|
|
|
|
|
|
} |
1318
|
|
|
|
|
|
|
# }}} |
1319
|
|
|
|
|
|
|
# is_door_open {{{ |
1320
|
|
|
|
|
|
|
sub is_door_open { |
1321
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1322
|
0
|
|
|
|
|
|
my @loc = @_[0 .. 1]; |
1323
|
0
|
0
|
|
|
|
|
my $dir = lc $_[2]; $dir = $1 if $dir =~ m/^([nsew])./i; |
|
0
|
|
|
|
|
|
|
1324
|
0
|
|
|
|
|
|
my $door; |
1325
|
|
|
|
|
|
|
|
1326
|
0
|
0
|
|
|
|
|
croak "that location doesn't make sense" unless $this->_check_loc(\@loc); |
1327
|
0
|
0
|
|
|
|
|
croak "there isn't a door there" unless ref ($door = $this->{_the_map}[ $loc[1] ][ $loc[0] ]{od}{$dir}); |
1328
|
|
|
|
|
|
|
|
1329
|
0
|
|
|
|
|
|
return $door->{'open'}; |
1330
|
|
|
|
|
|
|
} |
1331
|
|
|
|
|
|
|
# }}} |
1332
|
|
|
|
|
|
|
# is_door {{{ |
1333
|
|
|
|
|
|
|
sub is_door { |
1334
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1335
|
0
|
|
|
|
|
|
my @loc = @_[0 .. 1]; |
1336
|
0
|
0
|
|
|
|
|
my $dir = lc $_[2]; $dir = $1 if $dir =~ m/^([nsew])./i; |
|
0
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
|
1338
|
0
|
0
|
|
|
|
|
croak "that location doesn't make sense" unless $this->_check_loc(\@loc); |
1339
|
0
|
0
|
|
|
|
|
return 1 if ref $this->{_the_map}[ $loc[1] ][ $loc[0] ]{od}{ $dir }; |
1340
|
0
|
|
|
|
|
|
return 0; |
1341
|
|
|
|
|
|
|
} |
1342
|
|
|
|
|
|
|
# }}} |
1343
|
|
|
|
|
|
|
# open_door {{{ |
1344
|
|
|
|
|
|
|
sub open_door { |
1345
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1346
|
0
|
|
|
|
|
|
my @loc = @_[0 .. 1]; |
1347
|
0
|
0
|
|
|
|
|
my $dir = lc $_[2]; $dir = $1 if $dir =~ m/^([nsew])./i; |
|
0
|
|
|
|
|
|
|
1348
|
0
|
|
|
|
|
|
my $door; |
1349
|
|
|
|
|
|
|
|
1350
|
0
|
0
|
|
|
|
|
croak "that location doesn't make sense" unless $this->_check_loc(\@loc); |
1351
|
0
|
0
|
|
|
|
|
croak "there isn't a door there" unless ref ($door = $this->{_the_map}[ $loc[1] ][ $loc[0] ]{od}{$dir}); |
1352
|
0
|
0
|
|
|
|
|
croak "that door is already open" if $door->{'open'}; |
1353
|
|
|
|
|
|
|
|
1354
|
0
|
|
|
|
|
|
$door->{'open'} = 1; |
1355
|
0
|
|
|
|
|
|
$this->flush; |
1356
|
|
|
|
|
|
|
} |
1357
|
|
|
|
|
|
|
# }}} |
1358
|
|
|
|
|
|
|
# close_door {{{ |
1359
|
|
|
|
|
|
|
sub close_door { |
1360
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1361
|
0
|
|
|
|
|
|
my @loc = @_[0 .. 1]; |
1362
|
0
|
0
|
|
|
|
|
my $dir = lc $_[2]; $dir = $1 if $dir =~ m/^([nsew])./i; |
|
0
|
|
|
|
|
|
|
1363
|
0
|
|
|
|
|
|
my $door; |
1364
|
|
|
|
|
|
|
|
1365
|
0
|
0
|
|
|
|
|
croak "that location doesn't make sense" unless $this->_check_loc(\@loc); |
1366
|
0
|
0
|
|
|
|
|
croak "there isn't a door there" unless ref ($door = $this->{_the_map}[ $loc[1] ][ $loc[0] ]{od}{$dir}); |
1367
|
0
|
0
|
|
|
|
|
croak "that door isn't open" unless $door->{'open'}; |
1368
|
|
|
|
|
|
|
|
1369
|
0
|
|
|
|
|
|
$door->{'open'} = 0; |
1370
|
0
|
|
|
|
|
|
$this->flush; |
1371
|
|
|
|
|
|
|
} |
1372
|
|
|
|
|
|
|
# }}} |
1373
|
|
|
|
|
|
|
# map_range {{{ |
1374
|
|
|
|
|
|
|
sub map_range { |
1375
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1376
|
|
|
|
|
|
|
|
1377
|
0
|
0
|
|
|
|
|
return ( 0 .. $this->{xm} ) if wantarray; |
1378
|
0
|
|
|
|
|
|
return $this->{xm}; |
1379
|
|
|
|
|
|
|
} |
1380
|
|
|
|
|
|
|
# }}} |
1381
|
|
|
|
|
|
|
# map_domain {{{ |
1382
|
|
|
|
|
|
|
sub map_domain { |
1383
|
0
|
|
|
0
|
1
|
|
my $this = shift; |
1384
|
|
|
|
|
|
|
|
1385
|
0
|
0
|
|
|
|
|
return ( 0 .. $this->{ym} ) if wantarray; |
1386
|
0
|
|
|
|
|
|
return $this->{ym}; |
1387
|
|
|
|
|
|
|
} |
1388
|
|
|
|
|
|
|
# }}} |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
# {{{ FREEZE_THAW_HOOKS |
1391
|
|
|
|
|
|
|
FREEZE_THAW_HOOKS: { |
1392
|
|
|
|
|
|
|
my $going; |
1393
|
|
|
|
|
|
|
sub STORABLE_freeze { |
1394
|
0
|
0
|
|
0
|
0
|
|
return if $going; |
1395
|
0
|
|
|
|
|
|
my $this = shift; |
1396
|
0
|
|
|
|
|
|
$going = 1; |
1397
|
0
|
|
|
|
|
|
my $str = freeze($this); |
1398
|
0
|
|
|
|
|
|
$going = 0; |
1399
|
0
|
|
|
|
|
|
return $str; |
1400
|
|
|
|
|
|
|
} |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
sub STORABLE_thaw { |
1403
|
0
|
|
|
0
|
0
|
|
my $this = shift; |
1404
|
0
|
|
|
|
|
|
%$this = %{ thaw($_[1]) }; |
|
0
|
|
|
|
|
|
|
1405
|
0
|
|
|
|
|
|
$this->retag; |
1406
|
|
|
|
|
|
|
} |
1407
|
|
|
|
|
|
|
} |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
# }}} |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
1; |