ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/cfplus.ext
Revision: 1.17
Committed: Sun Apr 11 04:52:07 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-3_1, rel-3_0, HEAD
Changes since 1.16: +1 -1 lines
Log Message:
prefer AE functions over EV functions#

File Contents

# Content
1 #! perl
2
3 # additional support for cfplus client
4
5 use NPC_Dialogue;
6
7 =head1 CF+ protocol extensions
8
9 This module implements protocol extensions for use by the CF+ client, but
10 can be used by other clients as well. It uses the C<extcmd> mechanism
11 exclusively.
12
13 =over 4
14
15 =item ... = extcmd cfplus_support { version => $client_version }
16
17 Registers the client the the server. the client should send the highest
18 version of the protocol it supports itself, and the server returns the
19 highest version of the protocol it supports in the C<version> key itself.
20
21 =cut
22
23 cf::register_extcmd cfplus_support => sub {
24 my ($pl, %msg) = @_;
25
26 $pl->ns->{cfplus_ext} = $msg{version};
27
28 (version => $msg{version} >= 2 ? 2 : 0)
29 };
30
31 sub dialog_tell {
32 my ($id, $dialog, $msg) = @_;
33
34 my $pl = $dialog->{pl};
35 my ($reply, @kw) = $dialog->tell ($msg);
36
37 $reply = "..." unless defined $reply;
38
39 $pl->ext_msg ($id, update => msg => $pl->expand_cfpod ($reply), add_topics => \@kw)
40 if $reply ne ""; # NPC might not want to say, or wants to say something later
41 }
42
43 =item ... = extcmd lookat { dx => $dx, dy => $dy }
44
45 "Looks at" the mapspace displaced (dx|dy) relative to the player
46 and returns "interesting" information about it.
47
48 Keys it can return include:
49
50 npc_dialog => $name
51 There is an npc or other object that can "talk" to the player.
52
53 =cut
54
55 cf::register_extcmd lookat => sub {
56 my ($pl, $dx, $dy) = @_;
57
58 return unless $pl->ob && $pl->ob->map;
59
60 my $near = (abs $dx) <= 2 && (abs $dy) <= 2;
61
62 my %res;
63
64 if ($pl->cell_visible ($dx, $dy)) {
65 for my $ob ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) {
66 $res{npc_dialog} = [$ob->name, $dx, $dy]
67 if $near && $ob->has_dialogue && !$pl->{npc_dialog};
68 }
69 }
70
71 %res
72 };
73
74 =item ... = extcmd npc_dialog_begin { msgid => $id, dx => $dx, dy => $dy }
75
76 Tries to start a dialogue with the mapspace specified by $dx and $dy (see
77 C<extcmd lookat>). The $msgid will be used as a handle for all future
78 messages related to this dialog interaction.
79
80 It either replies with an error reply or starts a dialog by telling
81 the npc "hi" and returning a reply structure as with C<extcmd
82 npc_dialog_tell>.
83
84 =cut
85
86 cf::register_extcmd npc_dialog_begin => sub {
87 my ($pl, $id, $token) = @_;
88
89 #TODO:
90 # this is not a request, so returning does no good: make it a request and die on error
91
92 return unless $pl->ob && $pl->ob->map;
93
94 my ($name, $dx, $dy) = @$token;
95
96 return (error => "too far away") unless (abs $dx) <= 2 && (abs $dy) <= 2;
97 return (error => "nothing to talk there") unless $pl->cell_visible ($dx, $dy);
98 return (error => "only one dialog can be open at a time") if $pl->{npc_dialog}; # only one dialog at a time
99
100 for my $npc ($pl->ob->map->at ($pl->ob->x + $dx, $pl->ob->y + $dy)) {
101 if ($npc->has_dialogue) {
102 $pl->attach ("npc_dialog_active");
103 $pl->{npc_dialog} = new NPC_Dialogue pl => $pl, npc => $npc, id => $id;
104 dialog_tell $id, $pl->{npc_dialog}, "hi";
105 return;
106 }
107 }
108
109 (error => "nothing to talk to to found")
110 };
111
112 =item ... = extcmd npc_dialog_tell { msgid => $id, msg => $text }
113
114 Tells the NPC the given $text message and returns a reply structure which
115 can have the following keys:
116
117 msg => $reply_text,
118 add_topics => [additional topic strings]
119 del_topics => [invalidated topic strings]
120
121 =cut
122
123 cf::register_extcmd npc_dialog_tell => sub {
124 my ($pl, $id, $msg) = @_;
125
126 if (my $dialog = $pl->{npc_dialog}) {
127 dialog_tell $id, $dialog, $msg;
128 }
129 };
130
131 =item extcmd npc_dialog_end { msgid => $id }
132
133 Finishes the dialog, invalidating the handle.
134
135 =cut
136
137 cf::register_extcmd npc_dialog_end => sub {
138 my ($pl, $id) = @_;
139
140 if (my $dialog = delete $pl->{npc_dialog}) {
141 $pl->detach ("ncp_dialog_active");
142 }
143 };
144
145 =item ... = extcmd editor_support
146
147 Returns the value required by clients that have an editor to download and
148 upload maps from/to the server.
149
150 servertype => (game|test) type of this server
151 gameserver => the hostname:port of the normal game server
152 testserver => the hostname:port of the test server the maps can be tested on
153 cvs_root => the (http) url where the cvs root for downloading is located
154 lib_root => the (http) url where archetypes data can be found
155 upload => the (http) url where clients can upload maps
156
157 If those values are not supplied or empty strings, the server does not
158 support downloading, uploading, testing, respectively.
159
160 The upload script expects the following values in a multipart form upload:
161
162 client: a descriptive string describing the editor and version used to upload
163 path: absolute server-side map path beginning with /
164 map: the map file itself
165 mapdir: the cvs root url originally used to download the map
166 revision: cvs-revision originally used to download the map
167 comment: a comment supplied by the user that documents the changes
168 login: deliantra server login
169 password: deliantra server password, optionally used for authentication purposes
170
171 =cut
172
173 cf::register_extcmd editor_support => sub {
174 my ($pl, %msg) = @_;
175
176 my %cfg = map +($_ => $cf::CFG{"editor_$_"}), qw(servertype servertypes gameserver testserver cvs_root lib_root builder_ui);
177
178 # clients 2.10 and below check for type and "nameserver" :/
179 $cfg{type} = $cfg{servertype};
180 $cfg{nameserver} = $cfg{gameserver};
181
182 %cfg
183 };
184
185 sub unload {
186 for my $pl (cf::player::list) {
187 if (my $dialog = delete $pl->{npc_dialog}) {
188 $pl->detach ("npc_dialog_active");
189 $pl->ext_msg ($dialog->{id} => error => "npc dialogue module was reloaded");
190 }
191 }
192 }
193
194 cf::player::attachment npc_dialog_active =>
195 on_logout => sub {
196 my ($pl) = @_;
197
198 delete $pl->{npc_dialog};
199 $pl->detach ("npc_dialog_active");
200 },
201 on_map_change => sub {
202 my ($pl) = @_;
203
204 my $dialog = delete $pl->{npc_dialog}
205 or return;
206
207 $pl->ext_msg ($dialog->{id} => error => "out of range");
208 $pl->detach ("npc_dialog_active");
209 },
210 on_move => sub {
211 my ($pl, $dir) = @_;
212
213 # must delay a bit :/
214 my $delay; $delay = AE::timer 0, 0, sub {
215 undef $delay;
216
217 if (my $dialog = $pl->{npc_dialog}) {
218 my (undef, $dx, $dy) = $pl->ob->rangevector ($dialog->{npc});
219
220 return if (abs $dx) <= 2 && (abs $dy) <= 2;
221
222 delete $pl->{npc_dialog};
223 $pl->ext_msg ($dialog->{id} => error => "out of range");
224 $pl->detach ("npc_dialog_active");
225 }
226 };
227 },
228 ;
229
230 cf::player->attach (
231 on_login => sub {
232 my ($pl) = @_;
233
234 delete $pl->{npc_dialog};
235 },
236 );
237
238 =back
239
240 =cut
241