# tips.t | documentation

    #charset "us-ascii"

    /*
     *   TADS 3 Tips, by Krister Fundin (fundin@yahoo.com). Provides a uniform
     *   way of providing one-time only tips to the player (and especially to
     *   inexperienced players) when certain things happen in the game.
     */

    #include <adv3.h>

    /* ---------------------------------------------------------------------- */
    /*
     *   The tip manager keeps track of which tips we have shown. Since we don't
     *   want to unnecessarily show any tips more than once, we store this
     *   information both transiently (in the tipManager) and persistently (in
     *   the tip objects themselves). This should make sure that we at least
     *   cover these two types of cases:
     *
     *   - The player sees a tip, then restarts, undos or restores to an earlier
     *     position.
     *   - The player sees a tip, saves, then resumes play at some later time.
     */
    transient tipManager: InitObject, PostRestoreObject, PostUndoObject
        /*
         *   Show pending tips. This is called by a PromptDaemon before each new
         *   round of input.
         */
        showTips()
        {
            /*
             *   Go through our vector of pending tips. Use a 'for' loop instead
             *   a 'foreach' loop, in case showing one tip triggers another one.
             */
            for (local i = 1 ; i <= pendingTips.length ; i++)
            {
                /* show the description of the current tip */
                pendingTips[i].showTipDesc();
            }

            /* clear the vector of pending tips */
            pendingTips.setLength(0);
        }

        /* update tip information after a restore, restart or undo */
        execute()
        {
            /* go through all tips */
            forEachInstance(Tip, function(tip)
            {
                /*
                 *   see if this one has been shown, according to its own
                 *   persistent memory
                 */
                if (tip.shown)
                {
                    /*
                     *   It says that it has been shown. If it's not in our list
                     *   of shown tips, then add it.
                     */
                    if (shownTips.indexOf(tip) == nil)
                        shownTips += tip;
                }
                else
                {
                    /*
                     *   It says that it hasn't been shown. If it's in our list
                     *   of shown tips, then it must have been shown after all.
                     */
                    if (shownTips.indexOf(tip) != nil)
                        tip.shown = true;
                }
            });
        }

        /* a vector of tips to be displayed before the next prompt */
        pendingTips = static new Vector(2)

        /*
         *   A transient list of shown tips. Note that this must be a list and
         *   not a vector. When updating a list, we actually replace it with a
         *   new list, since lists are immutable. This is a transient change -
         *   it affects only the value of the shownTips property. Updating a
         *   vector, however, modifies the vector itself and leaves the property
         *   with the same reference. A vector itself is always persistent, so
         *   this change would be lost after E.G. a restore.
         */
        shownTips = []
    ;

    /*
     *   The Tip class. Each actual tip should be represented by an instance of
     *   this class. To show the tip, just call tipName.showTip(). If the tip
     *   has already been shown, or if the tips have been turned off completely,
     *   then nothing will be displayed.
     */
    class Tip: object
        /*
         *   The actual text to display when this tip is shown. We'll wrap it in
         *   <.tip> tags automatically, and also add a paragraph break before
         *   it.
         */
        desc = ""

        /* show this tip */
        showTip()
        {
            /* see if we should be shown */
            if (!shouldShowTip)
                return;

            /*
             *   defer the actual displaying of the tip until just before the
             *   next command prompt
             */
            tipManager.pendingTips.append(self);

            /* note that we have been shown */
            makeShown();
        }

        /* display our tip description, I.E. the actual tip */
        showTipDesc()
        {
            /* display a pargraph break and an opening <.tip> style tag */
            "<.p><.tip>";

            /* show our description */
            desc();

            /* close the <.tip> tag */
            "<./tip>";
        }

        /* should we show this tip when asked to? */
        shouldShowTip()
        {
            /*
             *   We'll show it as long as it hasn't been shown before and we
             *   haven't turned the tips off completely. Certain tips might want
             *   to be displayed even when all tips are turned off, if they
             *   contain information specific to a certain story or an extension
             *   that it uses. If so, then this method could be overridden.
             */
            return (!shown && tipMode.isOn);
        }

        /*
         *   Mark this tip as shown. This method can be called by outside code
         *   before the tip has been triggered. If the tip informs the player of
         *   a certain command, for instance, then it would become redundant if
         *   the player has already used that command.
         */
        makeShown()
        {
            /* set our shown flag */
            shown = true;

            /* also add us to the transient list of shown tips */
            tipManager.shownTips += self;
        }

        /* flag: has this tip been shown before? */
        shown = nil
    ;

    /*
     *   A style tag that we enclose tips with. By default, we just use plain
     *   parentheses, just like for notifications and parser messages, but this
     *   could be overridden if we wanted to display something fancier.
     */
    tipStyleTag: StyleTag 'tip'
        openText = '('
        closeText = ')'
    ;

    /*
     *   During pre-init, create a PromptDaemon for displaying tips. We don't
     *   want to display them directly when the showTip() method is called, to
     *   allow tips to be triggered from pretty much anywhere without having to
     *   worry about them showing up in the middle of some text.
     */
    PreinitObject
        execute()
        {
            /*
             *   Give this daemon a higher-than-average eventOrder, in case
             *   another PromptDaemon wants to display a tip. The standard tip
             *   about turning of score notification is triggered this way.
             */
            new PromptDaemon(tipManager, &showTips).eventOrder = 200;
        }
    ;

    /* ---------------------------------------------------------------------- */
    /*
     *   Next, we want to allow turning all tips on and off during the game. It
     *   should also be possible to turn the tips off for ALL games that use
     *   them, and thus we define a SettingsItem for this purpose. This means
     *   that the player can turn the tips off and then save this setting as the
     *   default.
     */
    tipMode: BinarySettingsItem
        /* we show tips by default */
        isOn = true

        /* the ID string to use in the configuration file */
        settingID = 'tips.showtips'

        /* show our description */
        settingDesc = (libMessages.tipStatusShort(isOn))
    ;

    /*
     *   Define a system action for turning tip mode on/off.
     */
    DefineSystemAction(TipMode)
        execSystemAction()
        {
            /* set the new tip mode */
            tipMode.isOn = stat_;

            /* acknowledge the change */
            libMessages.acknowledgeTipStatus(stat_);
        }
    ;

TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3