--- deliantra/server/ext/NPC_Dialogue.pm 2007/06/16 23:22:59 1.5 +++ deliantra/server/ext/NPC_Dialogue.pm 2009/10/26 02:55:07 1.17 @@ -14,12 +14,6 @@ use strict; -sub has_dialogue($) { - my ($ob) = @_; - - $ob->msg =~ /^\@match /; -} - sub parse_message($) { map [split /\n/, $_, 2], grep length, @@ -104,11 +98,43 @@ can be seen by all NPCs (so better name your flags uniquely). This is useful for storing e.g. quest information. See C<@setflag> and C<@ifflag>. +=item @find - see @find, below. + =back The environment is that standard "map scripting environment", which is limited in the type of constructs allowed (no loops, for example). +Here is a example: + +=over 4 + +=item B + + @match hi + @cond grep $_->name =~ /royalty/, $who->inv + You got royalties there! Wanna have! + +You may want to change the C method there to something like C, +C<slaying> or any other method that is allowed to be called on a +C<cf::object> here. + +=item B<matching for an item name and removing the matched item> + + @match found earhorn + @cond grep $_->slaying =~ /Gramp's walking stick/, $who->inv + @eval my @g = grep { $_->slaying =~ /Gramp's walking stick/ } $who->inv; $g[0]->decrease; + Thanks for the earhorn! + +This example is a bit more complex. The C<@eval> statement will search +the players inventory for the same term as the C<@cond> and then +decreases the number of objects used there. + +(See also the map: C<scorn/houses/cornerbrook.map> for an example how this is +used in the real world :-) + +=back + =item @eval perl Like C<@cond>, but proceed regardless of the outcome. @@ -116,7 +142,33 @@ =item @msg perl Like C<@cond>, but the return value will be stringified and prepended to -the message. +the reply message. + +=item @check match expression + +Executes a match expression (see +http://pod.tst.eu/http://cvs.schmorp.de/deliantra/server/lib/cf/match.pm) +to see if it matches. + +C<self> is the npc object, C<object>, C<source> and C<originator> are the +player communicating with the NPC. + +If the check fails, the match is skipped. + +=item @find match expression + +Like C<@check> in that it executes a match expression, but instead of +failing, it gathers all objects matched into the C<@find> array variable. + +When you want to skip the match when no objects have been found, combine +C<@find> with C<@cond>: + + @match see my spellbook + @find type=SPELLBOOK in inv + @cond @find + It looks dirty. + @match see my spellbook + I can't see any, where do you have it? =item @setstate state value @@ -149,13 +201,21 @@ markers and other information (e.g. reputation/alignment). Flags are persistent over the lifetime of a player, so be careful :) +Perversely enough, using C<@setfflag> without a C<value> clears the flag +as if it was never set, so always provide a flag value (e.g. C<1>) when +you want to set the flag. + See C<@ifflag> for an example. =item @ifflag flag value Requires that the named C<flag> has the given C<value>, otherwise this -topic is skipped. For more complex comparisons, see C<@cond> with -C<$flag>. Example: +topic is skipped. For more complex comparisons, see C<@cond> with +C<$flag>. + +If no C<value> is given, then the ifflag succeeds when the flag is true. + +Example: @match I want to do the quest! @setflag kings_quest 1 @@ -179,12 +239,24 @@ state won't be changed when the connection is triggered by other triggers. So be careful when triggering the connection from other objects. -When a state argument is given it should be either 0 or 1. 1 will 'push' the connection -and 0 will 'release' the connection. This is useful for example when you want to -let a npc control a door. +When a state argument is given it should be a positive integer. Any value +C<!= 0> will 'push' the connection (in general, you should specify C<1> +for this) and C<0> will 'release' the connection. This is useful for +example when you want to let an NPC control a door. Trigger all objects with the given connected-id by 'releasing' the connection. +=item @playersound face-name + +Plays the given sound face (either an alias or sound file path) so that +only the player talking to the npc can hear it. + +=item @npcsound face-name + +Plays the given sound face (either an alias or sound file path) as if +the npc had made that sound, i.e. it will be located at the npc and all +players near enough can hear it. + =item @addtopic topic Adds the given topic names (separated by C<|>) to the list of topics @@ -212,6 +284,8 @@ my $state = $self->{npc}{$self->{ob}->name}{dialog_state} ||= {}; my $flag = $self->{ob}{dialog_flag} ||= {}; + my @find; + my %vars = ( who => $self->{ob}, npc => $self->{npc}, @@ -235,6 +309,12 @@ } elsif ($cmd eq "comment") { # nop + } elsif ($cmd eq "playersound") { + $self->{ob}->contr->play_sound (cf::sound::find $args); + + } elsif ($cmd eq "npcsound") { + $self->{npc}->play_sound (cf::sound::find $args); + } elsif ($cmd eq "cond") { cf::safe_eval $args, %vars or next topic; @@ -243,6 +323,19 @@ cf::safe_eval $args, %vars; warn "\@eval evaluation error: $@\n" if $@; + } elsif ($cmd eq "check") { + eval { + cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob} + or next topic; + }; + warn "\@check evaluation error: $@\n" if $@; + + } elsif ($cmd eq "find") { + @find = eval { + cf::match::match $args, $self->{ob}, $self->{npc}, $self->{ob} + }; + warn "\@find evaluation error: $@\n" if $@; + } elsif ($cmd eq "msg") { push @replies, [$self->{npc}, (scalar cf::safe_eval $args, %vars)]; @@ -258,7 +351,8 @@ } elsif ($cmd eq "ifflag") { my ($name, $value) = split /\s+/, $args, 2; - $flag->{$name} eq $value + defined $value ? $flag->{$name} eq $value + : $flag->{$name} or next topic; } elsif ($cmd eq "ifstate") { @@ -268,13 +362,12 @@ } elsif ($cmd eq "trigger") { my ($con, $state) = split /\s+/, $args, 2; - $con = $con * 1; if (defined $state) { - $self->{npc}->map->trigger ($args, $state); + $self->{npc}->map->trigger ($con, $state, $self->{npc}, $self->{ob}); } else { - my $rvalue = \$self->{npc}{dialog_trigger}{$con}; - $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue); + my $rvalue = \$self->{npc}{dialog_trigger}{$con+0}; + $self->{npc}->map->trigger ($con, $$rvalue = !$$rvalue, $self->{npc}, $self->{ob}); } } elsif ($cmd eq "addtopic") { @@ -293,10 +386,6 @@ delete $self->{npc}{$self->{ob}->name}{dialog_state} unless %$state; delete $self->{ob}{dialog_flag} unless %$flag; - # combine lines into paragraphs - $reply =~ s/(?<=\S)\n(?=\w)/ /g; - $reply =~ s/\n\n/\n/g; - # ignores flags and npc from replies $reply = join "\n", (map $_->[1], @replies), $reply; @@ -310,6 +399,9 @@ } } + $self->{npc}->use_trigger ($self->{ob}) + if $self->{npc}->type == cf::MAGIC_EAR; + return wantarray ? ($reply, @kw) : $reply; } }