Best way to handle enterable containers/surfaces and enterable/rideable vehicles?

I helped make a horse you could ride one time, but I don't recall how it worked.

Recently, I needed a bed you could lie in in which you could lie, and I went through a few ideas before I just added a game.pov.in_bed boolean attribute to handle everything. I have to modify the commands which require touching any objects in the room around the bed this way, though -- especially if there's one object within reach from the bed but other objects which are out of reach. I'd rather modify the take script once than add verbs doing the same thing to each individual object.

At first, the bed was a transparent room inside of the Bedroom, and I had it working pretty well -- except I couldn't figure out where "You can't reach into the Bedroom." printed from sometimes. I found one place, and I fixed it so it said "You can't reach that from the bed.", but there were other commands that still printed the default. So, I decided that was too much work and went with the attribute thing.

I'm pretty sure I could have made this work well if I knew where the code was that was printing that default response.


Somebody had some code for vehicles that simply changed the player object to the vehicle that mostly worked well, but I couldn't get that to behave like I expected it to, either.

I think that was the horse, and I ended up using attributes (like with the bed) and moving the horse to each room I was headed to before I entered said room.


When using attributes like this, I modify the way it prints the room name, adding " (in the shower)" or " (riding the war unicorn)".


The way I handle it doesn't really seem worth sharing, to be honest. Too many things have to be fine-tuned to make everything print as expected.

This is another thing I wish we had a solid solution for before Quest 6 is born and steals all our attention from Quest 5. I'm pretty sure Quest 5 will still be popular for years after 6 is released. People will still be trying to do things in Quest 5, but most of us who help out on the forum will be more inclined to focus on 6 because (A) it is pure JS and we like that, and (B) it will be new and there's sure to be bugs we'll enjoy hunting down and exterminating.

There aren't many things people have commonly requested that haven't eventually been added to Quest. I don't know that vehicles and enterable containers are worthy of being included, but I do thing there should be a good library people could easily include. Plus, I don't like the idea of adding new features to Quest 5, per se; I think we should just try to fix all the known bugs for a final stable release.


Back to this. . .

As far as vehicles are concerned:

Enterable vehicle

  • Can it be powered on and off?
    • Does it require a key (or a code, or what have you)?
    • Is it (or does it have) a light source?
  • Does it require any form of fuel (or battery charging)?
  • Is it transparent? (Does it have windows that are always see-through? Can you see things in the vehicle's location?)
    • Can the windows be opened and closed?
  • Does it have any form of a door?
    • Does the door require a key?
  • Will it operate on land, on water, under water, in the air?
  • Is it too big to take indoors?
  • Can the player reach things outside of the vehicle if the door is open (or there is no door)?
  • Is it an NPC (perhaps a robot)?
  • How many passengers will fit inside?
  • Is it also a surface? (Can things be on it as well as in it?)
  • Can another vehicle fit inside of it? (Or on it?)

Rideable/mountable vehicle

  • Can it be powered on and off?
    • Does it require a key (or a code, or what have you)?
    • Is it (or does it have) a light source?
  • Does it require any form of fuel (or battery charging) (or food)?
  • Will it operate on land, on water, under water, in the air?
  • Is it too big to take indoors?
  • Can the player reach things in the vehicle's location?
  • What happens when the player drops something while on the vehicle? Where does the item go? On the vehicle, or in the vehicle's location?
  • Is it an NPC?
  • Is it also a container (or does it have a container as an integral part)? (Can things be in it as well as on it?)

At first, the bed was a transparent room inside of the Bedroom, and I had it working pretty well -- except I couldn't figure out where "You can't reach into the Bedroom." printed from sometimes. I found one place, and I fixed it so it said "You can't reach that from the bed.", but there were other commands that still printed the default. So, I decided that was too much work and went with the attribute thing.

This is one thing that bugs me. So many of the default commands repeat the same logic for generating messages about why you can't do something. DoTake, for example, will say "The (parent) is not open." if you attempt to take any object which is visible but not reachable. You can get around this by setting the room's blockingmessage (an attribute which isn't in the editor at all), but that seems a terrible bodge.

The function should be more like:

  <function name="BlockingMessage" parameters="blocked, prefix" type="string">
    obj = GetBlockingObject(blocked)
    if (Contains (obj, game.pov)) {
      return (prefix + CapFirst (GetDisplayAlias (game.pov)) + " can't reach " + blocked.article + " from " + GetDefiniteName (game.pov.parent) + ".")
    }
    else if (HasString(obj, "blockingmessage")) {
      return (prefix + obj.blockingmessage)
    }
    else {
      return (prefix + DynamicTemplate("ObjectNotOpen", obj))
    }
  </function>

That looks pretty good.

I also remember the room being dark and the bed not caring about that at all.

I think I have some code (somewhere) from whenever I created this thread in which all the room description stuff checks for the non-transparent parent. It also adds everything in the actual parent to the scope, and the same goes for the parents in between the actual parent and the top non-transparent parent. I was at a place where I needed to add code to either allow or disallow handling (and examining) of certain things in certain places in certain scenarios, when I decided I'd just come back to all this another day. Now, I can't find that code, but I saved it somewhere, damn it.


I also remember the room being dark and the bed not caring about that at all.

Hmm… it looks like it relies on darklevel already being set. So…

bedroom state
(when the player last entered it)
darklit or unvisited
bed state
(when the player was last entered it)
darklit or unvisiteddarklit or unvisited
Player in bedObjects in bed appear:hiddenreachablevisible not reachablereachable
Objects in bedroom appear:hiddenvisible not reachable
Player in bedroomObjects in bed appear:hiddenreachable
Objects in bedroom appear:

So… if there's a lamp in the room but your bed is dark, objects that are in the bed with you will appear to be outside it. That's because they are indirect children of the bedroom, so are in its ContainsVisible; but they're not in the bed's ScopeVisible so the function treats them as being in the room.

To be honest, I think that Quest's scope functions are a mess. ScopeVisibleNotReachable gets the visible scope for the outer room, and the reachable scope for the inner room, and then loops over them both removing one list from the other. ScopeVisible, on the other hand, runs both ScopeReachable and ScopeVisibleNotReachable and then adds them back to the same list. That means you're removing one list from another, only for the calling function to join them together again.

This also shows off a problem with the darkness system – CheckDarkness only checks the darkness level for the room the player is currently in. I previously wrestled with that in the case of a door whose description says what you can see through it. But it seems to be the case for a transparent room as well.

I think it should be something like this:

<type name="defaultobject">
  <!-- Don't forget all the stuff that's already there -->

  <changedparent type="script">
    if (GetBoolean (this, "lightsource")) {
      if (IsDefined ("oldvalue")) {
        CheckDarknessForRoom (oldvalue)
      }
      CheckDarknessForRoom (this.parent)
    }
    if (this = game.pov) {
      // all the stuff that's already there
    }
  </changedparent>

  <changedlightsource type="script">
    if (Equal (this.lightstrength, "strong")) {
      if (this.lightsource) {
        this.parent.darklevel = false
      }
      else {
        CheckDarknessForRoom (this.parent)
      }
    }
  </changedlightsource>

  <changedlightstrength type="script">
    if (GetBoolean (this, "lightsource")) {
      if (this.lightstrength = "strong") {
        this.parent.darklevel = false
      }
      else {
        CheckDarknessForRoom (this.parent)
      }
    }
  </changedlightstrength>

  <changeddarklevel>
    // This should only be changed by CheckDarknessForRoom, or by a light in the room turning on
    // so we can assume that either it's lit, or no child objects contain a light source
    if (CanSeeThrough (this) and HasObject (this, "parent")) {
      this.parent.darklevel = this.darklevel
    }
    foreach (obj, GetDirectChildren (this)) {
      if (CanSeeThrough (obj)) {
        obj.darklevel = this.darklevel
      }
    }
  </changeddarklevel>
</type>

<function name="CheckDarknessForRoom" parameters="room" type="boolean">
  isdark = GetBoolean (room, "dark")
  if (isdark and CanSeeThrough (room) and HasObject (room, "parent")) {
    isdark = CheckDarknessForRoom (room.parent)
  }
  if (isdark) {
    searchlist = GetDirectChildren (room)
    while (isdark and ListCount (searchlist) > 0) {
      obj = ListItem (searchlist, 0)
      list remove (searchlist, obj)
      if (obj.visible) {
        if (GetBoolean (obj, "lightsource") and Equal (obj.lightstrength, "strong")) {
          isdark = false
        }
        else if (CanSeeThrough (obj)) {
          searchlist = ListCombine (searchlist, GetDirectChildren (obj))
        }
      }
    }
  }
  // Still using the counterintuitive name "darklevel", rather than a more sensible "isdark" or "darknow"
  // Really, the inverse boolean "containslightsource" would make more sense to me
  // But I'll go with what existing scripts expect
  room.darklevel = isdark
  return (isdark)
</function>

<function name="CheckDarkness" type="boolean">
  return (CheckDarknessForRoom (game.pov.parent))
</function>

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

Support

Forums