NPCs reactions

The Pixie
NPCs will seem more realistic if they react to changes in their environment. If the player starts wearing a hat, for instance, it will be cool if the NPC acknowledges that. Here is a way to do that. It is not that simple; some experience in coding is required. Also, it cannot handle NPC and attributes names with spaces in them.

The first part is a turn script, which should be set to be enabled from the start. It iterates though everything in the current room, and if it is a character, looks for a reaction. Note that it tests whether the object is of the "talkingchar" from ConvLib; if you do not use that, you will need some other test.
      foreach (o, GetDirectChildren(player.parent)) {
if (DoesInherit(o, "talkingchar")) {
//msg("Doing for " + o.name)
if (not HasAttribute(o, "reactionslist")) {
o.reactionslist = NewStringList()
if (HasString(o, "reactions")) {
o.reactionslist = Split(o.reactions, "@")
list remove (o.reactionslist, StringListItem(o.reactionslist, 0))
}
else {
o.reactionslist = NewStringList()
}
}
todelete = NewStringList()
continue = true
foreach (s, o.reactionslist) {
ary = Split(s, "<br/>")
test = StringListItem(ary, 2)
test = Replace(test, "this", o.name)
flag = Eval(test)
if (flag and continue) {
for (i, 3, ListCount(ary) - 1) {
s2 = StringListItem(ary, i)
if (IsRegexMatch("^(=|\\+|\\-) ", s2)) {
SetValue(s2, o)
}
else if (StartsWith(s2, "~")) {
deleter = Mid(s2, 2)
foreach(s3, o.reactionslist) {
if (StartsWith(s3, "<br/>" + deleter + "<br/>")) {
list add (todelete, s3)
}
}
}
else if (not StartsWith(s2, "//")) {
msg(s2)
}
}
list add (todelete, s)
continue = false
}
}
foreach (s, todelete) {
list remove (o.reactionslist, s)
}
}
}

You also need a new function, called SetValue, no return value, taking parameters, s and char. Here is the code:
    ary = Split(s, " ")
if (ListCount(ary) < 4) error ("Insufficient arguments in reaction for " + char,name + ": " + s)
sign = StringListItem(ary, 0)
list remove(ary, sign)
objname = StringListItem(ary, 0)
list remove(ary, objname)
if (objname = "this") {
obj = char
}
else {
obj = GetObject(objname)
}
if (obj = null) error ("Object not found in reaction for " + obj.name + ": " + s)
attname = StringListItem(ary, 0)
list remove(ary, attname)
value = Eval(Replace(Join(ary, " "), "this", char.name))
if (sign = "=") {
set(obj, attname, value)
}
else {
oldvalue = GetAttribute (obj, attname)
if (sign = "-") {
value = -value
}
if (TypeOf(oldvalue) = "null") {
set(obj, attname, value)
}
else {
set(obj, attname, oldvalue + value)
}
}

If you are using ConvLib, you will find it convenient to edit it. Go to the end of the file, it should look like this:
    <control>
<caption>Run this greeting script</caption>
<controltype>script</controltype>
<attribute>greet</attribute>
<mustinherit>talkingchar</mustinherit>
</control>

</library>

You need to insert an extra control, so it looks like this:
    <control>
<caption>Run this greeting script</caption>
<controltype>script</controltype>
<attribute>greet</attribute>
<mustinherit>talkingchar</mustinherit>
</control>

<control>
<caption>Reactions</caption>
<controltype>richtext</controltype>
<attribute>reactions</attribute>
<mustinherit>talkingchar</mustinherit>
</control>
</tab>

</library>

If you reload your game, you will find a Reactions text area on the conversations tab for NPCs. So what do you do with it?

You can add any number of reactions to the NPC. In any turn, each NPC will only give one reaction, and the one highest in the list "wins". Each reaction consists of: a line with just an @; a line with the reaction's name; a line with a condition; and then any number of lines of actions. Here is a simple example, with three reactions:

@
metplayer
true
'Hi,' says the girl.
@
playerhasball
ball.parent = player
'Can I play with your ball?' asks the girl. 'My name's Mary.'
+ player count 5
= mary alias "Mary"+" Smith"
// Delete other
~playerhashat
@
playerhashat
ball.parent = player
'Can I try on your hat?' asks the girl. 'My name's Mary.'
+ player count 3
= mary alias "Mary"+" Smith"
// Deleteother
~playerhasball

The first is called "metplayer", and fires if "true", i.e., the first turn the player is in the same room. The text is printed.

The second reaction is called "playerhasball" and is more complicated. It fires if "ball.parent = player". The condition is standard Quest code, and you can use "this" to refer to the character. If the player does have the ball, the events fire. The first is just text that is displayed, the others illustrate the options available.

If the line starts with // it is ignored as a comment.

If it starts =, + or -, then an attribute gets set. You have to set this out very carefully, there must be the sign, the object name, the attribute name and the value, all separated by just one space. The value, however, can be any Quest code. If the symbol is = then the attribute is set to that value, if + then the value is added to it, if - it is taken away. It is up to you to ensure it is of a suitable type. In the example, player.count is increased by 5, and mary.alias is set to "Mary Smith".

Each reaction will only fire once, but you can also have one reaction disabling another. Use ~ to indicate that the named reaction is to be disabled. In the example above, the second one disables the third and vice versa so only one will ever fire.

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums