How do you make an object both a container and a surface at the same time?

How do you make an object both a container and a surface at the same time? I know I can not be the only one who has asked this question, yet going through several pages of searching and wording the search differently a few times failed to show me a thread with that, at least by title and description, as well as looking at general guides of containers and surfaces. Either that or I am just so tired I am simply missing it. I've also tried various things for the different containers and objects in game, but I can not get it to work. Maybe it is that I am just so tired and the answer is literally staring me in the face, but I got up extra early just to work on this... (this game is haunting me in my sleep... o_0 )

Regardless I want to have a night stand be the parent and the alarm clock be the child (if there is another way that is fine and maybe I am wording or thinking of this all wrong). You can not see the alarm clock until you look at the nightstand however. Now I believe a "surface" would work for that. But I also want this nightstand to have a drawer, meaning it is a container and inside will be "something". If I make it a container however, the alarm clock will not be seen simply by "looking" at the night stand first, which defeats the purpose. I want lots of objects around, on and in containers, but not to overload the player with descriptions when they first walk into a room, only "after" they have "looked" at something directly and of course also opened them.

I am hoping I am just over looking something and it is an easy fix, opposed to some kind of long intricate complicated code. But I will take what I can get and be thankful for it and my bloodshot eyes will thank you as well! ^^! It should be noted that there will be a lot of objects with items in my game that will need this feature. Almost every room will have something similar, maybe multiple surfaces/container hybrids. Players will be exploring and thus looking around a lot and I need containers or surfaces or whatever to act the way I need them to. It wont be a simple matter of making the nightstand not have a drawer or for a dresser with drawers not to have anything invisible on top of it, until it is looked at.

I think I made this coherent enough. 0_0 Thank you. ^^!


You could...

  1. Set the nightstand to a surface container and place the alarm clock inside the nightstand(surface).

  2. Nightstand Surface container setting
    2.1 Contents prefix: with
    2.2 Activate "Hide children until object is looked at" and
    2.3 activate "List children when object is looked at or opened"
    2.4 List prefix: You can see

  3. Create a drawer and set it to an openable/closeable container and place it inside the nightstand(surface).


A surface shows its child objects when you look at it. A container shows its child objects when it is open.

The problem here is that an object can only have one list of child objects. Quest doesn't distinguish between objects "in" and "on" something else. In order to have both a surface and a container, you would need to put the items in separate locations somehow.

I can think of ways to do this, but it would involve quite a bit of scripting and some modification of the core functions. The most obvious would be making the nightstand a surface and putting a "drawer" container in it. So the drawer is a separate object.

You could make the drawer scenery, so that it doesn't appear separately in the room description or the objects pane; but this is still a little inelegant. This is where the scripting comes in, to make it look better. I suspect that for this to work naturally, you would want to modify the core functions TryOpenClose (or possible AddToResolvedNames) so that "open nightstand" will open the drawer, as well as FormatObjectList and ListObjectContents so that the contents of both the container and the surface are listed when it is looked at.


My model would be giving the nightstand a realcontainer attribute which points to the drawer object.

In this case, the functions would be:

  <function name="TryOpenClose" parameters="doopen, object">
    if (doopen) {
      action = "open"
      scriptaction = "openscript"
    }
    else {
      action = "close"
      scriptaction = "closescript"
    }
    if (HasObject (object, "realcontainer")) {
      object = object.realcontainer
    }

    if (not ListContains(ScopeReachable(), object)) {
      msg (BlockingMessage(object, ""))
    }
    else {
      found = false

      if (GetBoolean(object, action)) {
        if (doopen) {
          if (object.isopen) {
            msg (DynamicTemplate("AlreadyOpen", object))
          }
          else {
            if (HasScript(object, scriptaction)) {
              do (object, scriptaction)
            }
            else {
              OpenObject (object)
            }
          }
        }
        else {
          if (not object.isopen) {
            msg (DynamicTemplate("AlreadyClosed", object))
          }
          else {
            if (HasScript(object, scriptaction)) {
              do (object, scriptaction)
            }
            else {
              CloseObject (object)
            }
          }
        }
      }
      else {
        if (doopen) {
          msg (DynamicTemplate("CantOpen", object))
        }
        else {
          msg (DynamicTemplate("CantClose", object))
        }
      }
    }
  </function>

  <function name="ListObjectContents" parameters="object">
    if ((GetBoolean(object, "isopen") or GetBoolean(object, "transparent")) and GetBoolean(object, "listchildren")) {
      if (GetBoolean(object, "hidechildren")) {
        object.hidechildren = false
      }
      if (HasString(object, "listchildrenprefix")) {
        listprefix = object.listchildrenprefix
      }
      else {
        listprefix = DynamicTemplate("ObjectContains", object)
      }
      list = FormatObjectList(listprefix, object, Template("And"), ".")
      if (not list = "") {
        msg (list)
      }
    }
    if (HasObject (object, "realcontainer")) {
      ListObjectContents (object.realcontainer)
    }
  </function>

  <function name="FormatObjectList" type="string" parameters="preList, parent, preFinal, postList">
    <![CDATA[
    result = ""
    count = 0
    list = RemoveSceneryObjects(GetDirectChildren(parent))
    if (CheckDarkness()) {
      list = RemoveDarkObjects(list)
    }
    listLength = ListCount(list)
    foreach (item, list) {
      if (LengthOf(result) = 0) result = preList + " "
      result = result + GetDisplayNameLink(item, "object")
      contentslist = NewStringList()
      if (CanSeeThrough(item)) {
        list add (contentslist, FormatObjectList(item.contentsprefix, item, preFinal, ""))
      }
      if (HasObject (item, "realcontainer")) {
        if (CanSeeThrough (item.realcontainer)) {
          list add (contentslist, FormatObjectList(item.realcontainer.contentsprefix, item.realcontainer, preFinal, ""))
        }
      }
      while (ListContains (contentslist, "")) {
        list remove (contentslist, "")
      }
      if (ListCount (contentslist) > 0) {
        result = "(" + Join (contentslist, ". ") + ")"
      }
      count = count + 1
      if (count = listLength - 1) {
        result = result + " " + preFinal + " "
      }
      else if (count < listLength) {
        result = result + ", "
      }
      else {
        result = result + postList
      }
    }
    return (result)
  ]]>
  </function>

