ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
(Generate patch)

Comparing deliantra/gde/GCE/MapEditor.pm (file contents):
Revision 1.57 by elmex, Fri Aug 10 12:20:51 2007 UTC vs.
Revision 1.73 by elmex, Thu Nov 27 22:47:14 2008 UTC

8 8
9use Gtk2; 9use Gtk2;
10use Gtk2::Gdk::Keysyms; 10use Gtk2::Gdk::Keysyms;
11use Gtk2::SimpleMenu; 11use Gtk2::SimpleMenu;
12 12
13use Crossfire; 13use Deliantra;
14use Crossfire::Map; 14use Deliantra::Map;
15use Crossfire::MapWidget; 15use Deliantra::MapWidget;
16 16
17use GCE::AttrEdit; 17use GCE::AttrEdit;
18use GCE::Util; 18use GCE::Util;
19use GCE::HashDialog; 19use GCE::HashDialog;
20 20
121 }, 121 },
122 "Map _Attachments" => { 122 "Map _Attachments" => {
123 callback => sub { $self->open_attach_edit }, 123 callback => sub { $self->open_attach_edit },
124 accelerator => "<ctrl>A" 124 accelerator => "<ctrl>A"
125 }, 125 },
126 "Map Meta _Info" => { 126# "Map Meta _Info" => {
127 callback => sub { $self->open_meta_info }, 127# callback => sub { $self->open_meta_info },
128 }, 128# },
129 Upload => { 129# Upload => {
130 item_type => '<Branch>', 130# item_type => '<Branch>',
131 children => [ 131# children => [
132 "Upload for testing" => { 132# "Upload for testing" => {
133 callback => sub { $self->upload_map_test }, 133# callback => sub { $self->upload_map_test },
134 }, 134# },
135 "Upload for inclusion" => { 135# "Upload for inclusion" => {
136 callback => sub { $self->upload_map_incl }, 136# callback => sub { $self->upload_map_incl },
137 }, 137# },
138 ] 138# ]
139 }, 139# },
140 "_Map Resize" => { 140 "_Map Resize" => {
141 callback => sub { $self->open_resize_map }, 141 callback => sub { $self->open_resize_map },
142 }, 142 },
143 "Close" => { 143 "Close" => {
144 callback => sub { $self->destroy }, 144 callback => sub { $self->destroy },
145 accelerator => "<ctrl>W",
145 }, 146 },
146 ] 147 ]
147 }, 148 },
148 _Edit => { 149 _Edit => {
149 item_type => '<Branch>', 150 item_type => '<Branch>',
297 298
298# FIXME: Fix the automatic update of the attribute editor! and also the stack view! 299# FIXME: Fix the automatic update of the attribute editor! and also the stack view!
299sub undo { 300sub undo {
300 my ($self) = @_; 301 my ($self) = @_;
301 302
302 my $map = $self->{map}; # the Crossfire::MapWidget 303 my $map = $self->{map}; # the Deliantra::MapWidget
303 304
304 $map->{undo_stack_pos} 305 $map->{undo_stack_pos}
305 or return; 306 or return;
306 307
307 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); 308 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
309
310 $::MAINWIN->update_stack_view ();
308} 311}
309 312
310sub get_stack_refs { 313sub get_stack_refs {
311 my ($self, $map, $x, $y) = @_; 314 my ($self, $map, $x, $y) = @_;
312 315
315 return [] unless @$cstack; 318 return [] unless @$cstack;
316 319
317 my @refs; 320 my @refs;
318 321
319 for my $arch (@$cstack) { 322 for my $arch (@$cstack) {
320 my ($ox, $oy) = ($x, $y); 323 my ($ix, $iy, $iarch, $istack) = devirtualize ($map, $x, $y, $arch, $cstack);
321 if ($arch->{_virtual}) {
322 $ox = $arch->{virtual_x};
323 $oy = $arch->{virtual_y};
324 $arch = $arch->{_virtual};
325 $cstack = $map->get ($ox, $oy);
326 # XXX: This heavily blows up if $arch isn't on $cstack now.. and it actually really does :(
327 }
328
329 push @refs, 324 push @refs,
330 GCE::ArchRef->new ( 325 GCE::ArchRef->new (
331 arch => $arch, 326 arch => $iarch,
332 source => 'map', 327 source => 'map',
333 cb => sub { 328 cb => sub {
334 $map->change_begin ('attredit'); 329 $map->change_begin ('attredit');
335 $map->change_stack ($ox, $oy, $cstack); 330 $map->change_stack ($ix, $iy, $istack);
336 331
337 if (my $changeset = $map->change_end) { 332 if (my $changeset = $map->change_end) {
338 splice @{ $map->{undo_stack} ||= [] }, 333 splice @{ $map->{undo_stack} ||= [] },
339 $map->{undo_stack_pos}++, 1e6, 334 $map->{undo_stack_pos}++, 1e6,
340 $changeset; 335 $changeset;
347} 342}
348 343
349sub redo { 344sub redo {
350 my ($self) = @_; 345 my ($self) = @_;
351 346
352 my $map = $self->{map}; # the Crossfire::MapWidget 347 my $map = $self->{map}; # the Deliantra::MapWidget
353 348
354 $map->{undo_stack} 349 $map->{undo_stack}
355 and $map->{undo_stack_pos} < @{$map->{undo_stack}} 350 and $map->{undo_stack_pos} < @{$map->{undo_stack}}
356 or return; 351 or return;
357 352
362 my ($mapfile) = @_; 357 my ($mapfile) = @_;
363 if (-e "$mapfile.meta") { 358 if (-e "$mapfile.meta") {
364 open my $metafh, "<", "$mapfile.meta" 359 open my $metafh, "<", "$mapfile.meta"
365 or warn "Couldn't open meta file $mapfile.meta: $!"; 360 or warn "Couldn't open meta file $mapfile.meta: $!";
366 my $metadata = do { local $/; <$metafh> }; 361 my $metadata = do { local $/; <$metafh> };
367 return Crossfire::from_json ($metadata); 362 return Deliantra::decode_json ($metadata);
368 } 363 }
369} 364}
370 365
371sub save_meta_info { 366sub save_meta_info {
372 my ($mapfile, $metainfo) = @_; 367 my ($mapfile, $metainfo) = @_;
373 open my $metafh, ">", "$mapfile.meta" 368 open my $metafh, ">", "$mapfile.meta"
374 or warn "Couldn't write meta file $mapfile.meta: $!"; 369 or warn "Couldn't write meta file $mapfile.meta: $!";
375 print $metafh Crossfire::to_json ($metainfo); 370 print $metafh Deliantra::encode_json ($metainfo);
376} 371}
377 372
378sub open_map { 373sub open_map {
379 my ($self, $path, $key) = @_; 374 my ($self, $path, $key) = @_;
380 375
381 $self->{mapkey} = $key; 376 $self->{mapkey} = $key;
382 377
383 if (ref $path) { 378 if (ref $path) {
384 $self->{map}->set_map ($path); 379 $self->{map}->set_map ($path);
385 delete $self->{meta_info}; 380 delete $self->{meta_info};
386 $self->set_title ('<ram>'); 381 $self->set_window_title;
387 382
388 } else { 383 } else {
389 my $ok = 0; 384 my $ok = 0;
390 if (-e $path && -f $path) { 385 if (-e $path && -f $path) {
391 $ok = 1; 386 $ok = 1;
399 } 394 }
400 unless ($ok) { 395 unless ($ok) {
401 die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n"; 396 die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n";
402 } 397 }
403 $self->{path} = $path; 398 $self->{path} = $path;
404 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path); 399 $self->{map}->set_map (my $m = new_from_file Deliantra::Map $path);
405 $self->{meta_info} = load_meta_info ($path); 400 $self->{meta_info} = load_meta_info ($path);
406 $self->set_title ("gce - map editor - $self->{path}"); 401 $self->set_window_title ($self->{path});
402 $::MAINWIN->add_recent($path);
407 } 403 }
404 $self->update_overlays;
408 $self->close_windows; 405 $self->close_windows;
409} 406}
410 407
411sub save_map { 408sub save_map {
412 my ($self) = @_; 409 my ($self) = @_;
415 $self->{map}{map}->write_file ($self->{path}); 412 $self->{map}{map}->write_file ($self->{path});
416 if ($self->{meta_info}) { 413 if ($self->{meta_info}) {
417 save_meta_info ($self->{path}, $self->{meta_info}); 414 save_meta_info ($self->{path}, $self->{meta_info});
418 } 415 }
419 quick_msg ($self, "saved to $self->{path}"); 416 quick_msg ($self, "saved to $self->{path}");
420 $self->set_title ("gce - map editor - $self->{path}"); 417 $self->set_window_title ($self->{path});
418 $::MAINWIN->add_recent($self->{path});
421 } else { 419 } else {
422 $self->save_map_as; 420 $self->save_map_as;
423 } 421 }
424} 422}
425 423
424sub set_window_title {
425 my ($self, $title) = @_;
426
427 $title = 'Unsaved'
428 unless $title;
429
430 $self->set_title(File::Basename::basename($title).' - deliantra editor');
431}
432
426sub save_map_as { 433sub save_map_as {
427 my ($self) = @_; 434 my ($self) = @_;
428 435
429 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path}); 436 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
430 437
431 if ('ok' eq $fc->run) { 438 if ('ok' eq $fc->run) {
432 439
433 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder; 440 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
434 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++; 441 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
435 442
436 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename); 443 if($fc->get_filename) {
437 if ($self->{meta_info}) { 444 $self->{path} = $fc->get_filename;
438 save_meta_info ($self->{path}, $self->{meta_info}); 445 $self->save_map;
439 } 446 }
440 quick_msg ($self, "saved to $self->{path}");
441 $self->set_title ("gce - map editor - $self->{path}");
442 } 447 }
443 448
444 $fc->destroy; 449 $fc->destroy;
450}
451
452sub _get_conns_for_obj {
453 my ($obj, $x, $y, $rconns) = @_;
454
455 if (defined $obj->{connected}) {
456 $rconns->{$x}->{$y}->{$obj->{connected}} = 1;
457
458 } elsif (defined $obj->{msg}) {
459 my $msg = $obj->{msg};
460
461 while ($msg =~ s/\@trigger\s+(\d+)//) {
462 $rconns->{$x}->{$y}->{$1} = 1;
463 }
464 }
465}
466
467sub update_overlays {
468 my ($self, $sx, $sy, $stack) = @_;
469 my $conns = {};
470
471 if (not $stack) {
472 my ($w, $h) = ($self->{map}{map}{width}, $self->{map}{map}{height});
473
474 for (my $x = 0; $x < $w; $x++) {
475 for (my $y = 0; $y < $h; $y++) {
476 _get_conns_for_obj ($_, $x, $y, $conns)
477 for @{$self->{map}->get_ro ($x, $y)};
478 }
479 }
480
481 # delete prev. overlays
482 for (keys %{$self->{_conn_overlays}}) {
483 $self->{map}->overlay ($_);
484 }
485 } else {
486 # del old overlay for this place
487 my $ovl = "connection_$sx\_$sy";
488 $self->{map}->overlay ($ovl) if delete $self->{_conn_overlays}->{$ovl};
489 _get_conns_for_obj ($_, $sx, $sy, $conns)
490 for @$stack;
491 }
492
493 # put new overlays there
494 for my $x (keys %$conns) {
495 for my $y (keys %{$conns->{$x}}) {
496 my $ovlname = "connection_$x\_$y";
497 my $conns_ovl = join (', ', keys %{$conns->{$x}->{$y}});
498 $self->{_conn_overlays}->{$ovlname} = 1;
499 my ($a, $t, $ac)
500 = Gtk2::Pango->parse_markup (
501 "<span size=\"xx-small\">$conns_ovl</span>", ''
502 );
503 my $pl = $self->{map}->create_pango_layout ('');
504 $pl->set_attributes ($a);
505 $pl->set_text ($t);
506 my ($ink_rect, $logical_rect) = $pl->get_pixel_extents;
507
508 $self->{map}->overlay (
509 $ovlname, $x * TILESIZE, $y * TILESIZE, TILESIZE, TILESIZE,
510 sub {
511 my ($mapwin, $x, $y) = @_;
512 if (!$self->{_conn_upd_ovl_gc_fg}) {
513 my $gc
514 = $self->{_conn_upd_ovl_gc_fg}
515 = Gtk2::Gdk::GC->new ($mapwin->{window});
516 my $cm = $mapwin->{window}->get_colormap;
517 $gc->set_foreground (gtk2_get_color ($mapwin, "yellow"));
518 $gc->set_background (gtk2_get_color ($mapwin, "black"));
519 }
520 $mapwin->{window}->draw_rectangle (
521 $mapwin->style->black_gc,
522 1,
523 $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
524 );
525 $mapwin->{window}->draw_rectangle (
526 $self->{_conn_upd_ovl_gc_fg},
527 0,
528 $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
529 );
530 $mapwin->{window}->draw_layout_with_colors (
531 $mapwin->style->black_gc, $x + 1, $y + 1, $pl,
532 $self->{connection_overlay_foreground},
533 $self->{connection_overlay_background},
534 )
535 }
536 );
537 }
538 }
445} 539}
446 540
447################################################################# 541#################################################################
448###### DIALOGOUES ############################################### 542###### DIALOGOUES ###############################################
449################################################################# 543#################################################################
462 ref_hash => $self->{map}{map}{info}, 556 ref_hash => $self->{map}{map}{info},
463 dialog => [ 557 dialog => [
464 [width => 'Width' => 'string'], 558 [width => 'Width' => 'string'],
465 [height => 'Height' => 'string'], 559 [height => 'Height' => 'string'],
466 ], 560 ],
467 close_on_save => 1, 561 close_on_save => 1,
562 save_button_label => 'resize',
468 save_cb => sub { 563 save_cb => sub {
469 my ($info) = @_; 564 my ($info) = @_;
470 $self->{map}{map}->resize ($info->{width}, $info->{height}); 565 $self->{map}{map}->resize ($info->{width}, $info->{height});
471 $self->{map}->invalidate_all; 566 $self->{map}->set_map ($self->{map}{map});
567 $self->update_overlays;
472 } 568 }
473 ); 569 );
474 570
475 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); 571 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
476 572
518 [path => 'Map path' => 'string'], 614 [path => 'Map path' => 'string'],
519 ], 615 ],
520 close_on_save => 1, 616 close_on_save => 1,
521 save_cb => sub { 617 save_cb => sub {
522 my ($meta) = @_; 618 my ($meta) = @_;
523 warn "UPLOAD[".Crossfire::to_json ($meta)."]\n"; 619 warn "UPLOAD[".Deliantra::encode_json ($meta)."]\n";
524 } 620 }
525 ); 621 );
526 622
527 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); 623 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
528 624
549 [cf_password=> 'Password' => 'password'], 645 [cf_password=> 'Password' => 'password'],
550 [path => 'Map path' => 'string'], 646 [path => 'Map path' => 'string'],
551 ], 647 ],
552 save_cb => sub { 648 save_cb => sub {
553 my ($meta) = @_; 649 my ($meta) = @_;
554 warn "UPLOAD[".Crossfire::to_json ($meta)."]\n"; 650 warn "UPLOAD[".Deliantra::encode_json ($meta)."]\n";
555 } 651 }
556 ); 652 );
557 653
558 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); 654 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
559 655
593sub open_map_info { 689sub open_map_info {
594 my ($self) = @_; 690 my ($self) = @_;
595 return if $self->{map_info}; 691 return if $self->{map_info};
596 692
597 my $w = $self->{map_info} = Gtk2::Window->new ('toplevel'); 693 my $w = $self->{map_info} = Gtk2::Window->new ('toplevel');
598 $w->set_title ("gcrossedit - map info"); 694 $w->set_title ("deliantra editor - map info");
599 695
600 $w->add (my $vb = Gtk2::VBox->new); 696 $w->add (my $vb = Gtk2::VBox->new);
601 $vb->add (my $sw = Gtk2::ScrolledWindow->new); 697 $vb->add (my $sw = Gtk2::ScrolledWindow->new);
602 $sw->set_policy ('automatic', 'automatic'); 698 $sw->set_policy ('automatic', 'automatic');
603 $sw->add (my $txt = Gtk2::TextView->new); 699 $sw->add (my $txt = Gtk2::TextView->new);
701################################################################# 797#################################################################
702 798
703sub INIT_INSTANCE { 799sub INIT_INSTANCE {
704 my ($self) = @_; 800 my ($self) = @_;
705 801
706 $self->set_title ('gce - map editor'); 802 $self->set_window_title;
707 $self->add (my $vb = Gtk2::VBox->new); 803 $self->add (my $vb = Gtk2::VBox->new);
708 804
709 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); 805 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
710 806
711 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); 807 $vb->pack_start (my $map = $self->{map} = Deliantra::MapWidget->new, 1, 1, 0);
808
809 $map->signal_connect_after (stack_change => sub {
810 my ($map, $x, $y, $stack) = @_;
811 $self->update_overlays ($x, $y, $stack);
812 $::MAINWIN->update_map_pos ($self, $x, $y);
813 });
814 $map->signal_connect_after (swap_stack_change => sub {
815 my ($map, $x, $y, $stack) = @_;
816 $self->update_overlays ($x, $y, $stack);
817 $::MAINWIN->update_map_pos ($self, $x, $y);
818 });
819
820 $self->{connection_overlay_foreground}
821 = Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0);
822 $self->{connection_overlay_background}
823 = Gtk2::Gdk::Color->new (0, 0, 0);
824 # my $ygc
825 # = $self->{connection_overlay_yellow_gc}
826 # = Gtk2::Gdk::GC->new ($self->{map}{window});
827 # $ygc->set_foreground (Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0));
712 828
713 $map->signal_connect_after (key_press_event => sub { 829 $map->signal_connect_after (key_press_event => sub {
714 my ($map, $event) = @_; 830 my ($map, $event) = @_;
715 831
716 my $kv = $event->keyval; 832 my $kv = $event->keyval;
717 833
718 my $ret = 0; 834 my $ret = 0;
719 835
720 my ($x, $y) = $map->coord ($map->get_pointer); 836 my ($x, $y) = $map->coord ($map->get_pointer);
721 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
722 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
723 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }], 837 for ([c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
724 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }], 838 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
725 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }], 839 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
726 ) 840 )
727 { 841 {
728 my $ed = $_; 842 my $ed = $_;
729 843
730 if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) { 844 if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
731 my $was_in_draw = defined $self->{draw_mode}; 845 my $was_in_draw = defined $self->{draw_mode};
732 846
733 $self->stop_drawmode ($map) 847 $self->stop_drawmode ($map) if $was_in_draw;
734 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
735 848
736 $ed->[1]->(); 849 $ed->[1]->();
737 $ret = 1; 850 $ret = 1;
738 851
739 $self->start_drawmode ($map) 852 $self->start_drawmode ($map) if $was_in_draw;
740 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
741 } 853 }
742 } 854 }
743 855
744 if ($self->ea->special_arrow) { 856 if ($self->ea->special_arrow) {
745 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); 857 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
749 } 861 }
750 862
751 $ret 863 $ret
752 }); 864 });
753 865
754 $map->signal_connect_after (key_release_event => sub {
755 my ($map, $event) = @_;
756
757 my $ret = 0;
758
759 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
760 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
761 {
762 my $was_in_draw = defined $self->{draw_mode};
763
764 $self->stop_drawmode ($map)
765 if $was_in_draw;
766
767 delete $self->{ea_alt};
768 $ret = 1;
769
770 $self->start_drawmode ($map)
771 if $was_in_draw;
772 }
773
774 if ($self->ea->special_arrow) {
775 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
776 } else {
777 # FIXME: Get the original cursor and insert it here
778 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
779 }
780
781 $ret
782 });
783 $map->signal_connect_after (button_press_event => sub { 866 $map->signal_connect_after (button_press_event => sub {
784 my ($map, $event) = @_; 867 my ($map, $event) = @_;
785 868
786 if ((not $self->{draw_mode}) and $event->button == 1) { 869 if ((not $self->{draw_mode}) and $event->button == 1) {
787 my $ea = $self->ea; 870 my $ea = $self->ea;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines