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.4 by root, Sat Jun 16 14:35:41 2007 UTC vs.
Revision 1.16 by root, Mon Oct 26 02:48:02 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,
100 96
101=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
102can 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
103useful 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>.
104 100
101=item @find - see @find, below.
102
105=back 103=back
106 104
107The environment is that standard "map scripting environment", which is 105The environment is that standard "map scripting environment", which is
108limited in the type of constructs allowed (no loops, for example). 106limited in the type of constructs allowed (no loops, for example).
109 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
110=item @eval perl 138=item @eval perl
111 139
112Like C<@cond>, but proceed regardless of the outcome. 140Like C<@cond>, but proceed regardless of the outcome.
113 141
114=item @msg perl 142=item @msg perl
115 143
116Like 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
117the 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?
118 172
119=item @setstate state value 173=item @setstate state value
120 174
121Sets 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
122associated 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
145associated 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
146respect to a particular player, which makes them suitable to store quest 200respect to a particular player, which makes them suitable to store quest
147markers and other information (e.g. reputation/alignment). Flags are 201markers and other information (e.g. reputation/alignment). Flags are
148persistent over the lifetime of a player, so be careful :) 202persistent over the lifetime of a player, so be careful :)
149 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
150See C<@ifflag> for an example. 208See C<@ifflag> for an example.
151 209
152=item @ifflag flag value 210=item @ifflag flag value
153 211
154Requires 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
155topic is skipped. For more complex comparisons, see C<@cond> with 213topic is skipped. For more complex comparisons, see C<@cond> with
156C<$flag>. Example: 214C<$flag>.
215
216If no C<value> is given, then the ifflag succeeds when the flag is true.
217
218Example:
157 219
158 @match I want to do the quest! 220 @match I want to do the quest!
159 @setflag kings_quest 1 221 @setflag kings_quest 1
160 Then seek out Bumblebee in Navar, he will tell you... 222 Then seek out Bumblebee in Navar, he will tell you...
161 @match I did the quest 223 @match I did the quest
175When 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
176internal 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
177state 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
178be careful when triggering the connection from other objects. 240be careful when triggering the connection from other objects.
179 241
180When 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
181and 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>
182let 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.
183 246
184Trigger all objects with the given connected-id by 'releasing' the connection. 247Trigger all objects with the given connected-id by 'releasing' the connection.
248
249=item @playersound face-name
250
251Plays the given sound face (either an alias or sound file path) so that
252only the player talking to the npc can hear it.
253
254=item @npcsound face-name
255
256Plays the given sound face (either an alias or sound file path) as if
257the npc had made that sound, i.e. it will be located at the npc and all
258players near enough can hear it.
185 259
186=item @addtopic topic 260=item @addtopic topic
187 261
188Adds the given topic names (separated by C<|>) to the list of topics 262Adds the given topic names (separated by C<|>) to the list of topics
189returned. 263returned.
207 my @replies; 281 my @replies;
208 my @match; # @match/@parse command results 282 my @match; # @match/@parse command results
209 283
210 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; 284 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
211 my $flag = $self->{ob}{dialog_flag} ||= {}; 285 my $flag = $self->{ob}{dialog_flag} ||= {};
286
287 my @find;
212 288
213 my %vars = ( 289 my %vars = (
214 who => $self->{ob}, 290 who => $self->{ob},
215 npc => $self->{npc}, 291 npc => $self->{npc},
216 state => $state, 292 state => $state,
231 or next topic; 307 or next topic;
232 308
233 } elsif ($cmd eq "comment") { 309 } elsif ($cmd eq "comment") {
234 # nop 310 # nop
235 311
312 } elsif ($cmd eq "playersound") {
313 $self->{ob}->contr->play_sound (cf::sound::find $args);
314
315 } elsif ($cmd eq "npcsound") {
316 $self->{npc}->play_sound (cf::sound::find $args);
317
236 } elsif ($cmd eq "cond") { 318 } elsif ($cmd eq "cond") {
237 cf::safe_eval $args, %vars 319 cf::safe_eval $args, %vars
238 or next topic; 320 or next topic;
239 321
240 } elsif ($cmd eq "eval") { 322 } elsif ($cmd eq "eval") {
241 cf::safe_eval $args, %vars; 323 cf::safe_eval $args, %vars;
242 warn "\@eval evaluation error: $@\n" if $@; 324 warn "\@eval evaluation error: $@\n" if $@;
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 $@;
243 338
244 } elsif ($cmd eq "msg") { 339 } elsif ($cmd eq "msg") {
245 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; 340 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
246 341
247 } elsif ($cmd eq "setflag") { 342 } elsif ($cmd eq "setflag") {
264 $state->{$name} eq $value 359 $state->{$name} eq $value
265 or next topic; 360 or next topic;
266 361
267 } elsif ($cmd eq "trigger") { 362 } elsif ($cmd eq "trigger") {
268 my ($con, $state) = split /\s+/, $args, 2; 363 my ($con, $state) = split /\s+/, $args, 2;
269 $con = $con * 1;
270 364
271 if (defined $state) { 365 if (defined $state) {
272 $self->{npc}->map->trigger ($args, $state); 366 $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob});
273 } else { 367 } else {
274 my $rvalue = \$self->{npc}{dialog_trigger}{$con}; 368 my $rvalue = \$self->{npc}{dialog_trigger}{$con+0};
275 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue); 369 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob});
276 } 370 }
277 371
278 } elsif ($cmd eq "addtopic") { 372 } elsif ($cmd eq "addtopic") {
279 push @kw, split /\|/, $args; 373 push @kw, split /\|/, $args;
280 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 374 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
288 } 382 }
289 } 383 }
290 384
291 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 385 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
292 delete $self->{ob}{dialog_flag} unless %$flag; 386 delete $self->{ob}{dialog_flag} unless %$flag;
293
294 # combine lines into paragraphs
295 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
296 $reply =~ s/\n\n/\n/g;
297 387
298 # ignores flags and npc from replies 388 # ignores flags and npc from replies
299 $reply = join "\n", (map $_->[1], @replies), $reply; 389 $reply = join "\n", (map $_->[1], @replies), $reply;
300 390
301 # now mark up all matching keywords 391 # now mark up all matching keywords
306 last; 396 last;
307 } 397 }
308 } 398 }
309 } 399 }
310 400
401 $self->{npc}->use_trigger ($self->{ob})
402 if $self->{npc}->type == cf::MAGIC_EAR;
403
311 return wantarray ? ($reply, @kw) : $reply; 404 return wantarray ? ($reply, @kw) : $reply;
312 } 405 }
313 } 406 }
314 } 407 }
315 408

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines