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.3 by root, Mon Jun 19 10:15:10 2006 UTC vs.
Revision 1.15 by root, Fri Dec 15 19:06:29 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) = @_;
7
8 my ($token, $client_version) = split / /, $data, 2;
9
10 $pl->send ("ext $token 1");
11};
12
13{
14 package NPC_Dialogue;
15
16 sub has_dialogue {
17 my ($ob) = @_;
18
19 $ob->get_message =~ /^\@match /;
20 }
21
22 sub parse_message($) {
23 map [split /\n/, $_, 2],
24 grep length,
25 split /^\@match /m,
26 $_[0]
27 }
28
29 sub new {
30 my ($class, %arg) = @_;
31
32 my $self = bless {
33 %arg,
34 }, $class;
35
36 $self->{match} ||= [parse_message $self->{npc}->get_message];
37
38 $self;
39 }
40
41 sub greet {
42 my ($self) = @_;
43
44 $self->tell ("hi")
45 }
46
47 sub tell {
48 my ($self, $msg) = @_; 24 my ($pl, $msg) = @_;
49 25
50 for my $match (@{ $self->{match} }) { 26 # $msg->{version}
51 for (split /\|/, $match->[0]) {
52 if ($_ eq "*" || 0 <= index $msg, $_) {
53 my $reply = $match->[1];
54 27
55 # combine lines into paragraphs 28 (version => 2)
56 $reply =~ s/(?<=\S)\n(?=\w)/ /g; 29};
57 $reply =~ s/\n\n/\n/g;
58
59 my @kw;
60 # now mark up all matching keywords
61 for my $match (@{ $self->{match} }) {
62 for (sort { (length $b) <=> (length $a) } split /\|/, $match->[0]) {
63 if ($reply =~ /\b\Q$_\E\b/i) {
64 push @kw, $_;
65 last;
66 }
67 }
68 }
69
70 return wantarray ? ($reply, @kw) : $reply;
71 }
72 }
73 }
74 }
75
76 ()
77}
78 30
79my %dialog; # currently active dialogs 31my %dialog; # currently active dialogs
80 32
33my $timer = Event->timer (interval => 0.2, parked => 1, data => cf::WF_AUTOCANCEL, cb => sub {
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;
37
38 $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "out of range");
39 delete $dialog{$id};
40 }
41
42 $_[0]->w->stop unless keys %dialog;
43});
44
81sub dialog_tell { 45sub dialog_tell {
82 my ($token, $dialog, $msg) = @_; 46 my ($id, $dialog, $msg) = @_;
83 47
84 my $pl = $dialog->{ob}->contr; 48 my $pl = $dialog->{ob}->contr;
85 my ($reply, @kw) = $dialog->tell ($msg); 49 my ($reply, @kw) = $dialog->tell ($msg);
86 $reply = "..." unless $reply; 50 $reply = "..." unless $reply;
87 $pl->send ("ext $token msg " . join "\x00", $reply, @kw); 51
52 $pl->ext_reply ($id => msgtype => "reply", msg => $reply, add_topics => \@kw);
88} 53}
89 54
55=item ... = extcmd lookat { dx => $dx, dy => $dy }
56
57"Looks at" the mapspace displaced (dx|dy) relative to the player
90# return "interesting" information about the given tile 58and returns "interesting" information about it.
91# 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
92cf::register_extcmd lookat => sub { 67cf::register_extcmd lookat => sub {
93 my ($pl, $data) = @_; 68 my ($pl, $msg) = @_;
69 my ($dx, $dy) = @$msg{qw(dx dy)};
94 70
95 my ($token, $dx, $dy) = split / /, $data;
96 my $near = (abs $dx) <= 2 && (abs $dy) <= 2; 71 my $near = (abs $dx) <= 2 && (abs $dy) <= 2;
97 72
98 my %res; 73 my %res;
99 74
100 if ($pl->cell_visible ($dx, $dy)) { 75 if ($pl->cell_visible ($dx, $dy)) {
102 $res{npc_dialog} = $ob->name 77 $res{npc_dialog} = $ob->name
103 if $near && NPC_Dialogue::has_dialogue $ob; 78 if $near && NPC_Dialogue::has_dialogue $ob;
104 } 79 }
105 } 80 }
106 81
107 $pl->send ("ext $token " . join "\x00", %res); 82 %res
108}; 83};
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
109 96
110cf::register_extcmd npc_dialog_begin => sub { 97cf::register_extcmd npc_dialog_begin => sub {
111 my ($pl, $data) = @_; 98 my ($pl, $msg) = @_;
112 99 my ($id, $dx, $dy) = @$msg{qw(msgid dx dy)};
113 my ($token, $dx, $dy) = split / /, $data;
114 100
115 return unless (abs $dx) <= 2 && (abs $dy) <= 2; 101 return unless (abs $dx) <= 2 && (abs $dy) <= 2;
116 return unless $pl->cell_visible ($dx, $dy); 102 return unless $pl->cell_visible ($dx, $dy);
117 103
118 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)) {
119 if (NPC_Dialogue::has_dialogue $npc) { 105 if (NPC_Dialogue::has_dialogue $npc) {
120 $dialog{$token} = new NPC_Dialogue ob => $pl->ob, npc => $npc; 106 $dialog{$id} = new NPC_Dialogue ob => $pl->ob, npc => $npc;
121 dialog_tell $token, $dialog{$token}, "hi"; 107 dialog_tell $id, $dialog{$id}, "hi";
108 $timer->start;
122 return; 109 return;
123 } 110 }
124 } 111 }
125 112
126 $pl->send ("ext $token error"); 113 (msgtype => "error", msg => "nothing to talk to found")
127}; 114};
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
128 127
129cf::register_extcmd npc_dialog_tell => sub { 128cf::register_extcmd npc_dialog_tell => sub {
130 my ($pl, $data) = @_; 129 my ($pl, $msg) = @_;
131 130
132 my ($token, $msg) = split / /, $data, 2; 131 dialog_tell $msg->{msgid}, $dialog{$msg->{msgid}}, $msg->{msg}
132 if $dialog{$msg->{msgid}};
133 133
134 dialog_tell $token, $dialog{$token}, $msg 134 ()
135 if $dialog{$token};
136}; 135};
136
137=item extcmd npc_dialog_end { msgid => $id }
138
139Finishes the dialog, invalidating the handle.
140
141=cut
137 142
138cf::register_extcmd npc_dialog_end => sub { 143cf::register_extcmd npc_dialog_end => sub {
139 my ($pl, $token) = @_;
140
141 delete $dialog{$token};
142};
143
144sub on_logout {
145 my ($pl, $host) = @_; 144 my ($pl, $msg) = @_;
146 145
146 delete $dialog{$msg->{msgid}};
147
148 ()
149};
150
151cf::attach_to_players
152 on_logout => sub {
153 my ($pl) = @_;
154
147 delete $dialog{$_} for grep $pl->ob == $dialog{$_}{ob}, keys %dialog; 155 delete $dialog{$_} for grep $pl->ob == $dialog{$_}{ob}, keys %dialog;
156 },
157;
148 158
159=item ... = extcmd editor_support
160
161Returns the value required by clients that have an editor to download and
162upload maps from/to the server.
163
164 servertype => (game|test) type of this server
165 gameserver => the hostname:port of the normal game server
166 testserver => the hostname:port of the test server the maps can be tested on
167 cvs_root => the (http) url where the cvs root for downloading is located
168 lib_root => the (http) url where crossfire.0 and archetypes can be found
169 upload => the (http) url where clients can upload maps
170
171If those values are not supplied or empty strings, the server does not
172support downloading, uploading, testing, respectively.
173
174The upload script expects the following values in a multipart form upload:
175
176 client: a descriptive string describing the editor and version used to upload
177 path: absolute server-side map path beginning with /
178 map: the map file itself
179 mapdir: the cvs root url originally used to download the map
180 revision: cvs-revision originally used to download the map
181 comment: a comment supplied by the user that documents the changes
182 cf_login: crossfire server login
183 cf_password: crossfire server password, optionally used for authentication purposes
184
185=cut
186
187cf::register_extcmd editor_support => sub {
188 my ($pl, $msg) = @_;
189
190 map +($_ => $cf::CFG{"editor_$_"}), qw(servertype gameserver testserver cvs_root lib_root builder_ui)
191};
192
193sub unload {
194 while (my ($id, $dialog) = each %dialog) {
195 $dialog->{ob}->contr->ext_reply ($id => msgtype => "error", msg => "npc dialogue module was reloaded");
149 0 196 }
197
198 %dialog = ();
150} 199}
151 200
152sub on_clock { 201=back
153 return 0 unless %dialog;
154 202
155 while (my ($token, $dialog) = each %dialog) { 203=cut
156 my (undef, $dx, $dy) = $dialog->{ob}->rangevector ($dialog->{npc});
157 next if (abs $dx) <= 2 && (abs $dy) <= 2;
158 204
159 $dialog->{ob}->contr->send ("ext $token out_of_range");
160 delete $dialog{$token};
161 }
162
163 0
164}
165

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines