ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/cfplus.ext
(Generate patch)

Comparing deliantra/maps/perl/cfplus.ext (file contents):
Revision 1.1 by root, Mon Jun 12 13:23:43 2006 UTC vs.
Revision 1.11 by root, Sun Oct 1 11:23:30 2006 UTC

1#! perl 1#! perl
2 2
3# additional support for cfplus client 3# additional support for cfplus client
4 4
5use NPC_Dialogue;
6
7=head1 CF+ protocol extensions
8
9This module implements protocol extensions for use by the CF+ client, but
10can be used by other clients as well. It uses the C<extcmd> mechanism
11exclusively.
12
13=over 4
14
15=item ... = extcmd cfplus_support { version => $client_version }
16
17Registers the client the the server. the client should send the highest
18version of the protocol it supports itself, and the server returns the
19highest version of the protocol it supports in the C<version> key itself.
20
21=cut
22
5cf::register_extcmd cfplus_support => sub { 23cf::register_extcmd cfplus_support => sub {
6 my ($pl, $data) = @_; 24 my ($pl, $msg) = @_;
7 25
8 my ($token, $client_version) = split / /, $data, 2; 26 # $msg->{version}
9 27
10 $pl->send ("ext $token 1"); 28 (version => 2)
11}; 29};
12
13sub parse_message($) {
14 map [split /\n/, $_, 2],
15 grep length,
16 split /^\@match /m,
17 $_[0]
18}
19 30
20my %dialog; # currently active dialogs 31my %dialog; # currently active dialogs
21 32
22sub dialog_tell { 33my $timer = Event->timer (interval => 0.2, parked => 1, cb => sub {
23 my ($dialog, $msg) = @_; 34 while (my ($id, $dialog) = each %dialog) {
35 my (undef, $dx, $dy) = $dialog->{ob}->rangevector ($dialog->{npc});
36 next if (abs $dx) <= 2 && (abs $dy) <= 2;
24 37
25 my $pl = cf::player::find $dialog->{name}; 38 $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "out of range");
26 39 delete $dialog{$id};
27 for my $match (@{ $dialog->{match} }) {
28 for (split /\|/, $match->[0]) {
29 if ($_ eq "*" || 0 <= index $msg, $_) {
30 my $reply = $match->[1];
31
32 # combine lines into paragraphs
33 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
34 $reply =~ s/\n\n/\n/g;
35
36 my @kw;
37 # now mark up all matching keywords
38 for my $match (@{ $dialog->{match} }) {
39 for (sort { (length $b) <=> (length $a) } split /\|/, $match->[0]) {
40 if ($reply =~ /\b\Q$_\E\b/i) {
41 push @kw, $_;
42 last;
43 }
44 }
45 }
46
47 $pl->send ("ext $dialog->{token} msg " . join "\x00", $reply, @kw);
48 return;
49 }
50 }
51 } 40 }
52 41
53 $pl->send ("ext $dialog->{token} msg ..."); 42 $_[0]->w->stop unless keys %dialog;
43});
44
45sub dialog_tell {
46 my ($id, $dialog, $msg) = @_;
47
48 my $pl = $dialog->{ob}->contr;
49 my ($reply, @kw) = $dialog->tell ($msg);
50 $reply = "..." unless $reply;
51
52 $pl->ext_reply ($id => msgtype => "reply", msg => $reply, add_topics => \@kw);
54} 53}
55 54
55=item ... = extcmd lookat { dx => $dx, dy => $dy }
56
57"Looks at" the mapspace displaced (dx|dy) relative to the player
56# return "interesting" information about the given tile 58and returns "interesting" information about it.
57# currently only returns the npc_dialog title when a dialog is possible 59
60Keys it can return include:
61
62 npc_dialog => $name
63 There is an npc or other object that can "talk" to the player.
64
65=cut
66
58cf::register_extcmd lookat => sub { 67cf::register_extcmd lookat => sub {
59 my ($pl, $data) = @_; 68 my ($pl, $msg) = @_;
69 my ($dx, $dy) = @$msg{qw(dx dy)};
60 70
61 my ($token, $dx, $dy) = split / /, $data; 71 my $near = (abs $dx) <= 2 && (abs $dy) <= 2;
62 72
63 my %res; 73 my %res;
64
65 my $near = abs ($dx) <= 2 && abs ($dy) <= 2;
66 74
67 if ($pl->cell_visible ($dx, $dy)) { 75 if ($pl->cell_visible ($dx, $dy)) {
68 for my $ob ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) { 76 for my $ob ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) {
69 $res{npc_dialog} = $ob->name 77 $res{npc_dialog} = $ob->name
70 if $near && $ob->message =~ /^\@match /; 78 if $near && NPC_Dialogue::has_dialogue $ob;
71 } 79 }
72 } 80 }
73 81
74 $pl->send ("ext $token " . join "\x00", %res); 82 %res
75}; 83};
76 84
85=item ... = extcmd npc_dialog_begin { msgid => $id, dx => $dx, dy => $dy }
86
87Tries to start a dialogue with the mapspace specified by $dx and $dy (see
88C<extcmd lookat>). The $msgid will be used as a handle for all future
89messages related to this dialog interaction.
90
91It either replies with an error reply or starts a dialog by telling
92the npc "hi" and returning a reply strcuture as with C<extcmd
93npc_dialog_tell>.
94
95=cut
96
77cf::register_extcmd npc_dialog_begin => sub { 97cf::register_extcmd npc_dialog_begin => sub {
78 my ($pl, $data) = @_; 98 my ($pl, $msg) = @_;
99 my ($id, $dx, $dy) = @$msg{qw(msgid dx dy)};
79 100
80 my ($token, $dx, $dy) = split / /, $data;
81
82 return unless abs ($dx) <= 2 && abs ($dy) <= 2; 101 return unless (abs $dx) <= 2 && (abs $dy) <= 2;
83 return unless $pl->cell_visible ($dx, $dy); 102 return unless $pl->cell_visible ($dx, $dy);
84 103
85 for my $npc ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) { 104 for my $npc ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) {
86 if (my @match = parse_message $npc->get_message) { 105 if (NPC_Dialogue::has_dialogue $npc) {
87 $dialog{$token} = { 106 $dialog{$id} = new NPC_Dialogue ob => $pl->ob, npc => $npc;
88 name => $pl->ob->name,
89 token => $token,
90 npc => $npc,
91 match => \@match,
92 };
93
94 dialog_tell $dialog{$token}, "hi"; 107 dialog_tell $id, $dialog{$id}, "hi";
108 $timer->start;
95 return; 109 return;
96 } 110 }
97 } 111 }
98 112
99 $pl->send ("ext $token error"); 113 (msgtype => "error", msg => "nothing to talk to found")
100}; 114};
101 115
116=item ... = extcmd npc_dialog_tell { msgid => $id, msg => $text }
117
118Tells the NPC the given $text message and returns a reply structure which
119can have the following keys:
120
121 msgtype => "reply"
122 msg => $reply_text,
123 add_topics => [additional topic strings]
124 del_topics => [invalidated topic strings]
125
126=cut
127
102cf::register_extcmd npc_dialog_tell => sub { 128cf::register_extcmd npc_dialog_tell => sub {
103 my ($pl, $data) = @_; 129 my ($pl, $msg) = @_;
104 130
105 my ($token, $msg) = split / /, $data, 2; 131 dialog_tell $msg->{msgid}, $dialog{$msg->{msgid}}, $msg->{msg}
132 if $dialog{$msg->{msgid}};
106 133
107 dialog_tell $dialog{$token}, $msg 134 ()
108 if $dialog{$token};
109}; 135};
110 136
137=item extcmd npc_dialog_end { msgid => $id }
138
139Finishes the dialog, invalidating the handle.
140
141=cut
142
111cf::register_extcmd npc_dialog_end => sub { 143cf::register_extcmd npc_dialog_end => sub {
112 my ($pl, $token) = @_; 144 my ($pl, $msg) = @_;
113 145
114 delete $dialog{$token}; 146 delete $dialog{$msg->{msgid}};
147
148 ()
115}; 149};
116 150
151cf::attach_to_players
152 on_logout => sub {
153 my ($pl) = @_;
154
155 delete $dialog{$_} for grep $pl->ob == $dialog{$_}{ob}, keys %dialog;
156 },
157;
158
159cf::register_extcmd editor_support => sub {
160 my ($pl, $msg) = @_;
161
162 (
163 cvs_root => $cf::CFG{editor_cvs_root},
164 upload => $cf::CFG{editor_upload},
165 )
166};
167
168sub unload {
169 while (my ($id, $dialog) = each %dialog) {
170 $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "npc dialogue module was reloaded");
171 }
172
173 %dialog = ();
174}
175
176=back
177
178=cut
179

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines