ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra/Deliantra.pm
Revision: 1.14
Committed: Sat Feb 11 16:17:14 2006 UTC (18 years, 3 months ago) by root
Branch: MAIN
Changes since 1.13: +72 -8 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 elmex 1.1 =head1 NAME
2    
3     Crossfire - Crossfire maphandling
4    
5     =cut
6    
7 root 1.4 package Crossfire;
8    
9 elmex 1.3 our $VERSION = '0.1';
10 elmex 1.1
11     use strict;
12    
13 root 1.7 use base 'Exporter';
14    
15 root 1.13 use Carp ();
16 elmex 1.1 use Storable;
17    
18 elmex 1.11 #XXX: The map_* procedures scream for a map-object
19    
20     our @EXPORT =
21 root 1.12 qw(read_pak read_arch $ARCH TILESIZE editor_archs
22     arch_extends
23 elmex 1.11 map_get_tile_stack map_push_tile_stack map_pop_tile_stack
24     );
25 root 1.7
26 root 1.4 our $LIB = $ENV{CROSSFIRE_LIBDIR}
27 root 1.13 or Carp::croak "\$CROSSFIRE_LIBDIR must be set\n";
28 elmex 1.1
29 root 1.7 sub TILESIZE (){ 32 }
30 elmex 1.1
31 root 1.4 our $ARCH;
32 elmex 1.1
33 root 1.13 our %FIELD_MULTILINE = (
34 root 1.14 msg => "endmsg",
35     lore => "endlore",
36 root 1.13 );
37    
38     # not used yet, maybe alphabetical is ok
39     our @FIELD_ORDER = (qw(name name_pl));
40    
41 root 1.14 sub MOVE_WALK (){ 0x1 }
42     sub MOVE_FLY_LOW (){ 0x2 }
43     sub MOVE_FLY_HIGH (){ 0x4 }
44     sub MOVE_FLYING (){ 0x6 }
45     sub MOVE_SWIM (){ 0x8 }
46     sub MOVE_ALL (){ 0xf }
47    
48     sub normalize_arch($) {
49     my ($ob) = @_;
50    
51     my $arch = $ARCH->{$ob->{_name}}
52     or (warn "$ob->{_name}: no such archetype", return $ob);
53    
54     delete $ob->{$_} for qw(can_knockback can_parry can_impale can_cut can_dam_armour can_apply);
55    
56     if ($arch->{type} == 22) { # map
57     my %normalize = (
58     "enter_x" => "hp",
59     "enter_y" => "sp",
60     "width" => "x",
61     "height" => "y",
62     "reset_timeout" => "weight",
63     "swap_time" => "value",
64     "difficulty" => "level",
65     "darkness" => "invisible",
66     "fixed_resettime" => "stand_still",
67     );
68    
69     while (my ($k2, $k1) = each %normalize) {
70     if (defined (my $v = delete $ob->{$k1})) {
71     $ob->{$k2} = $v;
72     }
73     }
74     }
75    
76     if (defined (my $v = delete $ob->{no_pass})) {
77     $ob->{move_block} = $v ? MOVE_ALL : 0;
78     }
79     if (defined (my $v = delete $ob->{walk_on})) {
80     $ob->{move_on} = $v ? $ob->{move_on} | MOVE_WALK
81     : $ob->{move_on} & ~MOVE_WALK;
82     }
83     if (defined (my $v = delete $ob->{walk_off})) {
84     $ob->{move_off} = $v ? $ob->{move_off} | MOVE_WALK
85     : $ob->{move_off} & ~MOVE_WALK;
86     }
87     if (defined (my $v = delete $ob->{fly_on})) {
88     $ob->{move_on} = $v ? $ob->{move_on} | MOVE_FLY_LOW
89     : $ob->{move_on} & ~MOVE_FLY_LOW;
90     }
91     if (defined (my $v = delete $ob->{fly_off})) {
92     $ob->{move_off} = $v ? $ob->{move_off} | MOVE_FLY_LOW
93     : $ob->{move_off} & ~MOVE_FLY_LOW;
94     }
95     if (defined (my $v = delete $ob->{flying})) {
96     $ob->{move_type} = $v ? $ob->{move_type} | MOVE_FLY_LOW
97     : $ob->{move_type} & ~MOVE_FLY_LOW;
98     }
99    
100     # if value matches archetype default, delete
101     while (my ($k, $v) = each %$ob) {
102     if (exists $arch->{$k} and $arch->{$k} eq $v) {
103     delete $ob->{$k};
104     }
105     }
106    
107     $ob
108     }
109 root 1.13
110 root 1.4 sub read_pak($;$) {
111     my ($path, $cache) = @_;
112 elmex 1.1
113     eval {
114 root 1.4 defined $cache
115     && -M $cache < -M $path
116     && Storable::retrieve $cache
117 elmex 1.1 } or do {
118     my %pak;
119    
120     open my $fh, "<:raw", $path
121 root 1.13 or Carp::croak "$_[0]: $!";
122 elmex 1.1 while (<$fh>) {
123     my ($type, $id, $len, $path) = split;
124     $path =~ s/.*\///;
125     read $fh, $pak{$path}, $len;
126     }
127    
128 root 1.4 Storable::nstore \%pak, $cache
129     if defined $cache;
130 elmex 1.1
131     \%pak
132     }
133     }
134    
135     sub read_arch($;$) {
136 root 1.4 my ($path, $cache) = @_;
137    
138     eval {
139     defined $cache
140     && -M $cache < -M $path
141     && Storable::retrieve $cache
142     } or do {
143     my %arc;
144     my ($more, $prev);
145    
146     open my $fh, "<:raw", $path
147 root 1.13 or Carp::croak "$path: $!";
148 elmex 1.1
149 root 1.4 my $parse_block; $parse_block = sub {
150     my %arc = @_;
151 elmex 1.2
152 root 1.4 while (<$fh>) {
153     s/\s+$//;
154     if (/^end$/i) {
155     last;
156     } elsif (/^arch (\S+)$/) {
157 root 1.14 push @{ $arc{inventory} }, normalize_arch $parse_block->(_name => $1);
158 root 1.4 } elsif (/^lore$/) {
159     while (<$fh>) {
160     last if /^endlore\s*$/i;
161     $arc{lore} .= $_;
162     }
163     } elsif (/^msg$/) {
164     while (<$fh>) {
165     last if /^endmsg\s*$/i;
166     $arc{msg} .= $_;
167     }
168     } elsif (/^(\S+)\s*(.*)$/) {
169     $arc{lc $1} = $2;
170     } elsif (/^\s*($|#)/) {
171     #
172     } else {
173     warn "$path: unparsable line '$_' in arch $arc{_name}";
174     }
175     }
176 elmex 1.1
177 root 1.4 \%arc
178     };
179 elmex 1.1
180     while (<$fh>) {
181     s/\s+$//;
182 root 1.4 if (/^more$/i) {
183     $more = $prev;
184     } elsif (/^object (\S+)$/i) {
185     my $name = $1;
186     my $arc = $parse_block->(_name => $name);
187    
188     if ($more) {
189     $more->{more} = $arc;
190     } else {
191     $arc{$name} = $arc;
192 elmex 1.1 }
193 root 1.4 $prev = $arc;
194     $more = undef;
195     } elsif (/^arch (\S+)$/i) {
196 root 1.14 push @{ $arc{arch} }, normalize_arch $parse_block->(_name => $1);
197 elmex 1.1 } elsif (/^\s*($|#)/) {
198     #
199     } else {
200 root 1.4 warn "$path: unparseable top-level line '$_'";
201 elmex 1.1 }
202     }
203    
204 root 1.4 undef $parse_block; # work around bug in perl not freeing $fh etc.
205 elmex 1.2
206 root 1.4 Storable::nstore \%arc, $cache
207     if defined $cache;
208 elmex 1.1
209 root 1.4 \%arc
210 elmex 1.2 }
211 elmex 1.1 }
212    
213 elmex 1.11 # returns the arch/object stack from a tile on a map
214     sub map_get_tile_stack {
215     my ($map, $x, $y) = @_;
216     my $as;
217    
218     if ($x > 0 || $x < $map->{width}
219     || $y > 0 || $y < $map->{height}) {
220    
221     $as = $map->{map}{map}[$x][$y] || [];
222     }
223    
224     return $as;
225     }
226    
227     # pop the topmost arch/object from the stack of a tile on a map
228     sub map_pop_tile_stack {
229     my ($map, $x, $y) = @_;
230    
231     if ($x > 0 || $x < $map->{width}
232     || $y > 0 || $y < $map->{height}) {
233    
234     pop @{$map->{map}{map}[$x][$y]};
235     }
236     }
237    
238     # pushes the arch/object on the stack of a tile on a map
239     sub map_push_tile_stack {
240     my ($map, $x, $y, $arch) = @_;
241    
242     if ($x > 0 || $x < $map->{width}
243     || $y > 0 || $y < $map->{height}) {
244    
245     push @{$map->{map}{map}[$x][$y]}, $arch;
246     }
247     }
248    
249    
250 elmex 1.10 # put all archs into a hash with editor_face as it's key
251     # NOTE: the arrays in the hash values are references to
252     # the archs from $ARCH
253     sub editor_archs {
254     my %paths;
255    
256     for (keys %$ARCH) {
257     my $arch = $ARCH->{$_};
258     push @{$paths{$arch->{editor_folder}}}, \$arch;
259     }
260    
261     return \%paths;
262     }
263    
264     # arch_extends determines how the arch looks like on the map,
265     # bigfaces, linked faces and single faces are handled here
266     # it returns (<xoffset>, <yoffset>, <width>, <height>)
267     # NOTE: non rectangular linked faces are not considered
268     sub arch_extends {
269     my ($a) = @_;
270    
271     my $TC = \%Crossfire::Tilecache::TILECACHE;
272    
273     my $facename =
274     $a->{face} || $ARCH->{$a->{_name}}->{face}
275     or return ();
276    
277     my $tile = $TC->{$facename}
278     or (warn "no gfx found for arch '$facename' in arch_size ()"), return;
279    
280     if ($tile->{w} > 1 || $tile->{h} > 1) {
281     # bigfaces
282     return (0, 0, $tile->{w}, $tile->{h});
283    
284     } elsif ($a->{more}) {
285     # linked faces
286     my ($miw, $mih, $maw, $mah) = (0, 0, 0, 0);
287     do {
288     $miw > (0 + $a->{x}) and $miw = $a->{x};
289     $mih > (0 + $a->{y}) and $mih = $a->{y};
290     $maw < (0 + $a->{x}) and $maw = $a->{x};
291     $mah < (0 + $a->{y}) and $mah = $a->{y};
292     } while $a = $a->{more};
293    
294     return ($miw, $mih, ($maw - $miw) + 1, ($mah - $mih) + 1)
295    
296     } else {
297     # single face
298     return (0, 0, 1, 1);
299     }
300     }
301    
302 root 1.4 sub init($) {
303     my ($cachedir) = @_;
304 elmex 1.1
305 root 1.4 $ARCH = read_arch "$LIB/archetypes", "$cachedir/archetypes.pst";
306 elmex 1.1 }
307    
308     =head1 AUTHOR
309    
310     Marc Lehmann <schmorp@schmorp.de>
311     http://home.schmorp.de/
312    
313     Robin Redeker <elmex@ta-sa.org>
314     http://www.ta-sa.org/
315    
316     =cut
317 root 1.4
318     1