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.12 by root, Thu Dec 18 02:49:22 2008 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];
102 100
103=back 101=back
104 102
105The environment is that standard "map scripting environment", which is 103The environment is that standard "map scripting environment", which is
106limited in the type of constructs allowed (no loops, for example). 104limited in the type of constructs allowed (no loops, for example).
105
106Here is a example:
107
108=over 4
109
110=item B<matching for an item name>
111
112 @match hi
113 @cond grep $_->name =~ /royalty/, $who->inv
114 You got royalties there! Wanna have!
115
116You may want to change the C<name> method there to something like C<title>,
117C<slaying> or any other method that is allowed to be called on a
118C<cf::object> here.
119
120=item B<matching for an item name and removing the matched item>
121
122 @match found earhorn
123 @cond grep $_->slaying =~ /Gramp's walking stick/, $who->inv
124 @eval my @g = grep { $_->slaying =~ /Gramp's walking stick/ } $who->inv; $g[0]->decrease;
125 Thanks for the earhorn!
126
127This example is a bit more complex. The C<@eval> statement will search
128the players inventory for the same term as the C<@cond> and then
129decreases the number of objects used there.
130
131(See also the map: C<scorn/houses/cornerbrook.map> for an example how this is
132used in the real world :-)
133
134=back
107 135
108=item @eval perl 136=item @eval perl
109 137
110Like C<@cond>, but proceed regardless of the outcome. 138Like C<@cond>, but proceed regardless of the outcome.
111 139
164 192
165 @match hi 193 @match hi
166 @ifflag kings_quest 194 @ifflag kings_quest
167 Hi, I was told you want to do the kings quest? 195 Hi, I was told you want to do the kings quest?
168 196
169=item @trigger connected-id 197=item @trigger connected-id [state]
170 198
171Trigger all objects with the given connected-id. The trigger is stateful 199Trigger all objects with the given connected-id.
172and retains state per connected-id. 200
201When the state argument is omitted the trigger is stateful and retains an
202internal state per connected-id. There is a limitation to the use of this: The
203state won't be changed when the connection is triggered by other triggers. So
204be careful when triggering the connection from other objects.
205
206When a state argument is given it should be either 0 or 1. 1 will 'push' the connection
207and 0 will 'release' the connection. This is useful for example when you want to
208let a npc control a door.
209
210Trigger all objects with the given connected-id by 'releasing' the connection.
211
212=item @playersound face-name
213
214Plays the given sound face (either an alias or sound file path) so that
215only the player talking to the npc can hear it.
216
217=item @npcsound face-name
218
219Plays the given sound face (either an alias or sound file path) as if
220the npc had made that sound, i.e. it will be located at the npc and all
221players near enough can hear it.
173 222
174=item @addtopic topic 223=item @addtopic topic
175 224
176Adds the given topic names (separated by C<|>) to the list of topics 225Adds the given topic names (separated by C<|>) to the list of topics
177returned. 226returned.
186 my $lcmsg = lc $msg; 235 my $lcmsg = lc $msg;
187 236
188 topic: 237 topic:
189 for my $match (@{ $self->{match} }) { 238 for my $match (@{ $self->{match} }) {
190 for (split /\|/, $match->[0]) { 239 for (split /\|/, $match->[0]) {
191 if ($_ eq "*" || $lcmsg eq lc) { 240 if ($_ eq "*" || $lcmsg =~ /\b\Q$_\E\b/i) {
192 my $reply = $match->[1]; 241 my $reply = $match->[1];
193 my @kw; 242 my @kw;
194 243
195 my @replies; 244 my @replies;
196 my @match; # @match/@parse command results 245 my @match; # @match/@parse command results
219 or next topic; 268 or next topic;
220 269
221 } elsif ($cmd eq "comment") { 270 } elsif ($cmd eq "comment") {
222 # nop 271 # nop
223 272
273 } elsif ($cmd eq "playersound") {
274 $self->{ob}->contr->play_sound (cf::sound::find $args);
275
276 } elsif ($cmd eq "npcsound") {
277 $self->{npc}->play_sound (cf::sound::find $args);
278
224 } elsif ($cmd eq "cond") { 279 } elsif ($cmd eq "cond") {
225 cf::safe_eval $args, %vars 280 cf::safe_eval $args, %vars
226 or next topic; 281 or next topic;
227 282
228 } elsif ($cmd eq "eval") { 283 } elsif ($cmd eq "eval") {
251 my ($name, $value) = split /\s+/, $args, 2; 306 my ($name, $value) = split /\s+/, $args, 2;
252 $state->{$name} eq $value 307 $state->{$name} eq $value
253 or next topic; 308 or next topic;
254 309
255 } elsif ($cmd eq "trigger") { 310 } elsif ($cmd eq "trigger") {
311 my ($con, $state) = split /\s+/, $args, 2;
312 $con = $con * 1;
313
314 if (defined $state) {
315 $self->{npc}->map->trigger ($args, $state);
316 } else {
256 my $rvalue = \$self->{npc}{dialog_trigger}{$args*1}; 317 my $rvalue = \$self->{npc}{dialog_trigger}{$con};
257 $self->{npc}->map->trigger ($args, $$rvalue = !$$rvalue); 318 $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue);
319 }
258 320
259 } elsif ($cmd eq "addtopic") { 321 } elsif ($cmd eq "addtopic") {
260 push @kw, split /\|/, $args; 322 push @kw, split /\|/, $args;
261 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic}; 323 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
262 324
269 } 331 }
270 } 332 }
271 333
272 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; 334 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
273 delete $self->{ob}{dialog_flag} unless %$flag; 335 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 336
279 # ignores flags and npc from replies 337 # ignores flags and npc from replies
280 $reply = join "\n", (map $_->[1], @replies), $reply; 338 $reply = join "\n", (map $_->[1], @replies), $reply;
281 339
282 # now mark up all matching keywords 340 # now mark up all matching keywords
287 last; 345 last;
288 } 346 }
289 } 347 }
290 } 348 }
291 349
350 $self->{npc}->use_trigger if $self->{npc}->type == cf::MAGIC_EAR;
351
292 return wantarray ? ($reply, @kw) : $reply; 352 return wantarray ? ($reply, @kw) : $reply;
293 } 353 }
294 } 354 }
295 } 355 }
296 356

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines