… | |
… | |
3 | =head1 NAME |
3 | =head1 NAME |
4 | |
4 | |
5 | GCE::MapEditor - the map editing widget |
5 | GCE::MapEditor - the map editing widget |
6 | |
6 | |
7 | =cut |
7 | =cut |
|
|
8 | |
|
|
9 | use Devel::FindRef; |
8 | |
10 | |
9 | use Gtk2; |
11 | use Gtk2; |
10 | use Gtk2::Gdk::Keysyms; |
12 | use Gtk2::Gdk::Keysyms; |
11 | use Gtk2::SimpleMenu; |
13 | use Gtk2::SimpleMenu; |
12 | |
14 | |
… | |
… | |
25 | |
27 | |
26 | use Scalar::Util qw/weaken/; |
28 | use Scalar::Util qw/weaken/; |
27 | use Storable qw/dclone/; |
29 | use Storable qw/dclone/; |
28 | |
30 | |
29 | use Guard; |
31 | use Guard; |
|
|
32 | |
|
|
33 | use AnyEvent; |
30 | |
34 | |
31 | use strict; |
35 | use strict; |
32 | |
36 | |
33 | ################################################################# |
37 | ################################################################# |
34 | ###### WINDOW MANAGEMENT ######################################## |
38 | ###### WINDOW MANAGEMENT ######################################## |
… | |
… | |
77 | } |
81 | } |
78 | |
82 | |
79 | $menu->append (my $sep = new Gtk2::SeparatorMenuItem); |
83 | $menu->append (my $sep = new Gtk2::SeparatorMenuItem); |
80 | $sep->show; |
84 | $sep->show; |
81 | |
85 | |
82 | for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) { |
86 | my $curs = $self->cursor ("context menu at ($x,$y)", $x, $y); |
|
|
87 | |
|
|
88 | for (my $z = 0; $z < $curs->size; $z++) { |
|
|
89 | my $ar = $curs->cursor ($z); |
|
|
90 | |
83 | my $item = Gtk2::MenuItem->new ($sr->longname); |
91 | my $item = Gtk2::MenuItem->new ($ar->longname); |
84 | $menu->append ($item); |
92 | $menu->append ($item); |
85 | $item->set_submenu (my $smenu = new Gtk2::Menu); |
93 | $item->set_submenu (my $smenu = new Gtk2::Menu); |
86 | |
94 | |
87 | for my $act ( |
95 | for my $act ( |
88 | [ 'Add inventory' => sub { $_[0]->add_inv ($::MAINWIN->get_pick) } ], |
96 | [ 'Add inventory' => sub { $ar->push ($::MAINWIN->get_pick) } ], |
89 | [ 'Find in picker' => sub { $::MAINWIN->open_pick_window ({ selection => $sr->picker_folder }) } ], |
97 | [ 'Find in picker' => |
|
|
98 | sub { |
|
|
99 | $::MAINWIN->open_pick_window ( |
|
|
100 | { selection => $ar->picker_folder }) |
|
|
101 | } |
|
|
102 | ], |
90 | ) { |
103 | ) { |
91 | my $sitem = Gtk2::MenuItem->new ($act->[0]); |
104 | my $sitem = Gtk2::MenuItem->new ($act->[0]); |
92 | $smenu->append ($sitem); |
105 | $smenu->append ($sitem); |
93 | $sitem->signal_connect (activate => sub { $act->[1]->($sr) }); |
106 | $sitem->signal_connect (activate => $act->[1]); |
94 | $sitem->show; |
107 | $sitem->show; |
95 | } |
108 | } |
96 | |
109 | |
97 | $item->show; |
110 | $item->show; |
98 | } |
111 | } |
… | |
… | |
308 | $map->{undo_stack_pos} |
321 | $map->{undo_stack_pos} |
309 | or return; |
322 | or return; |
310 | |
323 | |
311 | $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); |
324 | $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); |
312 | |
325 | |
313 | $::MAINWIN->update_stack_view (); |
326 | $self->invalidate_cursors ('undo'); |
314 | } |
|
|
315 | |
|
|
316 | sub get_stack_refs { |
|
|
317 | my ($self, $map, $x, $y) = @_; |
|
|
318 | |
|
|
319 | my $cstack = $map->get ($x, $y); |
|
|
320 | |
|
|
321 | return [] unless @$cstack; |
|
|
322 | |
|
|
323 | my @refs; |
|
|
324 | |
|
|
325 | for my $arch (@$cstack) { |
|
|
326 | my ($ix, $iy, $iarch, $istack) = devirtualize ($map, $x, $y, $arch, $cstack); |
|
|
327 | push @refs, |
|
|
328 | GCE::ArchRef->new ( |
|
|
329 | arch => $iarch, |
|
|
330 | source => 'map', |
|
|
331 | cb => sub { |
|
|
332 | $map->change_begin ('attredit'); |
|
|
333 | $map->change_stack ($ix, $iy, $istack); |
|
|
334 | |
|
|
335 | if (my $changeset = $map->change_end) { |
|
|
336 | splice @{ $map->{undo_stack} ||= [] }, |
|
|
337 | $map->{undo_stack_pos}++, 1e6, |
|
|
338 | $changeset; |
|
|
339 | } |
|
|
340 | } |
|
|
341 | ); |
|
|
342 | } |
|
|
343 | |
|
|
344 | return @refs; |
|
|
345 | } |
327 | } |
346 | |
328 | |
347 | sub redo { |
329 | sub redo { |
348 | my ($self) = @_; |
330 | my ($self) = @_; |
349 | |
331 | |
350 | my $map = $self->{map}; # the Deliantra::MapWidget |
332 | my $map = $self->{map}; # the Deliantra::MapWidget |
351 | |
333 | |
352 | $map->{undo_stack} |
334 | $map->{undo_stack} |
353 | and $map->{undo_stack_pos} < @{$map->{undo_stack}} |
335 | and $map->{undo_stack_pos} < @{$map->{undo_stack}} |
354 | or return; |
336 | or return; |
355 | |
337 | |
356 | $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); |
338 | $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); |
|
|
339 | |
|
|
340 | $self->invalidate_cursors ('redo'); |
357 | } |
341 | } |
358 | |
342 | |
359 | sub load_meta_info { |
343 | sub load_meta_info { |
360 | my ($mapfile) = @_; |
344 | my ($mapfile) = @_; |
361 | if (-e "$mapfile.meta") { |
345 | if (-e "$mapfile.meta") { |
… | |
… | |
799 | ################################################################# |
783 | ################################################################# |
800 | ###### CURSORS ################################################## |
784 | ###### CURSORS ################################################## |
801 | ################################################################# |
785 | ################################################################# |
802 | |
786 | |
803 | sub cursor { |
787 | sub cursor { |
804 | my ($self, $owner, $x, $y, $z, @inv_path) = @_; |
788 | my ($self, $x, $y) = @_; |
805 | |
789 | |
806 | my $cursmap = ($self->{_cursors} ||= []); |
790 | my $cursmap = ($self->{_cursors} ||= []); |
807 | my $cursor; |
791 | my $cursor = $cursmap->[$x]->[$y]; |
808 | |
792 | |
809 | weaken $self; |
793 | weaken $self; |
810 | |
794 | |
811 | #d# warn "NEWCURSOR $x, $y, ($owner)\n"; |
795 | #d# warn "NEWCURSOR $x, $y, ($owner)\n"; |
812 | |
796 | |
|
|
797 | unless ($cursor) { |
813 | eval { |
798 | eval { |
814 | $cursor = GCE::ArchRef->new ( |
799 | $cursor = GCE::StackRef->new (src => [$self, $x, $y]); |
815 | x => $x, |
800 | $cursmap->[$x]->[$y] = $cursor; |
816 | y => $y, |
|
|
817 | map => $self, |
|
|
818 | owner => $owner |
|
|
819 | ); |
801 | }; |
820 | push @{$cursmap->[$x]->[$y]}, $cursor |
|
|
821 | }; |
|
|
822 | if ($@) { |
802 | if ($@) { |
823 | if ($@ =~ /bad arch cursor/) { |
803 | if ($@ =~ /bad arch cursor/) { |
824 | #d# warn "BAD CURSOR $x, $y, $owner\n"; |
804 | #d# warn "BAD CURSOR $x, $y, $owner\n"; |
825 | return undef; |
805 | return undef; |
826 | } else { |
806 | } else { |
827 | die $@; |
807 | die $@; |
828 | } |
808 | } |
829 | } |
809 | } |
830 | |
810 | |
831 | #d# warn "ADDED CURSOR $owner, $x, $y, => [$cursor] " . (1 * $cursor) . "\n"; |
811 | #d# warn "ADDED CURSOR $owner, $x, $y, => [$cursor] " . (1 * $cursor) . "\n"; |
832 | |
812 | |
833 | weaken $cursmap->[$x]->[$y]->[-1]; |
813 | weaken $cursmap->[$x]->[$y]; |
834 | |
|
|
835 | $cursor->{z} = $z if defined $z; |
|
|
836 | $cursor->{inv} = \@inv_path if @inv_path; |
|
|
837 | |
|
|
838 | my ($cx, $cy, $cid) = ($cursor->{x}, $cursor->{y}, 1 * $cursor); |
|
|
839 | $cursor->{guard} = guard { |
|
|
840 | if ($cid == $self->{_last_attr_edit_overlay_cid}) { |
|
|
841 | $self->{map}->overlay ('attr_edit'); |
|
|
842 | } |
|
|
843 | }; |
814 | } |
844 | |
815 | |
|
|
816 | #d# my $cu = $cursor; |
|
|
817 | #d# weaken $cu; |
|
|
818 | #d# |
|
|
819 | #d# my $n = "$cursor"; |
|
|
820 | #d# |
|
|
821 | #d# my $tv; |
|
|
822 | #d# $tv = AE::timer 1, 1, sub { |
|
|
823 | #d# if ($cu) { |
|
|
824 | #d# warn "OVERLAYREFALIV! $n: " . (Devel::FindRef::track \$cu) . "\n"; |
|
|
825 | #d# } else { |
|
|
826 | #d# undef $tv; |
|
|
827 | #d# } |
|
|
828 | #d# }; |
|
|
829 | |
845 | $self->update_cursor_overlay ($cursor); |
830 | # $self->update_cursor_overlay ($cursor); |
846 | $cursor |
831 | $cursor |
847 | } |
832 | } |
848 | |
833 | |
849 | sub cursors_at { |
834 | sub cursor_at { |
850 | my ($self, $x, $y) = @_; |
835 | my ($self, $x, $y) = @_; |
851 | my $cursmap = ($self->{_cursors} ||= []); |
836 | my $cursmap = ($self->{_cursors} ||= []); |
852 | $cursmap->[$x]->[$y] || [] |
837 | $cursmap->[$x]->[$y] |
853 | } |
838 | } |
854 | |
839 | |
855 | sub update_cursor_overlay { |
840 | sub invalidate_cursors { |
856 | my ($self, $cursor) = @_; |
841 | my ($self, $act) = @_; |
857 | |
842 | |
858 | if ($cursor->{owner} eq 'attr_edit') { |
843 | for my $cols (@{$self->{_cursors}}) { |
859 | $self->{_last_attr_edit_overlay_cid} = 1 * $cursor; |
844 | for my $curs (grep { defined $_ } @$cols) { |
860 | |
845 | $curs->changed ($act || 'some_invalidation', 1); |
861 | $self->{map}->overlay (attr_edit => |
|
|
862 | $cursor->{x} * TILESIZE, |
|
|
863 | $cursor->{y} * TILESIZE, |
|
|
864 | TILESIZE, |
|
|
865 | TILESIZE, |
|
|
866 | sub { |
|
|
867 | my ($self, $x, $y) = @_; |
|
|
868 | |
|
|
869 | if (!$self->{_conn_upd_curs_gc_fg}) { |
|
|
870 | my $gc |
|
|
871 | = $self->{_conn_upd_curs_gc_fg} |
|
|
872 | = Gtk2::Gdk::GC->new ($self->{window}); |
|
|
873 | my $cm = $self->{window}->get_colormap; |
|
|
874 | $gc->set_foreground (gtk2_get_color ($self, "green")); |
|
|
875 | $gc->set_background (gtk2_get_color ($self, "black")); |
|
|
876 | } |
|
|
877 | |
|
|
878 | $self->{window}->draw_rectangle ( |
|
|
879 | $_ & 1 ? $self->style->black_gc : $self->{_conn_upd_curs_gc_fg}, |
|
|
880 | 0, |
|
|
881 | $x + $_, $y + $_, |
|
|
882 | TILESIZE - 1 - $_ * 2, |
|
|
883 | TILESIZE - 1 - $_ * 2 |
|
|
884 | ) for 0..3; |
|
|
885 | } |
846 | } |
886 | ); |
|
|
887 | } |
847 | } |
888 | } |
|
|
889 | |
848 | |
|
|
849 | $self->{_cursors} = []; |
|
|
850 | } |
890 | |
851 | |
891 | ################################################################# |
852 | ################################################################# |
892 | ###### MAP EDITOR INIT ########################################## |
853 | ###### MAP EDITOR INIT ########################################## |
893 | ################################################################# |
854 | ################################################################# |
894 | |
855 | |