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.7 by elmex, Sat Aug 25 16:51:38 2007 UTC vs.
Revision 1.15 by root, Tue Oct 13 00:24:14 2009 UTC

11=cut 11=cut
12 12
13package NPC_Dialogue; 13package NPC_Dialogue;
14 14
15use strict; 15use strict;
16
17sub has_dialogue($) {
18 my ($ob) = @_;
19
20 $ob->msg =~ /^\@match /;
21}
22 16
23sub parse_message($) { 17sub parse_message($) {
24 map [split /\n/, $_, 2], 18 map [split /\n/, $_, 2],
25 grep length, 19 grep length,
26 split /^\@match /m, 20 split /^\@match /m,
102 96
103=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
104can 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
105useful 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>.
106 100
101=item @find - see @find, below.
102
107=back 103=back
108 104
109The environment is that standard "map scripting environment", which is 105The environment is that standard "map scripting environment", which is
110limited in the type of constructs allowed (no loops, for example). 106limited in the type of constructs allowed (no loops, for example).
111 107
125 121
126=item B<matching for an item name and removing the matched item> 122=item B<matching for an item name and removing the matched item>
127 123
128 @match found earhorn 124 @match found earhorn
129 @cond grep $_->slaying =~ /Gramp's walking stick/, $who->inv 125 @cond grep $_->slaying =~ /Gramp's walking stick/, $who->inv
130 @eval my @g = grep { $_->slaying =~ /Gramp's walking stick/ } $who->inv; $g[0]->decrease_ob_nr (1); 126 @eval my @g = grep { $_->slaying =~ /Gramp's walking stick/ } $who->inv; $g[0]->decrease;
131 Thanks for the earhorn! 127 Thanks for the earhorn!
132 128
133This example is a bit more complex. The C<@eval> statement will search 129This example is a bit more complex. The C<@eval> statement will search
134the players inventory for the same term as the C<@cond> and then 130the players inventory for the same term as the C<@cond> and then
135decreases the number of objects used there. 131decreases the number of objects used there.
144Like C<@cond>, but proceed regardless of the outcome. 140Like C<@cond>, but proceed regardless of the outcome.
145 141
146=item @msg perl 142=item @msg perl
147 143
148Like 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
149the 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?
150 172
151=item @setstate state value 173=item @setstate state value
152 174
153Sets 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
154associated 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
207When the state argument is omitted the trigger is stateful and retains an 229When the state argument is omitted the trigger is stateful and retains an
208internal state per connected-id. There is a limitation to the use of this: The 230internal state per connected-id. There is a limitation to the use of this: The
209state won't be changed when the connection is triggered by other triggers. So 231state won't be changed when the connection is triggered by other triggers. So
210be careful when triggering the connection from other objects. 232be careful when triggering the connection from other objects.
211 233
212When a state argument is given it should be either 0 or 1. 1 will 'push' the connection 234When a state argument is given it should be a positive integer. Any value
213and 0 will 'release' the connection. This is useful for example when you want to 235C<!= 0> will 'push' the connection (in general, you should specify C<1>
214let a npc control a door. 236for this) and C<0> will 'release' the connection. This is useful for
237example when you want to let an NPC control a door.
215 238
216Trigger all objects with the given connected-id by 'releasing' the connection. 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.
217 251
218=item @addtopic topic 252=item @addtopic topic
219 253
220Adds 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
221returned. 255returned.
239 my @replies; 273 my @replies;
240 my @match; # @match/@parse command results 274 my @match; # @match/@parse command results
241 275
242 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; 276 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
243 my $flag = $self->{ob}{dialog_flag} ||= {}; 277 my $flag = $self->{ob}{dialog_flag} ||= {};
278
279 my @find;
244 280
245 my %vars = ( 281 my %vars = (
246 who => $self->{ob}, 282 who => $self->{ob},
247 npc => $self->{npc}, 283 npc => $self->{npc},
248 state => $state, 284 state => $state,
263 or next topic; 299 or next topic;
264 300
265 } elsif ($cmd eq "comment") { 301 } elsif ($cmd eq "comment") {
266 # nop 302 # nop
267 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
268 } elsif ($cmd eq "cond") { 310 } elsif ($cmd eq "cond") {
269 cf::safe_eval $args, %vars 311 cf::safe_eval $args, %vars
270 or next topic; 312 or next topic;
271 313
272 } elsif ($cmd eq "eval") { 314 } elsif ($cmd eq "eval") {
273 cf::safe_eval $args, %vars; 315 cf::safe_eval $args, %vars;
274 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 $@;
275 330
276 } elsif ($cmd eq "msg") { 331 } elsif ($cmd eq "msg") {
277 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; 332 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
278 333
279 } elsif ($cmd eq "setflag") { 334 } elsif ($cmd eq "setflag") {
296 $state->{$name} eq $value 351 $state->{$name} eq $value
297 or next topic; 352 or next topic;
298 353
299 } elsif ($cmd eq "trigger") { 354 } elsif ($cmd eq "trigger") {
300 my ($con, $state) = split /\s+/, $args, 2; 355 my ($con, $state) = split /\s+/, $args, 2;
301 $con = $con * 1;
302 356
303 if (defined $state) { 357 if (defined $state) {
304 $self->{npc}->map->trigger ($args, $state); 358 $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob});
305 } else { 359 } else {
306 my $rvalue = \$self->{npc}{dialog_trigger}{$con}; 360 my $rvalue = \$self->{npc}{dialog_trigger}{$con+0};
307 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue); 361 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob});
308 } 362 }
309 363
310 } elsif ($cmd eq "addtopic") { 364 } elsif ($cmd eq "addtopic") {
311 push @kw, split /\|/, $args; 365 push @kw, split /\|/, $args;
312 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 366 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
320 } 374 }
321 } 375 }
322 376
323 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 377 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
324 delete $self->{ob}{dialog_flag} unless %$flag; 378 delete $self->{ob}{dialog_flag} unless %$flag;
325
326 # combine lines into paragraphs
327 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
328 $reply =~ s/\n\n/\n/g;
329 379
330 # ignores flags and npc from replies 380 # ignores flags and npc from replies
331 $reply = join "\n", (map $_->[1], @replies), $reply; 381 $reply = join "\n", (map $_->[1], @replies), $reply;
332 382
333 # now mark up all matching keywords 383 # now mark up all matching keywords
338 last; 388 last;
339 } 389 }
340 } 390 }
341 } 391 }
342 392
393 $self->{npc}->use_trigger ($self->{ob})
394 if $self->{npc}->type == cf::MAGIC_EAR;
395
343 return wantarray ? ($reply, @kw) : $reply; 396 return wantarray ? ($reply, @kw) : $reply;
344 } 397 }
345 } 398 }
346 } 399 }
347 400

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines