ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/Util.pm
Revision: 1.23
Committed: Fri Jan 5 14:17:12 2007 UTC (17 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.22: +7 -3 lines
Log Message:
fixed face problems and another bug in Util.pm

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