ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.27
Committed: Sat Apr 1 19:23:58 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.26: +6 -4 lines
Log Message:
fixed TODO: - follow exit does not work on virtual tiles

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