!---------------------------------------------------------------------------- ! OutOfRch.h version 2.00 4/7/99 ! Marnie Parker aka Doe aka FemaleDeer doeadeer3@aol.com !---------------------------------------------------------------------------- ! 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; ];