cfad47cfa3/t3compiler/tads3/lib/adv3/tips.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/*
4
 *   TADS 3 Tips, by Krister Fundin (fundin@yahoo.com). Provides a uniform
5
 *   way of providing one-time only tips to the player (and especially to
6
 *   unexperienced players) when certain things happen in the game.  
7
 */
8
9
#include <adv3.h>
10
11
/* ---------------------------------------------------------------------- */
12
/*
13
 *   The tip manager keeps track of which tips we have shown. Since we don't
14
 *   want to unnecessarily show any tips more than once, we store this
15
 *   information both transiently (in the tipManager) and persistently (in
16
 *   the tip objects themselves). This should make sure that we at least
17
 *   cover these two types of cases:
18
 *
19
 *   - The player sees a tip, then restarts, undos or restores to an earlier
20
 *     position.
21
 *   - The player sees a tip, saves, then resumes play at some later time.
22
 */
23
transient tipManager: InitObject, PostRestoreObject, PostUndoObject
24
    /*
25
     *   Show pending tips. This is called by a PromptDaemon before each new
26
     *   round of input.
27
     */
28
    showTips()
29
    {
30
        /*
31
         *   Go through our vector of pending tips. Use a 'for' loop instead
32
         *   a 'foreach' loop, in case showing one tip triggers another one.
33
         */
34
        for (local i = 1 ; i <= pendingTips.length ; i++)
35
        {
36
            /* show the description of the current tip */
37
            pendingTips[i].showTipDesc();
38
        }
39
40
        /* clear the vector of pending tips */
41
        pendingTips.setLength(0);
42
    }
43
44
    /* update tip information after a restore, restart or undo */
45
    execute()
46
    {
47
        /* go through all tips */
48
        forEachInstance(Tip, new function(tip)
49
        {
50
            /*
51
             *   see if this one has been shown, according to its own
52
             *   persistent memory
53
             */
54
            if (tip.shown)
55
            {
56
                /*
57
                 *   It says that it has been shown. If it's not in our list
58
                 *   of shown tips, then add it.
59
                 */
60
                if (shownTips.indexOf(tip) == nil)
61
                    shownTips += tip;
62
            }
63
            else
64
            {
65
                /*
66
                 *   It says that it hasn't been shown. If it's in our list
67
                 *   of shown tips, then it must have been shown after all.
68
                 */
69
                if (shownTips.indexOf(tip) != nil)
70
                    tip.shown = true;
71
            }
72
        });
73
    }
74
75
    /* a vector of tips to be displayed before the next prompt */
76
    pendingTips = static new Vector(2)
77
78
    /*
79
     *   A transient list of shown tips. Note that this must be a list and
80
     *   not a vector. When updating a list, we actually replace it with a
81
     *   new list, since lists are immutable. This is a transient change -
82
     *   it affects only the value of the shownTips property. Updating a
83
     *   vector, however, modifies the vector itself and leaves the property
84
     *   with the same reference. A vector itself is always persistent, so
85
     *   this change would be lost after E.G. a restore.
86
     */
87
    shownTips = []
88
;
89
90
/*
91
 *   The Tip class. Each actual tip should be represented by an instance of
92
 *   this class. To show the tip, just call tipName.showTip(). If the tip
93
 *   has already been shown, or if the tips have been turned off completely,
94
 *   then nothing will be displayed.
95
 */
96
class Tip: object
97
    /*
98
     *   The actual text to display when this tip is shown. We'll wrap it in
99
     *   <.tip> tags automatically, and also add a paragraph break before
100
     *   it.
101
     */
102
    desc = ""
103
104
    /* show this tip */
105
    showTip()
106
    {
107
        /* see if we should be shown */
108
        if (!shouldShowTip)
109
            return;
110
111
        /*
112
         *   defer the actual displaying of the tip until just before the
113
         *   next command prompt
114
         */
115
        tipManager.pendingTips.append(self);
116
117
        /* note that we have been shown */
118
        makeShown();
119
    }
120
121
    /* display our tip description, I.E. the actual tip */
122
    showTipDesc()
123
    {
124
        /* display a pargraph break and an opening <.tip> style tag */
125
        "<.p><.tip>";
126
127
        /* show our description */
128
        desc();
129
130
        /* close the <.tip> tag */
131
        "<./tip>";
132
    }
133
134
    /* should we show this tip when asked to? */
135
    shouldShowTip()
136
    {
137
        /*
138
         *   We'll show it as long as it hasn't been shown before and we
139
         *   haven't turned the tips off completely. Certain tips might want
140
         *   to be displayed even when all tips are turned off, if they
141
         *   contain information specific to a certain story or an extension
142
         *   that it uses. If so, then this method could be overridden.
143
         */
144
        return (!shown && tipMode.isOn);
145
    }
146
147
    /*
148
     *   Mark this tip as shown. This method can be called by outside code
149
     *   before the tip has been triggered. If the tip informs the player of
150
     *   a certain command, for instance, then it would become redundant if
151
     *   the player has already used that command.
152
     */
153
    makeShown()
154
    {
155
        /* set our shown flag */
156
        shown = true;
157
158
        /* also add us to the transient list of shown tips */
159
        tipManager.shownTips += self;
160
    }
161
162
    /* flag: has this tip been shown before? */
163
    shown = nil
164
;
165
166
/*
167
 *   A style tag that we enclose tips with. By default, we just use plain
168
 *   parentheses, just like for notifications and parser messages, but this
169
 *   could be overridden if we wanted to display something fancier.
170
 */
171
tipStyleTag: StyleTag 'tip'
172
    openText = '('
173
    closeText = ')'
174
;
175
176
/*
177
 *   During pre-init, create a PromptDaemon for displaying tips. We don't
178
 *   want to display them directly when the showTip() method is called, to
179
 *   allow tips to be triggered from pretty much anywhere without having to
180
 *   worry about them showing up in the middle of some text.
181
 */
182
PreinitObject
183
    execute()
184
    {
185
        /*
186
         *   Give this daemon a higher-than-average eventOrder, in case
187
         *   another PromptDaemon wants to display a tip. The standard tip
188
         *   about turning of score notification is triggered this way.
189
         */
190
        new PromptDaemon(tipManager, &showTips).eventOrder = 200;
191
    }
192
;
193
194
/* ---------------------------------------------------------------------- */
195
/*
196
 *   Next, we want to allow turning all tips on and off during the game. It
197
 *   should also be possible to turn the tips off for ALL games that use
198
 *   them, and thus we define a SettingsItem for this purpose. This means
199
 *   that the player can turn the tips off and then save this setting as the
200
 *   default.
201
 */
202
tipMode: BinarySettingsItem
203
    /* we show tips by default */
204
    isOn = true
205
206
    /* the ID string to use in the configuration file */
207
    settingID = 'tips.showtips'
208
209
    /* show our description */
210
    settingDesc = (libMessages.tipStatusShort(isOn))
211
;
212
213
/*
214
 *   Define a system action for turning tip mode on/off.
215
 */
216
DefineSystemAction(TipMode)
217
    execSystemAction()
218
    {
219
        /* set the new tip mode */
220
        tipMode.isOn = stat_;
221
222
        /* acknowledge the change */
223
        libMessages.acknowledgeTipStatus(stat_);
224
    }
225
;
226