~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/lib/extensions/SimpleAttachable.t

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#charset "us-ascii"
 
2
 
 
3
#include <adv3.h>
 
4
#include <en_us.h>
 
5
 
 
6
/*  
 
7
 *   SIMPLE ATTACHABLE
 
8
 *
 
9
 *   SimpleAttachable version 1.0 by Eric Eve
 
10
 *
 
11
 *   This file defines the SimpleAttachable class together with some 
 
12
 *   supporting objects. Feel free to use this in your own games if you find 
 
13
 *   it useful.
 
14
 *
 
15
 *   Attachables in general are complicated to handle, because they can 
 
16
 *   behave in so many different ways. The SimpleAttachable class is meant 
 
17
 *   to make handling one common case easier, in particular the case where a 
 
18
 *   smaller object is attached to a larger object and then moves round with 
 
19
 *   it.
 
20
 *
 
21
 *   More formally, a SimpleAttachable enforces the following rules:
 
22
 *
 
23
 *   (1)    In any attachment relationship between SimpleAttachables, one 
 
24
 *   object must be the major attachment, and all the others will be that 
 
25
 *   object's minor attachments (if there's a fridge with a red magnet and a 
 
26
 *   blue magnet attached, the fridge is the major attachement and the 
 
27
 *   magnets are its minor attachments).
 
28
 *
 
29
 *   (2)    A major attachment can have many minor attachments attached to 
 
30
 *   it at once, but a minor attachment can only be attached to one major 
 
31
 *   attachment at a time (this is a consequence of (3) below).
 
32
 *
 
33
 *   (3)    When a minor attachment is attached to a major attachment, the 
 
34
 *   minor attachment is moved into the major attachment. This automatically 
 
35
 *   enforces (4) below.
 
36
 *
 
37
 *   (4)    When a major attachment is moved (e.g. by being taken or pushed 
 
38
 *   around), its minor attachments automatically move with it.
 
39
 *
 
40
 *   (5)    When a minor attachment is taken, it is automatically detached 
 
41
 *   from its major attachment (if I take a magnet, I leave the fridge 
 
42
 *   behind).
 
43
 *
 
44
 *   (6)    When a minor attachment is detached from a major attachment it 
 
45
 *   is moved into the major attachment's location. 
 
46
 *
 
47
 *   (7)     The same SimpleAttachable can be simultaneously a minor item 
 
48
 *   for one object and a major item for one or more other objects (we could 
 
49
 *   attach a metal paper clip to the magnet while the magnet is attached to 
 
50
 *   the fridge; if we take the magnet the paper clip comes with it while the
 
51
 *   fridge is left behind).
 
52
 *
 
53
 *   (8)    If a SimpleAttachable is attached to a major attachment while 
 
54
 *   it's already attached to another major attachment, it will first be 
 
55
 *   detached from its existing major attachment before being attached to 
 
56
 *   the new one (ATTACH MAGNET TO OVEN will trigger an implicit DETACH 
 
57
 *   MAGNET FROM FRIDGE if the magnet was attached to the fridge).
 
58
 *
 
59
 *   (9)    Normally, both the major and the minor attachments should be of 
 
60
 *   class SimpleAttachable. 
 
61
 *
 
62
 *
 
63
 *   Setting up a SimpleAttachable is then straightforward, since all the 
 
64
 *   complications are handled on the class. In the simplest case all the 
 
65
 *   game author needs to do is to define the minorAttachmentItems property 
 
66
 *   on the major SimpleAttachable to hold a list of items that can be 
 
67
 *   attached to it, e.g.:
 
68
 *
 
69
 *   minorAttachmentItems = [redMagnet, blueMagnet]
 
70
 *
 
71
 *   If a more complex way of deciding what can be attached to a major 
 
72
 *   SimpleAttachable is required, override its isMajorItemFor() method 
 
73
 *   instead, so that it returns true for any obj that can be attached, e.g.:
 
74
 *
 
75
 *   isMajorItemFor(obj) { return obj.ofKind(Magnet); }
 
76
 *
 
77
 *   One further point to note: if you want a Container-type object to act 
 
78
 *   as a major SimpleAttachment, you'll need to make it a ComplexContainer.
 
79
 *
 
80
 */
 
81
 
 
82
ModuleID
 
83
  name = 'SimpleAttachable'
 
84
  byLine = 'by Eric Eve'
 
85
  htmlByLine = 'by <A href="mailto:eric.eve@hmc.ox.ac.uk">Eric Eve</a>'
 
86
  version = '1.0'  
 
87
;
 
88
 
 
89
 
 
90
class SimpleAttachable: Attachable
 
91
    
 
92
    /* Move the minor attachment into the major attachment. */
 
93
    handleAttach(other)
 
94
    {
 
95
        if(other.isMajorItemFor(self))           
 
96
            moveInto(other);
 
97
        
 
98
    }
 
99
    
 
100
    /* 
 
101
     *   When we're detached, if we were in the other object move us into the
 
102
     *   other object's location.
 
103
     */
 
104
    handleDetach(other)
 
105
    {
 
106
        if(isIn(other))
 
107
            moveInto(other.location);
 
108
    }
 
109
    
 
110
    /* 
 
111
     *   If a minor attachment is taken, first detach it from its major 
 
112
     *   attachment.
 
113
     */
 
114
    dobjFor(Take)
 
115
    {
 
116
        preCond = (nilToList(inherited) + objNotAttachedToMajor)
 
117
    }
 
118
    
 
119
    
 
120
    /*  
 
121
     *   If we're attached to a major attachment, treat TAKE US FROM MAJOR as
 
122
     *   equivalent to DETACH US FROM MAJOR.
 
123
     */
 
124
    dobjFor(TakeFrom) maybeRemapTo(isAttachedToMajor, DetachFrom, self,
 
125
                                   location)
 
126
       
 
127
    /*  
 
128
     *   If we're already attached to a major attachment, detach us from it 
 
129
     *   before attaching us to a different major atttachment.
 
130
     */
 
131
    dobjFor(AttachTo)
 
132
    {
 
133
        preCond = (nilToList(inherited) + objDetachedFromLocation)
 
134
    }
 
135
       
 
136
    iobjFor(AttachTo)
 
137
    {
 
138
        preCond = (nilToList(inherited) + objDetachedFromLocation)
 
139
    }
 
140
    
 
141
    /*  We're a major item for any item in our minorAttachmentItems list. */
 
142
    isMajorItemFor(obj)
 
143
    {
 
144
        return nilToList(minorAttachmentItems).indexOf(obj) != nil;
 
145
    }
 
146
    
 
147
    /*  
 
148
     *   The list of items that can be attached to us for which we would be 
 
149
     *   the major attachment item.
 
150
     */
 
151
    minorAttachmentItems = []
 
152
    
 
153
    /*   
 
154
     *   A pair of convenience methods to determined if we're attached to any
 
155
     *   items that are major or minor attachments relative to us.
 
156
     */
 
157
    isAttachedToMajor = (location && location.isMajorItemFor(self))
 
158
    isAttachedToMinor = (contents.indexWhich({x: self.isMajorItemFor(x)}) !=
 
159
                      nil)
 
160
    
 
161
    /*   
 
162
     *   Define if this item be listed when it's a minor item attached to 
 
163
     *   another item.
 
164
     */
 
165
    isListedWhenAttached = true
 
166
    
 
167
    isListed = (isAttachedToMajor ? isListedWhenAttached : inherited )
 
168
    
 
169
    isListedInContents = (isAttachedToMajor ? nil : inherited )
 
170
    
 
171
    /*  
 
172
     *   Customise the listers so that if we contain minor items as 
 
173
     *   attachments they're shows as being attached to us, not as being in 
 
174
     *   us.
 
175
     */    
 
176
    contentsLister =  (isAttachedToMinor ? majorAttachmentLister : inherited)
 
177
    
 
178
    inlineContentsLister = (isAttachedToMinor ? inlineListingAttachmentsLister :
 
179
                        inherited )
 
180
 
 
181
   
 
182
    /*  
 
183
     *   A SimpleAttachment can be attached to another SimpleAttachment if 
 
184
     *   one of the SimpleAttachments is a major item for the other.
 
185
     */
 
186
    canAttachTo(obj)
 
187
    {
 
188
        return isMajorItemFor(obj) || obj.isMajorItemFor(self);
 
189
    }
 
190
    
 
191
    
 
192
    /*  
 
193
     *   If I start the game located in an object that's a major item for me, 
 
194
     *   presumbably we're meant to start off attached.
 
195
     */
 
196
    initializeThing()
 
197
    {
 
198
        inherited;
 
199
        
 
200
        if(location && location.isMajorItemFor(self))            
 
201
            attachTo(location);
 
202
    }
 
203
;
 
204
 
 
205
/*  
 
206
 *   Custom lister to show the contents of a major attachment as being 
 
207
 *   attached to it.
 
208
 */
 
209
inlineListingAttachmentsLister: ContentsLister
 
