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.6 by elmex, Wed Aug 8 07:55:57 2007 UTC vs.
Revision 1.20 by root, Fri Mar 19 21:40:39 2010 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
121 117
122You may want to change the C<name> method there to something like C<title>, 118You may want to change the C<name> method there to something like C<title>,
123C<slaying> or any other method that is allowed to be called on a 119C<slaying> or any other method that is allowed to be called on a
124C<cf::object> here. 120C<cf::object> here.
125 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
126=back 136=back
127 137
128=item @eval perl 138=item @eval perl
129 139
130Like C<@cond>, but proceed regardless of the outcome. 140Like C<@cond>, but proceed regardless of the outcome.
131 141
132=item @msg perl 142=item @msg perl
133 143
134Like 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
135the 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 into an array and provides a reference to
162the array in the C<$find> variable.
163
164When you want to skip the match when no objects have been found, combine
165C<@find> with C<@cond>:
166
167 @match see my spellbook
168 @find type=SPELLBOOK in inv
169 @cond @$find
170 It looks dirty.
171 @match see my spellbook
172 I can't see any, where do you have it?
136 173
137=item @setstate state value 174=item @setstate state value
138 175
139Sets the named state C<state> to the given C<value>. State values are 176Sets the named state C<state> to the given C<value>. State values are
140associated with a specific player-NPC pair, so each NPC has its own state 177associated with a specific player-NPC pair, so each NPC has its own state
145See C<@ifstate> for an example. 182See C<@ifstate> for an example.
146 183
147=item @ifstate state value 184=item @ifstate state value
148 185
149Requires that the named C<state> has the given C<value>, otherwise this 186Requires that the named C<state> has the given C<value>, otherwise this
150topic is skipped. For more complex comparisons, see C<@cond> with 187topic is skipped. For more complex comparisons, see C<@cond> with
151C<$state>. Example: 188C<$state>. Example:
152 189
153 @match quest 190 @match quest
154 @setstate question quest 191 @setstate question quest
155 Do you really want to help find the magic amulet of Beeblebrox? 192 Do you really want to help find the magic amulet of Beeblebrox?
163associated with a specific player and can be seen by all NPCs. with 200associated with a specific player and can be seen by all NPCs. with
164respect to a particular player, which makes them suitable to store quest 201respect to a particular player, which makes them suitable to store quest
165markers and other information (e.g. reputation/alignment). Flags are 202markers and other information (e.g. reputation/alignment). Flags are
166persistent over the lifetime of a player, so be careful :) 203persistent over the lifetime of a player, so be careful :)
167 204
205Perversely enough, using C<@setfflag> without a C<value> clears the flag
206as if it was never set, so always provide a flag value (e.g. C<1>) when
207you want to set the flag.
208
168See C<@ifflag> for an example. 209See C<@ifflag> for an example.
169 210
170=item @ifflag flag value 211=item @ifflag flag value
171 212
172Requires that the named C<flag> has the given C<value>, otherwise this 213Requires that the named C<flag> has the given C<value>, otherwise this
173topic is skipped. For more complex comparisons, see C<@cond> with 214topic is skipped. For more complex comparisons, see C<@cond> with
174C<$flag>. Example: 215C<$flag>.
216
217If no C<value> is given, then the ifflag succeeds when the flag is true.
218
219Example:
175 220
176 @match I want to do the quest! 221 @match I want to do the quest!
177 @setflag kings_quest 1 222 @setflag kings_quest 1
178 Then seek out Bumblebee in Navar, he will tell you... 223 Then seek out Bumblebee in Navar, he will tell you...
179 @match I did the quest 224 @match I did the quest
193When the state argument is omitted the trigger is stateful and retains an 238When the state argument is omitted the trigger is stateful and retains an
194internal state per connected-id. There is a limitation to the use of this: The 239internal state per connected-id. There is a limitation to the use of this: The
195state won't be changed when the connection is triggered by other triggers. So 240state won't be changed when the connection is triggered by other triggers. So
196be careful when triggering the connection from other objects. 241be careful when triggering the connection from other objects.
197 242
198When a state argument is given it should be either 0 or 1. 1 will 'push' the connection 243When a state argument is given it should be a positive integer. Any value
199and 0 will 'release' the connection. This is useful for example when you want to 244C<!= 0> will 'push' the connection (in general, you should specify C<1>
200let a npc control a door. 245for this) and C<0> will 'release' the connection. This is useful for
246example when you want to let an NPC control a door.
201 247
202Trigger all objects with the given connected-id by 'releasing' the connection. 248Trigger all objects with the given connected-id by 'releasing' the connection.
249
250=item @playersound face-name
251
252Plays the given sound face (either an alias or sound file path) so that
253only the player talking to the npc can hear it.
254
255=item @npcsound face-name
256
257Plays the given sound face (either an alias or sound file path) as if
258the npc had made that sound, i.e. it will be located at the npc and all
259players near enough can hear it.
203 260
204=item @addtopic topic 261=item @addtopic topic
205 262
206Adds the given topic names (separated by C<|>) to the list of topics 263Adds the given topic names (separated by C<|>) to the list of topics
207returned. 264returned.
225 my @replies; 282 my @replies;
226 my @match; # @match/@parse command results 283 my @match; # @match/@parse command results
227 284
228 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; 285 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
229 my $flag = $self->{ob}{dialog_flag} ||= {}; 286 my $flag = $self->{ob}{dialog_flag} ||= {};
287
288 my @find;
230 289
231 my %vars = ( 290 my %vars = (
232 who => $self->{ob}, 291 who => $self->{ob},
233 npc => $self->{npc}, 292 npc => $self->{npc},
234 state => $state, 293 state => $state,
235 flag => $flag, 294 flag => $flag,
236 msg => $msg, 295 msg => $msg,
237 match => \@match, 296 match => \@match,
297 find => \@find,
238 ); 298 );
239 299
240 local $self->{ob}{record_replies} = \@replies; 300 local $self->{ob}{record_replies} = \@replies;
241 301
242 # now execute @-commands (which can result in a no-match) 302 # now execute @-commands (which can result in a no-match)
249 or next topic; 309 or next topic;
250 310
251 } elsif ($cmd eq "comment") { 311 } elsif ($cmd eq "comment") {
252 # nop 312 # nop
253 313
314 } elsif ($cmd eq "playersound") {
315 $self->{ob}->contr->play_sound (cf::sound::find $args);
316
317 } elsif ($cmd eq "npcsound") {
318 $self->{npc}->play_sound (cf::sound::find $args);
319
254 } elsif ($cmd eq "cond") { 320 } elsif ($cmd eq "cond") {
255 cf::safe_eval $args, %vars 321 cf::safe_eval $args, %vars
256 or next topic; 322 or next topic;
257 323
258 } elsif ($cmd eq "eval") { 324 } elsif ($cmd eq "eval") {
259 cf::safe_eval $args, %vars; 325 cf::safe_eval $args, %vars;
260 warn "\@eval evaluation error: $@\n" if $@; 326 warn "\@eval evaluation error: $@\n" if $@;
261 327
328 } elsif ($cmd eq "check") {
329 eval {
330 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
331 or next topic;
332 };
333 warn "\@check evaluation error: $@\n" if $@;
334
335 } elsif ($cmd eq "find") {
336 @find = eval {
337 cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob}
338 };
339 warn "\@find evaluation error: $@\n" if $@;
340
262 } elsif ($cmd eq "msg") { 341 } elsif ($cmd eq "msg") {
263 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; 342 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
264 343
265 } elsif ($cmd eq "setflag") { 344 } elsif ($cmd eq "setflag") {
266 my ($name, $value) = split /\s+/, $args, 2; 345 my ($name, $value) = split /\s+/, $args, 2;
267 $value ? $flag->{$name} = $value 346 defined $value ? $flag->{$name} = $value
268 : delete $flag->{$name}; 347 : delete $flag->{$name};
269 348
270 } elsif ($cmd eq "setstate") { 349 } elsif ($cmd eq "setstate") {
271 my ($name, $value) = split /\s+/, $args, 2; 350 my ($name, $value) = split /\s+/, $args, 2;
272 $value ? $state->{$name} = $value 351 defined $value ? $state->{$name} = $value
273 : delete $state->{$name}; 352 : delete $state->{$name};
274 353
275 } elsif ($cmd eq "ifflag") { 354 } elsif ($cmd eq "ifflag") {
276 my ($name, $value) = split /\s+/, $args, 2; 355 my ($name, $value) = split /\s+/, $args, 2;
277 $flag->{$name} eq $value 356 defined $value ? $flag->{$name} eq $value
357 : $flag->{$name}
278 or next topic; 358 or next topic;
279 359
280 } elsif ($cmd eq "ifstate") { 360 } elsif ($cmd eq "ifstate") {
281 my ($name, $value) = split /\s+/, $args, 2; 361 my ($name, $value) = split /\s+/, $args, 2;
282 $state->{$name} eq $value 362 defined $value ? $state->{$name} eq $value
363 : $state->{$name}
283 or next topic; 364 or next topic;
284 365
285 } elsif ($cmd eq "trigger") { 366 } elsif ($cmd eq "trigger") {
286 my ($con, $state) = split /\s+/, $args, 2; 367 my ($con, $state) = split /\s+/, $args, 2;
287 $con = $con * 1;
288 368
289 if (defined $state) { 369 if (defined $state) {
290 $self->{npc}->map->trigger ($args, $state); 370 $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob});
291 } else { 371 } else {
292 my $rvalue = \$self->{npc}{dialog_trigger}{$con}; 372 my $rvalue = \$self->{npc}{dialog_trigger}{$con+0};
293 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue); 373 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob});
294 } 374 }
295 375
296 } elsif ($cmd eq "addtopic") { 376 } elsif ($cmd eq "addtopic") {
297 push @kw, split /\|/, $args; 377 push @kw, split /\|/, $args;
298 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 378 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
306 } 386 }
307 } 387 }
308 388
309 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 389 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
310 delete $self->{ob}{dialog_flag} unless %$flag; 390 delete $self->{ob}{dialog_flag} unless %$flag;
311
312 # combine lines into paragraphs
313 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
314 $reply =~ s/\n\n/\n/g;
315 391
316 # ignores flags and npc from replies 392 # ignores flags and npc from replies
317 $reply = join "\n", (map $_->[1], @replies), $reply; 393 $reply = join "\n", (map $_->[1], @replies), $reply;
318 394
319 # now mark up all matching keywords 395 # now mark up all matching keywords
324 last; 400 last;
325 } 401 }
326 } 402 }
327 } 403 }
328 404
405 $self->{npc}->use_trigger ($self->{ob})
406 if $self->{npc}->type == cf::MAGIC_EAR;
407
329 return wantarray ? ($reply, @kw) : $reply; 408 return wantarray ? ($reply, @kw) : $reply;
330 } 409 }
331 } 410 }
332 } 411 }
333 412

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines