Objects Which Are Containers And Surfaces

10 views
Skip to first unread message

Kevin Forchione

unread,
Jun 22, 1999, 3:00:00 AM6/22/99
to
The Idea Behind Multiform (And Peak At Version 2.0)
===================================================

The concept of a surface is implemented as a special kind of
containment. Objects which have surfaces on which other objects
may sit are actually containers with an additional property of
"surfaceness".
...P. David Lebling, Zork and the Future

I once read a posting in which Andrew Plotkin, I believe, was explaining
to a newbie why an object couldn't be both a supporter and a container.
He was speaking of Inform, of course. TADS doesn't have supporters, but
the surface class is entirely equivalent. The reason had to do with the two
objects not really being distinct, except in the way they display the
elements
of their contents. As you know, TADS has exactly the same limitation.

The contents attribute is a fundamental property of an object, and the way
it
is utilized by both the compiler and the system libraries made it an
integral
and unalterable fact of life. But for an object to have either an inside, or
an
outside, but not both, seems unrealistic given the simulation nature of
object-oriented programming.

*** An object cannot be both a surface and a container ***

But let's stand the problem on it's head. Text-based games are, after all,
tricks of strings, lists, arrays, databases, etc. Isn't it more realistic to
suppose
an object's contents are either 'on the inside' or 'on the outside', but not
both
at the same time? That then reduces the matter down to a single boolean.

*** An object cannot simultaneously be both inside and outside of
another object ***

The following is a discussion of the multiform.t concept and a look at the
upcoming release 2.0

======================================================
A Single Boolean Attribute
-------------------------------------

multiform.t uses the following attribute to distinguish whether an object is
'on the surface' or 'inside'

thing.isOnSurface = nil

This approach reduces the problem to one of separating out elements of
the contents list, and leaves an author with four areas to address:

a. accesssibility rules
b. command execution
c. listing functions
d. the multiform class

==================================================
Accessibility Rules
--------------------------

Some time back I documented ADV.T's basic accessibility rules
regarding visibility and reachability, developing an alternative
scoping system, which had the great advantage of being a single,
called function. The way it works, and the way scoping works in TADS
general, is what I've called a 'bubble-up/filter-down' mechanism.

To discover the scope of an object, you first 'bubble-up' to some
high-level location (a scope ceiling). For ADV.T this is usually a
room location. You next begin a 'filter-down' process which builds
a list of all valid objects for a particular type of scope (visibility, or
reachability).

The filter is the determination of whether or not the contents of an object
are included in the list and thus further recursively explored. The filter
normally
investigates whether the object is an openable, if that openable is open
or transparent, etc.

It's here, right above the filtering process, that an author needs to make
changes to the scoping rules: to somehow examine an object's contents
list *before* we make the decision to start processing down into it. We
want to extract those elements of the contents list that are 'on the
surface'
and include them in our scoping list, recursing down into their contents
lists.
We want to then remove these elements from our evaluation beneath the
filter, as they would then become duplicates.

All of this has been discussed from a high-level design perspective
because theoretically this approach should be applicable to any system
which uses a 'bubble-up'/'filter-down' scoping mechanism. That includes
WorldClass and it includes Inform. Once you've cracked this nut, the rest
tends to fall into place logically.

An examination of multiform.t's scopeList() function below shows exactly
this process. Those author's intimately familiar with TADS will notice the
similarity between scopeList() and TADS' visibleList(). The sorting out
process for elements 'on the surface' occurs right before the check to
determine of the object's contents should be added to the scope list.

scopeList: function( loc, vantage, ceiling, level )
{
local ret := [];
local i, lst, len;

/* don't look in "nil" objects */
if (loc = nil)
return ret;

/*
* We don't want items on the surface of the scope ceiling in
* our scope list.
*
* check each location's contents to see if any are on the
* surface. If they are then add them to scope and recurse
* down into their contents. Remove any objects 'on surface'
* from lst, so that we don't pick them up again when we
* are dealing with 'inside'.
*/
lst := loc.contents;
if (loc <> ceiling)
{
local tmp := [];
len := length(lst);
for (i := 1 ; i <= len ; ++i)
{
/* recurse into this object */
if (lst[i].isOnSurface)
{
ret += lst[i];
tmp += lst[i];
ret += scopeList( lst[i], vantage, ceiling, level );
}
}
lst -= tmp;
}
/*
* The location is added to the list if,
* a. it is the vantage or the scope ceiling
* b. it meets the requirements of a scope definition
* for the given scope level.
*/
if (loc = vantage
or loc = ceiling
or isScopedef( loc, level ))
{
len := length(lst);
ret += lst;
for (i := 1 ; i <= len ; ++i)
{
/* recurse into this object */
ret += scopeList( lst[i], vantage, ceiling, level );
}
}

return ret;
}

If you're working with ADV.T's accessibility rules directly you'll need
to handle thing.isVisible() method and thing.isReachable() in
similar fashion. Scope.t 2.0 uses the scope function in place of these,
so multiform required only the scopeList() function modification.

=================================================
Command Execution
------------------------------

Quite a bit of the code in multiform.t comes from scope.t 2.0, which
was designed to accomodate the enterable class and isn't covered
in this discussion of multiform class objects (see
ftp.gmd.de/if-archive/programming/tads/examples/scope.zip for more
about scope 2.0)

The author will need to modify the following verification methods in
thing class:

modify thing
replace verDoPutIn()
{
if (io = nil)
return;

if (self.location = io and !self.isOnSurface)
{
caps(); self.thedesc; " is already in "; io.thedesc; "! ";
}
else if (io = self)
{
"%You% can't put "; self.thedesc; " in <<self.itselfdesc>>! ";
}
else if (io.isIn(self))
self.circularMessage(io);
else
self.verifyRemove(actor);
}
replace verDoPutOn(actor, io) =
{
if (io = nil)
return;

if (self.location = io and self.isOnSurface)
{
caps(); self.thedesc; " is already on "; io.thedesc; "! ";
}
else if (io = self)
{
"%You% can't put "; self.thedesc; " on <<self.itselfdesc>>! ";
}
else if (io.isIn(self))
self.circularMessage(io);
else
self.verifyRemove(actor);
}
;

These modifications allow us to move objects from the inside to
the outside of a multiform (and vice-versa) using the putVerb.
Further modification of thing class follows:

modify thing
doPutOn(actor, io) =
{
self.moveInto(io);
self.isOnSurface := true;
"Done. ";
}
moveInto(obj) =
{
local loc;

/*
* For the object containing me, and its container, and so
* forth, tell it via a Grab message that I'm going away.
*/
loc := self.location;
while (loc)
{
loc.Grab(self);
loc := loc.location;
}

if (self.location)
self.location.contents := self.location.contents - self;
self.location := obj;
if (obj)
obj.contents := obj.contents + self;
self.isOnSurface := nil;
}
;

All this does is set our boolean to true when we put one object
on another, and set it to nil when it is moved. This is *very *
convenient and saves having to set the attribute for every
movement-oriented command. All we need do is set it to true
for those commands which are 'on the surface' oriented.

Next we must modify the ldesc, doSearch(), and other methods
in the surface, container and openable classes.

/*
* surface: item
*
* The following modifications incorporate the surfaceList()
* method for display purposes.
*/
modify surface
replace ldesc =
{
if (self.surfaceList)
{
"On "; self.thedesc; " %you% see%s% ";
listcont(self.surfaceList); ". ";
}
else
{
"There's nothing on "; self.thedesc; ". ";
}
}
replace doSearch(actor) =
{
if (self.surfaceList)
"On <<self.thedesc>> %you% see%s%
<<listcont(self.surfaceList)>>. ";
else
"There's nothing on <<self.thedesc>>. ";
}
;

/*
* container: item
*
* The following modifications incorporate the containerList()
* method for display and bulk computation purposes.
*/
modify container
replace ldesc =
{
if (self.contentsVisible
and self.containerList)
{
"In "; self.thedesc; " %you% see%s% ";
listcont(self.containerList); ". ";
}
else
{
"There's nothing in "; self.thedesc; ". ";
}
}
replace ioPutIn(actor, dobj) =
{
local cont := self.containerList;

if (cont)
{
if (length(cont)=0) cont := [];
}
else
cont := [];

if (addbulk(cont) + dobj.bulk > self.maxbulk)
{
"%You% can't fit <<dobj.thatdesc>> in "; self.thedesc; ". ";
}
else
{
dobj.doPutIn(actor, self);
}
}
replace doSearch(actor) =
{
if (self.contentsVisible
and self.containerList)
"In <<self.thedesc>> %you% see%s%
<<listcont(self.containerList)>>. ";
else
"There's nothing in <<self.thedesc>>. ";
}
;

