ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.38
Committed: Thu Jun 1 14:33:14 2006 UTC (18 years ago) by elmex
Branch: MAIN
Changes since 1.37: +17 -7 lines
Log Message:
added a more general connect tool

File Contents

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