ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.32
Committed: Tue Apr 4 11:29:54 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.31: +10 -19 lines
Log Message:
Removed pick and made the attribut editor 'the current pick'.
Added context menu to map view with an follow item in it.

File Contents

# User Rev Content
1 elmex 1.1 package GCE::EditAction;
2    
3     =head1 NAME
4    
5     GCE::EditActions - this is the abstraction of edit actions (placing, deleting, etc.)
6    
7     =cut
8    
9     use Gtk2;
10     use Gtk2::Gdk::Keysyms;
11     use Gtk2::SimpleMenu;
12    
13     use Crossfire;
14     use Crossfire::MapWidget;
15    
16     use strict;
17    
18     sub new {
19     my $class = shift;
20     my $self = { @_ };
21     bless $self, $class;
22 elmex 1.6 $self->init;
23 elmex 1.1 return $self;
24     }
25    
26 elmex 1.10 sub name { } # a unique name for this tool (for storing it in a hash in the main window)
27    
28 elmex 1.7 sub tool_widget { if ($_[1]) { $_[0]->{widget} = $_[1] } $_[0]->{widget} }
29 elmex 1.6 sub init { }
30    
31 elmex 1.1 sub want_cursor { 1 }
32    
33     sub special_arrow { }
34    
35     # edits one tile of the map
36     sub edit_one {
37     my ($self, $x, $y) = @_;
38     # do one edition
39     }
40    
41     # edits a selection
42     sub edit_selection {
43     }
44    
45     # abstraction for edit_one and edit_selection ?
46     # takes selection if present
47     sub edit {
48 elmex 1.10 my ($self, $map, $x, $y) = @_;
49 elmex 1.1 }
50    
51     sub begin {
52 elmex 1.10 my ($self, $map, $x, $y) = @_;
53 root 1.3
54     $map->change_begin (ref $self);
55 elmex 1.1 }
56 root 1.3
57 elmex 1.1 sub end {
58 root 1.3 my ($self, $map) = @_;
59    
60     if (my $changeset = $map->change_end) {
61     splice @{ $map->{undo_stack} ||= [] },
62 root 1.4 $map->{undo_stack_pos}++, 1e6,
63 root 1.3 $changeset;
64    
65     #TODO: limit undo stack size to some preconfigured limit
66     }
67 elmex 1.1 }
68    
69 elmex 1.7 package GCE::EditAction::RadioModed;
70    
71     our @ISA = qw/GCE::EditAction/;
72    
73     sub add_mode_button {
74     my ($self, $vb, $lbl, $mode, $default) = @_;
75    
76     $vb->pack_start (my $b = Gtk2::RadioButton->new ($self->{place_radio_grp}, $lbl), 0, 1, 0);
77     unless (defined $self->{place_radio_grp}) {
78     $self->{place_radio_grp} = $b->get_group;
79    
80     unless (defined $default) {
81     $b->set_active (1);
82     $self->set_mode ($mode);
83     }
84     }
85     if ($default) {
86     $b->set_active (1);
87     $self->set_mode ($mode);
88     }
89     $b->signal_connect (clicked => sub {
90     $self->set_mode ($mode);
91     });
92     }
93    
94     sub set_mode {
95     my ($self, $mode) = @_;
96     $self->{place_mode} = $mode;
97     }
98    
99     sub get_mode {
100     my ($self) = @_;
101     $self->{place_mode}
102     }
103    
104     sub init {
105     my ($self) = @_;
106    
107     die "Implement me!!";
108    
109     # my $vb = new Gtk2::VBox;
110     # $self->_add_mode_button ($vb, "auto", "auto");
111     # $self->_add_mode_button ($vb, "top", "top");
112     # $self->_add_mode_button ($vb, "above floor", "above");
113     # $self->_add_mode_button ($vb, "below floor", "below");
114     # $self->_add_mode_button ($vb, "bottom", "bottom");
115     #
116     # $self->{widget} = $vb;
117     }
118 elmex 1.1
119     package GCE::EditAction::Pick;
120 elmex 1.7 use strict;
121 elmex 1.1
122 elmex 1.23 our @ISA = qw/GCE::EditAction::RadioModed/;
123 elmex 1.1
124 elmex 1.10 sub name { 'pick' }
125    
126 elmex 1.7 sub want_cursor { 0 }
127    
128 elmex 1.1 sub special_arrow { 'GDK_HAND2' }
129    
130 elmex 1.23 sub init {
131     my ($self) = @_;
132    
133     my $vb = new Gtk2::VBox;
134     $vb->pack_start ($self->{only_attr} = Gtk2::CheckButton->new ('pick attr'), 0, 1, 0);
135     $self->{widget} = $vb;
136     }
137    
138 elmex 1.1 sub begin {
139 elmex 1.10 my ($self, $map, $x, $y) = @_;
140     $self->edit ($map, $x, $y);
141 elmex 1.1 }
142    
143     sub edit {
144 elmex 1.10 my ($self, $map, $x, $y) = @_;
145 elmex 1.1
146 elmex 1.23 $::MAINWIN->update_stack_view ($map, $x, $y);
147    
148 elmex 1.1 my $cstack = $map->get ($x, $y);
149    
150     my $arch = $cstack->[-1];
151    
152     # virtual... grmbl....
153     # FIXME: I have to patch the stack of the real arch??? argl.. how??
154     if ($arch->{_virtual}) {
155 root 1.3 $x = $arch->{virtual_x};
156     $y = $arch->{virtual_y};
157 elmex 1.1 $arch = $arch->{_virtual};
158 elmex 1.6 $cstack = $map->get ($x, $y);
159 elmex 1.1 }
160    
161 elmex 1.32 # unless ($self->{only_attr}->get_active) {
162     # $::MAINWIN->set_pick ($arch)
163     # if @$cstack;
164     # }
165 elmex 1.1
166 root 1.5 $::MAINWIN->update_attr_editor ($arch, sub {
167 elmex 1.8 $map->change_begin (ref $self);
168 root 1.3 $map->change_stack ($x, $y, $cstack);
169 elmex 1.8 # XXX: Put this into a generic function!!! See also EditTools.pm
170     # FIXME: Fix the automatic update on undo here!
171     if (my $changeset = $map->change_end) {
172     splice @{ $map->{undo_stack} ||= [] },
173     $map->{undo_stack_pos}++, 1e6,
174     $changeset;
175     }
176 elmex 1.1 });
177 elmex 1.23 }
178 elmex 1.1
179 elmex 1.23 sub end {}
180 elmex 1.1
181 elmex 1.13 package GCE::EditAction::Perl;
182    
183     use GCE::Util;
184     use Gtk2;
185     use strict;
186    
187     our @ISA = qw/GCE::EditAction/;
188    
189     sub name { 'perl' }
190    
191 elmex 1.15 sub special_arrow { 'GDK_HEART' }
192    
193 elmex 1.13 sub init {
194     my ($self) = @_;
195    
196     my $vb = new Gtk2::VBox;
197 elmex 1.20 $vb->pack_start (my $combo = Gtk2::ComboBox->new_text, 0, 1, 0);
198 elmex 1.13 $vb->pack_start (my $sw = Gtk2::ScrolledWindow->new, 1, 1, 0);
199     $sw->add ($self->{txt} = Gtk2::TextView->new);
200    
201 elmex 1.20 my $code = {};
202     for (['unique floor' => 'my $i = 0; for (@$os) { $_->{is_floor} and $as->[$i]->{unique} = 1; $i++ }']) {
203     $combo->append_text ($_->[0]);
204     $code->{$_->[0]} = $_->[1];
205     }
206    
207     $combo->signal_connect (changed => sub {
208     my ($combo) = @_;
209     my $str = $combo->get_active_text;
210     $self->{txt}->get_buffer->set_text ($code->{$str});
211     });
212    
213 elmex 1.13 $self->tool_widget ($vb);
214     }
215    
216     sub want_cursor { 0 }
217    
218     sub begin {
219     my ($self, $map, $x, $y) = @_;
220    
221     $self->SUPER::begin ($map, $x, $y);
222     $self->edit ($map, $x, $y);
223     }
224    
225     sub edit {
226     my ($self, $map, $x, $y) = @_;
227    
228     my $pick = $::MAINWIN->get_pick;
229     my $as = $map->get ($x, $y);
230    
231     $as = $self->eval ($map, $pick, $as, $x, $y);
232     $map->change_stack ($x, $y, $as); # insert_arch_stack_layer ($as, $arch));
233     }
234    
235     sub eval {
236 elmex 1.20 my ($self, $map, $pick, $as, $x, $y) = @_;
237 elmex 1.13 my $buf = $self->{txt}->get_buffer;
238     my $code = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0);
239 elmex 1.20 my $f_idx = stack_find_floor ($as, 'from_top');
240     my $w_idx = stack_find_wall ($as, 'from_top');
241     my $os = [ map { $Crossfire::ARCH{$_->{_name}} } @$as ];
242    
243     unless (arch_is_floor ($as->[$f_idx])) { $f_idx = undef; }
244     unless (arch_is_floor ($as->[$w_idx])) { $w_idx = undef; }
245 elmex 1.13
246     eval $code;
247 elmex 1.20 return $as;
248 elmex 1.13 }
249    
250 elmex 1.18 package GCE::EditAction::FollowExit;
251     use Storable qw/dclone/;
252     use File::Spec::Functions;
253     use GCE::Util;
254     use Gtk2;
255     use strict;
256    
257     our @ISA = qw/GCE::EditAction/;
258    
259     sub name { 'place' }
260    
261     sub init {
262     my ($self) = @_;
263    
264     my $vb = new Gtk2::VBox;
265    
266     $self->tool_widget ($vb);
267     }
268    
269     sub want_cursor { 0 }
270    
271     sub begin {
272 elmex 1.21 my ($self, $map, $x, $y, $mape) = @_;
273 elmex 1.18
274     # $self->SUPER::begin ($map, $x, $y);
275 elmex 1.21 $self->edit ($map, $x, $y, $mape);
276 elmex 1.18 }
277    
278     sub edit {
279 elmex 1.21 my ($self, $map, $x, $y, $mape) = @_;
280 elmex 1.18
281     my $as = $map->get ($x, $y);
282    
283     my $exit;
284 elmex 1.27 for my $arch (@$as) {
285     if ($arch->{_virtual}) {
286     $arch = $arch->{_virtual};
287     }
288     if (arch_is_exit ($arch)) {
289     $exit = $arch;
290 elmex 1.18 }
291     }
292    
293 elmex 1.32 if ($exit and $exit->{slaying} !~ /^!/) {
294 elmex 1.22 my $dest = map2abs ($exit->{slaying}, $mape);
295 elmex 1.32 # XXX: Replace with statusbar message
296     unless (-e $dest) {
297     warn "Couldn't find '$dest'";
298     return
299     }
300 elmex 1.22 $::MAINWIN->open_map_editor ($dest);
301 elmex 1.18 }
302     }
303    
304     sub end {
305     my ($self, $map, $x, $y, $mape) = @_;
306     # $::MAINWIN->{edit_collection}{pick}->edit ($map, $x, $y);
307     #$self->SUPER::end ($map, $x, $y, $mape);
308     }
309    
310 elmex 1.1 package GCE::EditAction::Place;
311 root 1.28
312 elmex 1.14 use Storable qw/dclone/;
313 elmex 1.2 use GCE::Util;
314 elmex 1.6 use Gtk2;
315     use strict;
316 elmex 1.2
317 elmex 1.7 our @ISA = qw/GCE::EditAction::RadioModed/;
318 elmex 1.6
319 elmex 1.10 sub name { 'place' }
320    
321 elmex 1.6 sub init {
322     my ($self) = @_;
323    
324     my $vb = new Gtk2::VBox;
325 root 1.28
326 elmex 1.7 $self->add_mode_button ($vb, "auto", "auto");
327     $self->add_mode_button ($vb, "top", "top");
328     $self->add_mode_button ($vb, "above floor", "above");
329     $self->add_mode_button ($vb, "below floor", "below");
330     $self->add_mode_button ($vb, "bottom", "bottom");
331 elmex 1.6
332 elmex 1.7 $self->tool_widget ($vb);
333 elmex 1.6 }
334    
335 elmex 1.1 sub want_cursor { 0 }
336    
337     sub begin {
338 elmex 1.10 my ($self, $map, $x, $y) = @_;
339 elmex 1.1
340 elmex 1.10 $self->SUPER::begin ($map, $x, $y);
341     $self->edit ($map, $x, $y);
342 elmex 1.1 }
343    
344 root 1.28 # 1 up 2 right 4 down 8 left
345     my @join_ext = (
346     "0", # 0
347     "1_2", # 1
348     "1_4", # 2
349     "2_2_1", # 3
350     "1_1", # 4
351     "2_1_1", # 5
352     "2_2_2", # 6
353     "3_2", # 7
354     "1_3", # 8
355     "2_2_4", # 9
356     "2_1_2", # 10
357     "3_1", # 11
358     "2_2_3", # 12
359     "3_4", # 13
360     "3_3", # 14
361     "4", # 15
362     );
363    
364     sub autojoin {
365     my ($map, $pick, $x1, $y1, $x2, $y2) = @_;
366    
367     my $dx = $x2 - $x1;
368     my $dy = $y2 - $y1;
369    
370     my $dir = $dy ? ($dy == -1 ? 1 : $dy == 1 ? 4 : return)
371     : ($dx == -1 ? 8 : $dx == 1 ? 2 : $dx == 0 ? 0 : return);
372    
373     my $as = $map->get ($x1, $y1);
374    
375     (my $base = $pick->{_name}) =~ s/_0$//;
376    
377     for my $idx (0 .. $#$as) {
378     my $arch = $as->[$idx];
379     for my $dirs (0..15) {
380     my $name = $arch->{_name};
381    
382     if ($arch->{_name} eq "$base\_$join_ext[$dirs]") {
383     $dirs |= $dir;
384    
385     my $name = "$base\_$join_ext[$dirs]";
386    
387     if ($Crossfire::ARCH{$name}) {
388     %$arch = ( _name => $name );
389     $map->change_stack ($x1, $y1, $as);
390    
391     return 1;
392     }
393     }
394     }
395     }
396    
397     return 0;
398     }
399    
400 elmex 1.1 sub edit {
401 elmex 1.10 my ($self, $map, $x, $y) = @_;
402 elmex 1.1
403 elmex 1.10 my $pick = $::MAINWIN->get_pick;
404     my $as = $map->get ($x, $y);
405 elmex 1.1
406 root 1.28 my $autojoin = $pick->{_name} =~ /_0$/
407     && $self->get_mode eq "auto";
408    
409     autojoin $map, $pick, @{$self->{last_pos}}, $x, $y
410     if $autojoin && $self->{last_pos};
411    
412     if (!$autojoin
413     || !($self->{last_pos} ? autojoin $map, $pick, $x, $y, @{$self->{last_pos}},
414     : autojoin $map, $pick, $x, $y, $x, $y)) {
415     $self->stack_action ($as, dclone $pick);
416     $map->change_stack ($x, $y, $as);
417     autojoin $map, $pick, $x, $y, @{$self->{last_pos}}
418     if $autojoin && $self->{last_pos};
419     }
420    
421     $self->{last_pos} = [$x, $y];
422 elmex 1.1 }
423    
424 elmex 1.17 sub end {
425     my ($self, $map, $x, $y, $mape) = @_;
426 elmex 1.22 # $::MAINWIN->{edit_collection}{pick}->edit ($map, $x, $y);
427     # now actualize stack and attr editor
428     $::MAINWIN->update_stack_view ($map, $x, $y);
429    
430     my $cstack = $map->get ($x, $y);
431    
432     my $arch = $cstack->[-1];
433    
434 root 1.28 delete $self->{last_pos};
435    
436 elmex 1.22 # virtual... grmbl....
437     # FIXME: I have to patch the stack of the real arch??? argl.. how??
438     if ($arch->{_virtual}) {
439     $x = $arch->{virtual_x};
440     $y = $arch->{virtual_y};
441     $arch = $arch->{_virtual};
442     $cstack = $map->get ($x, $y);
443     }
444    
445 elmex 1.17 $self->SUPER::end ($map, $x, $y, $mape);
446     }
447    
448 elmex 1.6 sub stack_action {
449     my ($self, $stack, $arch) = @_;
450    
451 elmex 1.7 my $m = $self->get_mode;
452 elmex 1.6
453     if ($m eq 'top') {
454     if (@$stack == 0 or $stack->[-1]->{_name} ne $arch->{_name}) {
455     push @$stack, $arch;
456     }
457    
458     } elsif ($m eq 'bottom') {
459     if (@$stack == 0 or $stack->[0]->{_name} ne $arch->{_name}) {
460     unshift @$stack, $arch;
461     }
462    
463     } elsif ($m eq 'above') {
464     my $fidx = stack_find_floor ($stack, 'from_top');
465    
466     if (@$stack == 0
467     or not ($stack->[$fidx + 1])
468     or $stack->[$fidx + 1]->{_name} ne $arch->{_name})
469     {
470     splice (@$stack, $fidx + 1, 0, $arch);
471     }
472    
473     } elsif ($m eq 'below') {
474     my $fidx = stack_find_floor ($stack, 'from_bottom');
475    
476     if (@$stack == 0
477     or $fidx == 0
478     or not ($stack->[$fidx - 1])
479     or $stack->[$fidx - 1]->{_name} ne $arch->{_name})
480     {
481     splice (@$stack, $fidx, 0, $arch);
482     }
483    
484     } elsif ($m eq 'auto') {
485     my $fidx = stack_find_floor ($stack, 'from_top');
486     my $widx = stack_find_wall ($stack);
487    
488     if (arch_is_floor ($arch)) { # we want to place a floor tile
489    
490     if (arch_is_floor ($stack->[$fidx])) { # replace
491     $stack->[$fidx] = $arch;
492    
493     } else { # insert on bottom
494     unshift @$stack, $arch;
495     }
496    
497     } elsif (arch_is_wall ($arch)) { # we want to place a wall
498    
499     if (arch_is_wall ($stack->[$widx])) { # replace
500     $stack->[$widx] = $arch;
501    
502     } else { # insert above floor
503     splice (@$stack, $fidx + 1, 0, $arch);
504     }
505    
506     } else {
507    
508     if (arch_is_wall ($stack->[$widx])) {
509 elmex 1.9 # if we have a wall above the floor, replace it with the to place item
510     $stack->[$widx] = $arch;
511 elmex 1.6 return;
512     }
513    
514     if (@$stack == 0
515     or not ($stack->[-1])
516     or $stack->[-1]->{_name} ne $arch->{_name})
517     {
518     push @$stack, $arch;
519     }
520     }
521     }
522     }
523    
524 elmex 1.12 package GCE::EditAction::Select;
525 elmex 1.11 use GCE::Util;
526     use Gtk2;
527 elmex 1.12 use Crossfire;
528     use Storable qw/dclone/;
529 elmex 1.11 use strict;
530    
531     our @ISA = qw/GCE::EditAction::RadioModed/;
532    
533 elmex 1.12 sub name { 'select' }
534 elmex 1.11
535 elmex 1.15 sub special_arrow { 'GDK_CIRCLE' }
536    
537 elmex 1.11 sub init {
538     my ($self) = @_;
539    
540     my $vb = new Gtk2::VBox;
541    
542 elmex 1.19 $vb->pack_start (my $bt = Gtk2::Button->new_with_mnemonic ("_copy"), 0, 1, 0);
543 elmex 1.12 $bt->signal_connect (clicked => sub { $self->copy });
544     $vb->pack_start ($self->{paste_top} = Gtk2::CheckButton->new ('paste on top'), 0, 1, 0);
545 elmex 1.19 $vb->pack_start (my $bt = Gtk2::Button->new_with_mnemonic ("paste (_v)"), 0, 1, 0);
546 elmex 1.12 $bt->signal_connect (clicked => sub { $self->paste });
547     $vb->pack_start (Gtk2::HSeparator->new, 0, 1, 0);
548     $self->add_mode_button ($vb, "place", "place");
549     $self->add_mode_button ($vb, "erase", "erase");
550 elmex 1.15 $self->add_mode_button ($vb, "perl", "perl");
551 elmex 1.19 $vb->pack_start (my $bt = Gtk2::Button->new_with_mnemonic ("i_nvoke"), 0, 1, 0);
552 elmex 1.12 $bt->signal_connect (clicked => sub { $self->invoke });
553 elmex 1.11
554     $self->tool_widget ($vb);
555     }
556    
557 elmex 1.12 sub copy {
558     my ($self) = @_;
559    
560     return unless $self->{selection}->{a};
561     my ($x1, $y1) = @{$self->{selection}->{a}};
562     my ($x2, $y2) = @{$self->{selection}->{b}};
563    
564     if ($x1 > $x2) { ($x2, $x1) = ($x1, $x2) }
565     if ($y1 > $y2) { ($y2, $y1) = ($y1, $y2) }
566    
567     my $map = $self->{selection}->{map};
568    
569     $self->{copy_coords} = [$x1, $y1, $x2, $y2];
570     $self->{copy};
571     for (my $x = $x1; $x <= $x2; $x++) {
572     for (my $y = $y1; $y <= $y2; $y++) {
573     $self->{copy}->[$x - $x1]->[$y - $y1] = $map->get ($x, $y);
574     }
575     }
576     }
577    
578     sub paste {
579 elmex 1.23 my ($self, $map, $xp, $yp) = @_;
580 elmex 1.12
581     return unless $self->{selection}->{a};
582    
583     my ($x1, $y1);
584    
585 elmex 1.15 if (defined $xp) {
586 elmex 1.12 ($x1, $y1) = ($xp, $yp);
587     } else {
588     ($x1, $y1) = @{$self->{selection}->{a}};
589     }
590    
591 elmex 1.23 $map ||= $self->{selection}->{map};
592 elmex 1.12
593     my $p_o_top = $self->{paste_top}->get_active * 1;
594    
595     my $w = $self->{copy_coords}->[2] - $self->{copy_coords}->[0];
596     my $h = $self->{copy_coords}->[3] - $self->{copy_coords}->[1];
597     $self->{copy};
598     $self->SUPER::begin ($map, $x1, $y1);
599     for (my $x = $x1; $x <= ($x1 + $w); $x++) {
600     for (my $y = $y1; $y <= ($y1 + $h); $y++) {
601     my $cstck = $map->get ($x, $y);
602    
603     if ($p_o_top) {
604     push @$cstck, @{dclone ($self->{copy}->[$x - $x1]->[$y - $y1] || [])};
605     $map->change_stack ($x, $y, $cstck);
606     } else {
607     $map->change_stack ($x, $y, dclone ($self->{copy}->[$x - $x1]->[$y - $y1] || []));
608     }
609     }
610     }
611     $self->SUPER::end ($map);
612     $map->invalidate_all;
613     }
614    
615     sub invoke {
616     my ($self) = @_;
617    
618     return unless $self->{selection}->{a};
619     my ($x1, $y1) = @{$self->{selection}->{a}};
620     my ($x2, $y2) = @{$self->{selection}->{b}};
621    
622     if ($x1 > $x2) { ($x2, $x1) = ($x1, $x2) }
623     if ($y1 > $y2) { ($y2, $y1) = ($y1, $y2) }
624    
625     my $map = $self->{selection}->{map};
626    
627     my $m = $self->get_mode;
628     $self->SUPER::begin ($map, $x1, $y1);
629     for (my $x = $x1; $x <= $x2; $x++) {
630     for (my $y = $y1; $y <= $y2; $y++) {
631     if ($m eq 'place') {
632     $::MAINWIN->{edit_collection}{place}->edit ($map, $x, $y);
633     } elsif ($m eq 'erase') {
634     $::MAINWIN->{edit_collection}{erase}->edit ($map, $x, $y);
635 elmex 1.15 } elsif ($m eq 'perl') {
636     $::MAINWIN->{edit_collection}{perl}->edit ($map, $x, $y);
637 elmex 1.12 }
638     }
639     }
640     $self->SUPER::end ($map);
641     }
642    
643 elmex 1.11 sub want_cursor { 0 }
644    
645     sub begin {
646     my ($self, $map, $x, $y) = @_;
647    
648 elmex 1.29 if ($self->{selection}->{map}) {
649     $self->{selection}->{map}->overlay ('selection');
650     }
651 elmex 1.11 delete $self->{selection};
652    
653 elmex 1.12 $self->{selection}->{a} = [$x, $y];
654 elmex 1.11 $self->edit ($map, $x, $y);
655 elmex 1.12 }
656 elmex 1.11
657 elmex 1.12 sub end {
658 elmex 1.11 }
659    
660     sub edit {
661     my ($self, $map, $x, $y) = @_;
662    
663     $self->{selection}->{b} = [$x, $y];
664 elmex 1.12 $self->{selection}->{map} = $map;
665 elmex 1.29 $self->update_overlay ();
666 elmex 1.11 }
667    
668     sub update_overlay {
669 elmex 1.29 my ($self) = @_;
670    
671     my $map = $self->{selection}->{map};
672    
673     return unless (defined $self->{selection}->{a} and defined $self->{selection}->{b});
674 elmex 1.11
675 elmex 1.12 my ($x1, $y1) = @{$self->{selection}->{a}};
676     my ($x2, $y2) = @{$self->{selection}->{b}};
677    
678     if ($x1 > $x2) { ($x2, $x1) = ($x1, $x2) }
679     if ($y1 > $y2) { ($y2, $y1) = ($y1, $y2) }
680 elmex 1.11
681 elmex 1.12 my $w = ($x2 - $x1) + 1;
682     my $h = ($y2 - $y1) + 1;
683    
684     $map->overlay (selection =>
685     $x1 * TILESIZE, $y1 * TILESIZE,
686     $w * TILESIZE, $h * TILESIZE,
687     sub {
688     my ($self, $x, $y) = @_;
689     $self->{window}->draw_rectangle (
690     $_ & 1 ? $self->style->black_gc : $self->style->white_gc,
691     0,
692     $x + $_, $y + $_,
693     ($w * TILESIZE) - 1 - $_ * 2,
694     ($h * TILESIZE) - 1 - $_ * 2
695     ) for 0..3;
696     }
697     );
698 elmex 1.11 }
699    
700 elmex 1.1 package GCE::EditAction::Erase;
701 elmex 1.7 use GCE::Util;
702     use Gtk2;
703     use strict;
704 elmex 1.1
705 elmex 1.7 our @ISA = qw/GCE::EditAction::RadioModed/;
706 elmex 1.1
707 elmex 1.10 sub name { 'erase' }
708    
709 elmex 1.1 sub want_cursor { 0 }
710    
711 elmex 1.15 sub special_arrow { 'GDK_DIAMOND_CROSS' }
712 elmex 1.10
713 elmex 1.7 sub init {
714     my ($self) = @_;
715    
716     my $vb = new Gtk2::VBox;
717     $self->add_mode_button ($vb, "top", "top");
718     $self->add_mode_button ($vb, "walls", "walls");
719     $self->add_mode_button ($vb, "above floor", "above", 'default');
720     $self->add_mode_button ($vb, "floor", "floor");
721     $self->add_mode_button ($vb, "below floor", "below");
722     $self->add_mode_button ($vb, "bottom", "bottom");
723     $self->add_mode_button ($vb, "pick match", "match");
724    
725     $self->tool_widget ($vb);
726    
727 root 1.25 $vb->pack_start ($self->{no_wall_check} = Gtk2::CheckButton->new ("protect walls"), 0, 1, 0);
728     $vb->pack_start ($self->{no_monsters_check} = Gtk2::CheckButton->new ("protect monsters"), 0, 1, 0);
729 elmex 1.7 }
730    
731     sub check_excluded {
732     my ($self, $arch) = @_;
733    
734     my $a1 = $self->{no_monsters_check}->get_active;
735     my $a2 = $self->{no_wall_check}->get_active;
736    
737     my $r = ($self->{no_wall_check}->get_active && arch_is_wall ($arch))
738     || ($self->{no_monsters_check}->get_active && arch_is_monster ($arch));
739     return $r;
740     }
741    
742 elmex 1.1 sub begin {
743 elmex 1.10 my ($self, $map, $x, $y) = @_;
744 elmex 1.1
745 elmex 1.10 $self->SUPER::begin ($map, $x, $y);
746     $self->edit ($map, $x, $y);
747 elmex 1.1 }
748    
749     sub edit {
750 elmex 1.10 my ($self, $map, $x, $y) = @_;
751 elmex 1.1
752     my $as = $map->get ($x, $y);
753 elmex 1.7 $self->stack_action ($as);
754 root 1.3 $map->change_stack ($x, $y, $as);
755 elmex 1.1 }
756    
757 elmex 1.7 sub stack_action {
758     my ($self, $stack) = @_;
759    
760     my $m = $self->get_mode;
761    
762     if ($m eq 'top') {
763     pop @$stack;
764    
765     } elsif ($m eq 'bottom') {
766     shift @$stack;
767    
768     } elsif ($m eq 'above') {
769     my $fidx = stack_find_floor ($stack, 'from_top');
770    
771     if (arch_is_floor ($stack->[$fidx]) and $stack->[$fidx + 1]) {
772     splice (@$stack, $fidx + 1, 1)
773     unless $self->check_excluded ($stack->[$fidx + 1])
774    
775     } elsif (not arch_is_floor ($stack->[$fidx])) {
776     splice (@$stack, $fidx, 1)
777     unless $self->check_excluded ($stack->[$fidx])
778    
779     }
780    
781     } elsif ($m eq 'below') {
782     my $fidx = stack_find_floor ($stack, 'from_bottom');
783    
784     if ($fidx > 0 and not arch_is_floor ($stack->[$fidx - 1])) {
785     splice (@$stack, $fidx - 1, 1)
786     unless $self->check_excluded ($stack->[$fidx - 1])
787    
788     } elsif (not arch_is_floor ($stack->[$fidx])) { # no floor found
789     splice (@$stack, $fidx, 1)
790     unless $self->check_excluded ($stack->[$fidx])
791    
792     }
793    
794     } elsif ($m eq 'walls') {
795     my $widx = stack_find_wall ($stack, 'from_top');
796    
797     while (arch_is_wall ($stack->[$widx])) {
798     splice (@$stack, $widx, 1);
799     $widx = stack_find_wall ($stack, 'from_top')
800     }
801    
802     } elsif ($m eq 'floor') {
803     my $fidx = stack_find_floor ($stack, 'from_top');
804    
805     while (arch_is_floor ($stack->[$fidx])) {
806     splice (@$stack, $fidx, 1);
807     $fidx = stack_find_floor ($stack, 'from_top')
808     }
809    
810     } elsif ($m eq 'match') {
811     my $pick_name = $::MAINWIN->get_pick ()->{_name};
812     my $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
813    
814     while ($stack->[$idx] and $stack->[$idx]->{_name} eq $pick_name) {
815     splice (@$stack, $idx, 1);
816     $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
817     }
818     }
819     }
820    
821 elmex 1.18 package GCE::EditAction::ConnectExit;
822 elmex 1.16 use Storable qw/dclone/;
823     use GCE::Util;
824     use Gtk2;
825     use File::Spec::Functions;
826     use strict;
827    
828     our @ISA = qw/GCE::EditAction::RadioModed/;
829    
830 elmex 1.18 sub name { 'connectexit' }
831 elmex 1.16
832     sub init {
833     my ($self) = @_;
834    
835     my $vb = new Gtk2::VBox;
836     $self->add_mode_button ($vb, "exit", "exit");
837     # $self->add_mode_button ($vb, "triggers", "trig");
838 elmex 1.18 $vb->pack_start ($self->{sel_edt} = Gtk2::Entry->new, 0, 1, 0);
839 elmex 1.24 $self->{sel_edt}->set_text (catfile ($::CFG->{MAPDIR}));
840 elmex 1.18
841 elmex 1.16 $vb->pack_start ($self->{sel_lbl} = Gtk2::Label->new, 0, 0, 0);
842     $self->tool_widget ($vb);
843     }
844    
845     sub want_cursor { 0 }
846    
847     sub begin {
848     my ($self, $map, $x, $y, $mapedit) = @_;
849    
850     $self->edit ($map, $x, $y, $mapedit);
851     }
852    
853     sub edit {
854     my ($self, $map, $x, $y, $mapedit) = @_;
855    
856     my $pick = $::MAINWIN->get_pick;
857     my $as = $map->get ($x, $y);
858    
859     my $exit;
860     for (@$as) {
861 elmex 1.21 if (arch_is_exit ($_)) {
862 elmex 1.16 $exit = $_;
863     }
864     }
865    
866 elmex 1.26 my $mappath = $::CFG->{MAPDIR};
867    
868 elmex 1.16 if ($exit) {
869     if ($self->{sel_exit}) {
870    
871     $exit->{hp} = $self->{sel_exit}->[3];
872     $exit->{sp} = $self->{sel_exit}->[4];
873 elmex 1.26 #$exit->{slaying} = File::Spec->abs2rel ($self->{sel_exit}->[5], $ent);
874 elmex 1.16
875     my $exit2 = $self->{sel_exit}->[0];
876    
877     $exit2->{hp} = $x;
878     $exit2->{sp} = $y;
879    
880 elmex 1.26 my ($m1, $m2) = exit_paths ($mappath, $self->{sel_exit}->[5], $mapedit->{path});
881    
882     $exit2->{slaying} = $m2;
883     $exit->{slaying} = $m1;
884 elmex 1.16
885     $self->SUPER::begin ($map, $x, $y, $mapedit);
886     $map->change_stack ($x, $y, $as);
887     $self->SUPER::end ($map);
888     $self->SUPER::begin ($self->{sel_exit}->[1], $exit->{hp}, $exit->{sp});
889     $self->{sel_exit}->[1]->change_stack ($exit->{hp}, $exit->{sp}, $self->{sel_exit}->[2]);
890     $self->SUPER::end ($self->{sel_exit}->[1]);
891    
892 elmex 1.26 quick_msg ($mapedit, "$exit->{slaying} ($exit->{hp}:$exit->{sp}) $exit->{_name} <=> $exit2->{slaying} ($exit2->{hp}:$exit2->{sp}) $exit2->{_name}", 0);
893 elmex 1.16
894     $::MAINWIN->{edit_collection}{pick}->edit ($map, $x, $y);
895    
896     $self->{sel_exit} = undef;
897     $self->{sel_lbl}->set_text ('');
898     } else {
899     $self->{sel_lbl}->set_text ("src: ($x:$y) $exit->{_name}");
900     $self->{sel_exit} = [$exit, $map, $as, $x, $y, $mapedit->{path}];
901     }
902     } else {
903 elmex 1.18 if ($self->{sel_exit}) {
904    
905     my $exit2 = $self->{sel_exit}->[0];
906    
907     $exit2->{hp} = $x;
908     $exit2->{sp} = $y;
909 elmex 1.26 $exit2->{slaying} = undef;
910 elmex 1.18
911     unless ($exit2->{slaying} =~ m/^\.\./) {
912     $exit2->{slaying} = '/' . $exit2->{slaying};
913     }
914    
915     $self->SUPER::begin ($self->{sel_exit}->[1], $exit->{hp}, $exit->{sp});
916     $self->{sel_exit}->[1]->change_stack ($self->{sel_exit}->[3], $self->{sel_exit}->[4], $self->{sel_exit}->[2]);
917     $self->SUPER::end ($self->{sel_exit}->[1]);
918    
919 elmex 1.26 quick_msg ($mapedit, "($self->{sel_exit}->[3]:$self->{sel_exit}->[4]) $self->{sel_exit}->[0]->{_name} => ($x:$y) $exit2->{_name}", 0);
920 elmex 1.18
921     $::MAINWIN->{edit_collection}{pick}->edit ($map, $x, $y);
922    
923     $self->{sel_exit} = undef;
924     $self->{sel_lbl}->set_text ('');
925     } else {
926     quick_msg ($mapedit, "no exit object found");
927     }
928 elmex 1.16 }
929    
930     # $self->stack_action ($as, dclone ($pick));
931     #$map->change_stack ($x, $y, $as); # insert_arch_stack_layer ($as, $arch));
932     }
933    
934     sub end {}
935 elmex 1.7
936    
937 elmex 1.1 =head1 AUTHOR
938    
939     Marc Lehmann <schmorp@schmorp.de>
940     http://home.schmorp.de/
941    
942     Robin Redeker <elmex@ta-sa.org>
943     http://www.ta-sa.org/
944    
945     =cut
946 root 1.5
947     1
948 elmex 1.1