ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/Util.pm
Revision: 1.32
Committed: Mon Nov 2 12:33:36 2009 UTC (14 years, 7 months ago) by elmex
Branch: MAIN
CVS Tags: HEAD
Changes since 1.31: +1 -3 lines
Log Message:
fixed a bug in above/below floor.

File Contents

# User Rev Content
1 elmex 1.1 package GCE::Util;
2     =head1 NAME
3    
4     GCE::Util - some utility functions
5    
6     =over 4
7    
8     =cut
9    
10     use base 'Exporter';
11    
12 root 1.29 use Deliantra;
13 elmex 1.1
14     use Carp ();
15     use Storable;
16     use List::Util qw(min max);
17    
18 root 1.29 use Deliantra;
19     use Deliantra::MapWidget;
20 elmex 1.8 use File::Spec::Functions;
21 elmex 1.21 use File::Basename;
22     use File::Path;
23 elmex 1.30 #use HTTP::Request::Common;
24 elmex 1.13 use Cwd 'abs_path';
25 elmex 1.23 use strict;
26 elmex 1.2
27 elmex 1.18 our @EXPORT = qw(insert_arch_stack_layer replace_arch_stack_layer new_arch_pb
28     fill_pb_from_arch arch_is_floor stack_find_floor stack_find_wall
29     stack_find arch_is_wall arch_is_monster add_table_widget quick_msg
30 elmex 1.26 def arch_is_exit map2abs exit_paths pseudohtml2txt arch_is_connector
31 elmex 1.27 gtk2_get_color devirtualize);
32 elmex 1.14
33 elmex 1.26 my %allocated_colors;
34    
35 elmex 1.27 sub devirtualize {
36     my ($map, $x, $y, $arch, $stack) = @_;
37     if ($arch->{_virtual}) {
38     my @head = $map->get_head ($arch);
39     if (@head) {
40     return ($head[0], $head[1], $head[3]->[$head[2]], $head[3])
41     }
42     }
43     return ($x, $y, $arch, $stack)
44    
45     }
46    
47 elmex 1.26 sub gtk2_get_color {
48     my ($widget, $name) = @_;
49     my $colormap = $widget->{window}->get_colormap;
50     my $ret;
51    
52     if ($ret = $allocated_colors{$name}) {
53     return $ret;
54     }
55    
56     my $color = Gtk2::Gdk::Color->parse($name);
57     $colormap->alloc_color($color,1,1);
58    
59     $allocated_colors{$name} = $color;
60    
61     return $color;
62     }
63 elmex 1.14 sub pseudohtml2txt {
64     my ($html) = @_;
65    
66     $html =~ s/<br\s*?\/?>/\n/gsi;
67     $html =~ s/<b>(.*?)<\/b>/_\1_/gsi;
68 elmex 1.16 $html =~ s/<li>/\n* /gi;
69 elmex 1.15 $html =~ s/<\/?\s*li>//gi;
70     $html =~ s/<\/?\s*ul>//gi;
71     $html =~ s/&gt;/>/g;
72     $html =~ s/&lt;/</g;
73 elmex 1.14 $html
74     }
75 elmex 1.13
76     sub exit_paths {
77     my ($mappath, $map1path, $map2path) = @_;
78     $mappath = abs_path $mappath;
79     $map1path = abs_path $map1path;
80     $map2path = abs_path $map2path;
81    
82     if ( (substr $map1path, 0, length $mappath) eq $mappath
83     and (substr $map2path, 0, length $mappath) eq $mappath) {
84     substr $map1path, 0, length $mappath, '';
85     substr $map2path, 0, length $mappath, '';
86    
87     my ($v1, $d1, $f1) = File::Spec->splitpath ($map1path);
88     my ($v2, $d2, $f2) = File::Spec->splitpath ($map2path);
89    
90     my @di1 = File::Spec->splitdir ($d1);
91     my @di2 = File::Spec->splitdir ($d2);
92    
93     if ((defined $di1[1]) and (defined $di2[1]) and $di1[1] eq $di2[1]) {
94     my $m1 = File::Spec->abs2rel ($map1path, File::Spec->catdir (@di2));
95     my $m2 = File::Spec->abs2rel ($map2path, File::Spec->catdir (@di1));
96 elmex 1.31 $m1 =~ s/\.map$//;
97     $m2 =~ s/\.map$//;
98 elmex 1.13 return ($m1, $m2);
99     } else {
100 elmex 1.31 $map1path =~ s/\.map$//;
101     $map2path =~ s/\.map$//;
102 elmex 1.13 return ($map1path, $map2path);
103     }
104     } else {
105     return ('', '');
106     }
107     }
108 elmex 1.8
109     sub map2abs {
110     my ($dest, $mape) = @_;
111    
112 elmex 1.24 #$dest = abs_path $dest;
113 elmex 1.8 my $dir;
114     if (File::Spec->file_name_is_absolute($dest)) {
115 elmex 1.21 $dir = catdir ($::MAPDIR, $dest);
116 elmex 1.8 } else {
117     my ($v, $p, $f) = File::Spec->splitpath ($mape->{path});
118     $dir = File::Spec->rel2abs ($dest, File::Spec->catpath ($v, $p));
119     }
120     return $dir;
121     }
122 elmex 1.5
123     sub def($$) {
124     return defined ($_[0]) ? $_[0] : $_[1];
125     }
126    
127     sub quick_msg {
128     my $wid = shift;
129     my $msg;
130     my $win = $::MAINWIN;
131     if (ref $wid) {
132     $win = $wid;
133     $msg = shift;
134     } else {
135     $msg = $wid;
136     }
137     my $dia = Gtk2::Dialog->new ('Message', $win, 'destroy-with-parent', 'gtk-ok' => 'none');
138    
139     my $lbl = Gtk2::Label->new ($msg);
140     $dia->vbox->add ($lbl);
141     $dia->signal_connect (response => sub { $_[0]->destroy });
142    
143     unless (defined $_[0]) {
144     Glib::Timeout->add (1000, sub { $dia->destroy; 0 });
145     }
146    
147     $dia->show_all;
148     }
149 elmex 1.2
150     sub new_arch_pb {
151     # this is awful, is this really the best way?
152     my $pb = new Gtk2::Gdk::Pixbuf 'rgb', 1, 8, TILESIZE, TILESIZE;
153 elmex 1.25 fill_pb_from_arch ($pb, {});
154 elmex 1.2 return $pb;
155     }
156    
157     sub fill_pb_from_arch {
158 elmex 1.6 my ($pb, $a) = @_;
159    
160 root 1.29 my $o = $Deliantra::ARCH{$a->{_name}} || {};
161     my $face = $Deliantra::FACE{$a->{face} || $o->{face} || "blank.111"};
162 elmex 1.23 unless ($face) {
163 root 1.29 $face = $Deliantra::FACE{"blank.x11"}
164 elmex 1.23 or warn "no gfx found for arch '$a->{_name}'\n";
165     }
166 elmex 1.6
167     $face or return;
168 elmex 1.2
169     $pb->fill (0x00000000);
170     $TILE->composite ($pb,
171     0, 0,
172     TILESIZE, TILESIZE,
173 elmex 1.6 - ($face->{idx} % 64) * TILESIZE, - TILESIZE * int $face->{idx} / 64,
174 elmex 1.2 1, 1, 'nearest', 255
175     );
176     }
177 elmex 1.1
178     sub classify_arch_layer {
179     my ($arch) = @_;
180    
181     if ($arch->{invisible}) { # just a heuristic for 'special' tiles (er. pedestals)
182    
183     return 'below';
184    
185     } elsif ($arch->{monster}) {
186    
187     return 'top';
188    
189     } else { # $arch->{is_floor} and all other arches are 'between' monsters and floor
190    
191     return 'between';
192     }
193     }
194    
195 elmex 1.7 sub arch_is_exit {
196     my ($a) = @_;
197 root 1.29 my $type = $Deliantra::ARCH{$a->{_name}}->{type};
198 elmex 1.7 return $type eq '66' || $type eq '41';
199     }
200    
201 elmex 1.2 sub arch_is_floor {
202     my ($a) = @_;
203 root 1.29 my $ar = Deliantra::arch_attr $a;
204 elmex 1.19 return (
205     (substr $ar->{name}, 0, 5) eq 'Floor'
206     or (substr $ar->{name}, 0, 10) eq 'Shop Floor'
207     )
208 elmex 1.18 }
209    
210     sub arch_is_connector {
211     my ($a) = @_;
212 root 1.29 my $ar = Deliantra::arch_attr $a;
213 elmex 1.18 my $has_connect_field = 0;
214    
215     TOP: for (@{$ar->{section}}) {
216     my $name = shift @$_;
217     my @r = @$_;
218     if ($name eq 'general') {
219     for (@r) {
220     my ($k, $s) = ($_->[0], $_->[1]);
221     if ($k eq 'connected' && $s->{name} eq 'connection') {
222     $has_connect_field = 1;
223     last TOP;
224     }
225     }
226     last TOP;
227     }
228     }
229    
230     return $has_connect_field;
231 elmex 1.2 }
232    
233     sub arch_is_wall {
234     my ($a) = @_;
235 root 1.29 my $ar = Deliantra::arch_attr $a;
236 elmex 1.10 return $ar->{name} eq 'Wall';
237 root 1.29 #return $Deliantra::ARCH{$a->{_name}}->{no_pass};
238 elmex 1.2 }
239    
240 elmex 1.3 sub arch_is_monster {
241     my ($a) = @_;
242 root 1.29 my $arch = $Deliantra::ARCH{$a->{_name}};
243 elmex 1.3 return $arch->{alive} and ($arch->{monster} or $arch->{generator});
244     }
245    
246 elmex 1.2 sub stack_find {
247     my ($stack, $dir, $pred) = @_;
248    
249     if ($dir eq 'from_top') {
250     my $i = scalar (@$stack) - 1;
251     if ($i < 0) { $i = 0 }
252    
253     for (reverse @$stack) {
254     $pred->($_)
255     and return $i;
256    
257     $i--;
258     }
259    
260     } else {
261     my $i = 0;
262    
263     for (@$stack) {
264     $pred->($_)
265     and return $i;
266    
267     $i++;
268     }
269     }
270    
271 elmex 1.32 return undef;
272 elmex 1.2 }
273    
274     sub stack_find_floor {
275     my ($stack, $dir) = @_;
276     return stack_find ($stack, $dir, \&arch_is_floor);
277     }
278    
279     sub stack_find_wall {
280     my ($stack, $dir) = @_;
281     return stack_find ($stack, $dir, \&arch_is_wall);
282     }
283    
284 elmex 1.1 sub insert_arch_stack_layer {
285     my ($stack, $arch) = @_;
286    
287     unless (@$stack) {
288     return [ $arch ];
289     }
290    
291     my @outstack;
292    
293 root 1.29 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
294 elmex 1.1
295     if ($l eq 'between') {
296    
297     # loop until we reached the first 'between' arch above 'below' arches and the floor
298     while (my $a = shift @$stack) {
299    
300 root 1.29 unless ($Deliantra::ARCH{$a->{_name}}->{is_floor}
301     or classify_arch_layer ($Deliantra::ARCH{$a->{_name}}) eq 'below') {
302 elmex 1.1
303     unshift @$stack, $a;
304     last;
305     }
306    
307     push @outstack, $a;
308     }
309    
310     # ignore duplicates
311     # FIXME: Broken if non-floor are drawn (too tired to fix)
312     return [ @outstack, @$stack ]
313     if @outstack and $outstack[-1]->{_name} eq $arch->{_name};
314    
315     push @outstack, ($arch, @$stack);
316    
317     } elsif ($l eq 'top') {
318    
319     # ignore duplicates
320     return [ @$stack ]
321     if $stack->[-1]->{_name} eq $arch->{_name};
322    
323     @outstack = (@$stack, $arch);
324    
325     } else {
326    
327     # ignore duplicates
328     return [ @$stack ]
329     if $stack->[0]->{_name} eq $arch->{_name};
330    
331     @outstack = ($arch, @$stack);
332     }
333    
334     return \@outstack;
335     }
336    
337 elmex 1.4 sub add_table_widget {
338     my ($table, $row, $data, $type, $cb) = @_;
339     my $edwid;
340    
341     if ($type eq 'string') {
342     $table->attach_defaults (my $lbl = Gtk2::Label->new ($data->[0]), 0, 1, $row, $row + 1);
343     $edwid = Gtk2::Entry->new;
344     $edwid->set_text ($data->[1]);
345     $edwid->signal_connect (changed => sub {
346     $data->[1] = $_[0]->get_text;
347     $cb->($data->[1]) if $cb;
348     });
349     $table->attach_defaults ($edwid, 1, 2, $row, $row + 1);
350    
351     } elsif ($type eq 'button') {
352     $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($data), 0, 2, $row, $row + 1);
353     $b->signal_connect (clicked => ($cb || sub {}));
354    
355     } elsif ($type eq 'label') {
356     $table->attach_defaults (my $lbl = Gtk2::Label->new ($data->[0]), 0, 1, $row, $row + 1);
357     $edwid = Gtk2::Label->new ($data->[1]);
358     $table->attach_defaults ($edwid, 1, 2, $row, $row + 1);
359    
360     } else {
361     $edwid = Gtk2::Label->new ("FOO");
362     }
363     }
364    
365 elmex 1.1 sub replace_arch_stack_layer {
366     my ($stack, $arch) = @_;
367    
368     my @outstack;
369    
370 root 1.29 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
371 elmex 1.1
372     if ($l eq 'between') {
373    
374     while (shift @$stack) {
375 root 1.29 last unless $Deliantra::ARCH{$_->{_name}}->{is_floor};
376 elmex 1.1 push @outstack, $_;
377     }
378    
379 root 1.29 if (@outstack and $Deliantra::ARCH{$outstack[-1]->{_name}}->{is_floor}) {
380 elmex 1.1 pop @outstack;
381     }
382    
383     push @outstack, ($arch, @$stack);
384    
385     } elsif ($l eq 'top') {
386    
387     @outstack = (@$stack, $arch);
388    
389     } else {
390    
391     @outstack = ($arch, @$stack);
392     }
393    
394     return \@outstack;
395     }
396    
397 elmex 1.21 sub upload {
398 elmex 1.22 my ($login, $password, $srcrep, $path, $rev, $mapdata) = @_;
399 elmex 1.30 #require LWP::UserAgent;
400     #my $ua = LWP::UserAgent->new (
401     # agent => "deliantra editor",
402     # keep_alive => 1,
403     # env_proxy => 1,
404     # timeout => 30,
405     #);
406     #require HTTP::Request::Common;
407    
408     #my $res = $ua->post (
409     # $ENV{CFPLUS_UPLOAD},
410     # Content_Type => 'multipart/form-data',
411     # Content => [
412     # path => $path,
413     # mapdir => $srcrep,
414     # map => $mapdata,
415     # revision => $rev,
416     # cf_login => $login, #ENV{CFPLUS_LOGIN},
417     # cf_password => $password, #ENV{CFPLUS_PASSWORD},
418     # comment => "",
419     # ]
420     #);
421    
422     #if ($res->is_error) {
423     # # fatal condition
424     # warn $res->status_line;
425     #} else {
426     # # script replies are marked as {{..}}
427     # my @msgs = $res->decoded_content =~ m/\{\{(.*?)\}\}/g;
428     # warn map "$_\n", @msgs;
429     #}
430 elmex 1.21 }
431    
432 elmex 1.1 =head1 AUTHOR
433    
434     Marc Lehmann <schmorp@schmorp.de>
435     http://home.schmorp.de/
436    
437     Robin Redeker <elmex@ta-sa.org>
438     http://www.ta-sa.org/
439    
440     =cut
441     1;