precond.t
#charset "us-ascii"
/*
* Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved.
*
* TADS 3 Library: Pre-Conditions.
*
* This module defines the library pre-conditions. A pre-condition is an
* abstract object that encapsulates a condition that is required to
* apply before a command can be executed, and optionally an implied
* command that can bring the condition into effect. Pre-conditions can
* be associated with actions or with the objects of an action.
*/
#include "adv3.h"
/* ------------------------------------------------------------------------ */
/*
* An action pre-condition object. Each condition of an action is
* represented by a subclass of this class.
*/
class PreCondition: object
/*
* Check the condition on the given object (which may be nil, if
* this condition doesn't apply specifically to one of the objects
* in the command). If it is possible to meet the condition with an
* implicit command, and allowImplicit is true, try to execute the
* command. If the condition cannot be met, report a failure and
* use 'exit' to terminate the command.
*
* If allowImplicit is nil, an implicit command may not be
* attempted. In this case, if the condition is not met, we must
* simply report a failure and use 'exit' to terminate the command.
*/
checkPreCondition(obj, allowImplicit) { }
/*
* Verify the condition. This is called during the object
* verification step so that the pre-condition can add verifications
* of its own. This can be used, for example, to add likelihood to
* objects that already meet the condition. Note that it is
* generally not desirable to report illogical for conditions that
* checkPreCondition() enforces, because doing so will prevent
* checkPreCondition() from ever being reached and thus will prevent
* checkPreCondition() from attempting to carry out implicit actions
* to meet the condition.
*
* 'obj' is the object being checked. Note that because this is
* called during verification, the explicitly passed-in object must
* be used in the check rather than the current object in the global
* current action.
*/
verifyPreCondition(obj) { }
/*
* Precondition execution order. When we execute preconditions for a
* given action, we'll sort the list of all applicable preconditions
* in ascending execution order.
*
* For the most part, the relative order of two preconditions is
* arbitrary. In some unusual cases, though, the order is important,
* such as when applying one precondition can destroy the conditions
* that the other would try to create but not vice versa. When the
* order doesn't matter, this can be left at the default setting.
*/
preCondOrder = 100
;
/* ------------------------------------------------------------------------ */
/*
* A pre-condition that applies to a specific, pre-determined object,
* rather than the direct/indirect object of the command.
*/
class ObjectPreCondition: PreCondition
construct(obj, cond)
{
/*
* remember the specific object I act upon, and the underlying
* precondition to apply to that object
*/
obj_ = obj;
cond_ = cond;
}
/* route our check to the pre-condition using our specific object */
checkPreCondition(obj, allowImplicit)
{
/* check the precondition */
return cond_.checkPreCondition(obj_, allowImplicit);
}
/* route our verification check to the pre-condition */
verifyPreCondition(obj)
{
cond_.verifyPreCondition(obj_);
}
/* use the same order as our underlying condition */
preCondOrder = (cond_.preCondOrder)
/* the object we check with the condition */
obj_ = nil
/* the pre-condition we check */
cond_ = nil
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: object must be visible. This condition doesn't
* attempt any implied command to make the object visible, but merely
* enforces visibility before allowing the command.
*
* This condition is useful for commands that rely on visibly inspecting
* the object, such as "examine" or "look in". It is possible for an
* object to be in scope without being visible, since an object can be
* in scope by way of a non-visual sense.
*
* We enforce visibility with a verification test, not a precondition
* check.
*/
objVisible: PreCondition
verifyPreCondition(obj)
{
/* if the object isn't visible, disallow the command */
if (obj != nil && !gActor.canSee(obj))
{
/*
* If the actor is in the dark, that must be the problem.
* Otherwise, if the object can be heard or smelled but not
* seen, say so. In any other case, issue a generic message
* that we can't see the object.
*/
if (!gActor.isLocationLit())
inaccessible(&tooDarkMsg);
else if (obj.soundPresence && gActor.canHear(obj))
inaccessible(&heardButNotSeenMsg, obj);
else if (obj.smellPresence && gActor.canSmell(obj))
inaccessible(&smelledButNotSeenMsg, obj);
else
inaccessible(&mustBeVisibleMsg, obj);
}
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: object must be audible; that is, it must be within
* hearing range of the actor. This condition doesn't attempt any
* implied command to make the object audible, but merely enforces
* audibility before allowing the command.
*
* It is possible for an object to be in scope without being audible,
* since an object can be inside a container that is transparent to
* light but blocks all sound.
*
* We enforce this condition with a verification test.
*/
objAudible: PreCondition
verifyPreCondition(obj)
{
/* if the object isn't audible, disallow the command */
if (obj != nil && !gActor.canHear(obj))
inaccessible(&cannotHearMsg, obj);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: object must be within smelling range of the actor.
* This condition doesn't attempt any implied command to make the object
* smellable, but merely enforces the condition before allowing the
* command.
*
* It is possible for an object to be in scope without being smellable,
* since an object can be inside a container that is transparent to
* light but blocks all odors.
*
* We enforce this condition with a verification test.
*/
objSmellable: PreCondition
verifyPreCondition(obj)
{
/* if the object isn't within sense range, disallow the command */
if (obj != nil && !gActor.canSmell(obj))
inaccessible(&cannotSmellMsg, obj);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: actor must be standing. This is useful for travel
* commands to ensure that the actor is free of any entanglements from
* nested rooms prior to travel.
*/
actorStanding: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* check to see if the actor is standing - if so, we're done */
if (gActor.posture == standing)
return nil;
/* the actor isn't standing - try a "stand up" command */
if (allowImplicit && tryImplicitAction(Stand))
{
/*
* make sure that leaves the actor standing - if not,
* exit silently, since the reason for failure will have
* been reported by the "stand up" action
*/
if (gActor.posture != standing)
exit;
/* indicate that we executed an implicit command */
return true;
}
/* we can't stand up implicitly - report the problem and exit */
reportFailure(&mustBeStandingMsg);
exit;
}
;
/*
* Pre-condition: actor must be "travel ready." The exact meaning of
* "travel ready" is provided by the actor's immediately container. The
* 'obj' argument is always the travel connector to be traversed.
*/
actorTravelReady: PreCondition
checkPreCondition(obj, allowImplicit)
{
local loc = gActor.location;
/* check to see if the actor is standing - if so, we're done */
if (loc.isActorTravelReady(obj))
return nil;
/* the actor isn't standing - try a "stand up" command */
if (allowImplicit && gActor.location.tryMakingTravelReady(obj))
{
/*
* make sure that the actor really is travel-ready now - if
* not, exit silently, since the reason for failure will have
* been reported by the implicit action
*/
if (!loc.isActorTravelReady(obj))
exit;
/* indicate that we executed an implicit command */
return true;
}
/* we can't make the actor travel-ready - report failure and exit */
reportFailure(loc.notTravelReadyMsg);
exit;
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the traveler is directly in the given room. This will
* attempt to remove the traveler from any nested rooms within the given
* room, but cannot perform travel between rooms not related by
* containment.
*
* Note that the traveler is not necessarily the actor, because the actor
* could be in a vehicle.
*
* This is a class, because it has to be instantiated with more
* parameters than just a single 'obj' passed by default when evaluating
* preconditions. In particular, we need to know the actor performing
* the travel, the connector being traversed, and the room we need to be
* directly in.
*/
class TravelerDirectlyInRoom: PreCondition
construct(actor, conn, loc)
{
/* remember the actor, connector, and room */
actor_ = actor;
conn_ = conn;
loc_ = loc;
}
checkPreCondition(obj, allowImplicit)
{
/* ask the traveler to do the work */
return actor_.getTraveler(conn_)
.checkDirectlyInRoom(loc_, allowImplicit);
}
/* the actor doing the travel */
actor_ = nil
/* the connector being traversed */
conn_ = nil
/* the room we need to be directly in */
loc_ = nil
;
/*
* Pre-condition: the actor is directly in the given room. This differs
* from TravelerDirectlyInRoom in that this operates directly on the
* actor, regardless of whether the actor is in a vehicle.
*/
actorDirectlyInRoom: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* ask the actor to do the work */
return gActor.checkDirectlyInRoom(obj, allowImplicit);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: actor is ready to enter a nested location. This is
* useful for commands that cause travel within a location, such as "sit
* on chair": this ensures that the actor is either already in the given
* nested location, or is in the main location; and that the actor is
* standing. We simply call the actor to do the work.
*/
actorReadyToEnterNestedRoom: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* ask the actor to make the determination */
return gActor.checkReadyToEnterNestedRoom(obj, allowImplicit);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the target actor must be able to talk to the object.
* This is useful for actions that require communications, such as ASK
* ABOUT, TELL ABOUT, and TALK TO.
*/
canTalkToObj: PreCondition
checkPreCondition(obj, allowImplicit)
{
/*
* if the current actor can't talk to the given object, disallow
* the command
*/
if (obj != nil && !gActor.canTalkTo(obj))
{
reportFailure(&objCannotHearActorMsg, obj);
exit;
}
/* we don't perform any implicit commands */
return nil;
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: object must be held. This condition requires that an
* object of a command must be held by the actor. If it is not, we will
* attempt a recursive "take" command on the object.
*
* This condition is useful for commands where the object is to be
* manipulated in some way, or used to manipulate some other object.
* For example, the key in "unlock door with key" would normally have to
* be held.
*/
objHeld: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if the object is already held, there's nothing we need to do */
if (obj == nil || obj.meetsObjHeld(gActor))
return nil;
/* the object isn't being held - try an implicit 'take' command */
if (allowImplicit && obj.tryHolding())
{
/*
* we successfully executed the command; check to make sure
* it worked, and if not, abort the command without further
* comment (if the command failed, presumably the command
* showed an explanation as to why)
*/
if (!obj.meetsObjHeld(gActor))
exit;
/* tell the caller we executed an implicit command */
return true;
}
/* it's not held and we can't take it - fail */
reportFailure(&mustBeHoldingMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
/* lower the likelihood rating for anything not being held */
verifyPreCondition(obj)
{
/* if the object isn't being held, reduce its likelihood rating */
if (obj != nil && !obj.meetsObjHeld(gActor))
logicalRankOrd(80, 'implied take', 150);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: a given source object must be able to touch the
* object. This requires that the source object (given by our property
* 'sourceObj') has a clear 'touch' path to the target object.
*
* This is a base class for arbitrary object-to-object touch conditions.
* In most cases, you'll want to use the more specific touchObj, which
* tests that the current actor can touch the current object.
*/
class TouchObjCondition: PreCondition
/* construct with a given source object */
construct(src) { sourceObj = src; }
/*
* the source object - this is the object that is attempting to
* touch the target object
*/
sourceObj = nil
/* check the condition */
checkPreCondition(obj, allowImplicit)
{
local pastObs;
/*
* If we can touch the object, we can proceed with no implicit
* actions.
*/
if (sourceObj.canTouch(obj))
return nil;
/* we haven't tried removing any obstructors yet */
pastObs = new Vector(8);
/*
* Repeatedly look for and attempt to remove obstructions.
* There could be multiple things in the way, so try to remove
* each one we find until either we fail to remove an
* obstruction or we run out of obstructions.
*/
for (;;)
{
local stat;
local path;
local result;
local obs;
/* get the path for reaching out and touching the object */
path = sourceObj.getTouchPathTo(obj);
/* if we have a path, look for an obstructor */
if (path != nil)
{
/* traverse the path to find what blocks our touch */
stat = sourceObj.traversePath(path, function(ele, op)
{
/*
* If we can continue the reach via this path element,
* simply keep going. Otherwise, stop the reach here.
*/
result = ele.checkTouchViaPath(sourceObj, obj, op);
if (result.isSuccess)
{
/* no objection here - keep going */
return true;
}
else
{
/* stop here, noting the obstruction */
obs = ele;
return nil;
}
});
/*
* if we now have a clear path, we're done - simply return
* true to indicate that we ran one or more implicit
* commands
*/
if (stat)
return true;
}
else
{
/*
* we have no path, so the object must be in an
* unconnected location; we don't know the obstructor in
* this case
*/
obs = nil;
}
/*
* 'result' is a CheckStatus object explaining why we can't
* reach past 'obs', which is the first object that
* obstructs our reach.
*
* If the obstructor is not visible or we couldn't find one,
* we can't do anything to try to remove it; simply report
* that we can't reach the target object and give up.
*/
if (obs == nil || !gActor.canSee(obs))
{
reportFailure(&cannotReachObjectMsg, obj);
exit;
}
/*
* Ask the obstructor to get out of the way if possible.
*
* If we've already tried to remove this same obstructor on
* a past iteration, don't try again, as there's no reason
* to think an implicit command will work any better this
* time.
*/
if (pastObs.indexOf(obs) != nil
|| !allowImplicit
|| !obs.tryImplicitRemoveObstructor(touch, obj))
{
/*
* We can't remove the obstruction - either we've tried
* an implicit command on this same obstructor and
* failed, or we can't try an implicit command at all.
* In any case, use the explanation of the problem from
* the CheckStatus result object.
*/
reportFailure(result.msgProp, result.msgParams...);
exit;
}
/*
* if the implied command failed, simply give up now -
* there's no need to go on, since the implied command will
* have already explained why it failed
*/
if (gTranscript.currentActionHasReport({x: x.isFailure}))
exit;
/*
* We've tried an implied command to remove this obstructor,
* but that isn't guaranteed to make the target touchable,
* as there could be further obstrutions, or the implied
* command could have failed to actually remove the
* obstruction. Keep iterating. To avoid looping forever
* in the event the implicit command we just tried isn't
* good enough to remove this obstruction, make a note of
* the obstruction we just tried to remove; if we find it
* again on a subsequent iteration, we'll know that we've
* tried before to remove it and failed, and thus we'll know
* to give up without making the same doomed attempt again.
*/
pastObs.append(obs);
}
}
verifyPreCondition(obj)
{
/*
* If there's no source object, do nothing at this point. We can
* have a nil source object when we're resolving nouns for a
* two-object action, and we have a cross-object condition (for
* example, we require that the indirect object can touch the
* direct object). In these cases, when we're resolving the
* first-resolved noun phrase, the second-resolved noun phrase
* won't be known yet. The only purpose of verification at times
* like these is to improve our guess about an ambiguous match,
* but we have nothing to add at such times, so we can simply
* return without doing anything.
*/
if (sourceObj == nil)
return;
/* if we can't touch the object, make it less likely */
if (!sourceObj.canTouch(obj))
{
/*
* If we can't see the object, we must be able to sense it
* by some means other than sight, so it must have a
* sufficiently distinctive sound or odor to put it in
* scope. Explain this: "you can hear it but you can't see
* it", or the like.
*/
if (gActor.canSee(obj))
{
local info;
/*
* It's visible but cannot be reached from here, so it
* must be too far away, inside a closed but transparent
* container, or something like that.
*
* If it's at a distance, rule it illogical, since
* there's not usually anything automatic we can do to
* remove the distance obstruction.
*
* If it's not distant, don't rule it illogical, but do
* reduce the likelihood ranking, so that we'll prefer a
* different object that can readily be touched. Since
* we can see where the object is, we might know how to
* remove the obstruction to reachability.
*/
info = gActor.bestVisualInfo(obj);
if (info != nil && info.trans == distant)
{
/* it's distant - assume we can't fix this */
inaccessible(&tooDistantMsg, obj);
}
else
{
/*
* it's not distant; rank it logical (since we might
* be able to clear the obstruction with an implied
* action), but at reduced likelihood (in case
* there's something that doesn't need any prior
* implied action to reach)
*/
logicalRankOrd(80, 'unreachable but visible', 150);
}
}
else
{
/*
* if it has a sound presence, then "you can hear it but
* you can't see it"; if it has a smell presence, then
* "you can smell it but you can't see it"; otherwise,
* you simply can't see it
*/
if (obj.soundPresence && gActor.canHear(obj))
{
/* it can be heard but not seen */
inaccessible(&heardButNotSeenMsg, obj);
}
else if (obj.smellPresence && gActor.canSmell(obj))
{
/* it can be smelled but not seen */
inaccessible(&smelledButNotSeenMsg, obj);
}
else if (!gActor.isLocationLit())
{
/* it's too dark to see the object */
inaccessible(&tooDarkMsg);
}
else
{
/* it simply cannot be seen */
inaccessible(&mustBeVisibleMsg, obj);
}
}
}
}
/*
* This condition tends to be fragile, in the sense that other
* preconditions for the same action have the potential to undo any
* implicit action that we perform to make an object touchable. This
* is most likely to happen when we implicitly move the actor (moving
* in or out of a nested room, for example) to put the actor within
* reach of the target object. To reduce the likelihood that this
* fragility will be visible to a player, try to execute this
* condition after other conditions. Most other preconditions tend
* to be "stickier" - less likely to be undone by subsequent
* preconditions.
*/
preCondOrder = 200
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: actor must be able to touch the object. This doesn't
* require that the actor is actually holding the object, but the actor
* must be able to physically touch the object. This ensures that the
* actor and object are not, for example, separated by a transparent
* barrier.
*
* If there is a transparent barrier, we will attempt to remove the
* barrier by calling the barrier object's tryImplicitRemoveObstructor
* method. Objects that can be opened in an obvious fashion will
* perform an implicit recursive "open" command, and other types of
* objects can provide customized behavior as appropriate.
*/
touchObj: TouchObjCondition
/* we want to test reaching from the current actor to the target object */
sourceObj = (gActor)
;
/*
* Pre-condition: the indirect object must be able to touch the target
* object. This can be used for actions where the direct object is going
* to be manipulated by an "agent" of the action (i.e., the indirect
* object), rather than directly by the actor: MOVE X WITH Y, for
* example.
*
* Note that the target object of this condition should be the direct
* object in most cases, so this condition should usually be used like
* this:
*
* dobjFor(MoveWith) { preCond = [iobjTouchObj] }
*
* In other words, this is a precondition that we apply in most cases to
* the *direct* object.
*/
iobjTouchObj: TouchObjCondition
/* the indirect object has to be able to touch the target object */
sourceObj = (gIobj)
;
/*
* Pre-condition: the direct object can touch the target object. This
* is useful for situations where the direct object is being manipulated
* directly and the indirect object is more of a passive participant in
* the action, such as PLUG CORD INTO OUTLET.
*/
dobjTouchObj: TouchObjCondition
/* the direct object has to be able to touch the target object */
sourceObj = (gDobj)
;
/* ------------------------------------------------------------------------ */
/*
* A precondition ensuring that the target object is in the same
* immediate location as a given object.
*/
class SameLocationCondition: PreCondition
/*
* construct dynamically, setting the other object whose location we
* must match
*/
construct(obj) { sourceObj = obj; }
/* the object whose location we must match */
sourceObj = nil
/* check the condition */
checkPreCondition(obj, allowImplicit)
{
local moveObj;
local targetLoc;
/* if we're in the same container, we're fine */
if (obj.location == sourceObj.location)
return nil;
/*
* Pick an object to move. By default, pick the target object;
* but if the target object is non-portable, then trying to move
* it will fail, so pick the source object.
*/
if (obj.ofKind(NonPortable))
{
/* 'obj' is unportable, so try moving the source object */
moveObj = sourceObj;
targetLoc = obj.location;
}
else
{
/* 'obj' is portable, so try moving it by default */
moveObj = obj;
targetLoc = sourceObj.location;
}
/* try moving the object, and return the result */
if (allowImplicit && targetLoc.tryMovingObjInto(moveObj))
{
/* if it didn't work, abort the action */
if (obj.location != sourceObj.location)
exit;
/* tell the caller we executed an implied action */
return true;
}
/* we can't move it - report the failure and abort the action */
targetLoc.mustMoveObjInto(moveObj);
exit;
}
;
/*
* require that the target object be in the same immediate location as
* the direct object
*/
sameLocationAsDobj: SameLocationCondition
sourceObj = (gDobj)
;
/*
* require that the target object be in the same immediate location as
* the indirect object
*/
sameLocationAsIobj: SameLocationCondition
sourceObj = (gIobj)
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: actor must have room to hold the object directly (such
* as in the actor's hands). We'll let the actor do the work.
*/
roomToHoldObj: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* let the actor check the precondition */
return gActor.tryMakingRoomToHold(obj, allowImplicit);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the actor must not be wearing the object. If the
* actor is currently wearing the object, we'll try asking the actor to
* doff the object.
*
* Note that this pre-condition never needs to be combined with objHeld,
* because an object being worn is not considered to be held, and
* Wearable implicitly doffs an article when it must be held.
*/
objNotWorn: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if the object isn't being worn, we have nothing to do */
if (obj == nil || !obj.isWornBy(gActor))
return nil;
/* try an implicit 'doff' command */
if (allowImplicit && tryImplicitAction(Doff, obj))
{
/*
* we executed the command - make sure it worked, and abort
* if it didn't
*/
if (obj.isWornBy(gActor))
exit;
/* tell the caller we executed an implicit command */
return true;
}
/* report the problem and terminate the command */
reportFailure(&cannotBeWearingMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
/* lower the likelihood rating for anything being worn */
verifyPreCondition(obj)
{
/* if the object is being worn, reduce its likelihood rating */
if (obj != nil && obj.isWornBy(gActor))
logicalRankOrd(80, 'implied doff', 150);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the object is open.
*/
class ObjOpenCondition: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if the object is already open, we're already done */
if (obj == nil || obj.isOpen)
return nil;
/* try an implicit 'open' command on the object */
if (allowImplicit && tryImplicitAction(Open, obj))
{
/*
* we executed the command - make sure it worked, and abort
* if it didn't
*/
if (!obj.isOpen)
exit;
/* tell the caller we executed an implied command */
return true;
}
/* can't open it implicitly - report the failure */
conditionFailed(obj);
exit;
}
/*
* The condition failed - report the failure and give up. We
* separate this to allow subclasses to report failure differently
* for specialized types of opening.
*/
conditionFailed(obj)
{
/* can't open it implicitly - report failure and give up */
reportFailure(&mustBeOpenMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
}
/* reduce the likelihood rating for anything that isn't already open */
verifyPreCondition(obj)
{
/* if the object is closed, reduce its likelihood rating */
if (obj != nil && !obj.isOpen)
logicalRankOrd(80, 'implied open', 150);
}
;
/*
* The basic object-open condition
*/
objOpen: ObjOpenCondition;
/*
* Pre-condition: a door must be open. This differs from the regular
* objOpen condition only in that we use a customized version of the
* failure report.
*/
doorOpen: ObjOpenCondition
conditionFailed(obj)
{
/*
* We can generate implicit open-door commands as a result of
* travel, which means that the actor issuing the command might
* never have explicitly referred to the door. (This is not the
* case for most preconditions, which refer to objects directly
* used in the command and thus within the actor's awareness, at
* least initially.) So, if the door isn't visible to the
* actor, don't tell the actor they have to open the door;
* instead, just show the standard no-travel message for the
* door.
*/
if (gActor.canSee(obj))
{
/* they can see the door, so tell them they need to open it */
reportFailure(&mustOpenDoorMsg, obj);
/* set this as the pronoun antecedent */
gActor.setPronounObj(obj);
}
else
{
/*
* they can't see the door - call the door's routine to
* indicate that travel is not possible
*/
obj.cannotTravel();
}
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the object is closed.
*/
objClosed: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if the object is already closed, we're already done */
if (obj == nil || !obj.isOpen)
return nil;
/* try an implicit 'close' command on the object */
if (allowImplicit && tryImplicitAction(Close, obj))
{
/*
* we executed the command - make sure it worked, and abort
* if it didn't
*/
if (obj.isOpen)
exit;
/* tell the caller we executed an implied command */
return true;
}
/* can't close it implicitly - report failure and give up */
reportFailure(&mustBeClosedMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
/* reduce the likelihood rating for anything that isn't already closed */
verifyPreCondition(obj)
{
/* if the object is closed, reduce its likelihood rating */
if (obj != nil && obj.isOpen)
logicalRankOrd(80, 'implied close', 150);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the object is unlocked.
*/
objUnlocked: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if the object is already unlocked, we're already done */
if (obj == nil || !obj.isLocked)
return nil;
/* try an implicit 'unlock' command on the object */
if (allowImplicit && tryImplicitAction(Unlock, obj))
{
/*
* we executed the command - make sure it worked, and abort
* if it didn't
*/
if (obj.isLocked)
exit;
/* tell the caller we executed an implied command */
return true;
}
/* can't unlock it implicitly - report failure and give up */
reportFailure(&mustBeUnlockedMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
/* reduce the likelihood rating for anything that's locked */
verifyPreCondition(obj)
{
/* if the object is locked, reduce its likelihood rating */
if (obj != nil && obj.isLocked)
logicalRankOrd(80, 'implied unlock', 150);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: destination for "drop" is an outermost room. If the
* drop destination is a nested room, we'll try returning the actor to
* the outermost room via an implicit command.
*/
dropDestinationIsOuterRoom: PreCondition
checkPreCondition(obj, allowImplicit)
{
local dest;
/*
* if the actor's location's drop location is the outermost
* room, we don't need to do anything special
*/
dest = gActor.getDropDestination(obj, nil);
if (dest.getOutermostRoom() == dest)
return nil;
/*
* the default drop destination is not an outermost room; try an
* implicit command to return the actor to an outermost room
*/
return actorDirectlyInRoom.checkPreCondition(
dest.getOutermostRoom(), allowImplicit);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: object is burning. This can be used for matches,
* candles, and the like. If the object's isLit is nil, we'll attempt a
* "burn" command on the object.
*/
objBurning: PreCondition
checkPreCondition(obj, allowImplicit)
{
/* if it's already burning, there's nothing to do */
if (obj == nil || obj.isLit)
return nil;
/* try an implicit 'burn' command */
if (allowImplicit && tryImplicitAction(Burn, obj))
{
/* we executed a 'burn' - give up if it didn't work */
if (!obj.isLit)
exit;
/* tell the caller we executed an implied command */
return true;
}
/* we can't burn it implicitly - report failure and give up */
reportFailure(&mustBeBurningMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
verifyPreCondition(obj)
{
/* if the object is not already burning, reduce its likelihood */
if (obj != nil && !obj.isLit)
logicalRankOrd(80, 'implied burn', 150);
}
;
/* ------------------------------------------------------------------------ */
/*
* Pre-condition: the object is empty. This ensures that the object
* does not contain any other objects.
*
* Note that we unconditionally try to remove all objects. If a
* container needs to have some objects that can be removed and others
* that can't (such as components within the container), then the
* container will have to be implemented as a ComplexContainer - the
* non-removable components should be made contents of the enclosing
* ComplexContainer, and the secret inner container should be the one
* subject to this precondition.
*/
objEmpty: PreCondition
checkPreCondition(obj, allowImplicit)
{
local chi;
/*
* if there's no object, or the object already has no contents,
* there's nothing to do
*/
if (obj == nil || obj.contents.length() == 0)
return nil;
/*
* Try an implicit 'take x' on the object's first child.
*
* Note that we only try this on the first object, because the
* precondition mechanism automatically re-applies all
* preconditions after any one of them performs an implied
* command. If we have multiple objects that must be removed,
* that basic loop will ensure that we'll come back here as many
* times as necessary.
*/
chi = obj.contents[1];
if (allowImplicit && tryImplicitAction(TakeFrom, chi, obj))
{
/* make sure it worked */
if (chi.isIn(obj))
exit;
/* tell the caller we tried an implied command */
return true;
}
/* we can't remove the objects implicitly, so give up */
reportFailure(&mustBeEmptyMsg, obj);
/* make it the pronoun */
gActor.setPronounObj(obj);
/* abort the command */
exit;
}
;
TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3