Placing things in scope [SOLVED]

Before you read anything in this thread, you should familiarize yourself with the actual information.

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

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

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

http://docs.textadventures.co.uk/quest/ui-callback.html

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


I am attempting to wrap my mind around things which can be accomplished with scope.

...but I'm not doing a very good job.


Anyone have any good examples?


In Quest 5.7.2 you can just put world in the Scope field of the command.


K.V., why do you talk to yourself?


XD I read this post when it first went up, and totally missed that


POP QUIZ

Why do I talk to myself?

A. I am a crazy person.

B. I can come back to this thread months from now (after I've forgotten the details), and I'll be proud of myself for leaving good notes.

C. I was just trying to help you guys and gals.

D. No one else listens to me.

E. All of the above.


E obviously. Mostly A, but definitely E.


Absolutely right!

You get a star for the day!!!


F. You are one of the few people smart enough to understand yourself. : )


D.

Definitely D. With a little A sprinkled in.


I still can't do very much with scope...

(To see what brought me back to this, click here: http://textadventures.co.uk/forum/quest/topic/egoik0xgukucht6dikjtrw/make-npc-companion-carry-objects#2295ed97-4720-49e5-8e5f-26448b8132c4)


The Kindergarten Stuff

I can make a game with a clock object, which does not exist in the game world (meaning it isn't in a room).

I then enable 'Advanced features' on the game object, and put this in the Scope backdrop thingy:

image


Now I can interact with the clock, no matter what.

image

That clock is IN SCOPE!


Example game's code:

<!--Saved by Quest 5.7.6623.30864-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Scoping Things Out">
    <gameid>0d1663e9-87f0-47b1-8ca1-49cb3cc40ec6</gameid>
    <version>0.0.1</version>
    <firstpublished>2018</firstpublished>
    <scopebackdrop type="script">
      list add (items, clockobject)
    </scopebackdrop>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <object name="clock_object">
    <inherit name="editor_object" />
    <alias>clock</alias>
    <look type="script">
      msg ("You check the time.")
      // I have ClockLib.aslx included in all of my games in my Core.aslx file.
      // This runs the 'clock' command's script, which just prints the clock message.
      invoke (clock.script)
    </look>
    <listalias>clock (which reads {game.clock})</listalias>
    <displayverbs type="stringlist">
      <value>Look at</value>
    </displayverbs>
  </object>
  <function name="GetListDisplayAlias" parameters="obj" type="string">
    if (HasString(obj, "listalias")) {
      result = ProcessText(obj.listalias)
    }
    else {
      result = GetDisplayAlias(obj)
    }
    return (result)
  </function>
</asl>

First Grade Stuff

In a totally different example, I can place the direct children of Fred (who is carrying things) in scope in a command.

In this example, if you ASK FRED FOR [SOMETHING], the direct children of Fred are placed in scope before that command runs the main script, allowing you to interact with them.

(You can enter FRED, INVENTORY to get a list of the things he's carrying in this example. (It was a very rough draft, and I didn't write the code which listed his belongings in any other scripts, such as LOOK AT (or just in his alias string).))

Example game's code:

<!--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>

After Dropping Out

That's right. I dropped of scope school (although it was actually more of a "learn by trial and error" thing).

I decided it would be much easier to alter the ScopeReachableNotHeldForRoom() function like so:

    <function name="ScopeReachableNotHeldForRoom" parameters="room" type="objectlist">
      <![CDATA[
    result = NewObjectList()
    foreach (obj, GetAllChildObjects(room)) {
      if (ContainsReachable(room, obj) and obj <> game.pov and not Contains(game.pov, obj)) {
        list add (result, obj)
      }
    }
    if (HasScript(game, "scopebackdrop")) {
      dict = NewDictionary()
      dictionary add (dict, "items", result)
      do (game, "scopebackdrop", dict)
    }
    else if (HasAttribute(game, "scopebackdrop")) {
      result = ListCombine (game.scopebackdrop, result)
      dict = NewDictionary()
      dictionary add (dict, "items", result)
    }
    return (result)
  ]]>
    </function>


Then, I added these two functions:

  <function name="AddToScope" parameters="obj">
    if (not ListContains(game.scopebackdrop,obj)) {
      list add (game.scopebackdrop, obj)
    }
  </function>
  <function name="RemoveFromScope" parameters="obj">
    if (ListContains(game.scopebackdrop,obj)) {
      list remove (game.scopebackdrop, obj)
    }
  </function>

Now I can place things in scope (and vice-versa) at will.

AddToScope(object)

RemoveFromScope(object)

The same thing may be possible with the default functions (using variables and attributes, as well as object names), but I tried, and tried, and I could never get much of anything to work for me. (This is probably due to ignorance on my part, but I digress.)


I've added those functions to the example game with the clock.

There are two test commands:

test1 adds an object named "dummy" to scope, and test2 removes it.


NOTE: You can still make an object scenery if you don't want it displayed in the Places and Objects pane, but you would still like the player to be able to interact with it.

Revised example game with clock:

<!--Saved by Quest 5.7.6623.30864-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Scoping Things Out">
    <gameid>0d1663e9-87f0-47b1-8ca1-49cb3cc40ec6</gameid>
    <version>0.0.1</version>
    <firstpublished>2018</firstpublished>
    <scopebackdrop type="objectlist">clock_object</scopebackdrop>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <description><![CDATA[{command:test1}<br/><br/>{command:test2}]]></description>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <object name="clock_object">
    <inherit name="editor_object" />
    <alias>clock</alias>
    <listalias>clock (which reads {game.clock})</listalias>
    <displayverbs type="stringlist">
      <value>Look at</value>
    </displayverbs>
    <look type="script">
      msg ("You check the time.")
      // I have ClockLib.aslx included in all of my games in my Core.aslx file.
      // This runs the 'clock' command's script, which just prints the clock message.
      invoke (clock.script)
    </look>
  </object>
  <object name="dummy">
    <inherit name="editor_object" />
  </object>
  <command name="scope_test1_cmd">
    <pattern>test1</pattern>
    <script>
      AddToScope (dummy)
    </script>
  </command>
  <command name="scope_test2_cmd">
    <pattern>test2</pattern>
    <script>
      RemoveFromScope (dummy)
    </script>
  </command>
  <function name="ScopeReachableNotHeldForRoom" parameters="room" type="objectlist"><![CDATA[
    result = NewObjectList()
    foreach (obj, GetAllChildObjects(room)) {
      if (ContainsReachable(room, obj) and obj <> game.pov and not Contains(game.pov, obj)) {
        list add (result, obj)
      }
    }
    if (HasAttribute(game, "scopebackdrop")) {
      result = ListCombine (game.scopebackdrop, result)
      dict = NewDictionary()
      dictionary add (dict, "items", result)
    }
    return (result)
  ]]></function>
  <function name="GetListDisplayAlias" parameters="obj" type="string">
    if (HasString(obj, "listalias")) {
      result = ProcessText(obj.listalias)
    }
    else {
      result = GetDisplayAlias(obj)
    }
    return (result)
  </function>
  <function name="AddToScope" parameters="obj">
    if (not ListContains(game.scopebackdrop,obj)) {
      list add (game.scopebackdrop, obj)
    }
  </function>
  <function name="RemoveFromScope" parameters="obj">
    if (ListContains(game.scopebackdrop,obj)) {
      list remove (game.scopebackdrop, obj)
    }
  </function>
</asl>

I'm trying to learn how to work with what is built into Quest instead of using my functions I modded and/or created (which you can find at the end of my previous post in this thread).

I've read this numerous times:

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

...but I still can't figure out how to make this work for the life of me:

EDIT: See the next post for the correct version of this script.

  <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>object2=object1</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>

giver is an attribute which only exists on NPCs who will give you things.

Each "giver" has a script dictionary called givedict, with an entry for each item they may be carrying.


PS

Fred is the only NPC in this game.

I even changed object2=object1 to object2=Fred, but it didn't work.

The only thing that I've tried that worked (without altering any functions) was putting Fred in the scope field.


UPDATE

mrangel posted code that made this work for this command without altering any built-in functions.

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

  <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>

I am very happy now that I have seen this work!!!


I still prefer my method, where I alter one function, add two small functions, then change game.scopebackdrop to an object list. This way, I can place whatever I please in or out of scope in any given situation before any command is entered (or before anything else happens, for that matter). Thus negating the need to doctor numerous commands, which would to lead to me forgetting to change the scope of a command or two, which would mean my game had bugs.


I'm sure Quest's default scripts are better than the hack I came up with in most instances, and it works for me either way now (thanks to mrangel and The Pixie), so I'm calling this one [SOLVED].


I still prefer my method, where I alter one function, add two small functions, then change game.scopebackdrop to an object list.

That has its uses; but as far as I can tell, it only works if you want to change the scope for all commands whose scope is currently room.
changecommandscope is designed for the circumstances where you want a specific command (like this one) to have an unusual scope.

I even changed object2=object1 to object2=Fred, but it didn't work.

You'd want the scope to be object1=room|object2=Fred in that case. It doesn't check for the presence of an = in the string unless there's also a |, I believe.


I still prefer my method, where I alter one function, add two small functions, then change game.scopebackdrop to an object list.

That has its uses; but as far as I can tell, it only works if you want to change the scope for all commands whose scope is currently room.

Sorry, I'm scatter-brained, and my posts reflect that.

I meant this:

    <function name="ScopeReachableNotHeldForRoom" parameters="room" type="objectlist">
      <![CDATA[
    result = NewObjectList()
    foreach (obj, GetAllChildObjects(room)) {
      if (ContainsReachable(room, obj) and obj <> game.pov and not Contains(game.pov, obj)) {
        list add (result, obj)
      }
    }
    // If the game is set up the normal way, where scopebackdrop is a script attribute
    if (HasScript(game, "scopebackdrop")) {
      dict = NewDictionary()
      dictionary add (dict, "items", result)
      do (game, "scopebackdrop", dict)
    }
     // Else, if the game is set up my new way, where scopebackdrop is an object list
    else if (HasAttribute(game, "scopebackdrop")) {
      result = ListCombine (game.scopebackdrop, result)
      dict = NewDictionary()
      dictionary add (dict, "items", result)
    }
    return (result)
  ]]>
    </function>
  <function name="AddToScope" parameters="obj">
    if (not HasScript(game, "scopebackdrop")) {
      if (not ListContains(game.scopebackdrop,obj)) {
        list add (game.scopebackdrop, obj)
      }
    }
  </function>
<!--  
  This will only remove things from scopebackdrop!  
  Anything actually in scope will not be effected.
 -->
  <function name="RemoveFromScope" parameters="obj">
    if (not HasScript(game, "scopebackdrop")) {
      if (ListContains(game.scopebackdrop,obj)) {
        list remove (game.scopebackdrop, obj)
      }
    }
  </function>

The room variable will (or should) always be game.pov.parent, since the ScopeReachableNotHeldForRoom() function is being called internally. (I never directly call that function, at any rate.)


One issue with my setup is you'd need a script to place an object in (or out of) scope BEFORE the player enters a command, but, as long as I use a room enter (and/or exit) script, possibly a turn script, this solves a lot of scenery object problems in regards to all available commands.

Okay... I say "problems", but that may be poor wording.

I mean I don't like to clone (or create duplicate) scenery objects. I also don't like to move objects around. (I'm thinking mainly of doors in games where rooms move around.)


I might also wish to change the items in the scopebackdropscript around at any given point.

The way it's set up by default, we can't add or remove items from the existing script. We can replace the script, or we can remove it, but that's it.

(I actually found a way to retrieve the items from the existing scopebackdrop script by creating a script with its own "items" list, then running the scopebackdrop script from within the newly created script, then copying that items list to a newly created list: game.backdrop_items. Then, I added my object to that list, and, finally, I replaced the scopebackdrop script with a new one: list add(items,game.backdrop_items). That was a bunch of stuff going on, especially considering I've neglected to mention all the code checking to see if things actually existed, and... blah. I decided it was much easier to switch over to the method at the very beginning of this post!)

I know there are many ways any given scenario could be handled, but I wanted to get a handle on controlling the scope to handle a few things, and I wanted it to effect all available commands sometimes, but only specific commands other times. (The latter is where I will use the newly added scope features, once I've learned how to do so.)


In Inform (and I know I can get Quest to do whatever Inform can do; once I acquire the know-how), I can put a tree in my front yard and make it so you can see it from the street like so:

After deciding the scope of the player when location is The Street:
	place tree in scope.

If I wanted to add the house (or anything else), it would be:

After deciding the scope of the player when location is The Street:
	place tree in scope;
        place house in scope.

Now, I can pretty much cover that particular scenario in Quest with a before enter script on the street:

AddToScope(tree)
AddToScope(house)

Then, the exit script:

RemoveFromScope(tree)
RemoveFromScope(house)

WITH THE DEFAULT SETUP:

If we could put things like Ralph.parent as the scope (which we probably can, and I just don't know that), we could keep him in scope, wherever he may roam.

(One problem with this is that it places everything in a room in scope. We can't just place one object in scope (I don't think). That happens to be what we want here, but it is not always the case.)

Let's say we have a pair of spy glasses, complete with audio and video features.

Ralph has a pair, as well.

When you don your glasses, you wish to see (or examine) everything in whatever room Ralph is in.

You also want to be able to enter commands like, "RALPH, GET THE PINK PANTHER", and have Ralph get the diamond without having to use #text# in your command pattern.

In fact, you want to be able to enter ANY command to interact with things you can "see", without Quest saying, "I can't see that (the thing I just mentioned)."


I was thinking maybe something like this would work with the default setup, but you can't use things like Ralph.parent:

if (whatever){
  foreach(cmd,AllCommands()){
    if(HasString(cmd,"scope")){
      cmd.scope = cmd.scope + ",Ralph.parent"
    }
  }
}

So, I ended up with this:

To enable:

    game.pov.parent.ralph_parent = Ralph.parent
    foreach (cmd, AllCommands()) {
      if (not cmd.name = "go") {
        if (HasString(cmd,"scope")) {
          cmd.scope_bak = cmd.scope
          cmd.scope = cmd.scope + ";ralph_parent"
        }
        else {
          cmd.scope_bak = ""
          cmd.scope = "ralph_parent"
        }
      }
    }

To disable::

      foreach (cmd, AllCommands()) {
        if (HasString(cmd,"scope")) {
          cmd.scope = cmd.scope_bak
        }
      }
      this.ralph_parent = null

You'd want the scope to be object1=room|object2=Fred in that case. It doesn't check for the presence of an = in the string unless there's also a |, I believe.

Aha!

That's a tasty nugget, if I ever saw one!


All in all, I'm thinking there are times when I'd need to use the scope attribute of a command and other times when I'd want to add or remove items from the scopebackdrop "list".

All I've really done is change the scopebackdrop attribute to an object list which can be very easily modified.

Conversely, mrangel has written some awesome code which allows us to do all sorts of things with scope of a specific command. I'm just not smart enough to be able to work it.


Just for the record, I am not on the same level as mrangel, Pixie, or Pertex. (I'm sure I've left someone out. Sorry!)

Those guys are playing chess. I'm just playing checkers.

When I drone on and on about things (i.e., scope), it's either because:

A. Whoever I'm corresponding with isn't understanding what I'm trying to accomplish (and/or why).

B. I believe A to be the case when it isn't, and I'm just being dumb silly.


I have a question about scope (I've read and read and read about it, I get the point of scopes, I understand the commands but still I can't put them to use.)

I have a book case.

As it is now I have to:

  1. Open it
  2. Type 'look', to see what's inside.

I want to:

  1. Open it and get the content listed.

I'm about to pull my hair out. And my beard.
Edith: And my head. GAAAAH!


And I apologize for hijacking a perfectly informative thread and making it about me and my total inability to get things done.


@CheeseMyBaby

On the "container" tab, there is an option "List contents when object is looked at or opened".

Or if you want a little more control over the format of the message, you could use the 'script when the object is opened':

  msg (FormatObjectList("You manage to open it, and inside you see ", this, ", and", "nothing but dust"))

@mrangel
I had picked open/closeable instead of container. Sheeeeeeeeeeeeeeez!

Thank you!


those options in the GUI/Editor, are built-in Object Types, which you can read about here (scroll down to the bottom/last section):

http://docs.textadventures.co.uk/quest/elements/object.html

you used the 'openable' Object Type / Inherited Attribute, which is used for 'door' applications, as opposed to a openable/closable/lockable 'container' applications

(though they're connected to/with a lot of other code, so there's a lot more issues going on with using them, the documentation doesn't cover all this interwoven-ness and other attributes that they have)


(a cool/sneaky idea: having a 'door' Object be a container: the door has a secret compartment, hehe. Few people would ever think of a door having something hidden within it, hehe)


Very true hege!


HK is quite clever/creative in hiding things/spaces, HK grins toothily :D

If HK hid something, no one could ever find it, muwhahaha!


Could you please hide my inability to code then HK? =)


Log in to post a reply.

Support

Forums