Make NPC companion carry objects?

Is it possible to make a companion NPC carry an object, and in room description make it say, "Fred (carrying x).

Thanks!


1. Set it up so you can GIVE something TO Fred.

2. In the GIVE this TO something script on the x object, put this under Fred's script:

msg ("You give x to Fred.")
MoveObject (this, Fred)
Fred.alias = GetDisplayAlias(Fred) + " (carrying x)"

image

image


OR...

You can make Fred a surface type of container, changing the Contents prefix and adding a List prefix and message to print when trying to put something on him.

image

EDITED
In this case, the GIVE this TO OTHER OBJECT script targeting Fred should be:

msg ("You give x to Fred.")
MoveObject (this, Fred)

If Fred is the only NPC who might have this, and you want the player to be able to just take it, you can make the object's take script this:

if (this.parent=Fred) {
  msg ("Fred hands it over.")
}
else {
  msg ("You pick it up.")
}
AddToInventory (this)

To go an extra step (in either example), add the x object to Fred's ASK script, so he can tell you about it, hand it over, or tell you what needs to be done to acquire it.

(I know you know this part, but someone else may be working from this post one day.)


Cheers!


Thanks K.V. !


Argh!

I missed a spot!

I edited the first script.


Hi, apologies for hijacking thread.
If 'Fred' is carrying more objects, lets say 'y' and 'z', it seems a lot to do adding similar scripts for each object Fred is carrying.
Could you use a 'Scope' function.

Look at Fred
Fred is a tall, blond man. Fred is carrying x,y,z.

Could you use a script: GetDirectChildren(fred)?


If you go the second route, making Fred a surface, it will list what he's carrying when you look at him.

Otherwise, if you don't make Fred a surface, Doctor Agon is on top of it:

Fred.look

if (not ListCount(GetDirectChildren(Fred)) = 0){
  Fred.alias = "Fred (carrying: " + GetDirectChildren(Fred) + ")"
}
else {
  Fred.alias = "Fred"
}

I was thinking more on the lines of the players 'Inventory' command.
Setting up a function 'GetNpcInv' with a parameter 'object'
and the following script:

list = FormatObjectList(object.alias + " is carrying ", object, Template("And"), ".")
if (list = "") {
  msg (Template("NotCarryingAnything"))
}
else {
  msg (list)
}

Then in Fred's look at description, having it call the function, with the parameter 'Fred'.
I probably like making more work for myself than is necessary.


Oh, that is better!

...and my script would print a list rather than a formatted list!


How about this?


Revised

  <function name="NpcInventory" parameters="npc" type="string"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      return (CapFirst(npc.gender) + " is not carrying anything.")
    }
    pre = CapFirst(npc.gender) + " is carrying "
    return (FormatObjectList(pre, npc, "and", "."))
  ]]></function>

I never thought of using .gender, I did make a revision of my own to my scripting:

list = FormatObjectList(CapFirst(GetDisplayAlias(object)) + " is carrying ", object, Template("And"), ".")
if (list = "") {
  msg (CapFirst(GetDisplayAlias(object)) + " is not carrying anything.")
}
else {
  msg (list)
}

Just in case the objects alias had not been set.
Also didn't realise I hadn't altered the 'not carrying anything' section.


Edit: No, I'm wrong
I thought FormatObjectList had an optional final parameter like FormatList


@KV, Is there something missing from your script?
Does it need to print a message?
Update: Printing a message, instead of returning a value

<function name="NpcInventory" parameters="npc"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      msg (CapFirst(npc.gender) + " is not carrying anything.")
    }
    else {
      pre = CapFirst(npc.gender) + " is carrying "
      msg (FormatObjectList(pre, npc, "and", "."))
    }
  ]]></function>

@mrangel

I had the boolean at the end at first, and it kept throwing errors.

The docs show that last parameter, too:

FormatObjectList (string pre-list, object parent, string pre-final, string post-list, boolean use inventory verbs)

...then, I just added all the elements to the game so I could look at the function, and I found that parameter no longer exists.


@Doctor Agon

No, sir. I have a command which prints the returned string.

<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Fred Carries Things">
    <gameid>803a9ebb-d611-4253-b116-698982bba250</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <enter type="script">
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <object name="Fred">
      <inherit name="editor_object" />
      <inherit name="namedmale" />
      <object name="widget">
        <inherit name="editor_object" />
      </object>
      <object name="thingy">
        <inherit name="editor_object" />
      </object>
    </object>
  </object>
  <command name="npc_inventory_cmd">
    <pattern>#object#, inventory;#object#, i;#object# inventory;#object# i</pattern>
    <script>
      msg (ScopeNpcInventory (object))
    </script>
  </command>
  <function name="ScopeNpcInventory" parameters="npc" type="string"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      return (CapFirst(npc.gender) + " is not carrying anything.")
    }
    pre = CapFirst(npc.gender) + " is carrying "
    return (FormatObjectList(pre, npc, "and", "."))
  ]]></function>
</asl>

Ah, ok. Didn't realise that. My bad.


Well... Actually, I neglected to mention that part... So, you're good. (Or "your good", rather. Ha-ha!)


Revised, rebooted, and reduxed (with a little bit of code from everyone):

  <command name="npc_inventory_cmd">
    <pattern>#object#, inventory;#object#, i;#object# inventory;#object# i</pattern>
    <script>
      PrintNpcInventory (object)
    </script>
  </command>
  <function name="FormatNpcInventory" parameters="npc" type="string"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      return ("nothing")
    }
    return (FormatObjectList("", npc, "and", ""))
  ]]></function>
  <function name="PrintNpcInventory" parameters="npc">
    msg (CapFirst(GetDisplayName(npc)) + " is carrying " + FormatNpcInventory(npc) + ".")
  </function>

Weren't there some fancy scoping features which were added in the latest release?

Is there a way to put object1 in scope instead of "Fred", to cover all objects?

  <command name="ask_npc_for_object_cmd">
    <pattern>ask #object1# for #object2#;#object1#, give me #object2#</pattern>
    <scope>Fred</scope>
    <script>
      if (not HasAttribute(object1,"giver")) {
        msg (CapFirst(object1.gender) + " can't do that.")
      }
      else {
        if (DictionaryContains(object1.givedict,object2.name)) {
          invoke (ScriptDictionaryItem(object1.givedict, object2.name))
        }
        else {
          msg (CapFirst(GetDisplayName(object1)) + " shakes " + object1.possessive + " head.")
        }
      }
    </script>
  </command>

Weren't there some fancy scoping features which were added in the latest release?

Is there a way to put object1 in scope instead of "Fred", to cover all objects?

Can't remember the names of all the options now; but off the top of my head (and it's nearly 3am, so sorry if I messed up the XML)…

  <command name="ask_npc_for_object_cmd">
    <pattern>ask #object1# for #object2#;#object1#, give me #object2#</pattern>
    <scope>object1=room|object2=none</scope>
    <changecommandscope type="script">
      if (variable = "object1") {
        // for 'object1', remove all non-giver items from scope
        // I'm assuming "giver" is a boolean attribute?
        foreach (to_remove, FilterByNotAttribute (items, "giver", true)) {
          list remove (items, to_remove)
        }
      }
      else if (variable = "object2") {
        giver = DictionaryItem(matched, "object1")
        // get the first matched object (giver), and add its children to scope
        foreach (o, GetDirectChildren(giver)) {
          list add (items, o)
        }
      }
    </changecommandscope>
    <script>
      if (not HasAttribute(object1,"giver")) {
        msg (CapFirst(object1.gender) + " can't do that.")
      }
      else {
        if (DictionaryContains(object1.givedict,object2.name)) {
          invoke (ScriptDictionaryItem(object1.givedict, object2.name))
        }
        else {
          msg (CapFirst(GetDisplayName(object1)) + " shakes " + object1.possessive + " head.")
        }
      }
    </script>
  </command>

How do you ask fred for x.
object x is inside 'fred' as it were.
I've tried various coding, but I keep getting the same response I can't see that (x).
Any help or guidance is appreciated.


Put Fred in scope. (Or make him a surface.)


I think I still might need more help.
Didn't really want to make 'Fred' a surface.


I put Fred (who is not a surface in this example) in scope in the command in this script:

http://textadventures.co.uk/forum/quest/topic/egoik0xgukucht6dikjtrw/make-npc-companion-carry-objects#8f314ea7-fe07-4933-a11c-d744d42b6ec0

Here's the entire game, to see everything working together:

<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Fred Carries Things">
    <gameid>803a9ebb-d611-4253-b116-698982bba250</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <enter type="script">
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <object name="Fred">
      <inherit name="editor_object" />
      <inherit name="namedmale" />
      <givedict type="scriptdictionary">
        <item key="thingy"><![CDATA[
          msg ("\"It's my favorite thingy,\" says Fred.  \"Take good care of it.\"<br/><br/>He hands it over.")
          AddToInventory (thingy)
        ]]></item>
        <item key="widget"><![CDATA[
          firsttime {
            msg ("\"This widget is mine,\" says Fred.  \"Go find your own.\"<br/>")
          }
          otherwise {
            msg ("\"I've already told you,\" says Fred.  \"You can't have it.\"<br/>")
          }
        ]]></item>
      </givedict>
      <giver />
      <object name="widget">
        <inherit name="editor_object" />
      </object>
      <object name="thingy">
        <inherit name="editor_object" />
      </object>
      <object name="cane">
        <inherit name="editor_object" />
      </object>
    </object>
  </object>
  <command name="npc_inventory_cmd">
    <pattern>#object#, inventory;#object#, i;#object# inventory;#object# i</pattern>
    <script>
      PrintNpcInventory (object)
    </script>
  </command>
  <command name="ask_npc_for_object_cmd">
    <pattern>ask #object1# for #object2#;#object1#, give me #object2#;get #object2# from #object1#;take #object2# from #object1#</pattern>
    <scope>Fred</scope>
    <script>
      if (not HasAttribute(object1,"giver")) {
        msg (CapFirst(object1.gender) + " can't do that.")
      }
      else {
        if (DictionaryContains(object1.givedict,object2.name)) {
          invoke (ScriptDictionaryItem(object1.givedict, object2.name))
        }
        else {
          msg (CapFirst(GetDisplayName(object1)) + " shakes " + object1.possessive + " head.")
        }
      }
    </script>
  </command>
  <function name="FormatNpcInventory" parameters="npc" type="string"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      return ("nothing")
    }
    return (FormatObjectList("", npc, "and", ""))
  ]]></function>
  <function name="PrintNpcInventory" parameters="npc">
    msg (CapFirst(GetDisplayName(npc)) + " is carrying " + FormatNpcInventory(npc) + ".")
  </function>
</asl>

...and mrangel shows a way to put anything in the room in scope in the post just after it.


I looked at both those two codes and I thought;
script dictionary??? Huh? Wha'th'ell??
I was aware of this thing called a script dictionary, but never actually used it.
Another nugget of information to add.
Thanks


Script Dictionary:

