ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.33
Committed: Tue Apr 4 13:05:51 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.32: +2 -8 lines
Log Message:
Implemented a different inventory editor, added context menu to picker to
add stuff to the inventory (drag&drop doesn't work yet, will be done later).

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