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, 6 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

# 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 Deliantra;
13
14 use Carp ();
15 use Storable;
16 use List::Util qw(min max);
17
18 use Deliantra;
19 use Deliantra::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 devirtualize);
32
33 my %allocated_colors;
34
35 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 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 sub pseudohtml2txt {
64 my ($html) = @_;
65
66 $html =~ s/<br\s*?\/?>/\n/gsi;
67 $html =~ s/<b>(.*?)<\/b>/_\1_/gsi;
68 $html =~ s/<li>/\n* /gi;
69 $html =~ s/<\/?\s*li>//gi;
70 $html =~ s/<\/?\s*ul>//gi;
71 $html =~ s/&gt;/>/g;
72 $html =~ s/&lt;/</g;
73 $html
74 }
75
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 $m1 =~ s/\.map$//;
97 $m2 =~ s/\.map$//;
98 return ($m1, $m2);
99 } else {
100 $map1path =~ s/\.map$//;
101 $map2path =~ s/\.map$//;
102 return ($map1path, $map2path);
103 }
104 } else {
105 return ('', '');
106 }
107 }
108
109 sub map2abs {
110 my ($dest, $mape) = @_;
111
112 #$dest = abs_path $dest;
113 my $dir;
114 if (File::Spec->file_name_is_absolute($dest)) {
115 $dir = catdir ($::MAPDIR, $dest);
116 } 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
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
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 fill_pb_from_arch ($pb, {});
154 return $pb;
155 }
156
157 sub fill_pb_from_arch {
158 my ($pb, $a) = @_;
159
160 my $o = $Deliantra::ARCH{$a->{_name}} || {};
161 my $face = $Deliantra::FACE{$a->{face} || $o->{face} || "blank.111"};
162 unless ($face) {
163 $face = $Deliantra::FACE{"blank.x11"}
164 or warn "no gfx found for arch '$a->{_name}'\n";
165 }
166
167 $face or return;
168
169 $pb->fill (0x00000000);
170 $TILE->composite ($pb,
171 0, 0,
172 TILESIZE, TILESIZE,
173 - ($face->{idx} % 64) * TILESIZE, - TILESIZE * int $face->{idx} / 64,
174 1, 1, 'nearest', 255
175 );
176 }
177
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 sub arch_is_exit {
196 my ($a) = @_;
197 my $type = $Deliantra::ARCH{$a->{_name}}->{type};
198 return $type eq '66' || $type eq '41';
199 }
200
201 sub arch_is_floor {
202 my ($a) = @_;
203 my $ar = Deliantra::arch_attr $a;
204 return (
205 (substr $ar->{name}, 0, 5) eq 'Floor'
206 or (substr $ar->{name}, 0, 10) eq 'Shop Floor'
207 )
208 }
209
210 sub arch_is_connector {
211 my ($a) = @_;
212 my $ar = Deliantra::arch_attr $a;
213 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 }
232
233 sub arch_is_wall {
234 my ($a) = @_;
235 my $ar = Deliantra::arch_attr $a;
236 return $ar->{name} eq 'Wall';
237 #return $Deliantra::ARCH{$a->{_name}}->{no_pass};
238 }
239
240 sub arch_is_monster {
241 my ($a) = @_;
242 my $arch = $Deliantra::ARCH{$a->{_name}};
243 return $arch->{alive} and ($arch->{monster} or $arch->{generator});
244 }
245
246 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 return undef;
272 }
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 sub insert_arch_stack_layer {
285 my ($stack, $arch) = @_;
286
287 unless (@$stack) {
288 return [ $arch ];
289 }
290
291 my @outstack;
292
293 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
294
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 unless ($Deliantra::ARCH{$a->{_name}}->{is_floor}
301 or classify_arch_layer ($Deliantra::ARCH{$a->{_name}}) eq 'below') {
302
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 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 sub replace_arch_stack_layer {
366 my ($stack, $arch) = @_;
367
368 my @outstack;
369
370 my $l = classify_arch_layer ($Deliantra::ARCH{$arch->{_name}});
371
372 if ($l eq 'between') {
373
374 while (shift @$stack) {
375 last unless $Deliantra::ARCH{$_->{_name}}->{is_floor};
376 push @outstack, $_;
377 }
378
379 if (@outstack and $Deliantra::ARCH{$outstack[-1]->{_name}}->{is_floor}) {
380 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 sub upload {
398 my ($login, $password, $srcrep, $path, $rev, $mapdata) = @_;
399 #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 }
431
432 =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;