ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/Util.pm
Revision: 1.29
Committed: Thu Dec 27 22:28:01 2007 UTC (16 years, 5 months ago) by root
Branch: MAIN
Changes since 1.28: +18 -18 lines
Log Message:
upgrade Crossfire to Deliantra

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     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     return ($m1, $m2);
97     } else {
98     return ($map1path, $map2path);
99     }
100     } else {
101     return ('', '');
102     }
103     }
104 elmex 1.8
105     sub map2abs {
106     my ($dest, $mape) = @_;
107    
108 elmex 1.24 #$dest = abs_path $dest;
109 elmex 1.8 my $dir;
110     if (File::Spec->file_name_is_absolute($dest)) {
111 elmex 1.21 $dir = catdir ($::MAPDIR, $dest);
112 elmex 1.8 } else {
113     my ($v, $p, $f) = File::Spec->splitpath ($mape->{path});
114     $dir = File::Spec->rel2abs ($dest, File::Spec->catpath ($v, $p));
115     }
116     return $dir;
117     }
118 elmex 1.5
119     sub def($$) {
120     return defined ($_[0]) ? $_[0] : $_[1];
121     }
122    
123     sub quick_msg {
124     my $wid = shift;
125     my $msg;
126     my $win = $::MAINWIN;
127     if (ref $wid) {
128     $win = $wid;
129     $msg = shift;
130     } else {
131     $msg = $wid;
132     }
133     my $dia = Gtk2::Dialog->new ('Message', $win, 'destroy-with-parent', 'gtk-ok' => 'none');
134    
135     my $lbl = Gtk2::Label->new ($msg);
136     $dia->vbox->add ($lbl);
137     $dia->signal_connect (response => sub { $_[0]->destroy });
138    
139     unless (defined $_[0]) {
140     Glib::Timeout->add (1000, sub { $dia->destroy; 0 });
141     }
142    
143     $dia->show_all;
144     }
145 elmex 1.2
146     sub new_arch_pb {
147     # this is awful, is this really the best way?
148     my $pb = new Gtk2::Gdk::Pixbuf 'rgb', 1, 8, TILESIZE, TILESIZE;
149 elmex 1.25 fill_pb_from_arch ($pb, {});
150 elmex 1.2 return $pb;
151     }
152    
153     sub fill_pb_from_arch {
154 elmex 1.6 my ($pb, $a) = @_;
155    
156 root 1.29 my $o = $Deliantra::ARCH{$a->{_name}} || {};
157     my $face = $Deliantra::FACE{$a->{face} || $o->{face} || "blank.111"};
158 elmex 1.23 unless ($face) {
159 root 1.29 $face = $Deliantra::FACE{"blank.x11"}
160 elmex 1.23 or warn "no gfx found for arch '$a->{_name}'\n";
161     }
162 elmex 1.6
163     $face or return;
164 elmex 1.2
165     $pb->fill (0x00000000);
166     $TILE->composite ($pb,
167     0, 0,
168     TILESIZE, TILESIZE,
169 elmex 1.6 - ($face->{idx} % 64) * TILESIZE, - TILESIZE * int $face->{idx} / 64,
170 elmex 1.2 1, 1, 'nearest', 255
171     );
172     }
173 elmex 1.1
174     sub classify_arch_layer {
175     my ($arch) = @_;
176    
177     if ($arch->{invisible}) { # just a heuristic for 'special' tiles (er. pedestals)
178    
179     return 'below';
180    
181     } elsif ($arch->{monster}) {
182    
183     return 'top';
184    
185     } else { # $arch->{is_floor} and all other arches are 'between' monsters and floor
186    
187     return 'between';
188     }
189     }
190    
191 elmex 1.7 sub arch_is_exit {
192     my ($a) = @_;
193 root 1.29 my $type = $Deliantra::ARCH{$a->{_name}}->{type};
194 elmex 1.7 return $type eq '66' || $type eq '41';
195     }
196    
197 elmex 1.2 sub arch_is_floor {
198     my ($a) = @_;
199 root 1.29 my $ar = Deliantra::arch_attr $a;
200 elmex 1.19 return (
201     (substr $ar->{name}, 0, 5) eq 'Floor'
202     or (substr $ar->{name}, 0, 10) eq 'Shop Floor'
203     )
204 elmex 1.18 }
205    
206     sub arch_is_connector {
207     my ($a) = @_;
208 root 1.29 my $ar = Deliantra::arch_attr $a;
209 elmex 1.18 my $has_connect_field = 0;
210    
211     TOP: for (@{$ar->{section}}) {
212     my $name = shift @$_;
213     my @r = @$_;
214     if ($name eq 'general') {
215     for (@r) {
216     my ($k, $s) = ($_->[0], $_->[1]);
217     if ($k eq 'connected' && $s->{name} eq 'connection') {
218     $has_connect_field = 1;
219     last TOP;
220     }
221     }
222     last TOP;
223     }
224     }
225    
226     return $has_connect_field;
227 elmex 1.2 }
228    
229     sub arch_is_wall {
230     my ($a) = @_;
231 root 1.29 my $ar = Deliantra::arch_attr $a;
232 elmex 1.10 return $ar->{name} eq 'Wall';
233 root 1.29 #return $Deliantra::ARCH{$a->{_name}}->{no_pass};
234 elmex 1.2 }
235    
236 elmex 1.3 sub arch_is_monster {
237     my ($a) = @_;
238 root 1.29 my $arch = $Deliantra::ARCH{$a->{_name}};
239 elmex 1.3 return $arch->{alive} and ($arch->{monster} or $arch->{generator});
240     }
241    
242 elmex 1.2 sub stack_find {
243     my ($stack, $dir, $pred) = @_;
244    
245    
246     if ($dir eq 'from_top') {
247     my $i = scalar (@$stack) - 1;
248     if ($i < 0) { $i = 0 }
249    
250     for (reverse @$stack) {
251     $pred->($_)
252     and return $i;
253    
254     $i--;
255     }
256    
257     } else {
258     my $i = 0;
259    
260     for (@$stack) {
261     $pred->($_)
262     and return $i;
263    
264     $i++;
265     }
266     }
267    
268     return 0;
269    
270     }
271    
272     sub stack_find_floor {
273     my ($stack, $dir) = @_;
274     return stack_find ($stack, $dir, \&arch_is_floor);
275     }
276    
277     sub stack_find_wall {
278     my ($stack, $dir) = @_;
279     return stack_find ($stack, $dir, \&arch_is_wall);
280     }
281    
282 elmex 1.1 sub insert_arch_stack_layer {
283     my ($stack, $arch) = @_;
284    
285     unless (@$stack) {
286     return [ $arch ];
287     }
288    
289     my @outstack;
290    
291 root 1.29 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
292 elmex 1.1
293     if ($l eq 'between') {
294    
295     # loop until we reached the first 'between' arch above 'below' arches and the floor
296     while (my $a = shift @$stack) {
297    
298 root 1.29 unless ($Deliantra::ARCH{$a->{_name}}->{is_floor}
299     or classify_arch_layer ($Deliantra::ARCH{$a->{_name}}) eq 'below') {
300 elmex 1.1
301     unshift @$stack, $a;
302     last;
303     }
304    
305     push @outstack, $a;
306     }
307    
308     # ignore duplicates
309     # FIXME: Broken if non-floor are drawn (too tired to fix)
310     return [ @outstack, @$stack ]
311     if @outstack and $outstack[-1]->{_name} eq $arch->{_name};
312    
313     push @outstack, ($arch, @$stack);
314    
315     } elsif ($l eq 'top') {
316    
317     # ignore duplicates
318     return [ @$stack ]
319     if $stack->[-1]->{_name} eq $arch->{_name};
320    
321     @outstack = (@$stack, $arch);
322    
323     } else {
324    
325     # ignore duplicates
326     return [ @$stack ]
327     if $stack->[0]->{_name} eq $arch->{_name};
328    
329     @outstack = ($arch, @$stack);
330     }
331    
332     return \@outstack;
333     }
334    
335 elmex 1.4 sub add_table_widget {
336     my ($table, $row, $data, $type, $cb) = @_;
337     my $edwid;
338    
339     if ($type eq 'string') {
340     $table->attach_defaults (my $lbl = Gtk2::Label->new ($data->[0]), 0, 1, $row, $row + 1);
341     $edwid = Gtk2::Entry->new;
342     $edwid->set_text ($data->[1]);
343     $edwid->signal_connect (changed => sub {
344     $data->[1] = $_[0]->get_text;
345     $cb->($data->[1]) if $cb;
346     });
347     $table->attach_defaults ($edwid, 1, 2, $row, $row + 1);
348    
349     } elsif ($type eq 'button') {
350     $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($data), 0, 2, $row, $row + 1);
351     $b->signal_connect (clicked => ($cb || sub {}));
352    
353     } elsif ($type eq 'label') {
354     $table->attach_defaults (my $lbl = Gtk2::Label->new ($data->[0]), 0, 1, $row, $row + 1);
355     $edwid = Gtk2::Label->new ($data->[1]);
356     $table->attach_defaults ($edwid, 1, 2, $row, $row + 1);
357    
358     } else {
359     $edwid = Gtk2::Label->new ("FOO");
360     }
361     }
362    
363 elmex 1.1 sub replace_arch_stack_layer {
364     my ($stack, $arch) = @_;
365    
366     my @outstack;
367    
368 root 1.29 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
369 elmex 1.1
370     if ($l eq 'between') {
371    
372     while (shift @$stack) {
373 root 1.29 last unless $Deliantra::ARCH{$_->{_name}}->{is_floor};
374 elmex 1.1 push @outstack, $_;
375     }
376    
377 root 1.29 if (@outstack and $Deliantra::ARCH{$outstack[-1]->{_name}}->{is_floor}) {
378 elmex 1.1 pop @outstack;
379     }
380    
381     push @outstack, ($arch, @$stack);
382    
383     } elsif ($l eq 'top') {
384    
385     @outstack = (@$stack, $arch);
386    
387     } else {
388    
389     @outstack = ($arch, @$stack);
390     }
391    
392     return \@outstack;
393     }
394    
395 elmex 1.21 sub upload {
396 elmex 1.22 my ($login, $password, $srcrep, $path, $rev, $mapdata) = @_;
397     require LWP::UserAgent;
398     my $ua = LWP::UserAgent->new (
399 elmex 1.28 agent => "deliantra editor",
400 elmex 1.22 keep_alive => 1,
401     env_proxy => 1,
402     timeout => 30,
403     );
404 elmex 1.21 require HTTP::Request::Common;
405    
406     my $res = $ua->post (
407     $ENV{CFPLUS_UPLOAD},
408     Content_Type => 'multipart/form-data',
409     Content => [
410     path => $path,
411 elmex 1.22 mapdir => $srcrep,
412 elmex 1.21 map => $mapdata,
413     revision => $rev,
414     cf_login => $login, #ENV{CFPLUS_LOGIN},
415     cf_password => $password, #ENV{CFPLUS_PASSWORD},
416     comment => "",
417     ]
418     );
419    
420     if ($res->is_error) {
421     # fatal condition
422     warn $res->status_line;
423     } else {
424     # script replies are marked as {{..}}
425     my @msgs = $res->decoded_content =~ m/\{\{(.*?)\}\}/g;
426     warn map "$_\n", @msgs;
427     }
428     }
429    
430 elmex 1.1 =head1 AUTHOR
431    
432     Marc Lehmann <schmorp@schmorp.de>
433     http://home.schmorp.de/
434    
435     Robin Redeker <elmex@ta-sa.org>
436     http://www.ta-sa.org/
437    
438     =cut
439     1;