ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/Util.pm
Revision: 1.26
Committed: Sat Aug 25 15:21:04 2007 UTC (16 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.25: +20 -1 lines
Log Message:
now the overlay connections also have a yellow border

File Contents

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