ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/NPC_Dialogue.pm
(Generate patch)

Comparing deliantra/maps/perl/NPC_Dialogue.pm (file contents):
Revision 1.1 by root, Mon Jun 19 10:19:51 2006 UTC vs.
Revision 1.9 by root, Fri Jul 21 00:25:29 2006 UTC

8 8
9=cut 9=cut
10 10
11package NPC_Dialogue; 11package NPC_Dialogue;
12 12
13use strict;
14
13sub has_dialogue { 15sub has_dialogue($) {
14 my ($ob) = @_; 16 my ($ob) = @_;
15 17
16 $ob->get_message =~ /^\@match /; 18 $ob->get_message =~ /^\@match /;
17} 19}
18 20
39 my ($self) = @_; 41 my ($self) = @_;
40 42
41 $self->tell ("hi") 43 $self->tell ("hi")
42} 44}
43 45
46=item ($reply, @topics) = $dialog->tell ($msg)
47
48Tells the dialog object something and returns its response and optionally
49a number of topics that are refered to by this topic.
50
51It supports a number of command constructs. They have to follow the
52C<@match> directive, and there can be multiple commands that will be
53executed in order.
54
55=over 4
56
57=item @parse regex
58
59Parses the message using a perl regular expression (by default
60case-insensitive). Any matches will be available as C<< $match->[$index]
61>>.
62
63If the regular expression does not match, the topic is skipped.
64
65Example:
66
67 @match deposit
68 @parse deposit (\d+) (\S+)
69 @eval bank::deposit $match->[0], $match->[1]
70
71=item @cond perl
72
73Evaluates the given perl code. If it returns false (or causes an
74exception), the topic will be skipped, otherwise topic interpretation is
75resumed.
76
77The following local variables are defined within the expression:
78
79=over 4
80
81=item $who - The cf::object::player object that initiated the dialogue.
82
83=item $npc - The NPC (or magic_ear etc.) object that is being talked to.
84
85=item $msg - The actual message as passed to this method.
86
87=item $match - An arrayref with previous results from C<@parse>.
88
89=item $state - A hashref that stores state variables associated
90with the NPC and the player, that is, it's values relate to the the
91specific player-NPC interaction and other players will see a different
92state. Useful to react to players in a stateful way. See C<@setstate> and
93C<@ifstate>.
94
95=item $flag - A hashref that stores flags associated with the player and
96can be seen by all NPCs (so better name your flags uniquely). This is
97useful for storing e.g. quest information. See C<@setflag> and C<@ifflag>.
98
99=back
100
101The environment is that standard "map scripting environment", which is
102limited in the type of constructs allowed (no loops, for example).
103
104=item @eval perl
105
106Like C<@cond>, but proceed regardless of the outcome.
107
108=item @msg perl
109
110Like C<@cond>, but the return value will be stringified and prepended to
111the message.
112
113=item @setstate state value
114
115Sets the named state C<state> to the given C<value>. State values are
116associated with a specific player-NPC pair, so each NPC has its own state
117with respect to a particular player, which makes them useful to store
118information about previous questions and possibly answers. State values
119get reset whenever the NPC gets reset.
120
121See C<@ifstate> for an example.
122
123=item @ifstate state value
124
125Requires that the named C<state> has the given C<value>, otherwise this
126topic is skipped. For more complex comparisons, see C<@cond> with
127C<$state>. Example:
128
129 @match quest
130 @setstate question quest
131 Do you really want to help find the magic amulet of Beeblebrox?
132 @match yes
133 @ifstate question quest
134 Then fetch it, stupid!
135
136=item @setflag flag value
137
138Sets the named flag C<flag> to the given C<value>. Flag values are
139associated with a specific player and can be seen by all NPCs. with
140respect to a particular player, which makes them suitable to store quest
141markers and other information (e.g. reputation/alignment). Flags are
142persistent over the lifetime of a player, so be careful :)
143
144See C<@ifflag> for an example.
145
146=item @ifflag flag value
147
148Requires that the named C<flag> has the given C<value>, otherwise this
149topic is skipped. For more complex comparisons, see C<@cond> with
150C<$flag>. Example:
151
152 @match I want to do the quest!
153 @setflag kings_quest 1
154 Then seek out Bumblebee in Navar, he will tell you...
155 @match I did the quest
156 @ifflag kings_quest 1
157 Really, which quets?
158
159And Bumblebee might have:
160
161 @match hi
162 @ifflag kings_quest
163 Hi, I was told you want to do the kings quest?
164
165=item @trigger connected-id
166
167Trigger all objects with the given connected-id. The trigger is stateful
168and retains state per connected-id.
169
170=item @addtopic topic
171
172Adds the given topic names (separated by C<|>) to the list of topics
173returned.
174
175=back
176
177=cut
178
44sub tell { 179sub tell {
45 my ($self, $msg) = @_; 180 my ($self, $msg) = @_;
46 181
182 my $lcmsg = lc $msg;
183
184 topic:
47 for my $match (@{ $self->{match} }) { 185 for my $match (@{ $self->{match} }) {
48 for (split /\|/, $match->[0]) { 186 for (split /\|/, $match->[0]) {
49 if ($_ eq "*" || 0 <= index $msg, $_) { 187 if ($_ eq "*" || $lcmsg eq lc) {
50 my $reply = $match->[1]; 188 my $reply = $match->[1];
189 my @kw;
190
191 my @replies;
192 my @match; # @match/@parse command results
193
194 my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {};
195 my $flag = $self->{ob}{dialog_flag} ||= {};
196
197 my %vars = (
198 who => $self->{ob},
199 npc => $self->{npc},
200 state => $state,
201 flag => $flag,
202 msg => $msg,
203 match => \@match,
204 );
205
206 local $self->{ob}{record_replies} = \@replies;
207
208 # now execute @-commands (which can result in a no-match)
209 while ($reply =~ s/^\@(\w+)\s*([^\n]*)\n?//) {
210 my ($cmd, $args) = ($1, $2);
211
212 if ($cmd eq "parse" || $cmd eq "match") { # match is future rename
213 no re 'eval'; # default, but make sure
214 @match = $msg =~ /$args/i
215 or next topic;
216
217 } elsif ($cmd eq "cond") {
218 cf::safe_eval $args, %vars
219 or next topic;
220
221 } elsif ($cmd eq "eval") {
222 cf::safe_eval $args, %vars;
223 warn "\@eval evaluation error: $@\n" if $@;
224
225 } elsif ($cmd eq "msg") {
226 push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)];
227
228 } elsif ($cmd eq "setflag") {
229 my ($name, $value) = split /\s+/, $args, 2;
230 $value ? $flag->{$name} = $value
231 : delete $flag->{$name};
232
233 } elsif ($cmd eq "setstate") {
234 my ($name, $value) = split /\s+/, $args, 2;
235 $value ? $state->{$name} = $value
236 : delete $state->{$name};
237
238 } elsif ($cmd eq "ifflag") {
239 my ($name, $value) = split /\s+/, $args, 2;
240 $flag->{$name} eq $value
241 or next topic;
242
243 } elsif ($cmd eq "ifstate") {
244 my ($name, $value) = split /\s+/, $args, 2;
245 $state->{$name} eq $value
246 or next topic;
247
248 } elsif ($cmd eq "trigger") {
249 my $rvalue = \$self->{npc}{dialog_trigger}{$args*1};
250
251 my $trigger = cf::object::new "magic_ear";
252 $trigger->set_value ($$rvalue);
253
254 # needs to be on the map for remove_button_link to work
255 # the same *should* be true for add_button_link....
256 $self->{npc}->map->insert_object ($trigger, 0, 0);
257
258 $trigger->add_button_link ($self->{npc}->map, $args);
259
260 $trigger->use_trigger;
261
262 $trigger->remove_button_link;
263 $trigger->remove;
264 $trigger->free;
265
266 $$rvalue = !$$rvalue;
267
268 } elsif ($cmd eq "addtopic") {
269 push @kw, split /\|/, $args;
270 $self->{add_topic}->(split /\s*\|\s*/, $args) if $self->{add_topic};
271
272 } elsif ($cmd eq "deltopic") {
273 # not yet implemented, do it out-of-band
274 $self->{del_topic}->(split /\s*\|\s*/, $args) if $self->{del_topic};
275
276 } else {
277 warn "unknown dialogue command <$cmd,$args> used (from " . $self->{npc}->get_message . ")";
278 }
279 }
280
281 delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state;
282 delete $self->{ob}{dialog_flag} unless %$flag;
51 283
52 # combine lines into paragraphs 284 # combine lines into paragraphs
53 $reply =~ s/(?<=\S)\n(?=\w)/ /g; 285 $reply =~ s/(?<=\S)\n(?=\w)/ /g;
54 $reply =~ s/\n\n/\n/g; 286 $reply =~ s/\n\n/\n/g;
55 287
56 my @kw; 288 # ignores flags and npc from replies
289 $reply = join "\n", (map $_->[1], @replies), $reply;
290
57 # now mark up all matching keywords 291 # now mark up all matching keywords
58 for my $match (@{ $self->{match} }) { 292 for my $match (@{ $self->{match} }) {
59 for (sort { (length $b) <=> (length $a) } split /\|/, $match->[0]) { 293 for (sort { (length $b) <=> (length $a) } split /\|/, $match->[0]) {
60 if ($reply =~ /\b\Q$_\E\b/i) { 294 if ($reply =~ /\b\Q$_\E\b/i) {
61 push @kw, $_; 295 push @kw, $_;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines