ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/EditAction.pm
Revision: 1.7
Committed: Sun Mar 12 12:18:55 2006 UTC (18 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.6: +161 -36 lines
Log Message:
Implemented first parts of the new attribute editor.

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 my ($arch) = @_;
154 print "UPD: $arch\n"; #d#
155
156 $map->change_stack ($x, $y, $cstack);
157 });
158
159 $::MAINWIN->update_stack_view ($map, $x, $y);
160 }
161
162 package GCE::EditAction::Place;
163
164 use GCE::Util;
165 use Gtk2;
166 use strict;
167
168 our @ISA = qw/GCE::EditAction::RadioModed/;
169
170 sub init {
171 my ($self) = @_;
172
173 my $vb = new Gtk2::VBox;
174 $self->add_mode_button ($vb, "auto", "auto");
175 $self->add_mode_button ($vb, "top", "top");
176 $self->add_mode_button ($vb, "above floor", "above");
177 $self->add_mode_button ($vb, "below floor", "below");
178 $self->add_mode_button ($vb, "bottom", "bottom");
179
180 $self->tool_widget ($vb);
181 }
182
183 sub want_cursor { 0 }
184
185 sub begin {
186 my ($self, $map, $x, $y, $btn) = @_;
187
188 $self->SUPER::begin ($map, $x, $y, $btn);
189 $self->edit ($map, $x, $y, $btn);
190 }
191
192 sub edit {
193 my ($self, $map, $x, $y, $btn) = @_;
194
195 if ($btn == 3) { # btn 3 does pick
196
197 my $cstack = $map->get ($x, $y) or return;
198 my $arch = $cstack->[-1];
199
200 $arch->{_virtual}
201 and $arch = $arch->{_virtual};
202
203 $::MAINWIN->set_pick ($arch)
204 if @$cstack;;
205
206 } else {
207
208 my $pick = $::MAINWIN->get_pick;
209 my $as = $map->get ($x, $y);
210
211 my $arch = { _name => $pick->{_name} };
212
213 $self->stack_action ($as, $arch);
214 $map->change_stack ($x, $y, $as); # insert_arch_stack_layer ($as, $arch));
215 # push @$as, $arch
216 # unless @$as && $as->[-1]->{_name} eq $arch->{_name};
217
218 #$map->set ($x, $y, $as);
219 }
220 }
221
222 sub stack_action {
223 my ($self, $stack, $arch) = @_;
224
225 my $m = $self->get_mode;
226
227 if ($m eq 'top') {
228 if (@$stack == 0 or $stack->[-1]->{_name} ne $arch->{_name}) {
229 push @$stack, $arch;
230 }
231
232 } elsif ($m eq 'bottom') {
233 if (@$stack == 0 or $stack->[0]->{_name} ne $arch->{_name}) {
234 unshift @$stack, $arch;
235 }
236
237 } elsif ($m eq 'above') {
238 my $fidx = stack_find_floor ($stack, 'from_top');
239
240 if (@$stack == 0
241 or not ($stack->[$fidx + 1])
242 or $stack->[$fidx + 1]->{_name} ne $arch->{_name})
243 {
244 splice (@$stack, $fidx + 1, 0, $arch);
245 }
246
247 } elsif ($m eq 'below') {
248 my $fidx = stack_find_floor ($stack, 'from_bottom');
249
250 if (@$stack == 0
251 or $fidx == 0
252 or not ($stack->[$fidx - 1])
253 or $stack->[$fidx - 1]->{_name} ne $arch->{_name})
254 {
255 splice (@$stack, $fidx, 0, $arch);
256 }
257
258 } elsif ($m eq 'auto') {
259 my $fidx = stack_find_floor ($stack, 'from_top');
260 my $widx = stack_find_wall ($stack);
261
262 if (arch_is_floor ($arch)) { # we want to place a floor tile
263
264 if (arch_is_floor ($stack->[$fidx])) { # replace
265 $stack->[$fidx] = $arch;
266
267 } else { # insert on bottom
268 unshift @$stack, $arch;
269 }
270
271 } elsif (arch_is_wall ($arch)) { # we want to place a wall
272
273 if (arch_is_wall ($stack->[$widx])) { # replace
274 $stack->[$widx] = $arch;
275
276 } else { # insert above floor
277 splice (@$stack, $fidx + 1, 0, $arch);
278 }
279
280 } else {
281
282 if (arch_is_wall ($stack->[$widx])) {
283 # if we have a wall above the floor, we don't want to place anything
284 # on that tile anymore
285 return;
286 }
287
288 if (@$stack == 0
289 or not ($stack->[-1])
290 or $stack->[-1]->{_name} ne $arch->{_name})
291 {
292 push @$stack, $arch;
293 }
294 }
295 }
296 }
297
298 package GCE::EditAction::Erase;
299 use GCE::Util;
300 use Gtk2;
301 use strict;
302
303 our @ISA = qw/GCE::EditAction::RadioModed/;
304
305 sub want_cursor { 0 }
306
307 sub init {
308 my ($self) = @_;
309
310 my $vb = new Gtk2::VBox;
311 $self->add_mode_button ($vb, "top", "top");
312 $self->add_mode_button ($vb, "walls", "walls");
313 $self->add_mode_button ($vb, "above floor", "above", 'default');
314 $self->add_mode_button ($vb, "floor", "floor");
315 $self->add_mode_button ($vb, "below floor", "below");
316 $self->add_mode_button ($vb, "bottom", "bottom");
317 $self->add_mode_button ($vb, "pick match", "match");
318
319 $self->tool_widget ($vb);
320
321 $vb->pack_start ($self->{no_wall_check} = Gtk2::CheckButton->new ("exclude walls"), 0, 1, 0);
322 $vb->pack_start ($self->{no_monsters_check} = Gtk2::CheckButton->new ("exclude monsters"), 0, 1, 0);
323 }
324
325 sub check_excluded {
326 my ($self, $arch) = @_;
327
328 my $a1 = $self->{no_monsters_check}->get_active;
329 my $a2 = $self->{no_wall_check}->get_active;
330
331 my $r = ($self->{no_wall_check}->get_active && arch_is_wall ($arch))
332 || ($self->{no_monsters_check}->get_active && arch_is_monster ($arch));
333 return $r;
334 }
335
336 sub begin {
337 my ($self, $map, $x, $y, $btn) = @_;
338
339 $self->SUPER::begin ($map, $x, $y, $btn);
340 $self->edit ($map, $x, $y, $btn);
341 }
342
343 sub edit {
344 my ($self, $map, $x, $y, $btn) = @_;
345
346 my $as = $map->get ($x, $y);
347 $self->stack_action ($as);
348 $map->change_stack ($x, $y, $as);
349 }
350
351 sub stack_action {
352 my ($self, $stack) = @_;
353
354 my $m = $self->get_mode;
355
356 if ($m eq 'top') {
357 pop @$stack;
358
359 } elsif ($m eq 'bottom') {
360 shift @$stack;
361
362 } elsif ($m eq 'above') {
363 my $fidx = stack_find_floor ($stack, 'from_top');
364
365 if (arch_is_floor ($stack->[$fidx]) and $stack->[$fidx + 1]) {
366 splice (@$stack, $fidx + 1, 1)
367 unless $self->check_excluded ($stack->[$fidx + 1])
368
369 } elsif (not arch_is_floor ($stack->[$fidx])) {
370 splice (@$stack, $fidx, 1)
371 unless $self->check_excluded ($stack->[$fidx])
372
373 }
374
375 } elsif ($m eq 'below') {
376 my $fidx = stack_find_floor ($stack, 'from_bottom');
377
378 if ($fidx > 0 and not arch_is_floor ($stack->[$fidx - 1])) {
379 splice (@$stack, $fidx - 1, 1)
380 unless $self->check_excluded ($stack->[$fidx - 1])
381
382 } elsif (not arch_is_floor ($stack->[$fidx])) { # no floor found
383 splice (@$stack, $fidx, 1)
384 unless $self->check_excluded ($stack->[$fidx])
385
386 }
387
388 } elsif ($m eq 'walls') {
389 my $widx = stack_find_wall ($stack, 'from_top');
390
391 while (arch_is_wall ($stack->[$widx])) {
392 splice (@$stack, $widx, 1);
393 $widx = stack_find_wall ($stack, 'from_top')
394 }
395
396 } elsif ($m eq 'floor') {
397 my $fidx = stack_find_floor ($stack, 'from_top');
398
399 while (arch_is_floor ($stack->[$fidx])) {
400 splice (@$stack, $fidx, 1);
401 $fidx = stack_find_floor ($stack, 'from_top')
402 }
403
404 } elsif ($m eq 'match') {
405 my $pick_name = $::MAINWIN->get_pick ()->{_name};
406 my $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
407
408 while ($stack->[$idx] and $stack->[$idx]->{_name} eq $pick_name) {
409 splice (@$stack, $idx, 1);
410 $idx = stack_find ($stack, 'from_top', sub { $_[0]->{_name} eq $pick_name });
411 }
412 }
413 }
414
415
416
417 =head1 AUTHOR
418
419 Marc Lehmann <schmorp@schmorp.de>
420 http://home.schmorp.de/
421
422 Robin Redeker <elmex@ta-sa.org>
423 http://www.ta-sa.org/
424
425 =cut
426
427 1
428