ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/NPC_Dialogue.pm
(Generate patch)

Comparing deliantra/server/ext/NPC_Dialogue.pm (file contents):
Revision 1.1 by root, Fri Dec 15 19:29:18 2006 UTC vs.
Revision 1.15 by root, Tue Oct 13 00:24:14 2009 UTC

4 4
5=head1 DESCRIPTION 5=head1 DESCRIPTION
6 6
7NPC dialogue support module. 7NPC dialogue support module.
8 8
9=over 4
10
9=cut 11=cut
10 12
11package NPC_Dialogue; 13package NPC_Dialogue;
12 14
13use strict; 15use strict;
14
15sub has_dialogue($) {
16 my ($ob) = @_;
17
18 $ob->msg =~ /^\@match /;
19}
20 16
21sub parse_message($) { 17sub parse_message($) {
22 map [split /\n/, $_, 2], 18 map [split /\n/, $_, 2],
23 grep length, 19 grep length,
24 split /^\@match /m, 20 split /^\@match /m,
26} 22}
27 23
28sub new { 24sub new {
29 my ($class, %arg) = @_; 25 my ($class, %arg) = @_;
30 26
27 $arg{ob} = $arg{pl}->ob;
28
31 my $self = bless { 29 my $self = bless {
32 %arg, 30 %arg,
33 }, $class; 31 }, $class;
34 32
35 $self->{match} ||= [parse_message $self->{npc}->msg]; 33 $self->{match} ||= [parse_message $self->{npc}->msg];
98 96
99=item $flag - A hashref that stores flags associated with the player and 97=item $flag - A hashref that stores flags associated with the player and
100can be seen by all NPCs (so better name your flags uniquely). This is 98can be seen by all NPCs (so better name your flags uniquely). This is
101useful for storing e.g. quest information. See C<@setflag> and C<@ifflag>. 99useful for storing e.g. quest information. See C<@setflag> and C<@ifflag>.
102 100
101=item @find - see @find, below.
102
103=back 103=back
104 104
105The environment is that standard "map scripting environment", which is 105The environment is that standard "map scripting environment", which is
106limited in the type of constructs allowed (no loops, for example). 106limited in the type of constructs allowed (no loops, for example).
107 107
108Here is a example:
109
110=over 4
111
112=item B<matching for an item name>
113
114 @match hi
115 @cond grep $_->name =~ /royalty/, $who->inv
116 You got royalties there! Wanna have!
117
118You may want to change the C<name> method there to something like C<title>,
119C<slaying> or any other method that is allowed to be called on a
120C<cf::object> here.
121
122=item B<matching for an item name and removing the matched item>
123
124 @match found earhorn
125 @cond grep $_->slaying =~ /Gramp's walking stick/, $who->inv
126 @eval my @g = grep { $_->slaying =~ /Gramp's walking stick/ } $who->inv; $g[0]->decrease;
127 Thanks for the earhorn!
128
129This example is a bit more complex. The C<@eval> statement will search
130the players inventory for the same term as the C<@cond> and then
131decreases the number of objects used there.
132
133(See also the map: C<scorn/houses/cornerbrook.map> for an example how this is
134used in the real world :-)
135
136=back
137
108=item @eval perl 138=item @eval perl
109 139
110Like C<@cond>, but proceed regardless of the outcome. 140Like C<@cond>, but proceed regardless of the outcome.
111 141
112=item @msg perl 142=item @msg perl
113 143
114Like C<@cond>, but the return value will be stringified and prepended to 144Like C<@cond>, but the return value will be stringified and prepended to
115the message. 145the reply message.
146
147=item @check match expression
148
149Executes a match expression (see
150http://pod.tst.eu/http://cvs.schmorp.de/deliantra/server/lib/cf/match.pm)
151to see if it matches.
152
153C<self> is the npc object, C<object>, C<source> and C<originator> are the
154player communicating with the NPC.
155
156If the check fails, the match is skipped.
157
158=item @find match expression
159
160Like C<@check> in that it executes a match expression, but instead of
161failing, it gathers all objects matched into the C<@find> array variable.
162
163When you want to skip the match when no objects have been found, combine
164C<@find> with C<@cond>:
165
166 @match see my spellbook
167 @find type=SPELLBOOK in inv
168 @cond @find
169 It looks dirty.
170 @match see my spellbook
171 I can't see any, where do you have it?
116 172
117=item @setstate state value 173=item @setstate state value
118 174
119Sets the named state C<state> to the given C<value>. State values are 175Sets the named state C<state> to the given C<value>. State values are
120associated with a specific player-NPC pair, so each NPC has its own state 176associated with a specific player-NPC pair, so each NPC has its own state
164 220
165 @match hi 221 @match hi
166 @ifflag kings_quest 222 @ifflag kings_quest
167 Hi, I was told you want to do the kings quest? 223 Hi, I was told you want to do the kings quest?
168 224
169=item @trigger connected-id 225=item @trigger connected-id [state]
170 226
171Trigger all objects with the given connected-id. The trigger is stateful 227Trigger all objects with the given connected-id.
172and retains state per connected-id. 228
229When the state argument is omitted the trigger is stateful and retains an
230internal state per connected-id. There is a limitation to the use of this: The
231state won't be changed when the connection is triggered by other triggers. So
232be careful when triggering the connection from other objects.
233
234When a state argument is given it should be a positive integer. Any value
235C<!= 0> will 'push' the connection (in general, you should specify C<1>
236for this) and C<0> will 'release' the connection. This is useful for
237example when you want to let an NPC control a door.
238
239Trigger all objects with the given connected-id by 'releasing' the connection.
240
241=item @playersound face-name
242
243Plays the given sound face (either an alias or sound file path) so that
244only the player talking to the npc can hear it.
245
246=item @npcsound face-name
247
248Plays the given sound face (either an alias or sound file path) as if
249the npc had made that sound, i.e. it will be located at the npc and all
250players near enough can hear it.
173 251
174=item @addtopic topic 252=item @addtopic topic
175 253
176Adds the given topic names (separated by C<|>) to the list of topics 254Adds the given topic names (separated by C<|>) to the list of topics
177returned. 255returned.
186 my $lcmsg = lc $msg; 264 my $lcmsg = lc $msg;
187 265
188 topic: 266 topic:
189 for my $match (@{ $self->{match} }) { 267 for my $match (@{ $self->{match} }) {
190 for (split /\|/, $match->[0]) { 268 for (split /\|/, $match->[0]) {
191 if ($_ eq "*" || $lcmsg eq lc) { 269 if ($_ eq "*" || $lcmsg =~ /\b\Q$_\E\b/i) {
192 my $reply = $match->[1]; 270 my $reply = $match->[1];
193 my @kw; 271 my @kw;
194 272
195 my @replies; 273 my @replies;
196 my @match; # @match/@parse command results 274 my @match; # @match/@parse command results
197 275
198 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; 276 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
199 my $flag = $self->{ob}{dialog_flag} ||= {}; 277 my $flag = $self->{ob}{dialog_flag} ||= {};
278
279 my @find;
200 280
201 my %vars = ( 281 my %vars = (
202 who => $self->{ob}, 282 who => $self->{ob},
203 npc => $self->{npc}, 283 npc => $self->{npc},
204 state => $state, 284 state => $state,
219 or next topic; 299 or next topic;
220 300
221 } elsif ($cmd eq "comment") { 301 } elsif ($cmd eq "comment") {
222 # nop 302 # nop
223 303
304 } elsif ($cmd eq "playersound") {
305 $self->{ob}->contr->play_sound (cf::sound::find $args);
306
307 } elsif ($cmd eq "npcsound") {
308 $self->{npc}->play_sound (cf::sound::find $args);
309
224 } elsif ($cmd eq "cond") { 310 } elsif ($cmd eq "cond") {
225 cf::safe_eval $args, %vars 311 cf::safe_eval $args, %vars
226 or next topic; 312 or next topic;
227 313
228 } elsif ($cmd eq "eval") { 314 } elsif ($cmd eq "eval") {
229 cf::safe_eval $args, %vars; 315 cf::safe_eval $args, %vars;
230 warn "\@eval evaluation error: $@\n" if $@; 316 warn "\@eval evaluation error: $@\n" if $@;
317
318 } elsif ($cmd eq "check") {
319 eval {
320 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
321 or next topic;
322 };
323 warn "\@check evaluation error: $@\n" if $@;
324
325 } elsif ($cmd eq "find") {
326 @find = eval {
327 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
328 };
329 warn "\@find evaluation error: $@\n" if $@;
231 330
232 } elsif ($cmd eq "msg") { 331 } elsif ($cmd eq "msg") {
233 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; 332 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
234 333
235 } elsif ($cmd eq "setflag") { 334 } elsif ($cmd eq "setflag") {
251 my ($name, $value) = split /\s+/, $args, 2; 350 my ($name, $value) = split /\s+/, $args, 2;
252 $state->{$name} eq $value 351 $state->{$name} eq $value
253 or next topic; 352 or next topic;
254 353
255 } elsif ($cmd eq "trigger") { 354 } elsif ($cmd eq "trigger") {
355 my ($con, $state) = split /\s+/, $args, 2;
356
357 if (defined $state) {
358 $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob});
359 } else {
256 my $rvalue = \$self->{npc}{dialog_trigger}{$args*1}; 360 my $rvalue = \$self->{npc}{dialog_trigger}{$con+0};
257 $self->{npc}->map->trigger ($args, $$rvalue = !$$rvalue); 361 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob});
362 }
258 363
259 } elsif ($cmd eq "addtopic") { 364 } elsif ($cmd eq "addtopic") {
260 push @kw, split /\|/, $args; 365 push @kw, split /\|/, $args;
261 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 366 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
262 367
269 } 374 }
270 } 375 }
271 376
272 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 377 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
273 delete $self->{ob}{dialog_flag} unless %$flag; 378 delete $self->{ob}{dialog_flag} unless %$flag;
274
275 # combine lines into paragraphs
276 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
277 $reply =~ s/\n\n/\n/g;
278 379
279 # ignores flags and npc from replies 380 # ignores flags and npc from replies
280 $reply = join "\n", (map $_->[1], @replies), $reply; 381 $reply = join "\n", (map $_->[1], @replies), $reply;
281 382
282 # now mark up all matching keywords 383 # now mark up all matching keywords
287 last; 388 last;
288 } 389 }
289 } 390 }
290 } 391 }
291 392
393 $self->{npc}->use_trigger ($self->{ob})
394 if $self->{npc}->type == cf::MAGIC_EAR;
395
292 return wantarray ? ($reply, @kw) : $reply; 396 return wantarray ? ($reply, @kw) : $reply;
293 } 397 }
294 } 398 }
295 } 399 }
296 400

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines