#! perl # additional support for cfplus client use NPC_Dialogue; =head1 CF+ protocol extensions This module implements protocol extensions for use by the CF+ client, but can be used by other clients as well. It uses the C mechanism exclusively. =over 4 =item ... = extcmd cfplus_support { version => $client_version } Registers the client the the server. the client should send the highest version of the protocol it supports itself, and the server returns the highest version of the protocol it supports in the C key itself. =cut cf::register_extcmd cfplus_support => sub { my ($pl, $msg) = @_; # $msg->{version} (version => 2) }; my %dialog; # currently active dialogs my $timer = Event->timer (interval => 0.2, parked => 1, cb => sub { while (my ($id, $dialog) = each %dialog) { my (undef, $dx, $dy) = $dialog->{ob}->rangevector ($dialog->{npc}); next if (abs $dx) <= 2 && (abs $dy) <= 2; $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "out of range"); delete $dialog{$id}; } $_[0]->w->stop unless keys %dialog; }); sub dialog_tell { my ($id, $dialog, $msg) = @_; my $pl = $dialog->{ob}->contr; my ($reply, @kw) = $dialog->tell ($msg); $reply = "..." unless $reply; $pl->ext_reply ($id => msgtype => "reply", msg => $reply, add_topics => \@kw); } =item ... = extcmd lookat { dx => $dx, dy => $dy } "Looks at" the mapspace displaced (dx|dy) relative to the player and returns "interesting" information about it. Keys it can return include: npc_dialog => $name There is an npc or other object that can "talk" to the player. =cut cf::register_extcmd lookat => sub { my ($pl, $msg) = @_; my ($dx, $dy) = @$msg{qw(dx dy)}; my $near = (abs $dx) <= 2 && (abs $dy) <= 2; my %res; if ($pl->cell_visible ($dx, $dy)) { for my $ob ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) { $res{npc_dialog} = $ob->name if $near && NPC_Dialogue::has_dialogue $ob; } } %res }; =item ... = extcmd npc_dialog_begin { msgid => $id, dx => $dx, dy => $dy } Tries to start a dialogue with the mapspace specified by $dx and $dy (see C). The $msgid will be used as a handle for all future messages related to this dialog interaction. It either replies with an error reply or starts a dialog by telling the npc "hi" and returning a reply strcuture as with C. =cut cf::register_extcmd npc_dialog_begin => sub { my ($pl, $msg) = @_; my ($id, $dx, $dy) = @$msg{qw(msgid dx dy)}; return unless (abs $dx) <= 2 && (abs $dy) <= 2; return unless $pl->cell_visible ($dx, $dy); for my $npc ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) { if (NPC_Dialogue::has_dialogue $npc) { $dialog{$id} = new NPC_Dialogue ob => $pl->ob, npc => $npc; dialog_tell $id, $dialog{$id}, "hi"; $timer->start; return; } } (msgtype => "error", msg => "nothing to talk to found") }; =item ... = extcmd npc_dialog_tell { msgid => $id, msg => $text } Tells the NPC the given $text message and returns a reply structure which can have the following keys: msgtype => "reply" msg => $reply_text, add_topics => [additional topic strings] del_topics => [invalidated topic strings] =cut cf::register_extcmd npc_dialog_tell => sub { my ($pl, $msg) = @_; dialog_tell $msg->{msgid}, $dialog{$msg->{msgid}}, $msg->{msg} if $dialog{$msg->{msgid}}; () }; =item extcmd npc_dialog_end { msgid => $id } Finishes the dialog, invalidating the handle. =cut cf::register_extcmd npc_dialog_end => sub { my ($pl, $msg) = @_; delete $dialog{$msg->{msgid}}; () }; cf::attach_to_players on_logout => sub { my ($pl) = @_; delete $dialog{$_} for grep $pl->ob == $dialog{$_}{ob}, keys %dialog; }, ; cf::register_extcmd editor_support => sub { my ($pl, $msg) = @_; ( cvs_root => $cf::CFG{editor_cvs_root}, upload => $cf::CFG{editor_upload}, ) }; sub unload { while (my ($id, $dialog) = each %dialog) { $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "npc dialogue module was reloaded"); } %dialog = (); } =back =cut