# signals.t | documentation

    #charset "us-ascii"
    #include "advlite.h"

    /*--------------------------------------------------------*/
    /*
     *   SIGNALS EXTENSION Still at an experimental stage
     *
     *   To use this extension, include the relations extensions before it.
     */

    /*
     *   A Signal is a kind of Relation that can be used to send a signal from a
     *   sender to all the objects related to that sender via this Signal Relation.
     *
     *   For a signal to be sent from a sender to a receiver, a relationship first
     *   needs to be set up between them with a statement like:
     *
     *   connect(sender, signal, receiver);
     *
     *   Where signal is either the programmatic name or the string name of the
     *   signal we want sent.
     *
     *   To break the link subsequently we can use:
     *
     *   unconnect(sender, signal, receiver);
     *
     *   [SIGNALS EXTENSION]
     */
    Signal: Relation
        /* Signals can potentially relate many things to many other things. */
        relationType = manyToMany

        /*
         *   Notify every object related to sender by us to handle us as a signal
         *   from sender.
         *
         *   If additional args are supplied, they can take one of two forms. Either
         *   values, which are then assigned in turn to the properties listed in our
         *   propList property, or two-element lists of the form [prop, val] where
         *   prop is a property pointer and val is the value to be assigned to this
         *   property. Note that these two forms cannot be mixed in the same call to
         *   this method, unless all the list form arguments come at the end.
    	 *
    	 *   [SIGNALS EXTENSION]
         */
        emit(sender, [args])
        {
            local prop, val;

            /*
             *   Clear out any property values left over from a previous call. We
             *   use null rather than nil as the 'cleared' value, since in some
             *   cases (e.g. in a moveSignal) nil could be a significant value.
             */
            propList.forEach({p: self.(p) = null });

            /* Assign additional arguments to properties. */
            for(local arg in args, local i=1;; i++)
            {
                /*
                 *   If each arg is a list, then the first item in the list is a
                 *   property pointer, and the second is the value to assign to that
                 *   property.
                 */
                if(dataType(arg) == TypeList)
                {
                   prop = arg[1];
                   val = arg[2];
                }
                /*
                 *   Otherwise arg is just a value, which is assigned to the
                 *   property found in the next element of propList.
                 */
                else
                {
                    val = arg;
                    prop = propList[i];
                }
                self.(prop) = val;
            }

            relatedTo(sender).forEach({ obj: obj.dispatchSignal(sender, self) });
        }

        /*
         *   A list of pointers to the properties to which additional arguments to
         *   our emit method should be assigned. [SIGNALS EXTENSION]
         */
        propList = []

    	/*
    	 *   A LookupTable liniking objects that might emit this signal (potential senders) to potential
    	 *   receivers of this signal, so that notifications can be sent from the former to the latter.
    	 *   Game code should not need to manipulate this table directly; it should instead be updated via
    	 *   the supplied connect() and unconnect() functions.
    	 *
    	 *   [SIGNALS EXTENSION]
    	 */
        dispatchTab = nil

        addHandler(sender, receiver, handler)
        {
            if(dispatchTab == nil)
                dispatchTab = new LookupTable();

            dispatchTab[[sender, receiver]] = handler;
        }

        removeHandler(sender, receiver)
        {
            if(dispatchTab != nil)
            {
                dispatchTab.removeElement([sender, receiver]);
            }
        }

    ;

    /* Signals to handle common state changes on Thing  [SIGNALS EXTENSION] */
    DefSignal(lit, lit);
    DefSignal(unlit, unlit);
    DefSignal(discover, discovered);
    DefSignal(undiscover, lost);
    DefSignal(lock, locked);
    DefSignal(unlock, unlocked);
    DefSignal(on, turned on);
    DefSignal(off, turned off);
    DefSignal(worn, worn) wearer = nil propList = [&wearer];
    DefSignal(doff, doffed);
    DefSignal(move, moved) destination = nil propList= [&destination];
    DefSignal(actmove, action moved) destination = nil propList= [&destination];
    DefSignal(seen, seen) location = nil propList = [&location];
    DefSignal(examine, examine) actor = nil propList = [&actor];
    DefSignal(take, take) actor = nil propList = [&actor];
    DefSignal(drop, drop) actor = nil propList = [&actor];
    DefSignal(open, open);
    DefSignal (close, closed);
    DefSignal(push, push);
    DefSignal(pull, pull);
    DefSignal(feel, feel);

     /*
      * MODIFICATIONS TO TadsObject for SIGNALS EXTENSION
      *
      * Add handling for emiting, handling and dispatching signals.
      */
    modify TadsObject
        /* Emit a signal  [SIGNALS EXTENSION] */
        emit(signal, [args])
        {
            /* Simply call the signal's emit method with ourselves as the sender. */
            signal.emit(self, args...);
        }

        /*
         *   Handle a signal from sender; game code will need to override particular
         *   instances. Note that this is a catch-all handler for signals we don't
         *   recognize or for which more specific handlers haven't been defined.
    	 *   [SIGNALS EXTENSION]
         */
        handle(sender, signal)
        {
        }

        /*
    	 *   Dispatch a signal to the appropriate handler method on this object.
         *   We look up the property pointer to use on the signal's dispatchTab
    	 *   LookupTable. If we find one and the property is defined on this object
    	 *   then we use that property to handle the signal. Otherwise, we simply
    	 *   use our catch-all generic handle(sender, signal) method.
    	 *
    	 *   [SIGNALS EXTENSION]
         */
        dispatchSignal(sender, signal)
        {
            local prop;

            if(signal.dispatchTab != nil && signal.dispatchTab[[sender, self]] != nil)
                prop = signal.dispatchTab[[sender, self]];

            else if(signal.propDefined(&handleProp)
                    && signal.propType(&handleProp) == TypeProp)
                prop = signal.handleProp;
            else
                prop = &handle;

            if(propDefined(prop))
                self.(prop)(sender, signal);
            else
                handle(sender, signal);
        }
    ;

     modify Thing
        /*
         *   Make various common state changes and actions emit the appropriate
         *   signals. [SIGNALS EXTENSION]
         */

    	/*
    	 *  emit a litSignal or unlitSignal when this object is lit or unlit.
    	 *  [SIGNALS EXTENSION]
    	 */
        makeLit(stat)
        {
            inherited(stat);
            emit(stat ? litSignal : unlitSignal);
        }

    	/*
    	 *  emit a discoverSignal or undiscoverSignal when this object is discovered or undiscovered.
    	 *  SIGNALS EXTENSION]
    	 */
        discover(stat = true)
        {
            inherited(stat);
            emit(stat ? discoverSignal : undiscoverSignal);
        }

    	/*
    	 *  emit a lockSignal or unlockSignal when this object is locked or unlocked.
    	 *  [SIGNALS EXTENSION]
    	 */
        makeLocked(stat)
        {
            inherited(stat);
            emit(stat ? lockSignal : unlockSignal);
        }

    	/*
    	 *  emit an onSignal or offSignal when this object is turned on or off
    	 *  [SIGNALS EXTENSION]
    	 */
        makeOn(stat)
        {
            inherited(stat);
            emit(stat ? onSignal: offSignal);
        }

    	/*
    	 *  emit a wornSignal or doffSignal when this object is worn or doffed (taken off).
    	 *  [SIGNALS EXTENSION]
    	 */
        makeWorn(stat)
        {
            inherited(stat);
            if(stat)
                emit(wornSignal, stat);
            else
                emit(doffSignal);
        }

    	/*
    	 *  emit a moveSignal when this object is moved.
    	 *  [SIGNALS EXTENSION]
    	 */
        moveInto(newCont)
        {
            inherited(newCont);

            emit(moveSignal, newCont);
        }

    	/*
    	 *  emit a actmoveSignal or unlitSignal when this object moved as part of action handling.
    	 *  [SIGNALS EXTENSION]
    	 */
        actionMoveInto(newCont)
        {
            inherited(newCont);

            emit(actmoveSignal, newCont);
        }

        /*
    	 *  emit a seenSignal or unlitSignal when this object is seen.
    	 *  [SIGNALS EXTENSION]
    	 */
        noteSeen()
        {
            inherited();

            emit(seenSignal, location);
        }

        /*
    	 *  emit an openSignal or closeSignal when this object is open or closed.
    	 *  [SIGNALS EXTENSION]
    	 */
        makeOpen(stat)
        {
            inherited(stat);
            emit(stat ? openSignal : closeSignal);
        }
    ;

    /*
     *  Function to set up a signalling relation between sender and receiver via the signal Signal.
     *  This first created a relation between sender and receiver [using the RELATIONS extension)
     *  And then, if the handler parameter is supplied, adds an appropriate entry to the signal's
     *  dispatchTab table to register that this is the handler to use on the receiver when signal is
     *  sent to receiver from sender.
     *  [SIGNALS EXTENSION]
     */
    connect(sender, signal, receiver, handler?)
    {
        signal = relationTable.getRelation(signal)[1];

        relate(sender, signal, receiver);
        if(handler)
            signal.addHandler(sender, receiver, handler);
    }

    /*
     * Function to remove the signalling relationship between sender and receiver via the signal
     * Signal. [SIGNALS EXTENSION]
     */
    unconnect(sender, signal, receiver)
    {
        signal = relationTable.getRelation(signal)[1];
        unrelate(sender, signal, receiver);

        if(receiver.propDefined(&removeSenderHandler))
           signal.removeHandler(sender, receiver);
    }

    modify TAction
        /*
    	 * The signal (if any) )o be emitted by the direct object of this action.
    	 * [SIGNALS EXTENSION]
         */
        signal = nil

    	/*
    	 * If this action defines an associated signal, then have the direct object emit the signal
    	 * after carrrying out out inherited handling. [SIGNALS EXTENSION]
    	 */
        doAction()
        {
            inherited();
            if(signal)
                curDobj.emit(signal);
        }
    ;

    modify Take
       /* [SIGNALS EXTENSION] */
        signal = takeSignal
    ;

    modify Drop
    /* [SIGNALS EXTENSION] */
        signal = dropSignal
    ;

    modify Examine
    /* [SIGNALS EXTENSION] */
        signal = examineSignal
    ;

    modify Push
    /* [SIGNALS EXTENSION] */
        signal = pushSignal
    ;

    modify Pull
    /* [SIGNALS EXTENSION] */
        signal = pullSignal
    ;

    modify Feel
    /* [SIGNALS EXTENSION] */
        signal = feelSignal
    ;

Adv3Lite Library Reference Manual
Generated on 01/03/2024 from adv3Lite version 1.6.2