With this, you could put a drawer (scenery, container) inside a nightstand (surface), and set the nightstand's realcontainer attribute to the drawer.
If you set the nightstand's listchildrenprefix to something like {either ListContains(game.pov.currentcommandresolvedobjects, drawer):It:The {object:drawer}} contains
then you could get game output looking like this:

You are in your bedroom.
You can see: A bed, a nightstand (on which there is an alarm clock. In the drawer is a pen and a banana), and a large walrus.

==> look nightstand
It's the same battered nightstand that's been there for years.
On it is an alarm clock.
The drawer contains a pen and a banana.

==> look drawer
The drawer sticks out of the front of the nightstand, and is made of the same wood. It is currently open.
It contains a pen and a banana.

The simplest way for me, would be to make the nightstand a surface with your clock on it, then make the drawer a completely separate object in the room, as a closed container. Have the drawer be invisible until the nightstand is examined.

So the drawer and the nightstand are two separate objects, but in the game it feels like they're one.

This way you can put things ON the nightstand, and IN the drawer.


That's a thought. If you wanted them to act like a single object, you'd also need to modify the put command. Possibly make it ^(put|insert|place|drop) (?<object1>.*) (?<text_preposition>on|in)(?:to| to|) (?<object2>.*)$, and then if there is a realcontainer, look at the preposition to choose the destination object.


I've been through this problem! I wanted to stick with built-in Quest features as far as possible but couldn't in situations like this. The approach I now take take is (using your example):

  1. Make the night stand a surface with both the alarm clock and drawer as objects on it (so it looks okay with game panes)
  2. For looking at the night stand, tick the option to hide children until looked at but don't tick the option to list the children automatically...I write the code for that in the look script.
  3. For opening and closing the drawer/night stand use open and close commands to filter out the cases you want to handle and pass on others. For example, the form for open would be:
  <command name="new_open">
    <pattern>open #object#</pattern>
    <script>
      switch (object) {
        case (drawer, night stand) {
          ...open drawer code...
        }
        default {
          TryOpenClose (true, object)
        }
      }
    </script>
  </command>

Sorry for the late reply. I had been having some major troubles with my browsers at home and even more recently CAPTCHA for some reason. Went to the library with my laptop to try a work around and it works perfectly, so I am assuming it is my connection instead. Hopefully I can get it resolved before people go home for their turkey dinners! ^^!

Regardless I appreciate everyone's suggestions. However I am not sure if I am suppose to type those/that code in or not somewhere or some how use Quest's scripting commands. If I have to put the code in, where exactly do I put it at? Am I doing it for each and every container/surface hybrid, only for a certain type of container/surface hybrid, as in once I make it for the nightstand command, it works for all nightstands everywhere or it is a one time thing in the main "game" section part of my game and will work with everything? I am not good at coding. Thank you all for your patience.


If you mean my code, you would need to use the code that I provided to replace some of the core functions. I know that in the desktop editor you can view and modify the core functions; but they're hidden by default. I'm not sure of the details of how to see and edit them, because I've only used the web editor.

I suspect the easiest method would be opening the game in full code view, and inserting all the code I gave you immediately before the </asl> line at the end. But make a backup copy first.

DavyB's suggestion will probably be easier (and will be possible in the web editor). To implement that you create a new command named "new_open" with the pattern "open #object#". And then in the script box, open code view and paste in the body:

      switch (object) {
        case (night stand) {
          TryOpenClose (true, drawer)
        }
        default {
          TryOpenClose (true, object)
        }
      }

this works on an object named "night stand" and an object named "drawer". If you want to make more than one, you would need to duplicate the 3 lines starting with case for each pair of objects, changing the names. You would also need a similar script for a new close command (changing "open" to "close", and "true" to "false"). And you would probably need to write a script for the nightstand's "look at", which lists the objects in the drawer as well.

I'd suggest this method if you have only one object, or if you have to use the web editor.
My method is designed so that it should work for any number of objects without writing extra code, even if that makes that first block of code a little more complex.


Jerrid, if it would help, I can send you a small game with the code I'm suggesting? If so, please send me a private message with an email address I can use.


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

Support

Forums