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.9 by root, Mon Apr 21 06:35:26 2008 UTC vs.
Revision 1.16 by root, Mon Oct 26 02:48:02 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
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
177associated with a specific player and can be seen by all NPCs. with 199associated with a specific player and can be seen by all NPCs. with
178respect to a particular player, which makes them suitable to store quest 200respect to a particular player, which makes them suitable to store quest
179markers and other information (e.g. reputation/alignment). Flags are 201markers and other information (e.g. reputation/alignment). Flags are
180persistent over the lifetime of a player, so be careful :) 202persistent over the lifetime of a player, so be careful :)
181 203
204Perversely enough, using C<@setfflag> without a C<value> clears the flag
205as if it was never set, so always provide a flag value (e.g. C<1>) when
206you want to set the flag.
207
182See C<@ifflag> for an example. 208See C<@ifflag> for an example.
183 209
184=item @ifflag flag value 210=item @ifflag flag value
185 211
186Requires that the named C<flag> has the given C<value>, otherwise this 212Requires that the named C<flag> has the given C<value>, otherwise this
187topic is skipped. For more complex comparisons, see C<@cond> with 213topic is skipped. For more complex comparisons, see C<@cond> with
188C<$flag>. Example: 214C<$flag>.
215
216If no C<value> is given, then the ifflag succeeds when the flag is true.
217
218Example:
189 219
190 @match I want to do the quest! 220 @match I want to do the quest!
191 @setflag kings_quest 1 221 @setflag kings_quest 1
192 Then seek out Bumblebee in Navar, he will tell you... 222 Then seek out Bumblebee in Navar, he will tell you...
193 @match I did the quest 223 @match I did the quest
207When the state argument is omitted the trigger is stateful and retains an 237When 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 238internal 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 239state won't be changed when the connection is triggered by other triggers. So
210be careful when triggering the connection from other objects. 240be careful when triggering the connection from other objects.
211 241
212When a state argument is given it should be either 0 or 1. 1 will 'push' the connection 242When 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 243C<!= 0> will 'push' the connection (in general, you should specify C<1>
214let a npc control a door. 244for this) and C<0> will 'release' the connection. This is useful for
245example when you want to let an NPC control a door.
215 246
216Trigger all objects with the given connected-id by 'releasing' the connection. 247Trigger all objects with the given connected-id by 'releasing' the connection.
217 248
218=item @playersound face-name 249=item @playersound face-name
219 250
250 my @replies; 281 my @replies;
251 my @match; # @match/@parse command results 282 my @match; # @match/@parse command results
252 283
253 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; 284 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
254 my $flag = $self->{ob}{dialog_flag} ||= {}; 285 my $flag = $self->{ob}{dialog_flag} ||= {};
286
287 my @find;
255 288
256 my %vars = ( 289 my %vars = (
257 who => $self->{ob}, 290 who => $self->{ob},
258 npc => $self->{npc}, 291 npc => $self->{npc},
259 state => $state, 292 state => $state,
288 321
289 } elsif ($cmd eq "eval") { 322 } elsif ($cmd eq "eval") {
290 cf::safe_eval $args, %vars; 323 cf::safe_eval $args, %vars;
291 warn "\@eval evaluation error: $@\n" if $@; 324 warn "\@eval evaluation error: $@\n" if $@;
292 325
326 } elsif ($cmd eq "check") {
327 eval {
328 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
329 or next topic;
330 };
331 warn "\@check evaluation error: $@\n" if $@;
332
333 } elsif ($cmd eq "find") {
334 @find = eval {
335 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
336 };
337 warn "\@find evaluation error: $@\n" if $@;
338
293 } elsif ($cmd eq "msg") { 339 } elsif ($cmd eq "msg") {
294 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; 340 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
295 341
296 } elsif ($cmd eq "setflag") { 342 } elsif ($cmd eq "setflag") {
297 my ($name, $value) = split /\s+/, $args, 2; 343 my ($name, $value) = split /\s+/, $args, 2;
313 $state->{$name} eq $value 359 $state->{$name} eq $value
314 or next topic; 360 or next topic;
315 361
316 } elsif ($cmd eq "trigger") { 362 } elsif ($cmd eq "trigger") {
317 my ($con, $state) = split /\s+/, $args, 2; 363 my ($con, $state) = split /\s+/, $args, 2;
318 $con = $con * 1;
319 364
320 if (defined $state) { 365 if (defined $state) {
321 $self->{npc}->map->trigger ($args, $state); 366 $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob});
322 } else { 367 } else {
323 my $rvalue = \$self->{npc}{dialog_trigger}{$con}; 368 my $rvalue = \$self->{npc}{dialog_trigger}{$con+0};
324 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue); 369 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob});
325 } 370 }
326 371
327 } elsif ($cmd eq "addtopic") { 372 } elsif ($cmd eq "addtopic") {
328 push @kw, split /\|/, $args; 373 push @kw, split /\|/, $args;
329 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 374 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
337 } 382 }
338 } 383 }
339 384
340 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 385 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
341 delete $self->{ob}{dialog_flag} unless %$flag; 386 delete $self->{ob}{dialog_flag} unless %$flag;
342
343 # combine lines into paragraphs
344 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
345 $reply =~ s/\n\n/\n/g;
346 387
347 # ignores flags and npc from replies 388 # ignores flags and npc from replies
348 $reply = join "\n", (map $_->[1], @replies), $reply; 389 $reply = join "\n", (map $_->[1], @replies), $reply;
349 390
350 # now mark up all matching keywords 391 # now mark up all matching keywords
355 last; 396 last;
356 } 397 }
357 } 398 }
358 } 399 }
359 400
401 $self->{npc}->use_trigger ($self->{ob})
402 if $self->{npc}->type == cf::MAGIC_EAR;
403
360 return wantarray ? ($reply, @kw) : $reply; 404 return wantarray ? ($reply, @kw) : $reply;
361 } 405 }
362 } 406 }
363 } 407 }
364 408

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines