Is there any way to reduce the clutter when objects are listed?

PROBLEM:
I am trying to make a game which pretty much tries to simulate SCUMM interface by removing the command bar and designing the whole game around only 10 words. All the words work nicely, but when I need to use object A with object B through command "usewith", I usually get a list of all the objects in the room + all the objects in my inventory to choose from. This is not an issue when I have only a few objects in my inventory, but eventually the list becomes a cluttered mess to the point it is very hard to even find an object I am looking for.

QUESTION:
Is there any way to reduce the clutter, so that when I use the word USE I get only the list of objects in the room, and when I use word COMBINE that I only get a list of inventory objects to choose from? Or at the very list just sort the listed items so that they are clearly divided into two sections - the ones in the world and the ones in my inventory. Even painting them in different color would be helpful.

I think this would reduce the clutter a lot! Thanks in advance to anyone who can help me with this.


Come on guys... please just a bit of help here... 😧


I have no idea how to solve this. I hope you get help though 'cause it sounds cool. Love SCUMM!!


Well after 7 hours of thinking and searching through these forums, I couldnt find what I was looking for, but then I got a vague idea how I might sidestep this issue. Namely I could upon choosing the USE command make a script that makes all pickable objects invisible and then make them visible again after the action is complete. Then I could do the same with COMBINE command, only I would make the world objects invisible. The problem is, I think this will be very hard to keep track of, because I will need to go retroactively back into every room and change this for every new object I make. I am hoping someone might have a better way how to program it, so I dont have to constantly keep track of things. Like, maybe there is a simple function or code to make all items I am carrying make invisible. I think that would pretty much solve my problem.

So basically all I need is one line of code to make my inventory objects invisible and then visible again. And then onle line of code to make all the world objects visible/invisible again.

Anyone? Please...


Are you making your own "Use" command, or using the built-in one?

If you're using the built in "use" command, you can customise the "what do you want to use it on?" menu by overriding this function:

  <function name="CreateUseMenuList" parameters="object" type="objectlist">
    objectlist = NewObjectList()
    objectlist = ScopeReachableNotHeld()
    excludelist = NewObjectList()
    list add (excludelist, game.pov)
    list add (excludelist, object)
    candidates = NewObjectList()
    candidates = ListExclude(RemoveSceneryObjects(objectlist), excludelist)
    return (candidates)
  </function>

If you're making your own "use" command, you could just change the code so it only displays the objects you want in the menu.

Or if you want to dispense with the menu entirely, you could replace the use command with:

<command name="use" template="use"><![CDATA[
  if (HasScript(object, "use")) {
    do (object, "use")
  }
  else {
    if (GetBoolean(object, "use")) {
      candidates = CreateUseMenuList (object)
      if (ListCount(candidates) = 0) {
        msg (Template("NoObjectsAvailable"))
      }
      else {
        if (HasString(object, "usemenuprompt")) {
          msg (object.usemenuprompt)
        }
        else {
          msg ("Now click the object you want to use it on.")
        }
        game.pov.usemenuobject = object
        game.suppressturnscripts = true
        SetTurnTimeout (2) {
          game.pov.usemenuobject = null
        }
      }
    }
    else {
      msg (DynamicTemplate("CantUse", object))
    }
  }
  ]]></command>

and override this function:

  <function name="GetDisplayVerbs" parameters="object" type="stringlist">
    if (Contains(game.pov, object)) {
      baselist = object.inventoryverbs
      multiverb = "combine"
    }
    else {
      baselist = object.displayverbs
      multiverb = "use"
    }

    if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
      return (baselist)
    }
    else {
      if (HasObject (game.pov, multiverb+"menuobject")) {
        multiobj = GetAttribute (game.pov, multiverb+"menuobject")
        if (not multiobj = object) {
          baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)
        }
      }

      if (HasAttribute(object, "generatedverbslist")) {
        verbs = object.generatedverbslist
      }
      else {
        verbs = NewStringList()
        foreach (attr, GetAttributeNames(object, false)) {
          if (ListContains(game.verbattributes, attr)) {
            cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
            if (HasString(cmd, "displayverb")) {
              displayverb = CapFirst(cmd.displayverb)
            }
            else {
              displayverb = CapFirst(attr)
            }
            if (not ListContains(baselist, displayverb)) {
              list add (verbs, displayverb)
            }
          }
        }
        object.generatedverbslist = verbs
      }
      if (GetBoolean(object, "useindividualverblist")) {
        return (verbs)
      }
      else {
        return (ListCombine(baselist, verbs))
      } 
    }
  </function>

Thanks for your help again, Mrangel. The option to skip the menus altogether would be perfect, but I am not sure if I know how to do this - even with the script you posted. So if I am getting your instructions correctly, you want me to:
Step 1: Show the hidden library elements
Step 2: Find the "Use" command and make a copy of it
Step 3: Copy this over it:

if (HasScript(object, "use")) {
    do (object, "use")
  }
  else {
    if (GetBoolean(object, "use")) {
      candidates = CreateUseMenuList (object)
      if (ListCount(candidates) = 0) {
        msg (Template("NoObjectsAvailable"))
      }
      else {
        if (HasString(object, "usemenuprompt")) {
          msg (object.usemenuprompt)
        }
        else {
          msg ("Now click the object you want to use it on.")
        }
        game.pov.usemenuobject = object
        game.suppressturnscripts = true
        SetTurnTimeout (2) {
          game.pov.usemenuobject = null
        }
      }
    }
    else {
      msg (DynamicTemplate("CantUse", object))
    }
  }
  ]]></command>

Step 4: Find the hidden function called "GetDisplayVerbs" and copy over it this here part:

if (Contains(game.pov, object)) {
      baselist = object.inventoryverbs
      multiverb = "combine"
    }
    else {
      baselist = object.displayverbs
      multiverb = "use"
    }

    if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
      return (baselist)
    }
    else {
      if (HasObject (game.pov, multiverb+"menuobject")) {
        multiobj = GetAttribute (game.pov, multiverb+"menuobject")
        if (not multiobj = object) {
          baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)
        }
      }

      if (HasAttribute(object, "generatedverbslist")) {
        verbs = object.generatedverbslist
      }
      else {
        verbs = NewStringList()
        foreach (attr, GetAttributeNames(object, false)) {
          if (ListContains(game.verbattributes, attr)) {
            cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
            if (HasString(cmd, "displayverb")) {
              displayverb = CapFirst(cmd.displayverb)
            }
            else {
              displayverb = CapFirst(attr)
            }
            if (not ListContains(baselist, displayverb)) {
              list add (verbs, displayverb)
            }
          }
        }
        object.generatedverbslist = verbs
      }
      if (GetBoolean(object, "useindividualverblist")) {
        return (verbs)
      }
      else {
        return (ListCombine(baselist, verbs))
      } 
    }
  </function>

I tried doing that, but when I start the game, I get this error:
Failed to load game.
The following errors occurred:
Error: Error adding script attribute 'script' to element 'use': Function not found: ']]>'

Now, perhaps I am not really following your instructions correctly, or maybe there is a typo in your script. Either way, I am stuck and this is the last part I need to finally be able to start making the game at a faster pace.

Would it be too much to ask if you look at my game and try to implement that code into it? Or maybe just try clarifying in a bit more detail what I need to do. It's really frustrating when I spend a weekend pretty much stuck and not being able to move forward. I hoped I would actually be able to make a whole another section for my game. :(


Actually, I managed to get the script to at least run. Now I am testing it. I didnt know which parts to copy from your code, so I copied the whole thing. Didn't know I have to remove the header and footnote section.

Will report how it goes. Hopefully it works.


Edit: Well, the game works, but I am not noticing any difference. I am not sure what your script changed. I still get a menu and everything works pretty much the same as before.

Again, I am probably just doing something wrong. Or maybe I need to use the script in a specific way? Help, please!


Edit2: Same thing happens when I try your first suggestion. I dont see any difference.


Edit3: Still no luck :(


Where are you seeing the menu?

Those scripts were something I'd thought about for my own game, to be more like how it works in some of the older games (SCUMM and similar). I never got around to testing it, though.

The "use" command is the one that runs when the player inputs "use spanner", whether by typing it in the command bar or by clicking on the spanner. If you have those scripts, when you use an object whose type on the Use/Give tab is "Display menu of objects this can be used on", the modified command should mean that rather than showing a menu, it just says "Now click the object you want to use it on."

Then when you click another non-inventory object, the modified GetDisplayVerbs function would cause "Use spanner on" to be added to the beginning of the verbs list (either the drop-down when you click an object name in the text, or the buttons under the 'Places and Objects' pane in the sidebar).

If you're still seeing the menu, there must be something I've missed.


Since I am not using the command bar, the only way I can use objects on their own or on other objects is through making custom verbs like "useon" or "combinewith". Then I usually get a menu with all the items I can interact with which are all the objects in the room, all the NPCs and all my inventory items. It's quite a mess as you can imagine. Almost unplayable when you have 15-20 objects in your inventory, so I really need your script to work. :(

I dont know what you did, but the game behaves exactly as by default. There is no difference from what I am noticing, but then again, maybe I am doing something wrong. I explained to you the steps I made, so you tell me?

I'm really frustrated. I have spent 2 days on this and no move forward... :(


Edit: I tried making a completely new test game now and I did the steps as I described in my previous post, and again the same resault. Damn! I would really love to skip the menu altogether and use items as in a SCUMM game. There must be something I am missing, or maybe your script isnt working as you were planning.


Maybe if you can make a short demo and upload it, i can go in and look what you did? Then I can just copy it into my game.


There is a default verb "use" which can be made to display a list of all objects, asking what you want to use the object on. It then runs the default "useon" verb.

If you've made your own "useon" verb, then whatever menu it displays will be the menu you have told it to display, not the standard 'use on' menu.

I can tell you how to modify the standard "use" command to work as you asked. But I can't tell you how to modify your useon command, because I can't see your useon command.


I tried using the default verb too. I got exactly the same result. Here is my whole code from the new test game, so you can tell me if I did anything wrong. You help me fix this and I'll make you a character in my game :)

<!--Saved by Quest 5.8.6836.13983-->
<asl version="580">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Test">
    <gameid>ceb00cba-a4c7-4ce5-82f3-e71892119160</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
  </game>
  <command name="use">
    <pattern>use #object#</pattern>
    <script>
      if (HasScript(object, "use")) {
        do (object, "use")
      }
      else {
        if (GetBoolean(object, "use")) {
          candidates = CreateUseMenuList (object)
          if (ListCount(candidates) = 0) {
            msg (Template("NoObjectsAvailable"))
          }
          else {
            if (HasString(object, "usemenuprompt")) {
              msg (object.usemenuprompt)
            }
            else {
              msg ("Now click the object you want to use it on.")
            }
            game.pov.usemenuobject = object
            game.suppressturnscripts = true
            SetTurnTimeout (2) {
              game.pov.usemenuobject = null
            }
          }
        }
        else {
          msg (DynamicTemplate("CantUse", object))
        }
      }
    </script>
  </command>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <object name="A">
      <inherit name="editor_object" />
      <take />
      <feature_usegive />
      <use type="script">
        msg ("Success on self.")
      </use>
      <selfuseon type="scriptdictionary">
        <item key="B">
          msg ("Success on B.")
        </item>
      </selfuseon>
    </object>
    <object name="B">
      <inherit name="editor_object" />
    </object>
  </object>
  <function name="GetDisplayVerbs" parameters="object" type="stringlist">
    if (Contains(game.pov, object)) {
      baselist = object.inventoryverbs
      multiverb = "combine"
    }
    else {
      baselist = object.displayverbs
      multiverb = "use"
    }
    if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
      return (baselist)
    }
    else {
      if (HasObject (game.pov, multiverb+"menuobject")) {
        multiobj = GetAttribute (game.pov, multiverb+"menuobject")
        if (not multiobj = object) {
          baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)
        }
      }
      if (HasAttribute(object, "generatedverbslist")) {
        verbs = object.generatedverbslist
      }
      else {
        verbs = NewStringList()
        foreach (attr, GetAttributeNames(object, false)) {
          if (ListContains(game.verbattributes, attr)) {
            cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
            if (HasString(cmd, "displayverb")) {
              displayverb = CapFirst(cmd.displayverb)
            }
            else {
              displayverb = CapFirst(attr)
            }
            if (not ListContains(baselist, displayverb)) {
              list add (verbs, displayverb)
            }
          }
        }
        object.generatedverbslist = verbs
      }
      if (GetBoolean(object, "useindividualverblist")) {
        return (verbs)
      }
      else {
        return (ListCombine(baselist, verbs))
      }
    }
  </function>
</asl>

Edit: I tried looking a bit deeper into your script and although it's like trying to decipher hieroglyphics to me, I did notice the line: if (GetBoolean(object, "use")) {

That line in the UI view looks like: if, object has flag, expression: object, flag name: use
So I tried adding a flag named "use" when using the object and I finally made a small breakthrough, but it still doesn't work. I get this error:
Now click the object you want to use it on.
Error running script: Error compiling expression 'ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)': Unknown object or variable 'uselist'
Error running script: Error evaluating expression 'Join(GetDisplayVerbs(object), "/")': Value cannot be null.Parameter name: values
Error running script: Error compiling expression 'ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)': Unknown object or variable 'uselist'
Error running script: Error compiling expression 'ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)': Unknown object or variable 'uselist'

At the very least I managed to get the message: Now click the object you want to use it on. :)


Sorry, dumb error there. Typing on my phone, I didn't notice autocorrect messing up my code.
The line:
baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)
should be
baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), baselist)


I also notice you have a script where if you just use an object on its own, it says "Success on self.".
With the default use system, this is where it would ask you what other object you want to use it on.

If you want an object to be usable on its own and have the ability to use it on other objects, that would require a slightly more complex command, combining the "use" and "useon" commands.
I think this was how you were trying to do it; probably a better approach. (in my earlier post, I assumed you were using the built-in "use" functionality).

Do you want the verb menu/buttons to have both "use" (on its own) and "use on" (other object) options? I think I can see a way to do that.


So I changed:

baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)

to

baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), baselist)

But I still get the same result.

Yes, I would like to be able to do both use on its own and use on other objects depending on the situation, but I cant even get the first script to work. The priority should be to make "useon" command work, because that's how most items will interact with each other. I can always use items by themselves by creating new specific words. But if you can make it so that I can do both with just 2 commands:
Use (on self)
and
Use on another object

Well... that would be swell.

Damn... been doing this whole day. Thanks for your help, but I am going to sleep now. I've been looking at the code and testing this thing 12 hours straight. I sure hope you can get it to work. Thanks for all your help so far.


OK, off the top of my head (and now edited a few times, but still untested)…

(I was dumb enough to start writing code on my phone, in the forum. I'm posting this here because I've got nowhere to save it, but it's not finished yet. If you spot me doing anything dumb, feel free to say)

Unfinished code
  <command name="useon">
    <pattern type="string"><![CDATA[^(?<text_verb>use|combine) (?<object1>.* )?(?<text_type>on|with) (?<object2>.+)$]]></pattern>
    <script>
      verb = LCase (Trim (text_verb))
      if (IsDefined ("object1")) {
        HandleWithVerb (verb, object1, object2)
      }
      else {
        object = object2
        candidates = ListExclude (ScopeReachableNotHeld(), object)
        if (ListCount(candidates) = 0) {
          msg (Template("NoObjectsAvailable"))
        }
        else {
          if (HasString (object, verb + "menuprompt")) {
            msg (GetString (object, verb + "menuprompt"))
          }
          else if (HasString(object, "usemenuprompt")) {
            msg (object.usemenuprompt)
          }
          else {
            msg ("Now click the object you want to "+verb+" it "+text_type+".")
          }
          game.pov.usemenuobject = object
          game.pov.useonverb = verb
          game.pov.useonconjunction = text_type
          game.suppressturnscripts = true
          SetTurnTimeout (1) {
            game.pov.useonverb = null
            game.pov.usemenuobject = null
            game.pov.useonconjunction = null
          }
        }
      }
    </script>
  </command>

<command name="use">
  <pattern type="string"><![CDATA[^use (?!on |with )(?<object>.+)$]]></pattern>
  <script><![CDATA[
    if (HasScript(object, "use")) {
      do (object, "use")
    }
    else if (GetBoolean(object, "use")) {
      candidates = ListExclude (ScopeReachableNotHeld(), object)
      if (ListCount(candidates) = 0) {
        msg (Template("NoObjectsAvailable"))
      }
      else {
        if (HasString(object, "usemenuprompt")) {
          msg (object.usemenuprompt)
        }
        else {
          msg ("Now click the object you want to use it with.")
        }
        game.pov.usemenuobject = object
        game.pov.useonverb = "use"
        game.pov.useonconjunction = "with"
        game.suppressturnscripts = true
        SetTurnTimeout (1) {
          game.pov.usemenuobject = null
          game.pov.useonverb = null
          game.pov.useonconjunction = null
        }
      }
    }
    else {
      msg (DynamicTemplate("CantUse", object))
    }
  ]]></script>
</command>

  <function name="GetDisplayVerbs" parameters="object" type="stringlist">
    if (Contains(game.pov, object)) {
      baselist = object.inventoryverbs
      multiverb = "combine"
    }
    else {
      baselist = object.displayverbs
      multiverb = "use"
    }
    if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
      return (baselist)
    }
    else {
      if (HasObject (game.pov, multiverb+"menuobject")) {
        multiobj = GetAttribute (game.pov, multiverb+"menuobject")
        if (not multiobj = object) {
          baselist = ListCombine (Split(multiverb+" "+GetDisplayAlias(multiobj) + " with"), uselist)
        }
      }
      if (HasAttribute(object, "generatedverbslist")) {
        verbs = object.generatedverbslist
      }
      else {
        verbs = NewStringList()
        foreach (attr, GetAttributeNames(object, false)) {
          if (ListContains(game.verbattributes, attr)) {
            cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
            if (HasString(cmd, "displayverb")) {
              displayverb = CapFirst(cmd.displayverb)
            }
            else {
              displayverb = CapFirst(attr)
            }
            if (not ListContains(baselist, displayverb)) {
              list add (verbs, displayverb)
            }
          }
        }
        object.generatedverbslist = verbs
      }
      if (GetBoolean(object, "useindividualverblist")) {
        return (verbs)
      }
      else {
        return (ListCombine(baselist, verbs))
      }
    }
  </function>

  <function name="HandleWithVerb" parameters="verb, object1, object2">
    handled = false
    params = QuickParams ("object1", object1, "object2", object2, "verb", verb)
    if (HasAttribute (object2, verb + "on")) {
      handler = GetAttribute (object2, verb + "on")
      dictionary add (params, "this", object2)
      dictionary add (params, "object", object1)
      switch (TypeOf (handler)) {
        case ("string") {
          game.text_processor_variables = params
          msg (handler)
          handled = true
        }
        case ("scriptdictionary") {
          if (DictionaryContains (handler, object1.name)) {
            invoke (ScriptDictionaryItem(handler, object1.name), params)
          }
          handled = true
        }
        case ("script") {
          invoke (handler, params)
          handled = true
        }
        default {
          error ("Unexpected type of "+verb+"on attribute for "+object2.name)
        }
      }
    }

    if (HasScript(object2, verb+"anything") and not handled) {
      dictionary add (params, "object", object1)
      do (object2, verb + "anything", parameters)
      handled = true
    }

    if (HasAttribute (object1, "self" + verb + "on") and not handled) {
      handler = GetAttribute (object2, "self" + verb + "on")
      dictionary add (params, "this", object1)
      dictionary add (params, "object", object2)
      switch (TypeOf (handler)) {
        case ("string") {
          game.text_processor_variables = params
          msg (handler)
          handled = true
        }
        case ("scriptdictionary") {
          if (DictionaryContains (handler, object2.name)) {
            invoke (ScriptDictionaryItem(handler, object2.name), params)
          }
          handled = true
        }
        case ("script") {
          invoke (handler, params)
          handled = true
        }
        default {
          error ("Unexpected type of self"+verb+"on attribute for "+object1.name)
        }
      }
    }

    if (HasScript(object1, "self"+verb+"anything") and not handled) {
      dictionary add (params, "object", object2)
      do (object2, "self" + verb + "anything", parameters)
      handled = true
    }
    
    if (not handled) {
      msg (DynamicTemplate("DefaultUseOn", object2, object1))
    }
  </function>

Mate, there is nothing I can see wrong. But then again I am not a coder. I tried your unfinished version, but I got an error upon starting the game. I guess I have to wait for the finished version. Any idea how long will it take? I wanna jump back to making my game, but I m not sure if I should wait for your script first.


Sorry, my brain's been pretty scrambled lately. Stressing over taxes, lack of people buying my books, new facebook policy changes. Coding's sometimes a way to clear my head, but then I keep bouncing around between different ways of doing the same thing.

I've edited the code above; but haven't had an opportunity to test it yet (due to not having regular access to a computer capable of running Quest).
Rather than having menus, this code would use the existing displayverb system. So for example the player clicks a hammer in their inventory, and chooses the "use on" button. Then when they click on a nail in the 'places and objects' sidebar, the first button will be "Use hammer on". They click that, and the game processes the command "use hammer on nail".

This should work for "use on", "use with", and "combine with" commands. It's just a case of putting the relevant verb into an object's displayverbs and/or inventoryverbs lists (on the 'object' tab, I think).
It uses the "useon" and "selfuseon" attributes, just like the built in "use" command. For 'combine', those attributes would be "combineon" and "selfcombineon"

Hope that all makes sense.


Thanks for still trying to work on this, mate. Unfortunately, I can't seem to make it work. Even with your latest updates, I still get the menu and upon choosing from the menu to interact with another object, I get the Error running script: Error compiling expression 'LCase (Trim (text_verb))': Unknown object or variable 'text_verb'

:(


I can't see how that can happen. text_verb is not an optional parameter; it must be defined. I'll test it myself when I next have access to a Windows computer.
Which command are you using? Are you choosing "Use on" from the dropdown or the sidebar?

Ugh ... one dumb mistake. I've Used the function name "HandleMultiVerb", when there's already a function of that name. I didn't realise it was colliding. I'll change that to avoid collisions.


Hej mrangel, I am using the "useon" command from the dropdown menu. But I tried both. I tried using it on the sidebar as well and it's the same error.


Are you sure you've got the whole pattern for the useon command?

I've made a few changes to the code above, but still don't have access to a Windows machine to test this. In this particular case, I can't even test using the web editor because it doesn't allow you to override the GetDisplayVerbs function


Yes, I am sure. Here is a completely new TEST game I made. Here is how it looks like when I try using "Use on" command:
You are in a room.
You can see an A and a B.

  • take A
    You pick it up.

  • use A
    WORKS ON ITSELF!

  • use on A
    With which object?
    1: B

As you can see, it gives me a menu again. So it works exactly the same as with the original program. The only difference is that if I type "use A on B", I get this error:

  • use a on b
    Error running script: Error compiling expression 'LCase (Trim (text_verb))': Unknown object or variable 'text_verb'

With which object?

My only guess is that "use on A" is being parsed as an instruction to use an object named "on A", and somehow the parser is correctly mapping that onto A.
I modified the pattern for the "use" command in the above post to try to prevent this. But this shouldn't be an issue in any case, because neither the "use" nor "useon" commands in the above post display a menu. I really can't see where this problem is creeping in. Anyone else have any suggestions?

Error running script: Error compiling expression 'LCase (Trim (text_verb))': Unknown object or variable 'text_verb'

I can test this part myself, using the web editor. I cannot replicate the error; and I really have no idea where it could be coming from.


I tried your latest update, but still exactly the same result. Maybe I am the one doing something wrong. Could you please explain where to copy/paste your scripts exactly? The way I understood your instructions was to "show library elements" and then overwrite the default commands with your scripts.

Then I just create a verb "Use on" on the object A tab (the fourth tab from the left). Then in the assignment section I choose "require another object" and I choose object B, and I write a response: "Works on B". Then i test the game.

Maybe I am doing something wrong here?


Ah, it looks like your verb is overriding both the default use on functionality, and my version. I didn't spot that initially because I most often use the web version, where the default way to make a "use on" command is on the "Use/Give" tab.

If you want to make it a verb rather than a command, I assume there's some way to modify the script of a verb (as they seem to inherit the "defaultverb" type). So if you find the verb you've created, it would presumably have a 'script' attribute you can modify. I'm not entirely sure how you'd go about that, I'm afraid.


But I tried doing it through the use/give tab too. It doesnt work. Use on itself works as normal. But "use on" gives me the message: You cant use it.


K.V.

Hello.

To start small, here's an example game where you can use a bat by itself, or you can use it on a ball. (I did not add the custom menu stuff from above. This is just to show how the mechanics work when using USE as a feature rather than creating a USE command.)


You are in a room.
You can see a bat and a ball.

> use bat
You'll have to pick it up first.

> get it
You pick it up.

> use it
You swing the bat a few times.

> use it on the ball
You are not holding the ball.

> get the ball
You pick it up.

> use the bat on the ball
You toss the ball up in the air and hit it with the bat. It lands across the room and rolls back to you, just near your feet.


The example game's code:

``` ```

Also, just a guess:

In your USE command's script, you could try changing

if (GetBoolean(object, "use")) {

to:

if (GetBoolean(object, "feature_usegive")) {

It could be throwing an error when it checks for a Boolean under the "use" attribute when that attribute is actually a script (which yours is). (This may not work. Mrangel may be checking "use" for a reason, and I just didn't see it.)


OK, it looks like I've been making this a lot more complex than it needs to be. I didn't realise that defaultverb already did some of what I want it to do.

So, starting over. Instead of modifying the "use" and "useon" commands, it might make more sense to modify the default verb.

@KV: If I modify the script in a type, will it modify all the commands that inherit it? I would assume so, but can't easily check. If so, then it should only be necessary to change one type and one function:

From looking at the code, this should prevent verbs with "Require another object" from generating a menu, instead adding "Use A on" to the displayverbs list of everything else.

I'm not sure I've got this right, because I can't figure out where the patterns of verbs are generated. So this is partly a guess, based on looking at the existing code. I don't know how creating a "use on" verb will interact with the default use/give system, or an object's "use" alone functionality. But this version of my code shouldn't change that; it should just replace the menu for "With which object?" to a different UX.

Modified code
  <type name="defaultverb">
    <separator>[VerbObjectSeparator]</separator>
    <multiobjectmenu>[MultiObjectVerbMenu]</multiobjectmenu>
    <multiobjectdefault>[DefaultMultiObjectVerb]</multiobjectdefault>
    <multiobjectmenuempty>[NoObjectsAvailable]</multiobjectmenuempty>
    <script type="script">
      <![CDATA[
      if (not IsDefined("object2")) {
        object2 = null
      }
      
      switch (TypeOf(object, this.property)) {
        case ("script") {
          if (object2 = null) {
            do (object, this.property)
          }
          else {
            msg (this.multiobjectdefault)
          }
        }
        case ("string") {
          if (object2 = null) {
            msg (GetString(object, this.property))
          }
          else {
            msg (this.multiobjectdefault)
          }
        }
        case ("scriptdictionary") {
          if (object2 <> null) {
            HandleMultiVerb (object, this.property, object2, this.multiobjectdefault)
          }
          else {
            objectlist = ListCombine (ScopeReachableInventory(), ScopeReachableNotHeld())
            excludelist = NewObjectList()
            list add (excludelist, game.pov)
            list add (excludelist, object)
            candidates = ListExclude(RemoveSceneryObjects(objectlist), excludelist)
            if (ListCount(candidates) = 0) {
              msg (this.multiobjectmenuempty)
            }
            else {
              game.pov.multiverb = this.property
              if (not HasString (this, "verbstem")) {
                this.verbstem = this.property
                foreach (word, Split (this.separator)) {
                  this.verbstem = Replace (Trim(LCase(word)), "")
                }
              }
              game.pov.multiverbstem = this.verbstem
              game.pov.multiverbseparator = ListItem (Split (this.separator), 0)
              game.pov.multiverbobject = object
              game.pov.multiverbobjectdefault = this.multiobjectdefault
              msg (this.multiobjectmenu)
              SuppressTurnscripts()
              SetTurnTimeout (1) {
                game.pov.multiverb = null
                game.pov.multiverbseparator = null
                game.pov.multiverbobject = null
                game.pov.multiverbobjectdefault = null
              }
            }
          }
        }
        case ("null") {
          if (this.defaulttext <> null) {
            msg (this.defaulttext)
          }
          else if (this.defaulttemplate <> null) {
            msg (DynamicTemplate(this.defaulttemplate, object))
          }
          else if (this.defaultexpression <> null) {
            params = NewDictionary()
            dictionary add (params, "object", object)
            msg (Eval(this.defaultexpression, params))
          }
          else {
            error ("No verb response defined")
          }
        }
        default {
          error ("No verb response defined")
        }
      }
      ]]>
    </script>
  </type>

  <function name="GetDisplayVerbs" parameters="object" type="stringlist">
    if (Contains(game.pov, object)) {
      baselist = object.inventoryverbs
    }
    else {
      baselist = object.displayverbs
    }
    if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
      return (baselist)
    }
    else {
      if (HasAttribute (game.pov, "multiverb")) {
        if (not game.pov.multiverbobject = object) {
          newlist = NewStringList()
          list add (newlist, game.pov.multiverb + " " + GetDisplayName (game.pov.multiverbobject) + " " + game.pov.multiverbseparator)
          foreach (item, baselist) {
            list add (newlist, item)
          }
          baselist = newlist
        }
      }
      if (HasAttribute(object, "generatedverbslist")) {
        verbs = object.generatedverbslist
      }
      else {
        verbs = NewStringList()
        foreach (attr, GetAttributeNames(object, false)) {
          if (ListContains(game.verbattributes, attr)) {
            cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
            if (HasString(cmd, "displayverb")) {
              displayverb = CapFirst(cmd.displayverb)
            }
            else {
              displayverb = CapFirst(attr)
            }
            if (not ListContains(baselist, displayverb)) {
              list add (verbs, displayverb)
            }
          }
        }
        object.generatedverbslist = verbs
      }
      if (GetBoolean(object, "useindividualverblist")) {
        return (verbs)
      }
      else {
        return (ListCombine(baselist, verbs))
      }
    }
  </function>

Thanks for not giving up on this mrangel. I tested your modified script on a fresh test game and if I installed it correctly, this is what I get.
You are in a room.
You can see an A and a B.

  • use A
    Works on self.

  • use with A
    With which object?

  • usewith an a with; using B
    I don't understand your command.

So we are making some progress here, because the menu is finally gone. Now when I use "use on" or "use with" command, I get a question: "With which object?" It would be awesome if I could just click on B at this point and be done with it, but unfortunately instead when I click on B I get this weird drop down menu with a very messed up line: "usewith an A with; using".

Another problem is that when I choose this weird line, I get a response: "I don't understand your command." instead of the script completing and saying: "Works on B."

Also the B object retains "usewith an A with; using" dropdown menu even after choosing it. This could be due to the script not finishing its process. Anyway, I think you are on the right track finally.

Another thing that perhaps needs to be mentioned is that this does not work at all through the default Use/Give tab. Instead I had to create a new verb called "Use on" on the verb tab and then choose "require another object" through there.


OK; I thought it might end up something like that, but I wasn't sure how it assembles the pattern.
The extra option at the top of the menu should be "use A with".

I'm going to go back now and make a couple of changes to the script; see if that helps.

OK ... that should work. If it doesn't handle it by itself, then giving the verb a string attribute named 'verbstem' that contains the first part of the verb ("use" in this case) might help.

You might also find (I'm not sure) that your "use with" verb will be expecting a command of the form "use with A with B".
If you get an odd error message, you could try changing the verb's pattern attribute to a regular expression: ^use (with (?<object1>.+)|(?<object1>.+) (with|using) (?<object2>.+))$.

(I'm assuming you can change the pattern attribute of a verb; I'm not used to the desktop editor)


It would be awesome if I could just click on B at this point and be done with it

I was originally trying to figure out how to do that. I can see a few ways it might be possible, but they all end up working with awkward combinations of Quest script and javascript.

One of my first thoughts was to rebuild the javascript so that instead of verb buttons and menus, clicking an object or command in the sidebar just types in the command bar. So the command bar is there, but the player doesn't type in it. When the played clicks "Look at" in the command pane, it types "Look at" in the command bar. Then they click on an apple in the inventory pane, and the text in the command bar becomes "Look at apple". Maybe with something like that, it would automatically send the command when you click it; whereas for "use" it would give the player a choice of either pressing the enter button, or clicking another object to use it on.
I think I can see how that would be done, but I think it would be a more complex task that I'd end up taking some time over.


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

Support

Forums