/*
* openable: container
*
* The following modifications incorporate the containerList()
* method for display purposes.
*/
modify openable
replace doOpen(actor) =
{
if (self.containerList)
{
"Opening "; self.thedesc; " reveals ";
listcont(self.containerList); ". ";
}
else
"Opened. ";
self.isopen := true;
}
;

One last change is necessary to the verification method in
transparentItem, to allow us to take objects from the surface
of this class of object

modify transparentItem
replace verGrab(obj) =
{
if (self.isopenable and not self.isopen)
{
if (not(obj.isOnSurface and find(self.contents, obj) <> 0))
"%You% will have to open << self.thedesc >> first. ";
}
}
;

This specifically limits itself to objects on the surface of this
particular transparent item, and prevents us from say, reaching
through a glass bottle to pull the wings off a fly.

===============================================
Listing Functions
------------------------

The last several modifications provided by multiform.t deal with
the listing of objects when inspected or in room descriptions.
Two very helpful methods have been added to thing class:

modify thing
/*
* containerList
*
* This method returns a list of all content objects with
* isOnSurface = nil. If the list is empty the method returns
* nil.
*/
containerList =
{
local i, list := [];

for (i := 1; i <= length(self.contents); ++i)
{
if (self.contents[i].isListed)
{
if (not self.contents[i].isOnSurface)
list += self.contents[i];
}
}
if (length(list))
return list;
else
return nil;
}
/*
* surfaceList
*
* This method returns a list of all content objects with
* isOnSurface = true. If the list is empty the method returns
* nil.
*/
surfaceList =
{
local i, list := [];

for (i := 1; i <= length(self.contents); ++i)
{
if (self.contents[i].isListed)
{
if (self.contents[i].isOnSurface)
list += self.contents[i];
}
}
if (length(list))
return list;
else
return nil;
}
;

These methods do the work of sorting out 'on the surface'
elements from 'inside' elements. With multiform.t version 2.0,
the functions will return null if no elements are found.

The following code has also been added to ADV.T's listcont()
and listfixedcontcont() functions directly beneath the definition
of local variables. This code allows these functions to be passed
either an object or a list as a parameter, which is highly convenient
when separating out the contents list.

if (datatype(obj) = 2 )
list := obj.contents;
else
list := obj;

And finally, a change to ADV.T needs to be made to
showcontcont(). This modification allows us to accurately
list the 'surface' and 'inside' contents of an object.

replace showcontcont: function(obj)
{
if (itemcnt(obj.contents))
{
if (obj.issurface)
{
if (not obj.isqsurface
and length(obj.surfaceList))
{
"Sitting on "; obj.thedesc;" is ";
listcont(obj.surfaceList);
". ";
}
}
if (obj.contentsVisible and not obj.isqcontainer
and length(obj.containerList))
{
caps();
obj.thedesc; " seem";
if (!obj.isThem) "s";
" to contain ";
listcont(obj.containerList);
". ";
}
}
if (obj.contentsVisible and not obj.isqcontainer
and length(obj.containerList))
listfixedcontcont(obj.containerList);
}
;

===============================================
The multiform Class
----------------------------

The multiform class, defined in version 1.0, inherited from both
openable and surface classes. As the first of its kind I wanted
something a little 'spectacular', so it inherited from openable
instead of container.

I spent some time racking my brains and combing various sorts
of literature, wondering what to call the new class, and worrying
about other variations. Finally, version 1.0 went in with the multiform
class.

But then it hit me like a brick in the head... Better to simply
modify the current classes of container, openable, and surface and
create an object to govern the order of display for ldesc and
doSearch(). Version 2.0 uses the following *prefix* object:

/*
* multiform: object
*
* This class can be *prefixed* to a class list for an object to
* allow it to combine the ldesc() and doSearch() methods for the
* openable, container, and surface classes.
*
* multiform class objects always display their 'surface' contents
* first, then their 'inside' contents, regardless of the order of
* the class list.
*/
class multiform: object
ldesc =
{
if (isclass(self,surface))
inherited surface.ldesc;
if (isclass(self,openable))
inherited openable.ldesc;
else if (isclass(self,container))
inherited container.ldesc;
}
doSearch(actor) =
{
if (isclass(self,surface))
inherited surface.doSearch;
if (isclass(self,container))
inherited container.doSearch;
}
;

================================================
Conclusions
-----------------

There are several implications to objects having both an 'inside'
and 'surface'.

1. These changes make it possible to place and remove objects
from the inside and surface of any object which has the appropriate
verification and action methods, not just multiform objects, which
merely serves to facilitate ldesc and other descriptions.

2. Traditionally, although an actor's possessions are considered to
be 'inside' him, it will now be possible to have possessions both
inside and on the surface of an actor. This could be used to allow
two different kinds of access to an actor's possessions, or could
allow for certain items to be hidden.

3. For object's whose initial location is a surface class it's probably
best to set such the object's isOnSurface attribute to true.

4. Any 'surface' oriented-verb will need to set the isOnSurface
attribute to true in its doVerb method. Occassionally you might
need to set tihe isOnSurface to nil for commands that do not use
the moveInto() method when removing objects from the surface of
another object (an example of this might be a stack of plates, only
the topmost being accessible.) Certain specialized commands,
such as ">turn inside out." would require special treatment.

5. Theoretically, at least, it should be possible to use a similar,
although simpler, contents sorting technique to give objects an
'under' disposition. Personally, the hider class seems to have
this in the bag.

6. As mentioned earlier, I believe that any system which uses a
'bubble-up'/'filter-down' scoping system can adopt multiform class
objects. This applies not only to ADV.T, but to WorldClass and
even Inform.

--Kevin

Paul E. Bell

unread,
Jun 22, 1999, 3:00:00 AM6/22/99
to

Graham Nelson wrote:
>
> In article <eNJGbzOv#GA.280@cpmsnbbsa05>, Kevin Forchione


> <URL:mailto:Lys...@email.msn.com> wrote:
> > I once read a posting in which Andrew Plotkin, I believe, was explaining
> > to a newbie why an object couldn't be both a supporter and a container.
> > He was speaking of Inform, of course.
>

> Nevertheless, it is possible for an Inform object to both contain
> and support things, and indeed to have sub-components attached
> as well. It takes a little coding and is less elegant than your
> general solution, but it does work. See the little example
> code "A Nasal Twinge", which as I recall features a washing
> machine with a top, an inside and some buttons.
>
I have that somewhere, or at least I think I do. A long time ago I
worked on converting all the old Inform 5.x code I could find to Inform
6.x code (not sure what happened to those files).

Out of curiosity, just how difficult would it be to incorporate the
above capabilities (as described by Kevin) into the Inform parserm.h?
What penalties would there be?