input (item's "key"): string
output (item's "value"): script

ScriptDictionaryItem (SCRIPT_DICTIONARY, INPUT) --> returns: OUTPUT (as a script)

so, you either have to:

  1. store the 'script output' into a Script VARIABLE:
<![CDATA[

  example_script_variable => ScriptDictionaryItem (SCRIPT_DICTIONARY, INPUT)

  example_object.example_script_attribute => ScriptDictionaryItem (SCRIPT_DICTIONARY, INPUT)

]]>
  1. use/call/do/invoke the script output:

invoke (ScriptDictionaryItem (SCRIPT_DICTIONARY, INPUT))


for example:

<game name="example_game">

  <attr name="start" type="script">

    do (example_object, "example_script_attribute")

  </attr>

</game>

<object name="example_object">

  <attr name="example_script_attribute" type="script">

    show menu ("Color?", this.example_scriptdictionary_attribute, false) {
      invoke (ScriptDictionaryItem (this.example_scriptdictionary_attribute, result))
    }

  </attr>

  <example_scriptdictionary_attribute type="scriptdictionary">

    <item key="red">
      msg ("Color: red")
    </item>

    <item key="blue">
      msg ("Color: blue")
    </item>

    <item key="yellow">
      msg ("Color: yellow")
    </item>

  </example_scriptdictionary_attribute>

</object>

Hi,
Just trying to understand your 'changecommandscope' script and how it works.

if (variable = "object1") {
  // for 'object1', remove all non-giver items from scope
  // I'm assuming "giver" is a boolean attribute?
  foreach (to_remove, FilterByNotAttribute (items, "giver", true)) {
    list remove (items, to_remove)
  }
}
else if (variable = "object2") {
  giver = DictionaryItem(matched, "object1")
  // get the first matched object (giver), and add its children to scope
  foreach (o, GetDirectChildren(giver)) {
    list add (items, o)
  }
}

A couple of questions though:
1: Why are you checking for object1 in the first section, if object1 is not in the room, wont quest say I cant see that(object1) anyway?
2: Why are you using the function 'DictionaryItem'.

Sorry, if these questions are a bit basic, but just trying to be a better coder.


Why are you checking for object1 in the first section, if object1 is not in the room, wont quest say, "I cant see that(object1) "anyway?

changecommandscope makes Quest "see" the item(s) in the script.

The scope is basically the list of things you can interact with.

This is adding something to that list before the script even checks the list to see what you can interact with.

So, if I have an object named "Fred", and I put "Fred" as the value for the scope attribute of that command, changecommandscope will add all of Fred's direct children to the list of things you can interact with before it tries to run script. Hence, everything carried by Fred can be examined, taken, or what have you. (You can also put things in an off-the-map room, and add that room to a command's scope attribute. Then, you could interact with all of those items, but they wouldn't show up in the Places and Objects pane or the room description.)


Why are you using the function 'DictionaryItem'.

This is totally a guess, because I'm not sure where that dictionary is being created, but I think it has something to do with the Regex cache.

I'm assuming matched is a dictionary which holds Regex values, and mrangel is pulling the value of object1 from that dictionary, then getting that object's direct children, which are add to scope before the command actually runs.


I'm basing that on this:

smack #object1# with #object#

Quest turns that into Regex:

^smack (?<object1>.+) with (?<object2>.+)$

I'm assuming that a dictionary called matched then stores the values once the command's script is run?


http://docs.textadventures.co.uk/quest/complex_commands.html

http://docs.textadventures.co.uk/quest/pattern_matching.html


NOTE:

That still didn't make this work when putting "object1" in the scope.**

Putting "Fred" in that command's scope works perfectly, though.

I was trying to make a command which would handle the variable object1, so that command could be used in any game at any time.

I messed up somehow when I first tried it, but mrangel's code works perfectly.


MY THEORY:

The GetScope() script is using GetObject() on whatever is in the scope field. FOOTNOTE 1

So, whatever we put in the scope field is a string.

You can only do GetObject(string). It doesn't work on an element (or a variable or an object). So, object or object1, game.pov, clone_name.prototype, game.pov.radio_contact, or anything like that will not work.

If it's an object you're entering in the scope, you're really entering object.name. Then, the GetScope() script treats it like a string.

Check out the GetScope() and GetScoping() scripts, if you really enjoy having your mind blown.


l find it easier to just make the NPC a surface.

http://textadventures.co.uk/forum/quest/topic/egoik0xgukucht6dikjtrw/make-npc-companion-carry-objects#8e7ba1b2-c9fb-42c3-83a2-d6849f6293e0

I've added a function and changed the take script on things which an NPC might be carrying, to make it universal.

EDITED TWICE

        <take type="script">
          TakeFromGiver (this)
        </take>
  <function name="TakeFromGiver" parameters="this">
    if (HasAttribute(this.parent, "giver")) {
      negatory = CapFirst(this.gender) + " seems to belong to "+ GetDisplayName(this.parent) + "."
      if (this.parent.giver) {
        if (not GetBoolean(this, "notgiveable")) {
          if (DictionaryContains(this.parent.givedict, this.name)) {
            invoke (ScriptDictionaryItem(this.parent.givedict, this.name))
          }
          else {
            msg (CapFirst(GetDisplayName(this.parent)) + " hands " + this.article + " over.")
            AddToInventory (this)
          }
        }
        else {
          if (DictionaryContains(this.parent.givedict, this.name)) {
            invoke (ScriptDictionaryItem(this.parent.givedict, this.name))
          }
          else {
            msg (negatory)
          }
        }
      }
      else {
        msg (negatory)
      }
    }
    else {
      msg ("You pick it up.")
      AddToInventory (this)
    }
  </function>

Example game (with Fred as a surface):

<!--Saved by Quest 5.7.6623.30864-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Fred Carries Things (Surface Edition)">
    <gameid>803a9ebb-d611-4253-b116-698982bba250</gameid>
    <version>0.0.1</version>
    <firstpublished>2018</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <enter type="script">
    </enter>
    <beforeenter type="script">
    </beforeenter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <object name="Fred">
      <inherit name="editor_object" />
      <inherit name="namedmale" />
      <inherit name="surface" />
      <giver />
      <take type="boolean">false</take>
      <feature_container />
      <contentsprefix>who is carrying</contentsprefix>
      <hidechildren type="boolean">false</hidechildren>
      <look type="script"><![CDATA[
        msg ("He's just your average NPC.<br/>")
        if (ListCount(GetDirectChildren(Fred))>0) {
          PrintNpcInventory (Fred)
        }
      ]]></look>
      <addscript type="script">
        msg ("\"Get that off me!\" says Fred.")
      </addscript>
      <givedict type="scriptdictionary">
        <item key="thingy"><![CDATA[
          msg ("\"It's my favorite thingy,\" says Fred.  \"Take good care of it.\"<br/><br/>He hands it over.")
          AddToInventory (thingy)
        ]]></item>
        <item key="widget"><![CDATA[
          firsttime {
            msg ("\"This widget is mine,\" says Fred.  \"Go find your own.\"<br/>")
          }
          otherwise {
            msg ("\"I've already told you,\" says Fred.  \"You can't have it.\"<br/>")
          }
        ]]></item>
      </givedict>
      <ask type="scriptdictionary">
        <item key="cane"><![CDATA[
          msg ("\"I was planning on keeping it for myself,\" he says, \"but you can take it.\"")
          cane.take => {
            TakeFromGiver (this)
          }
        ]]></item>
      </ask>
      <object name="widget">
        <inherit name="editor_object" />
        <take type="script">
          TakeFromGiver (this)
        </take>
      </object>
      <object name="thingy">
        <inherit name="editor_object" />
        <take type="script">
          TakeFromGiver (this)
        </take>
        <notgiveable />
      </object>
      <object name="cane">
        <inherit name="editor_object" />
        <take type="script">
          TakeFromGiver (this)
        </take>
      </object>
    </object>
  </object>
  <command name="npc_inventory_cmd">
    <pattern>#object#, inventory;#object#, i;#object# inventory;#object# i</pattern>
    <script>
      PrintNpcInventory (object)
    </script>
  </command>
  <command name="ask_npc_for_object_cmd">
    <pattern>ask #object1# for #object2#;#object1#, give me #object2#;get #object2# from #object1#;take #object2# from #object1#</pattern>
    <scope>Fred</scope>
    <script>
      if (not HasAttribute(object1,"giver")) {
        msg (CapFirst(object1.gender) + " can't do that.")
      }
      else {
        if (DictionaryContains(object1.givedict,object2.name)) {
          invoke (ScriptDictionaryItem(object1.givedict, object2.name))
        }
        else {
          msg (CapFirst(GetDisplayName(object1)) + " shakes " + object1.possessive + " head.")
        }
      }
    </script>
  </command>
  <function name="FormatNpcInventory" parameters="npc" type="string"><![CDATA[
    if (ListCount(GetDirectChildren(npc))<1) {
      return ("nothing")
    }
    return (FormatObjectList("", npc, "and", ""))
  ]]></function>
  <function name="PrintNpcInventory" parameters="npc">
    msg (CapFirst(GetDisplayName(npc)) + " is carrying " + FormatNpcInventory(npc) + ".")
  </function>
  <function name="TakeFromGiver" parameters="this">
    if (HasAttribute(this.parent, "giver")) {
      negatory = CapFirst(this.gender) + " seems to belong to "+ GetDisplayName(this.parent) + "."
      if (this.parent.giver) {
        if (not GetBoolean(this, "notgiveable")) {
          if (DictionaryContains(this.parent.givedict, this.name)) {
            invoke (ScriptDictionaryItem(this.parent.givedict, this.name))
          }
          else {
            msg (CapFirst(GetDisplayName(this.parent)) + " hands " + this.article + " over.")
            AddToInventory (this)
          }
        }
        else {
          if (DictionaryContains(this.parent.givedict, this.name)) {
            invoke (ScriptDictionaryItem(this.parent.givedict, this.name))
          }
          else {
            msg (negatory)
          }
        }
      }
      else {
        msg (negatory)
      }
    }
    else {
      msg ("You pick it up.")
      AddToInventory (this)
    }
  </function>
</asl>

1: Why are you checking for object1 in the first section, if object1 is not in the room, wont quest say I cant see that(object1) anyway?

==> Ask fre for box

Which Fre did you mean?
1. Fred
2. French flag

Paraphrasing off the top of my head, maybe that's not the exact wording, but you get the idea.
When determining what you can see, the parser uses the changecommandscope script to change the list of items.
In the case f object1, it removes everything that isn't a "giver" from the list, so you type "fre" and the parser knows you meant Fred.

2: Why are you using the function 'DictionaryItem'.

matched is a dictionary of the parameters that have already been identified. In this case the command is ask #object1# for #object2#. So if you put ask fred for box and he's holding multiple boxes, it will ask you to choose one. But when it's building the list of boxes, DictionaryItem(matched, "object1") will be Fred. It adds the boxes Fred is carrying to the list, but not the boxes Max is carrying, or the box you already have.


Ah, finally!

I thought I pasted mrangels entire code over mine last night, but I must have erred in some way.

This works:
http://textadventures.co.uk/forum/quest/topic/egoik0xgukucht6dikjtrw/make-npc-companion-carry-objects#881090f2-ad95-4139-958a-5050fde36a8d


(A little note: The script as I posted might have issues with the case of get #object2# from #object1#, because I'm not sure which order the scope script will be called for the two arguments. I think it will parse them in the order they occur i the player's input; so the object2 part of the script should form a list of all the objects held by all givers in the event that object1 hasn't been parsed yet. Or we could rely on the backup scope in that case, but that's a little inelegant)


Thanks all, I think I've got the hang of this now, and after reading this post;
http://textadventures.co.uk/forum/quest/topic/lyxihkcrnkgl1v97f1ym8w/placing-things-in-scope-solved
it's making a lot more sense.


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

Support

Forums