# Custom Preconditions

by Eric Eve

## Adapted for Adv3Lite by Eric Eve

Preconditions provide a powerful and reasonably easy-to-use way of enforcing conditions when certain actions are performed on or with certain objects: for example, the Player Character must be able to touch the door in order to open it, and must be holding the key in order to unlock anything with it. Preconditions can be particularly helpful since they can not only enforce conditions (such as that the key must be held before it can be used), but can also bring them about through implicit actions: if the key to the door is lying in plain sight it's simply annoying for the player to be told "You must be holding the key before you can unlock anything with it"; it's much neater if the parser recognizes what the player intends and makes the Player Character first pick up the key automatically so that the unlocking can proceed.

The standard library already defines many commonly useful preconditions, and applies them appropriately to a large range of actions (for further details, see the Preconditions section of the Technical Manual Article on Action Results). Sometimes, though, it can be useful to define preconditions of your own in your own game. This article will show you how.

## New Object Preconditions

We'll go on to discuss how to define a brand new precondition below, but we'll start by pointing out that this isn't always necessary. Sometimes we can get the effect we want by constructing a custom precondition with `new ObjectPreCondition` together with an existing precondition and the particular object we want it to apply to.

For example, suppose we have defined a new WRITE ON action, of the form WRITE ZANZIBAR ON PAPER; we don't want the Player Character to be able to write on the paper unless he's holding a pen, but we don't want to force the player to use the awkward syntax WRITE ZANZIBAR ON PAPER WITH PEN. One way to deal with with would be to make holding the pen a precondition of writing anything on the piece of paper. This would enforce the availability of a pen as a condition of writing, but it would also result in the Player Character picking up the pen as an implicit action if the pen is to hand but not held when the player issues a command like WRITE ZANZIBAR ON PAPER:

    >write Zanzibar on paper
    (first picking up the pen)
    You write "Zanzibar" on the piece of paper.

Since the implicit action (picking up the pen), and hence the precondition being enforced, involves an object (the pen) not explicitly mentioned in the player's command, you might think you'd have to write a special `penHeld` precondition to deal with it. But in fact there's no need for this, since we can simply use the existing `objHeld` precondition and apply it to a different object via the `new ObjectPreCondition` construct. This is typically used thus:

        new ObjectPreCondition(obj, cond)

Where `obj` is the object to which we want the `cond` precondition to apply.

So, for example, assuming we've already defined the a new WriteLiteralOnAction, if on the piece of paper we defined:

       paperPiece: Thing 'piece of paper'
          dobjFor(WriteLiteralOn)
          {
    	      preCond = [objHeld]
          }
       ;

Then we'd be making holding the piece of paper a precondition of writing on it (which we may or may not want), because a precondition normally applies to the object on which it's listed for a particular action. But since in this case what we really want is to enforce the precondition that the pen must be held in order to write on the paper, we can use the `new ObjectPreCondition` construct to 'redirect' the objHeld precondition from the paper to the pen:

    + paperPiece: Readable 'piece/paper' 'piece of paper'
        dobjFor(WriteLiteralOn)
        {
            preCond = [touchObj, new ObjectPreCondition(pen, objHeld)]
            verify() {}
            action()
            {
                "You write <<gLiteral>> on the piece of paper. ";
                writing += ('\n'+ gLiteral);
            }
        }
        writing = ''
        readDesc = "On the paper is written: <<writing>>"
    ;

The moral of this example is that before writing a completely new precondition, you should first ask yourself whether you can construct it using `new ObjectPreCondition` in conjunction with an existing precondition: this is generally possible if all you want to do is to apply an existing kind of precondition to an object that is not explicitly involved in the action (i.e. an object that is not the direct or indirect object of the current command). It may be, of course, that what you want to do can't be handled that way; for example, if several writing implements were available in your game and the Player Character just had to be holding one of them in order to be able to write on things, then you'd need a different approach. In the next section we'll see how to write a completely new precondition.

## Completely New Preconditions

The standard libary defines a good selection of preconditions to cover the most usual cases, but it may be that you'll come up against a situation that isn't covered by any of the existing preconditions. For example, suppose that your game contains one or more balls that can only be kicked when the actor is not holding them, then ideally you'd like an `objNotHeld` you could use like this:

    class Ball: Thing
        dobjFor(Attack)
        {
    	    preCond = [touchObj, objNotHeld]
    	    verify() { }
    	    action()
    	    {
    		    "{I} {give} {the dobj} a good kick. ";
    	    }
        }
    ;

Ideally, this would make the Player Character drop a ball he was holding in response to KICK BALL (or HIT BALL or ATTACK BALL) before carrying out the kicking action. Additionally, if the Player Character is carrying a red ball and a blue ball is lying on the ground, it should make KICK BALL prefer the ball that is not being carried (i.e. the blue ball). But since the library does not define an `objNotHeld` precondition, we'd need to define one for ourselves.

There are several things a PreCondition object needs to do if it is to function correctly, but rather than describe them in the abstract, it will far easier simply to start from an existing library precondition that's reasonably similar to what we want and then adapt it. This will ensure that we follow the coding pattern of the standard library preconditions, which should in turn ensure that our new precondition does what it should. Indeed, this is probably the best procedure to follow whenever you want to create a new precondition:

- Look through the library file precond.t (perhaps with the aid of the Library Reference Manual) until you find a precondition that does something reasonably similar to what you want your new precondition to do.
- Copy the code for the library precondition (which will be an object of type PreCondition, or possibly one of its subclasses) and paste it into your own code at the point at which you want to define your own custom precondition.
- Change the name of the PreCondition object you have just copied to the name you want for your new custom precondition (e.g. at the point where you have just copied the `objHeld` precondition into your own code you might change its name to `objNotHeld`).
- Then work through the copied PreCondition object in your own code, making all the other changes needed to ensure it does what you want.

As just stated, the fourth stage above may not look particularly helpful, but this will all become much clearer if we work through an example. Suppose that we indeed wish to create an `objNotHeld` precondition. Since this seems to be the reverse of the existing `objHeld` precondition, that might be the best place to start. So the first step is to locate the `objHeld` precondition in preCond.t (perhaps with the help of the Library Reference Manual), and the second step is to copy and paste that object definition into our own code:

    objHeld: PreCondition

        verifyPreCondition(obj)
        {
            /*
             *   If the object is fixed in place it can't be picked up, so there's
             *   no point in trying. BUT we also have to check for the case that the
             *   object is directly in the player (perhaps because a body part).
             */
            if(obj.isFixed && !obj.isDirectlyIn(gActor))
                illogical(obj.cannotTakeMsg);

            /*
             *   If the actor isn't carrying the object it's slightly less likely to
             *   be the one the player means.
             */
            if(!obj.isIn(gActor))
                logicalRank(90);

        }

        checkPreCondition(obj, allowImplicit)
        {
            /*
             *   if the object is already held, we're already done.
             */
            if (obj == nil || obj.isDirectlyIn(gActor))
                return true;

            /*
             *   If we're allowed to attempt an implicit action, try taking obj
             *   implicitly and see if we succeed.
             */
            if(allowImplicit)
            {
                /*
                 *   Try taking obj implicitly and note if we were allowed to make
                 *   the attempt.
                 */
                local tried = tryImplicitAction(Take, obj);

                /*
                 *   If obj is now held by the actor return true to signal that this
                 *   precondition has now been met.
                 */
                if(obj.isDirectlyIn(gActor))
                  return true;

                /*
                 *   Otherwise, if we tried but failed to take obj, return nil to
                 *   signal that this precondition can't be met (so the main action
                 *   cannot proceed). The attempt to take obj will have explained
                 *   why it failed, so there's no need for any further explanation
                 *   here.
                 */
                if(tried)
                    return nil;
            }

            /*
             *   If we reach here obj isn't being held by the actor and we weren't
             *   allowed to try to take it; display a message explaining the
             *   problem.
             */
            gMessageParams(obj);
            DMsg(need to hold, '{I} need{s/ed} to be holding {the obj} to do that. ');

            /* Then return nil to indicate that the precondition hasn't been met. */
            return nil;
        }
    ;

We next carry out Step 3 and change the name to the new name we want (if we don't we'll get a duplicate object definition compiler error when we try to compile, and the new name we want to use won't be recognized, so it's best to do this straight away before we forget):

    objNotHeld: PreCondition

Well, that's the first three steps out of the way, now we just have the final, most complex step. To make this clearer, we'll break it down into small sub-steps. The existing definition starts with:

        checkPreCondition(obj, allowImplicit)
        {
            /* if the object is already held, there's nothing we need to do */
            if (obj == nil || obj.isDirectlyIn(gActor))
                return true;

The `checkPreCondition` method is called at the point when implicit actions may be carried out to meet the condition we want to impose. The `obj` parameter is the object whose condition we're testing and which any implicit action would normally be carried out on. The `allowImplicit` parameter defines whether or not an implicit action may be attempted; during command execution the verify and checkPreCondition routines may be called more than once, but implicit actions are only allowed on the first pass (to prevent an infinite loop should the execution of an implicit command then bring about the need to carry out another implicit command that undoes the result of the first).

The first part of this method checks to see if an implicit action is actually necessary. In the original `objHeld` there's no need to do anything if the actor is already holding the object in question, so the method just returns true to tell its caller that it didn't need to do anything. In our new `objNotHeld` precondition we want to apply precisely the opposite test: we return true and do no more if the object is not already in the player (we almost certainly want our objNotHeld PreCondition to ensure not only that the player isn't directly holding the object but that it's not indirectly contained by the pc either):

         /*
          *   if the object is already not in the actor, we're already done.
          */
          if (obj == nil || !obj.isIn(gActor))
                return true;

The next part of the code in the original `objHeld` precondition then tries to carry out an implicit TAKE command and checks whether this has succeeded, provided that implicit actions are allowed at this stage of the proceedings:

            /*
             *   If we're allowed to attempt an implicit action, try taking obj
             *   implicitly and see if we succeed.
             */
            if(allowImplicit)
            {
                /*
                 *   Try taking obj implicitly and note if we were allowed to make
                 *   the attempt.
                 */
                local tried = tryImplicitAction(Take, obj);

                /*
                 *   If obj is now held by the actor return true to signal that this
                 *   precondition has now been met.
                 */
                if(obj.isDirectlyIn(gActor))
                  return true;

                /*
                 *   Otherwise, if we tried but failed to take obj, return nil to
                 *   signal that this precondition can't be met (so the main action
                 *   cannot proceed). The attempt to take obj will have explained
                 *   why it failed, so there's no need for any further explanation
                 *   here.
                 */
                if(tried)
                    return nil;
            }

If `allowImplicit` is nil then the test fails straight away and the entire code block is bypassed. If `allowImplicit` is true, then we try to take the object via a call to `tryImplicitAction(Take, obj)` which will attempt to take the object attempt to take the object. If the implicit action is attempted then `tryImplicitAction` will return true, but if it's not (e.g. because one of the objects involved is not in scope for the actor) it will return nil.

Our new `objNotHeld` precondition needs to follow the same coding pattern and much of the same logic, reversing it in just a couple of places: we need to try dropping the object instead of taking it, and we need to check that the object ends up not held, not that it ends up held. The revised code becomes:

            /*
             *   If we're allowed to attempt an implicit action, try taking obj
             *   implicitly and see if we succeed.
             */
            if(allowImplicit)
            {
                /*
                 *   Try dropping obj implicitly and note if we were allowed to make
                 *   the attempt.
                 */
                local tried = tryImplicitAction(Drop, obj);

                /*
                 *   If obj is now not in the actor return true to signal that this
                 *   precondition has now been met.
                 */
                if(!obj.isIn(gActor))
                  return true;

                /*
                 *   Otherwise, if we tried but failed to drop obj, return nil to
                 *   signal that this precondition can't be met (so the main action
                 *   cannot proceed). The attempt to drop obj will have explained
                 *   why it failed, so there's no need for any further explanation
                 *   here.
                 */
                if(tried)
                    return nil;
            }

The final part of the method has to deal with the case in which no implicit action was attempted, either because we're in the second pass and `allowImplicit` is nil or because the implicit action could not be attempted. In this case we need to display a message explaining why the main action cannot go ahead and abort the command. Just before aborting the command we make 'obj' a temporary message parameter that refers to obj so that we can construct the failure message.

            /*
             *   If we reach here obj isn't being held by the actor and we weren't
             *   allowed to try to take it; display a message explaining the
             *   problem.
             */
            gMessageParams(obj);
            DMsg(need to hold, '{I} need{s/ed} to be holding {the obj} to do that. ');

            /* Then return nil to indicate that the precondition hasn't been met. */
            return nil;

We can use almost precisely the same code for our new `objNotHeld` precondition, except that we need to supply a custom failure message appropriate to the different situation:

           /*
             *   If we reach here obj is still in the actor and we weren't
             *   allowed to try to drop it; display a message explaining the
             *   problem.
             */
            gMessageParams(obj);
            DMsg(need to not hold, '{I} need{s/ed} to be not holding {the obj} to do that. ');

            /* Then return nil to indicate that the precondition hasn't been met. */
            return nil;
        }

That completes the `checkPreCondition` method, but there's also the `verifyPreCondition` method, which we also need to adapt. On the `objHeld` precondition this makes it less likely that the command in question applies to an object that is not currently held by the actor (e.g., if EAT has a `objHeld` precondition attached to its direct object, then EAT CAKE will choose the chocolate cake held by the actor in preference to the fruit cake sitting on the table):

        verifyPreCondition(obj)
        {
            verifyPreCondition(obj)
        {
            /*
             *   If the object is fixed in place it can't be picked up, so there's
             *   no point in trying. BUT we also have to check for the case that the
             *   object is directly in the player (perhaps because a body part).
             */
            if(obj.isFixed && !obj.isDirectlyIn(gActor))
                illogical(obj.cannotTakeMsg);

            /*
             *   If the actor isn't carrying the object it's slightly less likely to
             *   be the one the player means.
             */
            if(!obj.isIn(gActor))
                logicalRank(90);

        }

        }
    ;

For our new `objNotHeld` precondition we simply need to reverse the logic so the likelihood rating is reduced for an object that is being held (so, e.g., KICK BALL prefers the blue ball on the ground to the red ball being carried):

        /* lower the likelihood rating for anything being held */
        verifyPreCondition(obj)
        {
            /*
             *   If the object is fixed in place it can't be moved, so there's
             *   no point in trying to drop it.
             */
            if(obj.isFixed)
                illogical(obj.cannotTakeMsg);

            /*
             *   If the actor is carrying the object it's slightly less likely to
             *   be the one the player means.
             */
            if(obj.isIn(gActor))
                logicalRank(90);

        }
    ;

With these changes, our new `objNotHeld` precondition becomes:

    objNotHeld: PreCondition
        verifyPreCondition(obj)
        {
            /*
             *   If the object is fixed in place it can't be moved, so there's
             *   no point in trying.
             */
            if(obj.isFixed)
                illogical(obj.cannotTakeMsg);

            /*
             *   If the actor is carrying the object it's slightly less likely to
             *   be the one the player means.
             */
            if(obj.isIn(gActor))
                logicalRank(90);

        }

        checkPreCondition(obj, allowImplicit)
        {
            /*
             *   if the object is already not in the actor, we're already done.
             */
            if (obj == nil || !obj.isIn(gActor))
                return true;

            /*
             *   If we're allowed to attempt an implicit action, try taking obj
             *   implicitly and see if we succeed.
             */
            if(allowImplicit)
            {
                /*
                 *   Try taking obj implicitly and note if we were allowed to make
                 *   the attempt.
                 */
                local tried = tryImplicitAction(Drop, obj);

                /*
                 *   If obj is now not in the actor return true to signal that this
                 *   precondition has now been met.
                 */
                if(!obj.isIn(gActor))
                  return true;

                /*
                 *   Otherwise, if we tried but failed to drop obj, return nil to
                 *   signal that this precondition can't be met (so the main action
                 *   cannot proceed). The attempt to drop obj will have explained
                 *   why it failed, so there's no need for any further explanation
                 *   here.
                 */
                if(tried)
                    return nil;
            }

            /*
             *   If we reach here obj is still in the actor and we weren't
             *   allowed to try to drop it; display a message explaining the
             *   problem.
             */
            gMessageParams(obj);
            DMsg(need to not hold, '{I} need{s/ed} to be not holding {the obj} to do that. ');

            /* Then return nil to indicate that the precondition hasn't been met. */
            return nil;
        }
    ;

    ;

TADS 3 Technical Manual
Table of Contents | TADS 3 In Depth > Custom Preconditions
Prev: How to Create Verbs Next: Message Parameter Substitutions