ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra/Deliantra/Map.pm
Revision: 1.11
Committed: Fri Mar 17 23:25:50 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.10: +3 -3 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Crossfire::Map - represent a crossfire map
4    
5     =cut
6    
7     package Crossfire::Map;
8    
9     our $VERSION = '0.1';
10    
11     use strict;
12    
13 root 1.2 use Carp ();
14 root 1.1 use Crossfire;
15    
16     use base 'Exporter';
17    
18     sub new {
19     my ($class, $width, $height) = @_;
20    
21 elmex 1.8 bless { info => { _name => 'map' }, width => $width, height => $height }, $class
22 root 1.1 }
23    
24     sub new_from_file {
25     new_from_archlist {$_[0]} read_arch $_[1]
26     }
27    
28     sub new_from_archlist {
29     my ($class, $mapa) = @_;
30    
31     my %meta;
32    
33     my ($mapx, $mapy);
34    
35     my $map;
36    
37     for (@{ $mapa->{arch} }) {
38     my ($x, $y) = (delete $_->{x}, delete $_->{y});
39    
40     if ($_->{_name} eq "map") {
41     $meta{info} = $_;
42    
43     $mapx = $_->{width} || $x;
44     $mapy = $_->{height} || $y;
45     } else {
46     push @{ $map->[$x][$y] }, $_;
47    
48     # arch map is unreliable w.r.t. width and height
49     $mapx = $x + 1 if $mapx <= $x;
50     $mapy = $y + 1 if $mapy <= $y;
51     #$mapx = $a->{x} + 1, warn "$mapname: arch '$a->{_name}' outside map width at ($a->{x}|$a->{y})\n" if $mapx <= $a->{x};
52     #$mapy = $a->{y} + 1, warn "$mapname: arch '$a->{_name}' outside map height at ($a->{x}|$a->{y})\n" if $mapy <= $a->{y};
53     }
54     }
55    
56     $meta{width} = $mapx;
57     $meta{height} = $mapy;
58     $meta{map} = $map;
59    
60     bless \%meta, $class
61     }
62    
63     sub new_pickmap {
64     my ($class, $archs, $width) = @_;
65    
66     # sort archs alphabetically
67     my $archs = [ sort { ${$a}->{_name} cmp ${$b}->{_name} } @$archs ];
68    
69     $width ||= 10; # default width
70    
71     my $num = @$archs;
72     my $map = { };
73     # overall placement coords
74     my $x = 0;
75     my $y = 0;
76    
77     my ($maxh, $maxw) = (0, 0); # maximum sizes, to set map width/height later
78     my $max_line_height = 1;
79    
80     for (my $i = 0; $i < $num; $i++) {
81     # check whether this tile was already written (see below at (b))
82     unless (defined $map->{map}[$x][$y]) {
83 root 1.5 my ($x1, $y1, $x2, $y2) = arch_extents ${$archs->[$i]};
84 root 1.1
85 root 1.5 if ($x + $x2 - $x1 + 1 > $width) {
86 root 1.1 $y += $max_line_height;
87     $max_line_height = 1;
88     $x = 0;
89     }
90    
91 root 1.5 $map->{map}[$x - $x1][$y - $y1] = [${$archs->[$i]}];
92 root 1.1
93 root 1.5 $x += $x2 - $x1 + 1;
94 root 1.1
95 root 1.5 $max_line_height = List::Util::max $max_line_height, $y2 - $y1 + 1;
96 root 1.1
97     } else {
98     $i--;
99     }
100    
101     $maxw = List::Util::max $maxw, $x;
102 root 1.7 $maxh = List::Util::max $maxh, $y + $max_line_height;
103 root 1.1 }
104    
105     $map->{height} = $maxh;
106     $map->{width} = $maxw;
107    
108 root 1.2 $map
109     }
110    
111     sub resize {
112     my ($self, $width, $height) = @_;
113    
114     $self->{width} = $width;
115     $self->{height} = $height;
116    
117     # i am sure this can be done more elegantly
118     @{$self->{map}} = @{$self->{map}}[0 .. $width - 1];
119    
120     for (@{$self->{map}}) {
121     @$_ = @$_[0 .. $height - 1];
122     }
123     }
124    
125     sub as_archlist {
126     my ($self) = @_;
127    
128     # wing map so we have no extra-map arches
129     $self->resize ($self->{width}, $self->{height});
130    
131     my @arch;
132    
133     for my $x (0 .. $self->{width} - 1) {
134     my $ass = $self->{map}[$x];
135     for my $y (0 .. $self->{height} - 1) {
136     for my $a (@{ $ass->[$y] || [] }) {
137 root 1.6 next if $a->{_virtual};
138    
139 root 1.2 # note: big faces _may_ span map boundaries
140    
141     my %a = %$a;
142     delete $a{x};
143     delete $a{y};
144     $a{x} = $x if $x;
145     $a{y} = $y if $y;
146    
147     push @arch, \%a;
148     }
149     }
150     }
151    
152     # now assemble meta info
153     my %meta = %{$self->{info}};
154    
155 root 1.4 $meta{width} = $self->{width};
156     $meta{height} = $self->{height};
157    
158     unshift @arch, Crossfire::normalize_arch \%meta;
159 root 1.2
160     \@arch
161     }
162    
163     sub as_mapstring {
164     my ($self) = @_;
165    
166     my $arch = $self->as_archlist;
167    
168     my $str;
169    
170     my $append; $append = sub {
171     my %a = %{$_[0]};
172    
173 root 1.4 Crossfire::normalize_arch \%a;
174    
175 root 1.2 $str .= "arch " . (delete $a{_name}) . "\n";
176    
177 root 1.10 my $inv = delete $a{inventory};
178 root 1.2
179 root 1.3 # put inventory last
180 root 1.2 for my $k (sort keys %a) {
181 root 1.6 next if $k =~ /^_/; # ignore our _-keys
182    
183 root 1.2 if (my $end = $Crossfire::FIELD_MULTILINE{$k}) {
184     $a{$k} =~ s/\n$//;
185     $str .= "$k\n$a{$k}\n$end\n";
186     } else {
187     $str .= "$k $a{$k}\n";
188     }
189     }
190    
191 root 1.3 if ($inv) {
192     $append->($_) for @$inv;
193     }
194    
195 root 1.2 $str .= "end\n";
196    
197     };
198    
199     for (@$arch) {
200     $append->($_);
201     }
202    
203     $str
204     }
205    
206     sub write_file {
207     my ($self, $path) = @_;
208    
209 root 1.11 open my $fh, ">:raw", "$path~" or Carp::croak "$path~: $!";
210     print $fh $self->as_mapstring or Carp::croak "$path~: $!";
211     close $fh or Carp::croak "$path~: $!";
212 root 1.2
213     rename "$path~", $path;
214 root 1.1 }
215    
216     =head1 AUTHOR
217    
218     Marc Lehmann <schmorp@schmorp.de>
219     http://home.schmorp.de/
220    
221     Robin Redeker <elmex@ta-sa.org>
222     http://www.ta-sa.org/
223    
224     =cut
225    
226     1