210
    showListEmpty(pov, parent) { }
 
211
    showListPrefixWide(cnt, pov, parent)
 
212
        { " (to which <<cnt > 1 ? '{are|were}' : '{is|was}'>>  attached "; }
 
213
    showListSuffixWide(itemCount, pov, parent)
 
214
        { ")"; }
 
215
;
 
216
 
 
217
/*  Special precondition for use when taking a minor attachment. */
 
218
 
 
219
objNotAttachedToMajor: PreCondition
 
220
    
 
221
    /* 
 
222
     *   Other things being equal, prefer to take an item that's not a minor 
 
223
     *   attachment (if the blue magnet is attached to the fridge and the red
 
224
     *   magnet is lying on the floor, then make TAKE MAGNET take the red 
 
225
     *   one).
 
226
     */
 
227
    verifyPreCondition(obj) 
 
228
    { 
 
229
        if(obj.location.isMajorItemFor(obj))
 
230
            logicalRank(90, 'attached');
 
231
    }
 
232
 
 
233
    
 
234
    checkPreCondition(obj, allowImplicit)
 
235
    {
 
236
        /* 
 
237
         *   if we don't already have any non-permanent attachments  that 
 
238
         *   are the major attachments for us, we're fine (as we don't 
 
239
         *   require removing permanent attachments); nothing more needs to 
 
240
         *   be done.  
 
241
         */
 
242
        if (obj.attachedObjects.indexWhich(
 
243
            {x: x.isMajorItemFor(obj) && !obj.isPermanentlyAttachedTo(x) 
 
244
                  }) == nil)
 
245
            return nil;
 
246
 
 
247
                
 
248
        local major = obj.attachedObjects.valWhich({x: x.isMajorItemFor(obj)});
 
249
        
 
250
        /* 
 
251
         *   Try implicitly detaching us from our major attachment.  
 
252
         */
 
253
        if (allowImplicit && tryImplicitAction(DetachFrom, obj, major))
 
254
        {
 
255
            /* 
 
256
             *   if we're still attached to a major attachment, we failed, 
 
257
             *   so abort
 
258
             */
 
259
            if (obj.attachedObjects.indexWhich(
 
260
                {x: !obj.isPermanentlyAttachedTo(x) 
 
261
                  && x.isMajorItemFor(obj)}) != nil)
 
262
                exit;
 
263
 
 
264
            /* tell the caller we executed an implied action */
 
265
            return true;
 
266
        }
 
267
 
 
268
        /* we must detach first */
 
269
        reportFailure(&mustDetachMsg, obj);
 
270
        exit;
 
271
    }
 
272
;
 
273
 
 
274
/*  
 
275
 *   Special precondition to detach us from an existing major attachment 
 
276
 *   before attaching us to another one. This needs to be different from 
 
277
 *   objNotAttachedToMajor so that we don't perform any unnecessary 
 
278
 *   detachments. 
 
279
 */
 
280
 
 
281
objDetachedFromLocation: PreCondition
 
282
    checkPreCondition(obj, allowImplicit)
 
283
    {
 
284
        /* 
 
285
         *   If the other object involved in the command is not a majorItem   
 
286
         *   for us, or we're not already in an object that we're attached 
 
287
         *   to which is our major item, then there's nothing to do. 
 
288
         */
 
289
        
 
290
        local other = (obj == gDobj ? gIobj : gDobj);
 
291
        local loc = obj.location;
 
292
            
 
293
        if(!other.isMajorItemFor(obj) || 
 
294
            !(loc.isMajorItemFor(obj) && obj.isAttachedTo(loc)))
 
295
            return nil;
 
296
 
 
297
                
 
298
               
 
299
        /* 
 
300
         *   if we don't already have any non-permanent attachments  that 
 
301
         *   are the major attachments for us, we're fine (as we don't 
 
302
         *   require removing permanent attachments); nothing more needs to 
 
303
         *   be done.  
 
304
         */
 
305
        if (allowImplicit && tryImplicitAction(DetachFrom, obj, loc))
 
306
        {
 
307
            /* if we're still attached to anything, we failed, so abort */
 
308
            if (loc.isMajorItemFor(obj) && obj.isAttachedTo(loc))
 
309
                exit;
 
310
 
 
311
            /* tell the caller we executed an implied action */
 
312
            return true;
 
313
        }
 
314
 
 
315
        /* we must detach first */
 
316
        reportFailure(&mustDetachMsg, obj);
 
317
        exit;
 
318
    }
 
319
    
 
320
;