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
|
|
500
|
use Game::TextMapper::Point; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
45
|
1
|
|
|
1
|
|
39
|
use Modern::Perl '2018'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
7
|
|
46
|
1
|
|
|
1
|
|
131
|
use Mojo::Base -role; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
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
|
40819
|
|
|
40819
|
0
|
55796
|
my ($x, $y) = @_; |
55
|
40819
|
|
|
|
|
62826
|
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
|
54359
|
|
|
54359
|
1
|
61543
|
my $self = shift; |
69
|
54359
|
|
|
|
|
61308
|
my $coordinates = shift; |
70
|
54359
|
|
|
|
|
138504
|
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
|
41074
|
|
|
41074
|
1
|
48674
|
my $self = shift; |
86
|
41074
|
|
|
|
|
51462
|
my ($x, $y) = @_; |
87
|
41074
|
100
|
|
|
|
61935
|
($x, $y) = $self->xy($x) if not defined $y; |
88
|
41074
|
100
|
100
|
|
|
85418
|
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
|
36
|
my $self = shift; |
104
|
20
|
|
|
|
|
59
|
my ($limit, @hexes) = @_; |
105
|
20
|
|
|
|
|
29
|
my @filtered; |
106
|
|
|
|
|
|
|
HEX: |
107
|
20
|
|
|
|
|
37
|
for my $hex (@hexes) { |
108
|
359
|
|
|
|
|
518
|
my ($x1, $y1) = $self->xy($hex); |
109
|
|
|
|
|
|
|
# check distances with all the hexes already in the list |
110
|
359
|
|
|
|
|
534
|
for my $existing (@filtered) { |
111
|
3854
|
|
|
|
|
5422
|
my ($x2, $y2) = $self->xy($existing); |
112
|
3854
|
|
|
|
|
6625
|
my $distance = $self->distance($x1, $y1, $x2, $y2); |
113
|
|
|
|
|
|
|
# warn "Distance between $x1$y1 and $x2$y2 is $distance\n"; |
114
|
3854
|
100
|
|
|
|
6604
|
next HEX if $distance < $limit; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
# if this hex wasn't skipped, it goes on to the list |
117
|
120
|
|
|
|
|
186
|
push(@filtered, $hex); |
118
|
|
|
|
|
|
|
} |
119
|
20
|
|
|
|
|
85
|
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
|
10
|
my $self = shift; |
135
|
4
|
|
|
|
|
10
|
my ($altitude) = @_; |
136
|
4
|
|
|
|
|
14
|
for my $y (1 .. $self->height) { |
137
|
50
|
|
|
|
|
127
|
for my $x (1 .. $self->width) { |
138
|
1800
|
|
|
|
|
2531
|
my $coordinates = coordinates($x, $y); |
139
|
1800
|
|
|
|
|
4261
|
$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
|
108
|
|
|
108
|
1
|
127
|
my $self = shift; |
155
|
108
|
|
|
|
|
159
|
my ($from, $to) = @_; |
156
|
108
|
|
|
|
|
184
|
for my $i ($self->neighbors()) { |
157
|
317
|
100
|
|
|
|
579
|
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; |