line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright (C) 2009-2021 Alex Schroeder |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify it under |
4
|
|
|
|
|
|
|
# the terms of the GNU Affero General Public License as published by the Free |
5
|
|
|
|
|
|
|
# Software Foundation, either version 3 of the License, or (at your option) any |
6
|
|
|
|
|
|
|
# later version. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT |
9
|
|
|
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
10
|
|
|
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
11
|
|
|
|
|
|
|
# details. |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License along |
14
|
|
|
|
|
|
|
# with this program. If not, see . |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=encoding utf8 |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Game::TextMapper::Schroeder::Base - a base role for map generators |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# create a map |
25
|
|
|
|
|
|
|
package World; |
26
|
|
|
|
|
|
|
use Modern::Perl; |
27
|
|
|
|
|
|
|
use Game::TextMapper::Schroeder::Base; |
28
|
|
|
|
|
|
|
use Mojo::Base -base; |
29
|
|
|
|
|
|
|
use Role::Tiny::With; |
30
|
|
|
|
|
|
|
with 'Game::TextMapper::Schroeder::Base'; |
31
|
|
|
|
|
|
|
# use it |
32
|
|
|
|
|
|
|
package main; |
33
|
|
|
|
|
|
|
my $map = World->new(height => 10, width => 10); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Map generators that work for both hex maps and square maps use this role and |
38
|
|
|
|
|
|
|
either the Hex or Square role to provide basic functionality for their regions, |
39
|
|
|
|
|
|
|
such as the number of neighbours they have (six or four). |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=cut |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
package Game::TextMapper::Schroeder::Base; |
44
|
1
|
|
|
1
|
|
775
|
use Game::TextMapper::Point; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
11
|
|
45
|
1
|
|
|
1
|
|
39
|
use Modern::Perl '2018'; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
12
|
|
46
|
1
|
|
|
1
|
|
254
|
use Mojo::Base -role; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# We're assuming that $width and $height have two digits (10 <= n <= 99). |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
has width => 30; |
51
|
|
|
|
|
|
|
has height => 10; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub coordinates { |
54
|
41951
|
|
|
41951
|
0
|
54583
|
my ($x, $y) = @_; |
55
|
41951
|
|
|
|
|
65160
|
return Game::TextMapper::Point::coord($x, $y); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 METHODS |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head2 xy($coordinates) |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
C<$coordinates> is a string with four digites and interpreted as coordinates and |
63
|
|
|
|
|
|
|
returned, e.g. returns (2, 3) for "0203". |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub xy { |
68
|
56117
|
|
|
56117
|
1
|
66056
|
my $self = shift; |
69
|
56117
|
|
|
|
|
63508
|
my $coordinates = shift; |
70
|
56117
|
|
|
|
|
149934
|
return (substr($coordinates, 0, 2), substr($coordinates, 2)); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 legal($x, $y) or $legal($coordinates) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
say "legal" if $map->legal(10,10); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Turn $coordinates into ($x, $y), assuming each to be two digits, i.e. "0203" |
78
|
|
|
|
|
|
|
turns into (2, 3). |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Return ($x, $y) if the coordinates are legal, i.e. on the map. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=cut |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub legal { |
85
|
40631
|
|
|
40631
|
1
|
48866
|
my $self = shift; |
86
|
40631
|
|
|
|
|
51315
|
my ($x, $y) = @_; |
87
|
40631
|
100
|
|
|
|
60114
|
($x, $y) = $self->xy($x) if not defined $y; |
88
|
40631
|
100
|
100
|
|
|
85322
|
return @_ if $x > 0 and $x <= $self->width and $y > 0 and $y <= $self->height; |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 remove_closer_than($limit, @coordinates) |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Each element of @coordinates is a string with four digites and interpreted as |
94
|
|
|
|
|
|
|
coordinates, e.g. "0203" is treated as (2, 3). Returns a list where each element |
95
|
|
|
|
|
|
|
is no closer than $limit to any existing element. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
This depends on L being used as a role by a |
98
|
|
|
|
|
|
|
class that implements C. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub remove_closer_than { |
103
|
20
|
|
|
20
|
1
|
46
|
my $self = shift; |
104
|
20
|
|
|
|
|
66
|
my ($limit, @hexes) = @_; |
105
|
20
|
|
|
|
|
27
|
my @filtered; |
106
|
|
|
|
|
|
|
HEX: |
107
|
20
|
|
|
|
|
56
|
for my $hex (@hexes) { |
108
|
381
|
|
|
|
|
566
|
my ($x1, $y1) = $self->xy($hex); |
109
|
|
|
|
|
|
|
# check distances with all the hexes already in the list |
110
|
381
|
|
|
|
|
556
|
for my $existing (@filtered) { |
111
|
4290
|
|
|
|
|
5836
|
my ($x2, $y2) = $self->xy($existing); |
112
|
4290
|
|
|
|
|
7507
|
my $distance = $self->distance($x1, $y1, $x2, $y2); |
113
|
|
|
|
|
|
|
# warn "Distance between $x1$y1 and $x2$y2 is $distance\n"; |
114
|
4290
|
100
|
|
|
|
7818
|
next HEX if $distance < $limit; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
# if this hex wasn't skipped, it goes on to the list |
117
|
124
|
|
|
|
|
317
|
push(@filtered, $hex); |
118
|
|
|
|
|
|
|
} |
119
|
20
|
|
|
|
|
123
|
return @filtered; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 flat($altitude) |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my $altitude = {}; |
125
|
|
|
|
|
|
|
$map->flat($altitude); |
126
|
|
|
|
|
|
|
say $altitude->{"0203"}; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Initialize the altitude map; this is required so that we have a list of legal |
129
|
|
|
|
|
|
|
hex coordinates somewhere. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub flat { |
134
|
4
|
|
|
4
|
1
|
11
|
my $self = shift; |
135
|
4
|
|
|
|
|
9
|
my ($altitude) = @_; |
136
|
4
|
|
|
|
|
17
|
for my $y (1 .. $self->height) { |
137
|
50
|
|
|
|
|
138
|
for my $x (1 .. $self->width) { |
138
|
1800
|
|
|
|
|
2478
|
my $coordinates = coordinates($x, $y); |
139
|
1800
|
|
|
|
|
4101
|
$altitude->{$coordinates} = 0; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 direction($from, $to) |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Return the direction (an integer) to step from C<$from> to reach C<$to>. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
This depends on L being used as a role by a |
149
|
|
|
|
|
|
|
class that implements C and C. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=cut |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub direction { |
154
|
136
|
|
|
136
|
1
|
170
|
my $self = shift; |
155
|
136
|
|
|
|
|
224
|
my ($from, $to) = @_; |
156
|
136
|
|
|
|
|
249
|
for my $i ($self->neighbors()) { |
157
|
347
|
100
|
|
|
|
634
|
return $i if $to eq coordinates($self->neighbor($from, $i)); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head1 SEE ALSO |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
L and L |
164
|
|
|
|
|
|
|
both use this class to provide common functionality. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=cut |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
1; |