[Reality] Design Flaws in Twisted Reality

Glyph Lefkowitz glyph@twistedmatrix.com
Sat, 8 Jan 2000 20:14:55 -0500 (EST)


Several design flaws in the Twisted Reality engine have come to my
attention: most of them relate to the way that `special' properties of
containers are handled.

The list of properties we have now -- "broadcast", "visible", and
"operable" -- are hardly intuitive, and don't fill the roles that they
were originally designed to.

First is the problem of the distinction between 'in' and 'on'.  In one of
Tenth's creations for the demo, we're noticing this the most strongly.  If
you're inside the guymelf, you shouldn't be visible from the room.  You
*should* be visible from the room if the guymelf is holding you in its
hand, or if you're sitting on it, for example.  The difference is between
whether or not the guymelf is 'open' or 'closed', really.  (The code that
makes your focus be outside it is custom.)

There are a few possible ways to take care of this.  One is to make every
object which operates in this manner be *two* objects, its 'inside' and
its 'outside'.  This seemed like somewhat of a hack to me.

The other is to maintain two lists of items -- one for the things 'on' a
container and one for the things 'in' it.  This lends itself to a new set
of booleans...

Rather than 'operable' and 'visible', we'd have open/closed and
transparent/opaque.  Much more intuitive syntax, I think :-) and better
symantics as well.  Since certain objects make their contents apparent
when they're open (cardboard boxes, glasses, mugs, bowls) and others don't
(plastic bags, wine bottles, clothing), we'd have to keep the 'broadcast'
boolean, but it's meaning would change slightly (places where previously
"is this thing's location broadcast?" was checked, would now check "is
this ON the object where it's located OR is the location open?")

And now on to James's pet peeve:

/**
 * This changes the "broadcast" status of this Location. (whether or
 * not objects are visible from the room)
 * Should really use properties, not a dedicated bit.
 * This is going away as soon as Glyph fixes it. :)
 */

public void setBroadcast(boolean inBroadcast)
...

When he says 'this is going away' what he means is that this should be a
boolean property on the Location, set in the same way all generic boolean
properties are set, not a hard-coded variable.

First let me describe why I did this as a special variable / function in
the first place.  It can be easily summed up in one word: updating.  Since
the original implementation of this had it adding to various lists (the
Location's inventory list, the Location's synonyms list, the Location's
place's synonyms list, etc) whether or not it got added or removed was
pretty important.

There are a lot of problems with this, even ignoring elegance of the code
involved.  Most notably, it is impossible to do perspective invisibility
-- the 'component'ness of an object is not perspective dependant (either
an object is a part of another object or it isn't: there isn't really a
POV on that one).

The most obvious way to fix this is just to maintain ALL of these lists
ALL of the time: I.E. assume that each Location is as promiscuous as
possible (open, broadcast, transparent) , then worry about these
properties when locating, moving (by 'moving' I mean displaying
notification messages that things are moving...) , or enumerating objects.

There is one problem with this.  Keeping in mind that dynamic properties
are calculated on the fly when you request them, that means that you not
only have to check what a property *is right now*, you have to remember
what it *was* the last time you checked.  For example, if you drop an
apple on the ground, and cast purple-people-invisibility on it (which
makes eveyone with purple hair unable to see it) and then someone with
purple hair walks into the room, and you de-invisible it ... they won't be
able to tell, unless you include in your verb an event which makes
everyone in the room re-request the item list (say by resetting their
focus). If you pick it up, it'll look very odd indeed.

Then there's the additional problem of inheritance.  We don't really want
open, closed, et. al. to be inherited from superclasses -- if we *do*
allow them to be inherited, then we need some way of alerting subclasses
that the superclasses have changed.  This is a minor issue, though, I
guess, since you really ought not to be messing with superclasses too
much...

So let's say we wanted to design the following puzzle:

If you're in the crypt at midnight (well, okay, between the hours of
12:00AM and 1:00AM), the spectre will appear to you and give you the Book
of the Names of the Dead.  Of course, you'll only be able to see him if
you're holding one of the Green Jewels.

The spectre is always there between 12 and 1.  He is just invisible: which
means you can't see him or interact with him, but he can see you -- unless
you're holding a green jewel.  Then you can ask him for stuff.

Now on to some players trying to solve this...

Jethro and Bob are chilling out in the crypt at midnight -- Jethro has a
green jewel, and Bob forgot his.  The Spectre enters, and Jethro sees him,
but Bob doesn't.  The spectre asks Jethro to lift one of the large stone
tiles that composes the floor. Being a bit of a puny guy, Jethro can't
pick it up.  So he asks Bob to.  Bob says he doesn't believe the spectre
is really there, so he asks Jethro for the green jewel.  Jethro gives it
to him.

Back to the technical side of things for a moment.  The Spectre's
'visible' bit really has to be a dynamic property in order to do this
correctly -- only people carrying green jewels should be seeing this.
Problem is, the spectre has to appear and disappear correctly!  When Bob
gets the jewel, there shouldn't have to be any additional code to make the
Spectre vanish from Jethro's view and appear in Bob's (assuming they're
both focused on the room).

How do we notice this?  It's a calculated value, and we don't really want
to be constantly scanning it to see if it changes.  It's not clear when it
has to be re-requested (at least to me) since the /engine/ doesn't know
that the movement of green jewels is significant; only the dynamic
property on the Spectre really cares about that.

On a COMPLETELY unrelated note, I also feel that the 'portal' model of
exits is somewhat kludgy and not too flexible.  To resolve this issue, I
think I'm going to try to remove the 'room' and 'portal' classes entirely,
and do something more along the lines of 'portal' properties on Locations.

Again, this gets into some interesting stuff with dynamic properties.
Where does the list of exits get calculated?  If a "dynamic" exit goes
away, how is the player notified?

At this point, I'm not really sure what to do about this, and given that
all of these issues are subtly interconnected, I would prefer not to
change *anything* until I have a clear plan of action.  I will continue
considering this stuff, but I've tried to present my thoughts so far so
somebody else who knows a little about the engine (or at least using it)
could help out.

I also know that this message may not have been entirely coherent, as it
is mostly a "brain dump" of my thoughts so far.  Requests for
clarification will be promptly responded to. (well, as promptly as
possible ^_^) Thanks, everybody.

----
The Tao is like a glob pattern:             It is masked but always present.
used but never used up.                     I don't know who built to it.
It is like the extern void:                 It came before the first kernel.
filled with infinite possibilities.         [glyph@twistedmatrix.com]