ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.8
Committed: Sun Mar 12 13:40:34 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.7: +8 -3 lines
Log Message:
Simple attribute editing now works. Still missing: bitmasks and there
are some refreshbugs in the StackView and AttrEdit. Should be refreshed
when the map detects changes...

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 tool_widget { if ($_[1]) { $_[0]->{widget} = $_[1] } $_[0]->{widget} }
27 sub init { }
28
29 sub want_cursor { 1 }
30
31 sub special_arrow { }
32
33 # edits one tile of the map
34 sub edit_one {
35 my ($self, $x, $y) = @_;
36 # do one edition
37 }
38
39 # edits a selection
40 sub edit_selection {
41 }
42
43 # abstraction for edit_one and edit_selection ?
44 # takes selection if present
45 sub edit {
46 my ($self, $map, $x, $y, $btn) = @_;
47 }
48
49 sub begin {
50 my ($self, $map, $x, $y, $btn) = @_;
51
52 $map->change_begin (ref $self);
53 }
54
55 sub end {
56 my ($self, $map) = @_;
57
58 if (my $changeset = $map->change_end) {
59 splice @{ $map->{undo_stack} ||= [] },
60 $map->{undo_stack_pos}++, 1e6,
61 $changeset;
62
63 #TODO: limit undo stack size to some preconfigured limit
64 }
65 }
66
67 package GCE::EditAction::RadioModed;
68
69 our @ISA = qw/GCE::EditAction/;
70
71 sub add_mode_button {
72 my ($self, $vb, $lbl, $mode, $default) = @_;
73
74 $vb->pack_start (my $b = Gtk2::RadioButton->new ($self->{place_radio_grp}, $lbl), 0, 1, 0);
75 unless (defined $self->{place_radio_grp}) {
76 $self->{place_radio_grp} = $b->get_group;
77
78 unless (defined $default) {
79 $b->set_active (1);
80 $self->set_mode ($mode);
81 }
82 }
83 if ($default) {
84 $b->set_active (1);
85 $self->set_mode ($mode);
86 }
87 $b->signal_connect (clicked => sub {
88 $self->set_mode ($mode);
89 });
90 }
91
92 sub set_mode {
93 my ($self, $mode) = @_;
94 $self->{place_mode} = $mode;
95 }
96
97 sub get_mode {
98 my ($self) = @_;
99 $self->{place_mode}
100 }
101
102 sub init {
103 my ($self) = @_;
104
105 die "Implement me!!";
106
107 # my $vb = new Gtk2::VBox;
108 # $self->_add_mode_button ($vb, "auto", "auto");
109 # $self->_add_mode_button ($vb, "top", "top");
110 # $self->_add_mode_button ($vb, "above floor", "above");
111 # $self->_add_mode_button ($vb, "below floor", "below");
112 # $self->_add_mode_button ($vb, "bottom", "bottom");
113 #
114 # $self->{widget} = $vb;
115 }
116
117 package GCE::EditAction::Pick;
118 use strict;
119
120 our @ISA = qw/GCE::EditAction/;
121
122 sub want_cursor { 0 }
123
124 sub special_arrow { 'GDK_HAND2' }
125
126 sub begin {
127 my ($self, $map, $x, $y, $btn) = @_;
128
129 $self->SUPER::begin ($map, $x, $y, $btn);
130 $self->edit ($map, $x, $y, $btn);
131 }
132
133 sub edit {
134 my ($self, $map, $x, $y, $btn) = @_;
135
136 my $cstack = $map->get ($x, $y);
137
138 my $arch = $cstack->[-1];
139
140 # virtual... grmbl....
141 # FIXME: I have to patch the stack of the real arch??? argl.. how??
142 if ($arch->{_virtual}) {
143 $x = $arch->{virtual_x};
144 $y = $arch->{virtual_y};
145 $arch = $arch->{_virtual};
146 $cstack = $map->get ($x, $y);
147 }
148
149 $::MAINWIN->set_pick ($arch)
150 if @$cstack && $btn == 1;
151
152 $::MAINWIN->update_attr_editor ($arch, sub {
153 $map->change_begin (ref $self);
154 $map->change_stack ($x, $y, $cstack);
155 # XXX: Put this into a generic function!!! See also EditTools.pm
156 # FIXME: Fix the automatic update on undo here!
157 if (my $changeset = $map->change_end) {
158 splice @{ $map->{undo_stack} ||= [] },
159 $map->{undo_stack_pos}++, 1e6,
160 $changeset;
161 }
162 });
163
164 $::MAINWIN->update_stack_view ($map, $x, $y);
165 }
166
167 package GCE::EditAction::Place;
168
169 use GCE::Util;
170 use Gtk2;
171 use strict;
172
173 our @ISA = qw/GCE::EditAction::RadioModed/;
174
175 sub init {
176 my ($self) = @_;
177
178 my $vb = new Gtk2::VBox;
179 $self->add_mode_button ($vb, "auto", "auto");
180 $self->add_mode_button ($vb, "top", "top");
181 $self->add_mode_button ($vb, "above floor", "above");
182 $self->add_mode_button ($vb, "below floor", "below");
183 $self->add_mode_button ($vb, "bottom", "bottom");
184
185 $self->tool_widget ($vb);
186 }
187
188 sub want_cursor { 0 }
189
190 sub begin {
191 my ($self, $map, $x, $y, $btn) = @_;
192
193 $self->SUPER::begin ($map, $x, $y, $btn);
194 $self->edit ($map, $x, $y, $btn);
195 }
196
197 sub edit {
198 my ($self, $map, $x, $y, $btn) = @_;
199
200 if ($btn == 3) { # btn 3 does pick
201
202 my $cstack = $map->get ($x, $y) or return;
203 my $arch = $cstack->[-1];
204
205 $arch->{_virtual}
206 and $arch = $arch->{_virtual};
207
208 $::MAINWIN->set_pick ($arch)
209 if @$cstack;;
210
211 } else {
212
213 my $pick = $::MAINWIN->get_pick;
214 my $as = $map->get ($x, $y);
215
216 my $arch = { _name => $pick->{_name} };
217
218 $self->stack_action ($as, $arch);
219 $map->change_stack ($x, $y, $as); # insert_arch_stack_layer ($as, $arch));
220 # push @$as, $arch
221 # unless @$as && $as->[-1]->{_name} eq $arch->{_name};
222
223 #$map->set ($x, $y, $as);
224 }
225 }
226
227 sub stack_action {
228 my ($self, $stack, $arch) = @_;
229
230 my $m = $self->get_mode;
231
232 if ($m eq 'top') {
233 if (@$stack == 0 or $stack->[-1]->{_name} ne $arch->{_name}) {
234 push @$stack, $arch;
235 }
236
237 } elsif ($m eq 'bottom') {
238 if (@$stack == 0 or $stack->[0]->{_name} ne $arch->{_name}) {
239 unshift @$stack, $arch;
240 }
241
242 } elsif ($m eq 'above') {
243 my $fidx = stack_find_floor ($stack, 'from_top');
244
245 if (@$stack == 0
246 or not ($stack->[$fidx + 1])
247 or $stack->[$fidx + 1]->{_name} ne $arch->{_name})
248 {
249 splice (@$stack, $fidx + 1, 0, $arch);
250 }
251
252 } elsif ($m eq 'below') {
253 my $fidx = stack_find_floor ($stack, 'from_bottom');
254
255 if (@$stack == 0
256 or $fidx == 0
257 or not ($stack->[$fidx - 1])
258 or $stack->[$fidx - 1]->{_name} ne $arch->{_name})
259 {
260 splice (@$stack, $fidx, 0, $arch);
261 }
262
263 } elsif ($m eq 'auto') {
264 my $fidx = stack_find_floor ($stack, 'from_top');
265 my $widx = stack_find_wall ($stack);
266
267 if (arch_is_floor ($arch)) { # we want to place a floor tile
268
269 if (arch_is_floor ($stack->[$fidx])) { # replace
270 $stack->[$fidx] = $arch;
271
272 } else { # insert on bottom
273 unshift @$stack, $arch;
274 }
275
276 } elsif (arch_is_wall ($arch)) { # we want to place a wall
277
278 if (arch_is_wall ($stack->[$widx])) { # replace
279 $stack->[$widx] = $arch;
280
281 } else { # insert above floor
282 splice (@$stack, $fidx + 1, 0, $arch);
283 }
284
285 } else {
286
287 if (arch_is_wall ($stack->[$widx])) {
288 # if we have a wall above the floor, we don't want to place anything
289 # on that tile anymore
290 return;
291 }
292
293 if (@$stack == 0
294 or not ($stack->[-1])
295 or $stack->[-1]->{_name} ne $arch->{_name})
296 {
297 push @$stack, $arch;
298 }
299 }
300 }
301 }
302
303 package GCE::EditAction::Erase;
304 use GCE::Util;
305 use Gtk2;
306 use strict;
307
308 our @ISA = qw/GCE::EditAction::RadioModed/;
309
310 sub want_cursor { 0 }
311
312 sub init {
313 my ($self) = @_;
314
315 my $vb = new Gtk2::VBox;
316 $self->add_mode_button ($vb, "top", "top");
317 $self->add_mode_button ($vb, "walls", "walls");
318 $self->add_mode_button ($vb, "above floor", "above", 'default');
319 $self->add_mode_button ($vb, "floor", "floor");
320 $self->add_mode_button ($vb, "below floor", "below");
321 $self->add_mode_button ($vb, "bottom", "bottom");
322 $self->add_mode_button ($vb, "pick match", "match");
323
324 $self->tool_widget ($vb);
325
326 $vb->pack_start ($self->{no_wall_check} = Gtk2::CheckButton->new ("exclude walls"), 0, 1, 0);
327 $vb->pack_start ($self->{no_monsters_check} = Gtk2::CheckButton->new ("exclude monsters"), 0, 1, 0);
328 }
329
330 sub check_excluded {
331 my ($self, $arch) = @_;
332
333 my $a1 = $self->{no_monsters_check}->get_active;
334 my $a2 = $self->{no_wall_check}->get_active;
335
336 my $r = ($self->{no_wall_check}->get_active && arch_is_wall ($arch))
337 || ($self->{no_monsters_check}->get_active && arch_is_monster ($arch));
338 return $r;
339 }
340
341 sub begin {
342 my ($self, $map, $x, $y, $btn) = @_;
343
344 $self->SUPER::begin ($map, $x, $y, $btn);
345 $self->edit ($map, $x, $y, $btn);
346 }
347
348 sub edit {
349 my ($self, $map, $x, $y, $btn) = @_;
350
351 my $as = $map->get ($x, $y);
352 $self->stack_action ($as);
353 $map->change_stack ($x, $y, $as);
354 }
355
356 sub stack_action {
357 my ($self, $stack) = @_;
358
359 my $m = $self->get_mode;
360
361 if ($m eq 'top') {
362 pop @$stack;
363
364 } elsif ($m eq 'bottom') {
365 shift @$stack;
366
367 } elsif ($m eq 'above') {
368 my $fidx = stack_find_floor ($stack, 'from_top');
369
370 if (arch_is_floor ($stack->[$fidx]) and $stack->[$fidx + 1]) {
371 splice (@$stack, $fidx + 1, 1)
372 unless $self->check_excluded ($stack->[$fidx + 1])
373
374 } elsif (not arch_is_floor ($stack->[$fidx])) {
375 splice (@$stack, $fidx, 1)
376 unless $self->check_excluded ($stack->[$fidx])
377
378 }
379
380 } elsif ($m eq 'below') {
381 my $fidx = stack_find_floor ($stack, 'from_bottom');
382
383 if ($fidx > 0 and not arch_is_floor ($stack->[$fidx - 1])) {
384 splice (@$stack, $fidx - 1, 1)
385 unless $self->check_excluded ($stack->[$fidx - 1])
386
387 } elsif (not arch_is_floor ($stack->[$fidx])) { # no floor found
388 splice (@$stack, $fidx, 1)
389 unless $self->check_excluded ($stack->[$fidx])
390
391 }
392
393 } elsif ($m eq 'walls') {
394 my $widx = stack_find_wall ($stack, 'from_top');
395
396 while (arch_is_wall ($stack->[$widx])) {
397 splice (@$stack, $widx, 1);
398 $widx = stack_find_wall ($stack, 'from_top')
399 }
400
401 } elsif ($m eq 'floor') {
402 my $fidx = stack_find_floor ($stack, 'from_top');
403
404 while (arch_is_floor ($stack->[$fidx])) {
405 splice (@$stack, $fidx, 1);
406 $fidx = stack_find_floor ($stack, 'from_top')
407 }
408
409 } elsif ($m eq 'match') {
410 my $pick_name = $::MAINWIN->get_pick ()->{_name};
411 my $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
412
413 while ($stack->[$idx] and $stack->[$idx]->{_name} eq $pick_name) {
414 splice (@$stack, $idx, 1);
415 $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
416 }
417 }
418 }
419
420
421
422 =head1 AUTHOR
423
424 Marc Lehmann <schmorp@schmorp.de>
425 http://home.schmorp.de/
426
427 Robin Redeker <elmex@ta-sa.org>
428 http://www.ta-sa.org/
429
430 =cut
431
432 1
433