!----------------------------------------------------------------------------
! OutOfRch.h version 2.00 4/7/99
! Marnie Parker aka Doe aka FemaleDeer
[email protected]
!----------------------------------------------------------------------------
! HISTORY
!
! Version 2.00 fixes an add_to_scope bug (when the scoped object is within the
! player) found by David Ledgard. Additionally, it adds a routine ability to
! reach_zones and allows the changing outofreach's default (take all) when no
! reach_zones are defined. Also included are new comments, including a warning
! note about classes and additive/non-additive properties.
! Version 1.01 fixes a slight bug in version 1.00.
!----------------------------------------------------------------------------
! PURPOSE (SUMMARY)
!
! A simple way (without scoping) to set limits on reaching when the player
! is in an enterable supporter or container, so they can take what is nearby
! but not what is in the rest of the room. The property, reach_zones, is used
! to define what other "areas" (stationary supporters/containers) are in reach
! (in addition to the player and enterable object, which are always in reach).
!
! Example: If the player is on a chair in front of a desk, the desk and desk
! drawer could be defined as reach_zones, within reach, and everything else
! as out of reach. If no reach_zones are defined, the property, limittake, can
! be set true to define everything in the location (except the player and
! enterable object) as out of reach.
!
! Can also include objects "on the floor" (when includefloor is true). Note
! that with the addition of the property, limittake, this routine has become a
! bit more complex than I would like, but due to user requests/misunderstandings
! it appears it have become necessary.
!
! Additionally lets the player exit a supporter or container without a new
! <Look> and, if the player is on a supporter, can drop items to the floor
! instead of onto the supporter. Usually I don't like dropped items ending up
! on the same thing that the player is sitting on (like that chair).
!
! Of course, when exiting, if the player is on a supporter on a supporter (or
! a container in a container), this will only exit the player to the next
! level. This can be easily be changed by changing the line, move player to
! parent(self), to, move player to location. Dropping can also be easily
! changed to drop the object to the next level, by changing the line, move
! noun to location, to, move noun to parent(self). However, the latter is
! the Inform default, anyway.
!
! The above is accomplished with a new class, InsideOrOn, for enterable
! supporters/containers. The react_before of this class does the in reach
! checking, exiting and dropping and is only called when the player is in
! the enterable object (the member of the InsideOrOn class).
! Include this file after parser.h.
!----------------------------------------------------------------------------
! NOTE ON CLASSES
!
! react_before is a non-additive class property. This means that if you create
! an object that is a member of the InsideOrOn class and it also has a
! react_before routine, the object's react_before completely "overwrites" the
! class' react_before. I.E., The class' react_before will NEVER BE called. You
! can, however, explicitly call it by inserting this:
!
! object.InsideOrOn::react_before();
!
! in the object's react_before. Note also that this is the way to call all
! non-additive class properties. Of course, if the object's property returns
! true, the class' property will still not be called. Consider yourself warned.
!----------------------------------------------------------------------------
! USAGE
!
! Note: Objects in the player and enterable object are always within reach.
!
!----------------------------------------------------------------------------
!
! PROPERTIES (top setting in each is the default):
!
! reach_zones (only for other stationary containers/supporters - "furniture")
! 0 = undefined
! array/routine = the area(s) within reach
! includefloor false = only objects in reach_zones (if defined), player
! and enterable object are in reach
! true = also include "takeable" objects "on the floor"
! limittake (applies only when reach_zones are undefined - 0)
! false = everything is within reach
! true = limit take (and other actions) to only objects in the
! player and enterable object (only those are in reach)
! droponfloor (applies only to supporters)
! true = dropped objects end up on the floor
! false = they end up on the supporter
!
!----------------------------------------------------------------------------
! DEFINE: reach_zones similarly to found_in. It can be set to 0 or defined
! with an array of objects or a routine (no strings). However, as with
! found_in, a routine can return only one object, not several. Also, as with
! found_in, the two cannot be combined to include both objects and a routine.
!
! Using the chair mentioned above as an example:
!
! reach_zones 0;
! reach_zones desk;
! reach_zones desk desk_drawer;
! reach_zones [; if (self has general) return desk; return 0; ];
! (in this case, general is a flag for when the chair is in front of the desk)
!
! NOT
!
! reach_zones desk [; if (self has general) return desk_drawer; return 0; ];
!
! This will not work with found_in and it won't work with reach_zones.
!
! Note: If you have a string in your routine, it will be printed twice,
! once when tested by the class InsideOrOn and once by the program. Since
! this is undesirable behavior, do not include a string in your routine.
!
!----------------------------------------------------------------------------
! COMBINATIONS:
!
! reach_zones defined (not 0)
!
! includefloor false only objects in reach_zones, the player and
! enterable object are in reach
!
! includefloor true only objects in reach_zones, "on the floor", in the
! player and enterable object are in reach
!----------------------------------------------------------------------------
! Limittake is only checked when reach_zones are undefined (0).
!
! reach_zones undefined (0)
!
! limittake true only objects in the player and enterable object
! includefloor false are in reach
!
! limittake true only objects "on the floor", in the player and
! includefloor true enterable object are in reach
!
! limittake false all objects are in reach when limittake is false
! includefloor false/true regardless of how includefloor is set, this is
! the Inform default
!----------------------------------------------------------------------------
! Droponfloor only applies to supporters, not containers, and is unaffected by
! the other properties.
!
! droponfloor true drop objects to the floor
! false drop objects to the supporter
!
!
! Note: If the player is on a supporter and they enter, "put x on floor", and
! drop on floor is not set true, the object will be dropped to what the player
! is on. This is normal Inform behavior, regardless of the fact that the word,
! "floor" or "ground", was used. Put noun on floor (d_obj) becomes a drop and,
! as mentioned, Inform normally drops objects to whatever the player is on.
!
!----------------------------------------------------------------------------
! EXAMPLES
!
! Object chair "chair"
! class InsideOrOn
! has supporter
! with name "chair",
! initial "There is a chair in front of the desk.",
! description "It is an ordinary chair.",
! includefloor true,
! reach_zones desk desk_drawer;
! Object table "table"
! class InsideOrOn
! has supporter static
! with name "table",
! initial "There is also a table here, between the desk and closet.",
! description "It is a very large and high.",
! limittake true,
! droponfloor false;
! When the player is on the chair, objects on the table or in the closet would
! be out of reach, while those in the player, on the chair (because the player
! and enterable object are always within reach), the desk, desk drawer and on
! the floor would be in reach (because desk and desk_drawer are defined as
! reach_zones and includefloor is set true). When the player is on the table,
! everything except objects in the player and table would be out of reach
! (because no reach_zones are defined and limittake is set true). Also,
! objects dropped from the chair will land on the floor (because the default
! for droponfloor is true), while those dropped from the table will land on
! the table (because droponfloor is set false).
! Object chair "chair"
! class InsideOrOn
! has supporter
! with name "chair",
! initial
! [; if (self hasnt general) "The chair is by the bed.";
! "The chair is in front of the desk."; ],
! description "It is an ordinary chair.",
! includefloor true,
! reach_zones [; if (self hasnt general) return bed; return desk; ],
! before
! [; Push, Pull : if (player in self)
! "You can't move the chair while you are sitting on it.";
! if (self hasnt general)
! { give self general;
! "You move the chair in front of the desk.";
! }
! else
! { give self ~general;
! "You move the chair next to the bed.";
! }
! ];
! Same as the above chair (see next paragraph), but when the player is on the
! chair, objects on the desk are only reachable when the chair is in front of
! it and objects on the bed are only reachable when the chair is beside it.
! A reach zone with an added_to_scope object automatically includes that scoped
! object. So the desk_drawer would also be reachable when the desk is reachable
! if the desk has add_to_scope desk_drawer. Thus the desk_drawer would not need
! to be defined as a separate reach_zone.
! Object chair "chair"
! class InsideOrOn
! has supporter
! with name "chair",
! initial "There is a chair here.",
! description "It is an ordinary chair.",
! limittake true;
! In this instance, everything not in the player and chair would be out of
! reach (because reach_zones are not defined and limittake is set true).
! Object table "table"
! class InsideOrOn
! has supporter static
! with name "table",
! initial "There is also a table here, between the desk and closet.",
! description "It is a very low table.",
! droponfloor false;
! Now everything would be within reach from the table (because no reach_zones
! are defined and the default for limittake is false). But objects dropped from
! the table would still end up on the table (because droponfloor is set false).
! Also see the note above the class routine, outofreach, for a definition of
! what objects are considered to be "on the floor".
! Hopefully these examples illustrate the various ways that outofrch.h can be
! used. It was initially only intended to provide reach_zones for enterable
! supporters/containers. It has since been altered to also be able to restrict
! what is in reach when no reach_zones are defined.
!----------------------------------------------------------------------------
Class InsideOrOn
has enterable
with reach_zones 0, ! reachable areas (other supporters & containers)
includefloor false, ! (t/f) also include objects "on the floor"
limittake false, ! (t/f) reach_zones = 0, other objects are out of reach
droponfloor true, ! (t/f) drop objects from this supporter to the floor
react_before
[; if (player notin self) rfalse;
Take, Remove, Search, Attack, Open, Close, Lock, Unlock, Push, Pull, Turn,
SwitchOn, SwitchOff, Touch, Taste, Smell, Squeeze, LookUnder, Empty
: if (self.outofreach(noun))
"You can't reach ", (the) noun, " from where you are.";
rfalse;
Insert, Puton, Transfer, EmptyT
: if (action==##EmptyT)
{ if (ObjectIsUntouchable(noun, 1)) rfalse; }
else
{ if (action==##Transfer)
{ if (noun notin player) rfalse; }
else if (parent(noun)~=player) rfalse;
}
if (action==##Insert or ##PutOn)
{ if ((second == d_obj) || (player in second)) <<Drop noun>>; }
if (self.outofreach(second))
"You can't reach ", (the) second, " from where you are.";
rfalse;
Exit : if ((self has container) && (self hasnt open)) rfalse;
move player to parent(self);
if (keep_silent == 0)
{ print "You get ";
if (self has supporter) print "off ";
else print "out of ";
print (the) self, ".^";
}
rtrue;
Drop : if ((self has container) || (~~(self.droponfloor))) rfalse;
if ((noun == player) || (noun notin player)) rfalse;
move noun to location;
if (keep_silent == 0)
print "Dropped.^";
rtrue;
],
! Copy of IndirectlyContains from verblibm.h, altered to include
! ObjectScopedBySomething to fix add_to_scope bug. Ditto with topholder,
! " added to fix add_to_scope bug.
! Find if the second object is in the first object.
contains
[ o1 o2 o3;
while (o2~=0)
{ if (o1==o2) rtrue;
o3 = ObjectScopedBySomething(o2);
if (o3 == 0) o2 = parent(o2);
else o2 = o3;
}
rfalse;
],
! Find the top container (parent) of the object, short of the location.
topholder
[ o1 o2;
while (o1 ~= location)
{ o2 = ObjectScopedBySomething(o1);
if (o2 == 0) o2 = parent(o1);
if (o2 == location) break;
o1 = o2;
}
return o1;
],
! This routine only returns true if the object is found to be definitely
! out of reach. It returns false if the object is within reach. If it is not
! in the location or is in a closed container it also returns false so that
! Inform can handle with its usual error messages.
! *** Note: The only way to differentiate between a movable ("takeable")
! container on the floor that could be within reach if includefloor is true
! and a non-movable (large or piece of scenery) container that could be out
! of reach if not defined in reach_zones, was to define a non-movable container
! (or supporter) as having either of these attributes: enterable, scenery,
! static or concealed. Bear this in mind. This was the bug in version 1.00.
! Examples: A sack object on the floor (if include floor is set true) without
! enterable, scenery, static or concealed would be considered in reach. A
! closet would probably have static (or scenery) and could be defined as a
! reach_zone, within reach, or if not included as a reach_zone, could be out
! of reach (if limittake is set true).
outofreach
[ o c p i j;
! Is the object not in the location or is it in a closed container?
if (ObjectIsUntouchable(o, 1)) rfalse;
! Next check if the object is in player and/or enterable (InsideOrOn) object.
if (self.contains(player, o)) rfalse;
if (self.contains(self, o)) rfalse;
! NO reach_zones are defined and limittake is false (meaning take all), so skip
! the rest of the checking.
if (ZRegion(self.&reach_zones-->0) ~= 1 or 2)
{ if (~~(self.limittake)) rfalse; }
! Find the top "holder" (container/supporter) of the object.
p = self.topholder(o);
! If reach_zones are defined, check them first. If the object (its top holder)
! is in a reach_zone (is the same as a reach zone), it is within reach.
if (ZRegion(self.&reach_zones-->0) == 1 or 2)
{ if (ZRegion(self.&reach_zones-->0) == 2)
{ j = self.reach_zones();
if (j == p) rfalse;
}
else
{ c = self.#reach_zones;
for (i = 0: i < (c/2): i++)
{ j = self.&reach_zones-->i;
if (j == p) rfalse;
}
}
}
! Next, check includefloor for both defined/undefined reach_zones. If
! includefloor is true and the object is "on the floor", it is within reach.
! See *** note above for why these attributes are used.
if (self.includefloor)
{ if (p hasnt enterable && p hasnt scenery && p hasnt static &&
p hasnt concealed) rfalse;
}
! The object isn't in the player, enterable object, a reach zone (if defined)
! or "on the floor" (if includefloor is true), so it is definitely out of reach.
rtrue;
];