Paul E. Bell
--
_____
| | _ \ _ _ |/ _ _(
| | (_X (_/`/\ (_) (_` |\(_) (_) (_|_) (/`
)

Graham Nelson

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to
In article <eNJGbzOv#GA.280@cpmsnbbsa05>, Kevin Forchione
<URL:mailto:Lys...@email.msn.com> wrote:
> I once read a posting in which Andrew Plotkin, I believe, was explaining
> to a newbie why an object couldn't be both a supporter and a container.
> He was speaking of Inform, of course.

Nevertheless, it is possible for an Inform object to both contain


and support things, and indeed to have sub-components attached
as well. It takes a little coding and is less elegant than your
general solution, but it does work. See the little example
code "A Nasal Twinge", which as I recall features a washing
machine with a top, an inside and some buttons.

--
Graham Nelson | gra...@gnelson.demon.co.uk | Oxford, United Kingdom


Andrew Plotkin

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to
Paul E. Bell (wd0...@millcomm.com) wrote:


> Graham Nelson wrote:
> >
> > In article <eNJGbzOv#GA.280@cpmsnbbsa05>, Kevin Forchione
> > <URL:mailto:Lys...@email.msn.com> wrote:

> > > I once read a posting in which Andrew Plotkin, I believe, was explaining
> > > to a newbie why an object couldn't be both a supporter and a container.
> > > He was speaking of Inform, of course.

And I was speaking particularly of the supporter and container attributes,
not of add_to_scope tricks, which is what Graham is talking about:



> > Nevertheless, it is possible for an Inform object to both contain
> > and support things, and indeed to have sub-components attached
> > as well. It takes a little coding and is less elegant than your
> > general solution, but it does work. See the little example
> > code "A Nasal Twinge", which as I recall features a washing
> > machine with a top, an inside and some buttons.

> Out of curiosity, just how difficult would it be to incorporate the


> above capabilities (as described by Kevin) into the Inform parserm.h?
> What penalties would there be?

The attributions have gotten entirely mixed, here. Do you mean the
capabilities described by Graham, in "A Nasal Twinge"?

That involves making a construction of several sub-objects, held together
with add_to_scope. It's not meaningful to talk about putting it in the
library. It *uses* the library (in particular, add_to_scope).

--Z

--

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the
borogoves..."

Kevin Forchione

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to
The multiform Concept Continued...
===========================

There are two more issues which need to be addressed. The first is scope
ceiling, which was neglected in release 2.0. As will be discussed below, the
module is sound enough under TADS normal scoping rules, but scope ceiling
needs consideration if we are to implement multiform items into any system
which allows actors entry into structures like openable containers.

The other issue is one concerning backward compatibility, which needs ot be
addressed because I have made it a practice to build upon ADV.T/STD.T,
instead of writing a replacement. These same concerns will be the same for
any author building upon a pre-existing system who hopes to have others use
his extensions.

================================
Scope Ceiling
-------------------

Although the changes mentioned in my previous post would work for systems
that use the highest-level location (i.e. generally rooms) as scope
ceilings, they are not sufficient for systems which require other levels of
scope ceiling.

Suppose we have an apple on the surface of a box. The box is a
non-transparent multiform and is closed. Most definitions of scope ceiling
would produce a scope ceiling of box for a vantage of apple. This is because
the apple is part of the contents of the box, and since the box is closed
this is the limit of scope.

But in reality the scope ceiling of the apple should be the same as that of
the box. Assuming that the box is in a room, then both box and apple should
have scope ceilings of room.

Scope.t 2.0 allows you to set limits on the calculation of scope ceiling.
This can be very convenient when we want to limit the scope of a vantage to
a particular region.

The enterable class is just such a case. This is a TADS class of container
which allows the actor to enter and leave. Tents, sleeping bags, and other
enclosures take advantage of this class. One of the tricks of the enterable
class is to simulate extension in space by limiting whether you can reach
objects within it ('you can't reach that from here.')

This is accomplished through the setting of an attribute to control whether
the effect is desired or not (if the attribute is not set then access is
permitted into the enterable regardless of the depth of its nesting).
Scoping then uses a scope limit (enforced ceiling) of the enterable, and
reverses the roles of vantage and object.

For instance, suppose your lantern is inside the tent, which is an enterable
exhibiting this extension property.

>take lantern

The normal scoping would have you as the vantage and calculate a scope
ceiling for you. But in this case the vantage becomes the lantern and the
scope ceiling becomes the tent. Accessibility is granted if you are within
the tent, but not if you are without.

This trick of reversing roles works very well.

*** Without enforcing an artificial limit upon scope ceiling if obj1 is
within the scope of obj2 for a given scopelevel definition (i.e. visibility,
reachability) then obj2 is within the scope of obj1 for that same scopelevel
definition (If I can see you, you can see me, etc). ***

(Note that this rule does *not* state that obj1 has the same scope as obj2.
I might have access to other things, such as possessions, that you do not.)

As illustrated, scope ceiling becomes very important. If we are to maintain
this fundamental equivalence the following changes must be made to
scopeCeiling():

/*
* scopeCeiling: function( vantage, limit, level )
*
* Function returns the scopeCeiling for a given vantage and scope level.
The
* scopeCeiling is the highest level location that the vantage has access to


* for the given scope level.
*

* limit acts to constrain the function a given scope ceiling "height",
* but the function will always return a valid scope ceiling for a
* valid location.
*
*/
replace scopeCeiling: function( vantage, limit, level )
{
local loc;

if (vantage = limit )
{
/* Adjustment for vantages on the 'surface' of objects */
if (vantage.location and vantage.isOnSurface)
return vantage.location;
else
return vantage;
}

/* Adjustment for vantages on the 'surface' of objects */
if (vantage.location and vantage.isOnSurface)
vantage := vantage.location;

if (vantage.location)
loc := vantage.location;
else
loc := vantage;
while( loc and loc.location )
{
if ( loc = limit )
break;

if ( level = SCOPE_VISIBLE
and not isScopedef( loc, level ))
break;

if ( level = SCOPE_REACHABLE
and not isScopedef( loc, level ))
break;

loc := loc.location;
}
return loc;
}
;

This modification puts us in good standing, whether we're using the
enterable class or not, and maintains the integrity of our scope list. (Good
news for those using the inform.t TADS library extension! See
ftp.gmd.de/if-archive/programming/tads/examples/inform.t for details.)

=======================================
Backward Compatibility
-------------------------------

Because the listing functions are likely to be modified in any current
system, the disposition of objects located within surfaces will not be
correct for pre-existing code. In particular, the objects will claim to be
'within' the surface, instead of 'on' it. While this will only affect
objects with inititial locations on surfaces whose isOnSurface attribute has
not been set to true, it might cause unpredictable (or, alas, sadly
predictable) results.

The simple solution is to provide a pre-initialisation function which sets
the attribute correctly for objects with locations on surfaces that are not
of the multiform class. This will make the process 'transparent' to the
author who wishes to plug-n-play the module into his existing code.

/*
* setisOnSurface: function
*
* This function will set the initial disposition of all
* objects within non-multiform surfaces to 'on the surface'.
* This will help authors to keep their coding compatible
* with multiform requirements.
*
* This should be run from preinit to save execution time.
*/
setisOnSurface: function
{
local i, o;

o := firstobj(surface);
while (o <> nil)
{
if (not o.ismultiform)
{
for (i := 1; i <= length(o.contents); ++i)
o.contents[i].isOnSurface := true;
}
o := nextobj(o, surface);
}
}

A special problem is the case of 'floating' objects: objects whose locations
are not initially set and tend to move about from one location to another.
These cannot be dealt with in any practical way and will always default to a
disposition of 'inside'. In the vast majority of cases this should pose no
difficulty, but there is bound to be the odd and exasperating exception.


=======================================
Concluding Remarks
----------------------------

...the need to navigate a newly added river prompted the
invention of vehicles (specifically, a boat).
...P. David Lebling, Marc Blank and Timothy Anderson

I can't help but feeling that only yesterday when I created the multiform
class I stumbled onto something very interesting... another way of looking
at things. What will it mean for the classes and objects we currently have?
I have this idea in the back of my mind of an alternate library system
designed around this... just waiting to be written.

Hmmm.

--Kevin

TenthStone

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to
On Wed, 23 Jun 1999 16:53:11 +0100, "Kevin Forchione"
<Lys...@email.msn.com> wrote:
>I can't help but feeling that only yesterday when I created the multiform
>class I stumbled onto something very interesting... another way of looking
>at things. What will it mean for the classes and objects we currently have?
>I have this idea in the back of my mind of an alternate library system
>designed around this... just waiting to be written.

I, actually, have been writing (probably) just such an alternate
library system. It's in alpha testing for now. Let me explain what
I've done.

Instead of flagging objects on a surface as existing in a special,
exceptional state from those in a container, I've developed a
system which deals with the two as entirely different. A surface
is not a container which is interacted with differently; instead,
it maintains its own contents lists seperate from those of a
container.

What this allows me to do is to expand upon the concept of
surfaces vs. containers. WorldClass has four different positions
around an object: on top of, inside, behind, and underneath.
By intelligent coincidence, those are the ones in my libraries.
An object defines itself to be in one of these places like so:

banana: item
in = fruitbasket
;

The real benefit of this is that it's actually cleaner conceptually.
It makes more sense, and it's easier to use. You don't have to
make sure everything in the same list is actually in the same
place. You can generalise about certain objects.

It's also much easier to expand upon. If, say, there were a need
to have an "in front of" position, it would take about ten minutes
of updating functions and writing new verbs. The more complicated
routines wouldn't need to be touched. The most difficult part of the
process would be to pick a name for the position ("front", probably).

I hope I've achieved it more straightforwardly than WorldClass,
though.

----------------
The Imperturbable TenthStone
mcc...@erols.com tenth...@hotmail.com mcc...@gsgis.k12.va.us

Kevin Forchione

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to

TenthStone <mcc...@erols.com> wrote in message
news:37714fc9...@news.erols.com...

This looks very interesting. I must admit that I am unfamiliar with how
WorldClass works. I hadn't known that such as system already exists for
TADS. Conceptually, I like your approach very much. It would make things
much easier all around. What's the difference between what you're developing
and what WorldClass does?

Paul E. Bell

unread,
Jun 23, 1999, 3:00:00 AM6/23/99
to

Andrew Plotkin wrote:
>
> Paul E. Bell (wd0...@millcomm.com) wrote:
>
> > Graham Nelson wrote:
> > >
> > > In article <eNJGbzOv#GA.280@cpmsnbbsa05>, Kevin Forchione
> > > <URL:mailto:Lys...@email.msn.com> wrote:

> > > > I once read a posting in which Andrew Plotkin, I believe, was explaining
> > > > to a newbie why an object couldn't be both a supporter and a container.
> > > > He was speaking of Inform, of course.
>

> And I was speaking particularly of the supporter and container attributes,
> not of add_to_scope tricks, which is what Graham is talking about:
>
> > > Nevertheless, it is possible for an Inform object to both contain
> > > and support things, and indeed to have sub-components attached
> > > as well. It takes a little coding and is less elegant than your
> > > general solution, but it does work. See the little example
> > > code "A Nasal Twinge", which as I recall features a washing
> > > machine with a top, an inside and some buttons.
>
> > Out of curiosity, just how difficult would it be to incorporate the
> > above capabilities (as described by Kevin) into the Inform parserm.h?
> > What penalties would there be?
>
> The attributions have gotten entirely mixed, here. Do you mean the
> capabilities described by Graham, in "A Nasal Twinge"?
>
> That involves making a construction of several sub-objects, held together
> with add_to_scope. It's not meaningful to talk about putting it in the
> library. It *uses* the library (in particular, add_to_scope).
>

Ok, Zarf (I finally made the connection), I meant: could the code for
allowing the object "contained" tell the object where it is contained
(by an attribute: in, on, behind, under) such as is talked about
elsewhere in this thread) be incorporated into the library? There
could, perhaps, be a constant defined to tell the library to use the new
capabilities, or the old.
--
Paul E. Bell Email and AIM: wd0...@millcomm.com
ifMUD: Helios | IRC: PKodon, DrWho4, and Helios
(I'd put my webpage here, if it had anything on it.)
_____ Pen Name/Arts & Crafts signature:

TenthStone

unread,
Jun 24, 1999, 3:00:00 AM6/24/99
to
On Wed, 23 Jun 1999 23:41:28 +0100, "Kevin Forchione"
<Lys...@email.msn.com> wrote:
>This looks very interesting. I must admit that I am unfamiliar with how
>WorldClass works. I hadn't known that such as system already exists for
>TADS. Conceptually, I like your approach very much. It would make things
>much easier all around. What's the difference between what you're developing
>and what WorldClass does?

Well, I'm trying to devine my way around WorldClass from the actual
library file, and it's slow going. The way I understand it _now_, as
opposed to several hours ago when I posted, and in agreement with
how I understood it long before that, is that WorldClass maintains a
single contents list with flags on the objects to determine the type.
This is similar to how multiform handles it, I believe, except it's
done much more generally.

Actually, it's done in a way almost more general than mine. Most of
the ten minutes I estimated to add a new position was modifying
existing functions. WorldClass, on the other hand uses nearly
inscrutable code that almost completely avoids the use of such
functions.

In my system, each object can choose one of four attributes
and assign it. That's why ".in = startroom" works. In exchange,
there are two methods which return the parent object and position,
called .locate and .position respectively. .position returns one of
four compiler symbols: In, On, Under, Behind. I have a lot of
functions occupied in returning property pointers given a certain
symbol:

locProp: function( type ) {
switch (type) {
case In: return &in;
case On: return &on;
case Under: return &under;
case Behind: return &behind;
default: return;
}
}

and each of these (along with a few others) would have to modified to
allow for a new position. Still, with a list it wouldn't be too hard.

Andrew Plotkin

unread,
Jun 24, 1999, 3:00:00 AM6/24/99
to
Paul E. Bell (wd0...@millcomm.com) wrote:

> > > > Nevertheless, it is possible for an Inform object to both contain
> > > > and support things, and indeed to have sub-components attached
> > > > as well. It takes a little coding and is less elegant than your
> > > > general solution, but it does work. See the little example
> > > > code "A Nasal Twinge", which as I recall features a washing
> > > > machine with a top, an inside and some buttons.
> >
> > > Out of curiosity, just how difficult would it be to incorporate the
> > > above capabilities (as described by Kevin) into the Inform parserm.h?
> > > What penalties would there be?
> >

> > That involves making a construction of several sub-objects, held together
> > with add_to_scope. It's not meaningful to talk about putting it in the
> > library. It *uses* the library (in particular, add_to_scope).
> >
> Ok, Zarf (I finally made the connection), I meant: could the code for
> allowing the object "contained" tell the object where it is contained
> (by an attribute: in, on, behind, under) such as is talked about
> elsewhere in this thread) be incorporated into the library?

No. In fact, there is no such code. That's not how it works.

I suggest you take a look at the "Nasal Twinge" source code, which is in
the archive somewhere under sample Inform code, and see how it works.

http://www.ifarchive.org/if-archive/infocom/compilers/inform5.5/examples/a_nasal_twinge.inf
...but it should work equally well in Inform 6.

Kevin Forchione

unread,
Jun 24, 1999, 3:00:00 AM6/24/99
to
==================================
Why I Chose The Multiform Approach
----------------------------------------------------

In this final chapter, we approach style in its broader meaning:
style in the sense of what is dinstinguished and distinguishing.
Here we leave solid ground. Who can confidently say what
ignites a certain combination of words, causing them to explode
in the mind?
--E. B. White, The Elements of Style (Third Edition, 1979)


While I like the look of:

apple: fooditem
on = theTable
;

I don't find it any easier to grasp than:

apple: fooditem
location = theTable
isOnSurface = true
;

While I like the idea of a multiple-contents list system, after
some time thinking about it, I find I can't agree that it is
"conceptually cleaner" than a single-contents list approach.

When I sat down to examine the possibility of building a
class that behaves like both a container and supporter I
explored the idea of a multiple-contents list system, but
rejected it on principle.

The following is from my design notes, which I used to
create my first posting on the subject. It contains some
of my thoughts for selecting the single-contents list approach:

"It has fascinated me for quite some time now that objects
cannot be both containers and surfaces. It seems a
paradoxical situation, since the goal of object-oriented
programming is to simulate real-world events.

"There seems to be two approaches to this:

"1. A dual listing system, where each object has two
contents lists, one for inside and one for outside.

"This method seems needlessly complicated. As you've
probably observed in my projects, I prefer to make
minimal modifications to the existing library structures.

"2. A simple flag.

"Since every object has only 1 location at a time (including
floating objects) flags have been used to indicate whether
an item was 'on' or 'in' another object, but this flag has
always been part of the container/surface itself. It seems
simple to have a flag which is set on the item, stating
whether it is 'on' or 'in' the object. This flag can remain a
simple boolean, since even objects moved into nil can be
said to be 'in' or 'on', it won't matter.

"It then becomes an issue of the library's accessibility rules
to determine whether an object is accessible, and a matter
of the listing functions to state whether the object is 'on' or 'in'.
Finally, the action methods themselves would set the flag,
depending upon the preposition."

From this I went on to create the multiform class.

============================================
Shadows and Fog
--------------------------

Whether you are extracting objects from one list via an
attribute system, or creating multiple lists, again via an
attribute system is functionally equivalent. The idea that
separate lists are conceptually cleaner and simpler to
expand upon does not seem to me to be supportable.

Building a multi-list system would require a fundamental
overhaul of the current ADV.T system. I don't wish to
pre-empt the benefits or elegance of such an endeavor,
as authors will obviously have dealt with each of the
following concerns and doubtless others I have not
forseen in ways at which I can only guess.

[The following thoughts are off the top of my head...
what I would examine if I were to have followed the
multiple list approach, and the very reasons why I
rejected it.]

1. We mustn't be misled. Multi-contents list systems
are as much attribute-driven as single-contents list
systems. We're dealing with shadows and fog ... tricks
of strings, lists, arrays... "in =" is fundamentally no different
than "isOnSurface".

2. Initial location. As far as the compiler is concerned, no
object would have an initial location. This must be provided
via a sorting mechanism in preinit() which must place every
object into the multiple-contents lists of other objects. Adding
a new disposition means adding a new list and modifying this
process.

3. Backward compatibility. Likewise one has either got to
ignore this, or have to implement a standard default for
certain classes, checking every object for a location attribute,
making a judgment call, then passing it on to the list
maintenance mechanism. Other factors, however, make
backward compatibility seem impractible. Any user code
with 'self.contents' appearing in it is problematical.

4. Accessibility rules. These will still require modification,
and this would require trolling through different sets of lists
from each object, depending upon the types of accessibility
being examined. While this is functionally equivalent to
picking off attributes, as a single-contents approach uses,
one needs to add every new list to the scoping mechanism.
With a single-contents list approach it wouldn't always be
necessary: something like 'in front of' being accessibly
equivalent to 'outside', while 'in back of' is accessibly
equivalent to 'inside'. Also, a bit-flag, as I propose for
multiform below would make this process even easier.

5. Verification and Action methods. Verification methods
will need to check to see if an object already possesses
a certain disposition. If the action is successful a mechanism
must remove the object from one list and move it to another.
Almost as simple as flipping a boolean.

6. Miscellaneous Functions and Methods. Modifications
would be needed for such functions and methods as addbulk(),
addweight(), isIn(), isCarrying(), nrmLookAround(), etc.
Everywhere the library uses self.contents will need to be
adapted to multiple lists. Adding a new disposition means
adding a new list, and that would require modification to
these functions.
A single-list approach has the advantage, again, that many
additional attributes, such as 'behind' or 'in front' can be handled
by inside/outside logic. For instance, the isIn() and isCarrying()
method doesn't appear to require modification in multiform.
Once modified under a bit-flag system these functions are
unlikely to need changing.

7. Listing Functions. These too, would need modification in
roughly similar manner to what has been done in multiform.

8. Display in Classes. Separate lists require modification to
all classes that display contents, such as surfaces, containers,
and openables to display the corresponding appropriate list.

9. Surfaces Which Are Containers. It would be possible to adapt
the above classes to display multiple-contents lists (a box could
display the in-list and the on-list), but these classes would then
have to have verification and action methods to gain access to
these other contents lists.
Creating a homogoneous object (like thing) which would allow
all actions would require verification and action methods to either
prevent or customize them and would be a bother. Otherwise
something like the multiform class, which accomodates both
sets of behaviour is required.

10. Maintenance. Library extensions that incorporate a new
disposition would need to take all of the previous into consideration.
A single-contents list system using a boolean would require less
modification, and one using bit-flags could potentially be
'plug-n-play' without having to modify the library.

Obviously it helps to proceed from a model, as WorldClass
apparently provides, but it looks like an awful lot of work to
create a multiple-contents list system in order to add a new
class: (container/surface) or even a new concept: inside/outside
to what is basically an already well-crafted and sound world model.

The single-contents list approach could be used on ADV.T
directly, using the library's existing visibleList()and
reachableList() functions. These can be modified to add the
dozen or so lines necessary to separate the surface elements
from the inside ones. Then reachableList() could be used by
isReachable(), and visibleList() by isVisible() for scoping.
This would tie the scoping mechanisms down to two functions
which are already integral parts of the library.

===============================
The Future of Multiform
--------------------------------

One thought strikes me from the above analysis, which is
worthy of consideration with regard to the multiform class.
Currently, as designed, it utilizes a boolean, but could just
as easily use a bit-flag which would easily facilitate the
addition of new dispositions. It would then be a simple matter
of checking with bitwise-and to determine object disposition,
making the addition of new disposition attributes unnecessary.

This is the approach I will be exploring for expanding multiform
beyond concepts of inside/outside. It offers minimal changes
to the library. It could also be used to carry additional
information so that the author could customize without having
to modify the library mechanisms.

This approach is very adaptable and capable of carrying a
great deal of information. Just as an example:

#define INSIDE 1
#define OUTSIDE 2
#define ONBOTTOM 4
#define ONTOP 8
#define BEHIND 16
#define INFRONT 32
#define FLOATINGIN 64

apple: fooditem
location = startroom
disposition = FLOATINGIN|INSIDE
ldesc =
{
if (self.disposition & floatingIn)
"(floating in) the <<self.location>>. ";
}

Granted, the categories above seem superfluous, and would
probably need an additional method or function that could be
passed the object and bit-value constant, returning true or nil,
to determine an object's disposition. But the method has the
following advantages:

* The action methods would set the bitwise-flag to the
appropriate value such as onbottom|inside.

* The addition of a new class of object or disposition could
then pick up an inherited disposition, building upon it.

* The scoping mechanism wouldn't need to be altered unless
the nature of accessibility is altered, the mechanism need
merely sort elements into 'inside' and 'outside' based on their
bit-flags.

* The listing functions can display as little or as much information
as desired, simply by checking the flag. These functions could
be tailored to accept bitflags parameters that allow them to display
different levels of information.


Well, we'll see...

But the best part of all, is that right here, right now, we have
multiform - containers *and* surfaces, as an interesting addition
to the library!

Isn't TADS amazing?

--Kevin

Kevin Forchione

unread,
Jun 24, 1999, 3:00:00 AM6/24/99
to
Actually nasal twinge is an excellent example of Inform's add_to_scope
property. I believe that it is also demonstrated in the DM for a sticky
label.

The puzzles in the Inform DM are some of the most challenging and
interesting I've come across.

--Kevin

TenthStone

unread,
Jun 25, 1999, 3:00:00 AM6/25/99
to
On Thu, 24 Jun 1999 17:57:07 +0100, "Kevin Forchione"
<Lys...@email.msn.com> wrote:

>While I like the look of:
>
>apple: fooditem
> on = theTable
>;
>
>I don't find it any easier to grasp than:
>
>apple: fooditem
> location = theTable
> isOnSurface = true
>;


I do. Knowing just one attribute of apple, I know everything
about its location. I don't have to check to see if there's another.

>While I like the idea of a multiple-contents list system, after
>some time thinking about it, I find I can't agree that it is
>"conceptually cleaner" than a single-contents list approach.

I disagree. An object on top of a table is not in the same place as
one below it. It's that simple.

Furthermore, how are you handling pure surfaces vs.
container/surfaces? Conceptually, pure surfaces should
set an object's "isOnSurface" property true every time
it is set upon them. To manage this, moveInto would probably have
to consult the target object every time, which is certainly
more complicated.

As for moveInto, how do you tell it to move an object into a surface?
YOu don't. You tell it to move the object into a container, and then
tell the object that it's on a surface. That's hardly "clean".

>"1. A dual listing system, where each object has two
>contents lists, one for inside and one for outside.
>
>"This method seems needlessly complicated. As you've
>probably observed in my projects, I prefer to make
>minimal modifications to the existing library structures.

Oh, I don't deny that I've had to radically change the library.
Of course, your system requires rather substantial changes as well.
Furthermore, the problem with simply patching the current library
over and over again is that it makes it hard for a programmer to
include two patches. "Can I include these two files?" the writer
wonders. "I guess I'll just hope nothing goes wrong."

>"2. A simple flag.
>
>"Since every object has only 1 location at a time (including
>floating objects) flags have been used to indicate whether
>an item was 'on' or 'in' another object, but this flag has
>always been part of the container/surface itself. It seems
>simple to have a flag which is set on the item, stating
>whether it is 'on' or 'in' the object. This flag can remain a
>simple boolean, since even objects moved into nil can be
>said to be 'in' or 'on', it won't matter.

But all of this simplicity is lost into the wind as soon as you
advance beyond two possible positions.

>"It then becomes an issue of the library's accessibility rules
>to determine whether an object is accessible, and a matter
>of the listing functions to state whether the object is 'on' or 'in'.
>Finally, the action methods themselves would set the flag,
>depending upon the preposition."

I don't see how this is different from what I've done. I just give
the listing functions an easier time of it -- they don't have to sort
.contents first just to make a list of the correct position.

>============================================
>Shadows and Fog
>--------------------------
>
>Whether you are extracting objects from one list via an
>attribute system, or creating multiple lists, again via an
>attribute system is functionally equivalent.

Sortof. Extracting lists requires the same task to be performed
repeatedly.

> 1. We mustn't be misled. Multi-contents list systems
>are as much attribute-driven as single-contents list
>systems. We're dealing with shadows and fog ... tricks
>of strings, lists, arrays... "in =" is fundamentally no different
>than "isOnSurface".

If you mean that to the player there's very little difference,
sure. I mean, that's the point. They're not supposed to be
able to tell.

If you mean that to the programmer there's very little difference,
I have to disagree. The command
apple.moveOnto( teacherdesk );
which reroutes to
apple.moveInto( teacherdesk, On );
is more concise, and thus easier to read and debug.

> 2. Initial location. . . . Adding a new disposition means adding


>a new list and modifying this process.

Sure. It means copying a line and changing two words there.

for (o := firstobj(); o; o := nextobj( o )) {
if (o.islamp) global.lampList += o;
if (isclass( o, floatingobject )) global.floatingList += o;
else {
i := o.in; if (i) i.incont += o;
i := o.on; if (i) i.oncont += o;
i := o.under; if (i) i.undercont += o;
i := o.behind; if (i) i.behindcont += o;
}
}

> 3. Backward compatibility. Likewise one has either got to
>ignore this, or have to implement a standard default for
>certain classes, checking every object for a location attribute,
>making a judgment call, then passing it on to the list
>maintenance mechanism. Other factors, however, make
>backward compatibility seem impractible.

Right. It's not purely backward-compatible. It would require

>Any user code with 'self.contents' appearing in it is problematical.

Not really. .contents is an alias for .allcont, which adds together
all the contents lists.

> 4. Accessibility rules. These will still require modification....

Naturally. Many things required modification. Work passed is
past.

> 5. Verification and Action methods. Verification methods
>will need to check to see if an object already possesses
>a certain disposition. If the action is successful a mechanism
>must remove the object from one list and move it to another.
>Almost as simple as flipping a boolean.

Again, you're right. Multiple contents lists require more maintenance
on behalf of the libraries. They shave off work in other places,
such as display and accessibility/visibility.

> 6. Miscellaneous Functions and Methods. Modifications
>would be needed for such functions and methods as addbulk(),
>addweight(), isIn(), isCarrying(), nrmLookAround(), etc.
>Everywhere the library uses self.contents will need to be
>adapted to multiple lists. Adding a new disposition means
>adding a new list, and that would require modification to
>these functions.

When I said the library was in alpha-testing, perhaps you
misunderstood. I've already written it. I'm just making sure it
works. Most of the problems I've found are in other things that I've
done.

>Once modified under a bit-flag system [isIn() and isCarrying()] are
>unlikely to need changing.

I believe.

> 7. Listing Functions. These too, would need modification in
>roughly similar manner to what has been done in multiform.

Not really. Most of the changes I made there were in adding
tall contents. listcont takes an additional parameter for position
(and many, many others for other things), and that's all.

> 8. Display in Classes. Separate lists require modification to
>all classes that display contents, such as surfaces, containers,
>and openables to display the corresponding appropriate list.

Not really. Objects, by default, call a routine called
listallcontents which, well, lists all non-"q" contents as well
as contents' contents.

> 9. Surfaces Which Are Containers. It would be possible to adapt
>the above classes to display multiple-contents lists (a box could
>display the in-list and the on-list), but these classes would then
>have to have verification and action methods to gain access to
>these other contents lists.

Come again? Verification and action methods? You mean, such
as "doLookunder"? That's hardly difficult work. You mean, such
as "ioPutUnder"? Again, that's not much, especially when you've
made a verified moveInto function (i.e. a function which performs
a moveInto only after making all the standard doPutX checks).

class thing: object
verDoPutUnder( actor, io ) = {


if (io = nil) return;

if (self.locate = io and self.position = Under)
"\^<< thedesc >> << fmtAre >> already underneath <<
io.thedesc >>. ";


else if (io = self)

"\^<< self.thedesc >> cannot be put underneath itself. ";
else if (io.isIn( self )) self.circularmessage(io);
else self.verifyRemove( actor );
}
doPutIn( actor, io ) = {
self.checkDrop;
if (self.putInto( io, In )) "Done. ";
}
;

That's all.

> Creating a homogoneous object (like thing) which would allow
>all actions would require verification and action methods to either
>prevent or customize them and would be a bother. Otherwise
>something like the multiform class, which accomodates both
>sets of behaviour is required.

Please explain what you mean.

> 10. Maintenance. Library extensions that incorporate a new
>disposition would need to take all of the previous into consideration.
>A single-contents list system using a boolean would require less
>modification, and one using bit-flags could potentially be
>'plug-n-play' without having to modify the library.

Potentially, but probably not. listing functions, particularly, would
be hard to expand upon, given that they would really need to
run through the flag and check for every single combination
of flags (since In|Floating and On|Floating and In and On would all be
different locations and perhaps, therefore, ideally listed
seperately).

If I were seriously considering an easy-addition system like that,
things could be traightforwardly fixed so that new positions would be
no more difficult to add than in a bit-flag system. Now that you
mention it, I probably should develop just such a system, just in case
an author descides it would be easier to have one. Thank you.

>Obviously it helps to proceed from a model, as WorldClass
>apparently provides, but it looks like an awful lot of work to
>create a multiple-contents list system in order to add a new
>class: (container/surface) or even a new concept: inside/outside
>to what is basically an already well-crafted and sound world model.

True, but it has already been created.

>The single-contents list approach could be used on ADV.T
>directly, using the library's existing visibleList()and
>reachableList() functions. These can be modified to add the
>dozen or so lines necessary to separate the surface elements
>from the inside ones.

In other words, we'll both have to modify the methods. Ah, well.

>This approach is very adaptable and capable of carrying a
>great deal of information. Just as an example:
>
>#define INSIDE 1
>#define OUTSIDE 2
>#define ONBOTTOM 4
>#define ONTOP 8
>#define BEHIND 16
>#define INFRONT 32
>#define FLOATINGIN 64

I recognise that you came up with these spontaneously, but if I can
make a suggestion, I would advise doing this another way.

#define Below 1
#define Above 2
#define Infront 3
#define Behind 4
#define Inside 5

Then, add another property to the object for "floating". You can
then add a method to determine whether an object is inside or
outside:
isinside = (self.disposition = In)
If there should happen to be another disposition falling within the
object itself (e.g. Roof), the subsequent modification would be
trivial.

I say this because this:
disposition = FLOATINGIN|INSIDE
seems unnecessarily hard to read to me. Even like so:
disposition = floating | inside
it's more difficult, plus you have to add redundant information when
moving objects into "outside" positions:
obj.disposition := ontop | outside;
because of course "on top" is outside.

>...need an additional method or function that could be passed the


>object and bit-value constant, returning true or nil, to determine
>an object's disposition.

A method sounds good; that way, no arguments need be passed at all.

> onbottom|inside.

Hmm. I'm not sure using onbottom like this is a good idea, since
there's no real connection between an object being below its
container and one being on the bottom of the container.

> * The addition of a new class of object or disposition could
>then pick up an inherited disposition, building upon it.

How would this be useful with anything besides "floating"?
I mean, sure, you can put objects into the same location this
way, but is that useful?

> * The listing functions can display as little or as much information
>as desired, simply by checking the flag. These functions could
>be tailored to accept bitflags parameters that allow them to display
>different levels of information.

You mean, bitflag masking? "Only list objects with non-zero
dispositions when masked with X?" Well, okay.

>Isn't TADS amazing?

Truly.

Kevin Forchione

unread,
Jun 25, 1999, 3:00:00 AM6/25/99
to
TenthStone <mcc...@erols.com> wrote in message
news:37729344...@news.erols.com...

> On Thu, 24 Jun 1999 17:57:07 +0100, "Kevin Forchione"
> <Lys...@email.msn.com> wrote:
>
> >While I like the look of:
> >
> >apple: fooditem
> > on = theTable
> >;
> >
> >I don't find it any easier to grasp than:
> >
> >apple: fooditem
> > location = theTable
> > isOnSurface = true
> >;
>
>
> I do. Knowing just one attribute of apple, I know everything
> about its location. I don't have to check to see if there's another.

This is true, and one of the things I like aobut your approach.

> >While I like the idea of a multiple-contents list system, after
> >some time thinking about it, I find I can't agree that it is
> >"conceptually cleaner" than a single-contents list approach.
>
> I disagree. An object on top of a table is not in the same place as
> one below it. It's that simple.

My use of contents is much simpler than that. I'm not actually using it
strictly as a discription of location (obviously, since I have only one list
to play with!) When you boil it down the approach is simply a sorting out of
different attributes from a list in such a way that accessibility differs as
well as description.

The single-contents approach is by no means O-O. This is, perhaps, it's
biggest drawback!

> Furthermore, how are you handling pure surfaces vs.
> container/surfaces? Conceptually, pure surfaces should
> set an object's "isOnSurface" property true every time
> it is set upon them. To manage this, moveInto would probably have
> to consult the target object every time, which is certainly
> more complicated.
>
> As for moveInto, how do you tell it to move an object into a surface?
> YOu don't. You tell it to move the object into a container, and then
> tell the object that it's on a surface. That's hardly "clean".

Actually, this is handled very simply. moveInto() sets the isOnSurface to
nil in *every* case. It is set to true only for those cases such as doPutOn,
where the action method is obviously surface-related. This means that any
new verb actions which are surface-related would need to set this attribute
too.

> >"1. A dual listing system, where each object has two
> >contents lists, one for inside and one for outside.
> >
> >"This method seems needlessly complicated. As you've
> >probably observed in my projects, I prefer to make
> >minimal modifications to the existing library structures.
>
> Oh, I don't deny that I've had to radically change the library.
> Of course, your system requires rather substantial changes as well.
> Furthermore, the problem with simply patching the current library
> over and over again is that it makes it hard for a programmer to
> include two patches. "Can I include these two files?" the writer
> wonders. "I guess I'll just hope nothing goes wrong."

Agreed. This is a problem with library extensions. I've enjoyed building
upon ADV.T, because of its stability and because much of the ground work was
already done, but compatibility is, unfortunately, the risk.

> >"2. A simple flag.
> >
> >"Since every object has only 1 location at a time (including
> >floating objects) flags have been used to indicate whether
> >an item was 'on' or 'in' another object, but this flag has
> >always been part of the container/surface itself. It seems
> >simple to have a flag which is set on the item, stating
> >whether it is 'on' or 'in' the object. This flag can remain a
> >simple boolean, since even objects moved into nil can be
> >said to be 'in' or 'on', it won't matter.
>
> But all of this simplicity is lost into the wind as soon as you
> advance beyond two possible positions.

That's probably true. Multiform class wasn't really designed to be anything
except a class which is both a container and a surface, and to show how this
could be done with a little modification of ADV.T. It's by no means a
'system' in the sense that you've been creating.

The reasonings and examples that I've presented here and previously have all
been targetted at a 'simple' method of adapting pre-existing systems, such
as ADV.T to include this class (Obviously WorldClass doesn't need it!).
Basically I chose a single-contents system because it involved the least
amount of modification and coding -- not that it is a superior approach, but
an equivalent one.

> >"It then becomes an issue of the library's accessibility rules
> >to determine whether an object is accessible, and a matter
> >of the listing functions to state whether the object is 'on' or 'in'.
> >Finally, the action methods themselves would set the flag,
> >depending upon the preposition."
>
> I don't see how this is different from what I've done. I just give
> the listing functions an easier time of it -- they don't have to sort
> .contents first just to make a list of the correct position.

I agree. The listing functions get the benefit of pre-sorted lists.

This is neat.

> > 4. Accessibility rules. These will still require modification....
>
> Naturally. Many things required modification. Work passed is
> past.

Agreed. I built upon scope.t, which saved me quite a bit of time.

> > 5. Verification and Action methods. Verification methods
> >will need to check to see if an object already possesses
> >a certain disposition. If the action is successful a mechanism
> >must remove the object from one list and move it to another.
> >Almost as simple as flipping a boolean.
>
> Again, you're right. Multiple contents lists require more maintenance
> on behalf of the libraries. They shave off work in other places,
> such as display and accessibility/visibility.

Seems there's always a trade-off with any approach.

> > 6. Miscellaneous Functions and Methods. Modifications
> >would be needed for such functions and methods as addbulk(),
> >addweight(), isIn(), isCarrying(), nrmLookAround(), etc.
> >Everywhere the library uses self.contents will need to be
> >adapted to multiple lists. Adding a new disposition means
> >adding a new list, and that would require modification to
> >these functions.
>
> When I said the library was in alpha-testing, perhaps you
> misunderstood. I've already written it. I'm just making sure it
> works. Most of the problems I've found are in other things that I've
> done.

Excellent.

> >Once modified under a bit-flag system [isIn() and isCarrying()] are
> >unlikely to need changing.
>
> I believe.
>
> > 7. Listing Functions. These too, would need modification in
> >roughly similar manner to what has been done in multiform.
>
> Not really. Most of the changes I made there were in adding
> tall contents. listcont takes an additional parameter for position
> (and many, many others for other things), and that's all.
>
> > 8. Display in Classes. Separate lists require modification to
> >all classes that display contents, such as surfaces, containers,
> >and openables to display the corresponding appropriate list.
>
> Not really. Objects, by default, call a routine called
> listallcontents which, well, lists all non-"q" contents as well
> as contents' contents.
>
> > 9. Surfaces Which Are Containers. It would be possible to adapt
> >the above classes to display multiple-contents lists (a box could
> >display the in-list and the on-list), but these classes would then
> >have to have verification and action methods to gain access to
> >these other contents lists.
>
> Come again? Verification and action methods? You mean, such
> as "doLookunder"? That's hardly difficult work. You mean, such
> as "ioPutUnder"? Again, that's not much, especially when you've
> made a verified moveInto function (i.e. a function which performs
> a moveInto only after making all the standard doPutX checks).

It's interesting how we've approached this. You check your doVerbs first
then perform a moveInto() with specialized results, and I've just done the
moveInto (no other choice when you've got a single contents!) and then set
my attribute.

> class thing: object
> verDoPutUnder( actor, io ) = {
> if (io = nil) return;
> if (self.locate = io and self.position = Under)
> "\^<< thedesc >> << fmtAre >> already underneath <<
> io.thedesc >>. ";
> else if (io = self)
> "\^<< self.thedesc >> cannot be put underneath itself. ";
> else if (io.isIn( self )) self.circularmessage(io);
> else self.verifyRemove( actor );
> }
> doPutIn( actor, io ) = {
> self.checkDrop;
> if (self.putInto( io, In )) "Done. ";
> }
> ;
>
> That's all.
>
> > Creating a homogoneous object (like thing) which would allow
> >all actions would require verification and action methods to either
> >prevent or customize them and would be a bother. Otherwise
> >something like the multiform class, which accomodates both
> >sets of behaviour is required.
>
> Please explain what you mean.

Yes, it's not very clear. The ordinary classes of ADV.T such as surface and
container were not meant to be combined. There isn't a class that handles
both putIn and putOn actions. So a choice has to be made. Either you allow
all objects to be putIn/putOn by adding verification/action methods to them,
or you stick to the specialized classes: surface, containter.

Obviously you wouldn't want to add methods to every object. But for then
you're left with having to create a special class that handles both putIn
andn putOn, like multiform does. It then becomes a matter of how you do
this. For instance, multiform is simply an object class that handles the
organizing of descriptions. All the behaviours come from surface and
container classes. Otherwise I'd have to create a multiform_openable,
multiform_container, etc. I didn't want to do that, because I wanted to
minimalize.

> > 10. Maintenance. Library extensions that incorporate a new
> >disposition would need to take all of the previous into consideration.
> >A single-contents list system using a boolean would require less
> >modification, and one using bit-flags could potentially be
> >'plug-n-play' without having to modify the library.
>
> Potentially, but probably not. listing functions, particularly, would
> be hard to expand upon, given that they would really need to
> run through the flag and check for every single combination
> of flags (since In|Floating and On|Floating and In and On would all be
> different locations and perhaps, therefore, ideally listed
> seperately).
>
> If I were seriously considering an easy-addition system like that,
> things could be traightforwardly fixed so that new positions would be
> no more difficult to add than in a bit-flag system. Now that you
> mention it, I probably should develop just such a system, just in case
> an author descides it would be easier to have one. Thank you.

So many ways to do things with TADS! That's why I love it.

> >Obviously it helps to proceed from a model, as WorldClass
> >apparently provides, but it looks like an awful lot of work to
> >create a multiple-contents list system in order to add a new
> >class: (container/surface) or even a new concept: inside/outside
> >to what is basically an already well-crafted and sound world model.
>
> True, but it has already been created.

I wrote this post before you explained your system ... WorldClass obviously
is closer to what I've done than what you've created.

Agreed. It's terribly ugly using bit-flags, even when they combine
attributes. Tough enough having isOnSurface = true. But then, the bit-flag
approach does offer the added flexibility.

> >...need an additional method or function that could be passed the
> >object and bit-value constant, returning true or nil, to determine
> >an object's disposition.
>
> A method sounds good; that way, no arguments need be passed at all.
>
> > onbottom|inside.
>
> Hmm. I'm not sure using onbottom like this is a good idea, since
> there's no real connection between an object being below its
> container and one being on the bottom of the container.
>
> > * The addition of a new class of object or disposition could
> >then pick up an inherited disposition, building upon it.
>
> How would this be useful with anything besides "floating"?
> I mean, sure, you can put objects into the same location this
> way, but is that useful?

I haven't given it any thought beyond its ability to carry 'extra'
information. I wasn't planning to expand into multiple dispositions when I
came up with multiform, and this is just an extension of the single-contents
list attribute approach. How it would function beyond 'two-dimensions', so
to speak, is something else.

> > * The listing functions can display as little or as much information
> >as desired, simply by checking the flag. These functions could
> >be tailored to accept bitflags parameters that allow them to display
> >different levels of information.
>
> You mean, bitflag masking? "Only list objects with non-zero
> dispositions when masked with X?" Well, okay.

Actually I had something in mind along the lines of Inform's listing
function bit-flags, english-style, etc. Merely checking the bit-flag from
within the listing function and displaying different levels based on a
style-requested parameter (itself a bit-flag) which would be used for
selection.

Hmmm. The future's going to get very interesting. Basically TADS is
expanding by leaps and bounds. There's a general feeling in the air that
some kind of multiple containment system should fit into this new universe.
When I wrote multiform it was a mere foray into this realm. I'm certain that
this isn't the final word in multiple containment. Plenty of room for the
imagination to play in.

--Kevin


TenthStone

unread,
Jun 29, 1999, 3:00:00 AM6/29/99
to
On Fri, 25 Jun 1999 22:56:13 +0100, "Kevin Forchione"
<Lys...@email.msn.com> wrote:

>TenthStone <mcc...@erols.com> wrote in message
>news:37729344...@news.erols.com...

>> As for moveInto, how do you tell it to move an object into a surface?
>> YOu don't. You tell it to move the object into a container, and then
>> tell the object that it's on a surface. That's hardly "clean".

Wow, that was antagonistic enough. Sorry.

>Actually, this is handled very simply. moveInto() sets the isOnSurface to
>nil in *every* case. It is set to true only for those cases such as doPutOn,
>where the action method is obviously surface-related. This means that any
>new verb actions which are surface-related would need to set this attribute
>too.

Well, that's what I mean. multiple-list moveInto, by necessity,
has to know where it's putting objects, so it only takes me one line
to do it. Of course, that's not some terrifically important point,
since if you liked you could easily add a flag argument to moveInto
yourself.

>The reasonings and examples that I've presented here and previously have all
>been targetted at a 'simple' method of adapting pre-existing systems, such
>as ADV.T to include this class (Obviously WorldClass doesn't need it!).
>Basically I chose a single-contents system because it involved the least
>amount of modification and coding -- not that it is a superior approach, but
>an equivalent one.

True.

>> > 5. Verification and Action methods. Verification methods
>> >will need to check to see if an object already possesses
>> >a certain disposition. If the action is successful a mechanism
>> >must remove the object from one list and move it to another.
>> >Almost as simple as flipping a boolean.
>>
>> Again, you're right. Multiple contents lists require more maintenance
>> on behalf of the libraries. They shave off work in other places,
>> such as display and accessibility/visibility.
>
>Seems there's always a trade-off with any approach.

Yeah, ain't it terrible?

>> When I said the library was in alpha-testing, perhaps you
>> misunderstood. I've already written it. I'm just making sure it
>> works. Most of the problems I've found are in other things that I've
>> done.
>
>Excellent.

Having said that, it'll probably be in alpha-testing for at least
another month. Heh.

>It's interesting how we've approached this. You check your doVerbs first
>then perform a moveInto() with specialized results, and I've just done the
>moveInto (no other choice when you've got a single contents!) and then set
>my attribute.

Yeah, that's true enough.

>Obviously you wouldn't want to add methods to every object. But for then
>you're left with having to create a special class that handles both putIn
>andn putOn, like multiform does. It then becomes a matter of how you do
>this. For instance, multiform is simply an object class that handles the
>organizing of descriptions. All the behaviours come from surface and
>container classes. Otherwise I'd have to create a multiform_openable,
>multiform_container, etc. I didn't want to do that, because I wanted to
>minimalize.

Well, okay. I mean, you're left with special behaviour, but that's
life.

>> If I were seriously considering an easy-addition system like that,
>> things could be traightforwardly fixed so that new positions would be
>> no more difficult to add than in a bit-flag system. Now that you
>> mention it, I probably should develop just such a system, just in case
>> an author descides it would be easier to have one. Thank you.
>
>So many ways to do things with TADS! That's why I love it.

By the way, the model as it currently stands works. I haven't tried
adding new positions yet, but that should work as well. It's a fairly
involved procedure, but it can be accomplished in a minimal amount
of time.

>I wrote this post before you explained your system ... WorldClass obviously
>is closer to what I've done than what you've created.

Well, at least as much as WorldClass is close to anything else.

>Agreed. It's terribly ugly using bit-flags, even when they combine
>attributes. Tough enough having isOnSurface = true. But then, the bit-flag
>approach does offer the added flexibility.

True. Of course, you can always use flags on top of what's already
there -- for, say, a class of objects which float right above the
surface where they've been put.

>> How would this be useful with anything besides "floating"?
>> I mean, sure, you can put objects into the same location this
>> way, but is that useful?
>
>I haven't given it any thought beyond its ability to carry 'extra'
>information. I wasn't planning to expand into multiple dispositions when I
>came up with multiform, and this is just an extension of the single-contents
>list attribute approach. How it would function beyond 'two-dimensions', so
>to speak, is something else.

Oh, okay. When you entitled the section "The Future of Multiform", I
thought you were planning to do something like that.

>> > * The listing functions can display as little or as much information
>> >as desired, simply by checking the flag. These functions could
>> >be tailored to accept bitflags parameters that allow them to display
>> >different levels of information.
>>
>> You mean, bitflag masking? "Only list objects with non-zero
>> dispositions when masked with X?" Well, okay.
>
>Actually I had something in mind along the lines of Inform's listing
>function bit-flags, english-style, etc. Merely checking the bit-flag from
>within the listing function and displaying different levels based on a
>style-requested parameter (itself a bit-flag) which would be used for
>selection.

Ah -- rather different from what I understood.

That would require a fairly complex listing function -- personally, I
would tend towards exploiting the more object-oriented features of
TADS in this regard. But, then again, Inform's been doing it that way
for awhile now.

>Hmmm. The future's going to get very interesting. Basically TADS is
>expanding by leaps and bounds. There's a general feeling in the air that
>some kind of multiple containment system should fit into this new universe.
>When I wrote multiform it was a mere foray into this realm. I'm certain that
>this isn't the final word in multiple containment. Plenty of room for the
>imagination to play in.

Well, one would hope.

Reply all
Reply to author
Forward
0 new messages