#! perl # depends=widget mandatory # this module implements a rather fancy worldmap our $WORLDMAP_UPDATE_INTERVAL = $cf::CFG{worldmap_update_interval} || 10; our %PLAYERINFO; sub update_worldmap { my ($ws) = @_; my $labels = delete $ws->{labels}; while (my ($k, $v) = each %PLAYERINFO) { if (my $old = $labels->{$k}) { $ws->{labels}{$k} = $old; } else { $ws->{labels}{$k} = $ws->new (Label => text => $k, fontsize => 0.2, c_rel => 1, c_halign => -.5, c_valign => -1, ); $ws->{canvas}->add ($ws->{labels}{$k}); } $ws->{labels}{$k}->set (c_x => $v->[0], c_y => $v->[1]); } } sub create_widgets { my ($ns) = @_; my $ws = $ns->new_widgetset; $ws->{toplevel} = my $w = $ws->new (Toplevel => title => "Worldmap", name => "server_item_worldmap", force_w => 400, force_h => 400, x => "center", y => "center", has_close_button => 1, on_delete => sub { shift->hide }, on_visibility_change => sub { $_[0]{visibility} = $_[1]; update_worldmap $_[0]{ws} if $_[1]; }, ); my $face = cf::face::find "res/worldmap.jpg"; $ns->send_face ($face); $ns->flush_fx; $w->add (my $sw = $ws->new (ScrolledWindow => scroll_x => 1, scroll_y => 1)); $sw->add (my $canvas = $ws->{canvas} = $ws->new (Canvas => expand => 1)); $ws->{mapface} = $ws->new (Face => expand => 1, size_w => undef, size_h => undef, face => $face, ); $ws->{canvas}->add ($ws->{mapface}); $ws } cf::object::attachment item_worldmap => on_apply => sub { my ($self, $who) = @_; my $ns = $who->contr->ns; if ($ns->{can_widget}) { my $ws = $ns->{ws_worldmap} ||= create_widgets $ns; $ws->{toplevel}->toggle_visibility; } else { $ns->send_msg ("log", "Your client doesn't support the (required) widget extension. Try CFPlus at http://crossfire.schmorp.de/.", cf::NDI_RED); } cf::override 1; }, ; cf::async_ext { my $schedule_interval = Coro::Event->timer (after => 1); while () { $schedule_interval->interval ($WORLDMAP_UPDATE_INTERVAL); $schedule_interval->next; # recalculate player info %PLAYERINFO = (); for (values %cf::PLAYER) { my $map = $_->ob->map or next; $map =~ /^\/world\/world_(\d\d\d)_(\d\d\d)/ or next; my $ob = $_->ob; my $x = ($1 - 100) * 50 + $ob->x; my $y = ($2 - 100) * 50 + $ob->y; 0 <= $x && 0 <= $y && $x < 1500 && $y < 1500 or next; # rounding saves network bandwidth... $PLAYERINFO{$ob->name} = [(sprintf "%.3f", $x / 1500), (sprintf "%.3f", $y / 1500)]; } for (values %cf::PLAYER) { my $ns = $_->ns or next; update_worldmap $ns->{ws_worldmap} if $ns->{ws_worldmap} && $ns->{ws_worldmap}{toplevel}{visibility}; } } };