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

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/* 
4
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
5
 *   
6
 *   TADS 3 Library - module ID's
7
 *   
8
 *   This module defines a framework for "module ID's."  The module ID
9
 *   mechanism allows library modules to identify themselves.  The module
10
 *   ID framework is modular, in that several libraries can be linked
11
 *   together without any prior knowledge of one another, and the module ID
12
 *   framework will be able to find all of their ID's.  
13
 */
14
15
/* include the library header */
16
#include "adv3.h"
17
18
/* we also require file access */
19
#include <file.h>
20
21
22
/* ------------------------------------------------------------------------ */
23
/*
24
 *   Module ID.  Each library add-in can define one of these, so that the
25
 *   "credits" command and the like can automatically show the version of
26
 *   each library module included in the finished game, without the game's
27
 *   author having to compile a list of the module versions manually.
28
 *   
29
 *   An easy way to implement a CREDITS command is to define a ModuleID
30
 *   object for the game itself, and override the showCredit() method to
31
 *   display the text of the game's credits.  The module object for the
32
 *   game itself should usually have a listingOrder of 1, because the
33
 *   author usually will want the game's information to be displayed first
34
 *   in any listing that shows each included library module (such as the
35
 *   VERSION command's output).  
36
 */
37
class ModuleID: object
38
    /* my name */
39
    name = ''
40
41
    /* the "byline" for the module, in plain text and HTML versions */
42
    byline = ''
43
    htmlByline = ''
44
45
    /* my version number string */
46
    version = ''
47
48
    /*
49
     *   Show my library credit.  By default won't show anything.
50
     *   Libraries should generally not override this, because we want to
51
     *   leave it up to the author to determine how the credits are
52
     *   displayed.  If a library overrides this, then the author won't be
53
     *   able to control the formatting of the library credit, which is
54
     *   undesirable.  
55
     */
56
    showCredit() { }
57
58
    /*
59
     *   Show version information.  By default, we show our name and
60
     *   version number, then start a new line.  The main game's module ID
61
     *   should generally override this to show an appropriate version
62
     *   message for the game, and any library add-ins that want to
63
     *   display their version information can override this to do so.  
64
     */
65
    showVersion()
66
    {
67
        gLibMessages.showVersion(name, version);
68
        "\n";
69
    }
70
71
    /*
72
     *   Show the "about this game" information.  By default, we show
73
     *   nothing here.  Typically, only the game's module ID object will
74
     *   override this; in the game's module ID object, this method should
75
     *   display any desired background information about the game that
76
     *   the author wants the player to see on typing the ABOUT command.
77
     *   
78
     *   The ABOUT command conventionally displays information about the
79
     *   game and its author - the kind of thing you'd find in an author's
80
     *   notes section in a book - along with any special instructions to
81
     *   the player, such as notes on unusual command syntax.  Information
82
     *   that players will find especially helpful include:
83
     *   
84
     *   - A list of any unusual command phrasings that the game uses.
85
     *   Ideally, you will disclose here every verb that's required to
86
     *   complete the game, beyond the basic set common to most games
87
     *   (LOOK, INVENTORY, NORTH, SOUTH, TAKE, DROP, PUT IN, etc).  By
88
     *   disclosing every necessary verb and phrasing, you can be certain
89
     *   to avoid "guess the verb" puzzles.  (Note that it's possible to
90
     *   disclose every *required* verb without disclosing every
91
     *   *accepted* verb - some verbs might be so suggestive of a
92
     *   particular puzzle solution that you wouldn't want to disclose
93
     *   them, but as long as you disclose less suggestive alternatives
94
     *   that can be used to solve the same puzzles, you have a valid
95
     *   defense against accusations of using "guess the verb" puzzles.)
96
     *   
97
     *   - A quick overview of the NPC conversation system, if any.
98
     *   Conversation systems have been slowly evolving as authors
99
     *   experiment with different styles, and at least three or four
100
     *   different conventions have emerged.  The default that experienced
101
     *   players will expect is the traditional ASK/TELL system, so it's
102
     *   especially important to mention your system if you're using
103
     *   something else.
104
     *   
105
     *   - An indication of the "cruelty" level of the game.  In
106
     *   particular, many experienced players find it helpful to know from
107
     *   the outset how careful they have to be about saving positions
108
     *   throughout play, so it's helpful to point out whether or not it's
109
     *   possible for the player character to be killed; whether it's
110
     *   possible to get into situations where the game becomes
111
     *   "unwinnable"; and if the game can become unwinnable, whether or
112
     *   not this will become immediately clear.  The kindest games never
113
     *   kill the PC and are always winnable, no matter what actions the
114
     *   player takes; it's never necessary to save these games except to
115
     *   suspend a session for later resumption.  The cruelest games kill
116
     *   the PC without warning (although if they offer an UNDO command
117
     *   from a "death" prompt, then even this doesn't constitute true
118
     *   cruelty), and can become unwinnable in ways that aren't readily
119
     *   and immediately apparent to the player, which means that the
120
     *   player could proceed for quite some time (and thus invest
121
     *   substantial effort) after the game is already lost.
122
     *   
123
     *   - A description of any special status line displays or other
124
     *   on-screen information whose meaning might not be immediately
125
     *   apparent.  
126
     */
127
    showAbout() { }
128
129
    /* 
130
     *   My listing order.  When we compile a list of modules, we'll sort
131
     *   the modules first by ascending listing order; any modules with
132
     *   the same listing order will be sorted alphabetically by name with
133
     *   respect to the other modules with the same listing order.
134
     *   
135
     *   The value 1 is reserved for the game's own ID object.  Note that
136
     *   the TADS 3 library defines a module ID with listing order 50,
137
     *   which is chosen so that the main library credit will appear after
138
     *   the game credits but before any extension credits using the
139
     *   default order value 100 that we define here.  Extensions are
140
     *   free, however, to use a number lower than 5 if they wish to
141
     *   appear before the main library credit.  
142
     */
143
    listingOrder = 100
144
145
    /* 
146
     *   get a list of all of the modules that are part of the game,
147
     *   sorted in listing order 
148
     */
149
    getModuleList()
150
    {
151
        local lst;
152
        
153
        /* compile a list of all of the modules */
154
        lst = new Vector(10);
155
        forEachInstance(ModuleID, { obj: lst.append(obj) });
156
        lst = lst.toList();
157
158
        /* 
159
         *   sort the list by listing order (and alphabetically by name
160
         *   where listing orders are the same) 
161
         */
162
        lst = lst.sort(SortAsc, new function(a, b) {
163
164
            /* if the listings order differ, sort by listing order */
165
            if (a.listingOrder != b.listingOrder)
166
                return a.listingOrder - b.listingOrder;
167
168
            /* the listing orders are the same; sort by name */
169
            if (a.name < b.name)
170
                return -1;
171
            else if (a.name > b.name)
172
                return 1;
173
            else
174
                return 0;
175
        });
176
177
        /* return the sorted list */
178
        return lst;
179
    }
180
;
181
182
/* ------------------------------------------------------------------------ */
183
/*
184
 *   A module ID with metadata.  During pre-initialization, we'll
185
 *   automatically write out a file with the metadata for each of these
186
 *   objects.  This is an abstract base class; a subclass must be created
187
 *   for each specific metadata format.  
188
 */
189
class MetadataModuleID: ModuleID, PreinitObject
190
    /* execute pre-initialization */
191
    execute()
192
    {
193
        /* write out our metadata */
194
        writeMetadataFile();
195
    }
196
197
    /* 
198
     *   write our metadata file - this must be overridden by each
199
     *   subclass to carry out the specific steps needed to create and
200
     *   write the metadata file in the appropriate format for the
201
     *   subclass 
202
     */
203
    writeMetadataFile() { }
204
;
205
206
/*
207
 *   A module ID with GameInfo metadata.  The GameInfo metadata format is
208
 *   the standard TADS format for descriptive data about the game.  The
209
 *   usual way to use GameInfo metadata is to create a file called
210
 *   "gameinfo.txt" for a game, then embed this file directly in the
211
 *   game's .t3 file using the TADS 3 resource bundler (t3res).  Once the
212
 *   gameinfo.txt is embedded in the .t3 file, tools will be able to read
213
 *   the game's descriptive data directly from the .t3 file.  For example,
214
 *   HTML TADS on Windows can read the information into its Game Chest,
215
 *   which allows the interpreter to show the full name of the game, the
216
 *   author, and a blurb describing the game, among other things.
217
 */
218
class GameInfoModuleID: MetadataModuleID
219
    /* 
220
     *   The IFID - this is a UUID uniquely identifying the game, using the
221
     *   standard UUID format (xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where
222
     *   each 'x' is a hexadecimal digit).  You should pick an IFID when
223
     *   you start each game project, and then keep the same IFID
224
     *   throughout the game's entire existence, *including* version
225
     *   updates.  Each new version release of the same game - even major
226
     *   new versions - should use the same IFID, so that the versions can
227
     *   all be related to one another as the same game.
228
     */
229
    IFID = ''
230
231
    /*
232
     *   The game's headline.  It's become an IF tradition to use a
233
     *   quasi-subtitle of the sort that Infocom used, of the form "An
234
     *   Interactive Mystery."  This can be used to define that subtitle.  
235
     */
236
    headline = ''
237
238
    /*
239
     *   If this game is part of a series, such as a trilogy, these can be
240
     *   used to identify the name of the series and the position in the
241
     *   series.  The series name should be something like "The Enchanter
242
     *   Trilogy"; the series number, if provided, should be a simple
243
     *   integer string ('1', '2', etc) giving the position in the series.
244
     *   Note that the series number isn't required even if a series name
245
     *   is specified, since some series are just groups of works without
246
     *   any particular ordering. 
247
     */
248
    seriesName = ''
249
    seriesNumber = ''
250
251
    /*
252
     *   The genre of the game.  Some games don't fit any particular genre,
253
     *   and some authors just don't like the idea of having to pigeonhole
254
     *   their games, so feel free to leave it out.  If there's a good fit
255
     *   to a well-established genre, though, you can specify it here.  We
256
     *   recommend you keep this short - one word, maybe two - and use a
257
     *   genre name that's generally recognized as such.  You might want to
258
     *   use Baf's Guide as a reference (http://www.wurb.com/if/genre).  
259
     */
260
    genreName = ''
261
262
    /*
263
     *   The forgiveness level, according to the Zarfian scale propounded
264
     *   by Andrew Plotkin on rec.arts.int-fiction.  This must be one of
265
     *   these terms, using the exact capitalization shown: Merciful,
266
     *   Polite, Tough, Nasty, Cruel.
267
     */
268
    forgivenessLevel = ''
269
    
270
    /* 
271
     *   The names and email addresses of the authors, in GameInfo format.
272
     *   This list must use the following format:
273
     *   
274
     *   author one <email>; author two <email> <email>; ...
275
     *   
276
     *   In other words, list the first author's name, followed by one or
277
     *   more email addresses, in angle brackets, for the first author.
278
     *   If more than one author is to be listed, add a semicolon,
279
     *   followed by the name of the second author, followed by the second
280
     *   author's email address or addresses, enclosing each in angle
281
     *   brackets.  Repeat as needed for additional authors.  The list
282
     *   does not need to end with a semicolon; semicolons are merely used
283
     *   to separate entries.  
284
     */
285
    authorEmail = ''
286
287
    /*
288
     *   The game's web site, if any.  If specified, this must be an
289
     *   absolute URL with http protocol - that is, it must be of the form
290
     *   "http://mydomain.com/...".  
291
     */
292
    gameUrl = ''
293
294
    /*
295
     *   Descriptive text for the game, in plain text format.  This is a
296
     *   short description that can be used, for example, in a catalog of
297
     *   games.  This should be a couple of sentences or so.
298
     */
299
    desc = ''
300
301
    /*
302
     *   Descriptive text for the game, as an HTML fragment.  This should
303
     *   have the same information as the 'desc', but this version can use
304
     *   HTML markups (including tags and character entities) to embellish
305
     *   the display of the text.  Any HTML markups should be "in-line"
306
     *   body elements only, not "block" or head elements, so that this
307
     *   text can be inserted into a larger HTML document.  For example,
308
     *   markups like <i> and <b> are fine, but <p> and <table> should not
309
     *   be used.  
310
     */
311
    htmlDesc = ''
312
313
    /*
314
     *   The release date.  By default, we compute this statically to be
315
     *   today's date.  This means this will be set to the date of
316
     *   compilation.  If the game wishes to override this, note that the
317
     *   GameInfo format requires this to be of the form YYYY-MM-DD.  For
318
     *   example, December 9, 2001 would be '2001-12-09'.  
319
     */
320
    releaseDate = static getGameInfoToday()
321
322
    /*
323
     *   The date of first publication.  This can be just a year in YYYY
324
     *   format, or a full YYYY-MM-DD date.  This is the original release
325
     *   date of the original version of the game, which is often of
326
     *   interest to archivists.  This should *not* be updated when a new
327
     *   release is made - it's always the date of *original* publication.
328
     */
329
    firstPublished = ''
330
331
    /*
332
     *   The language in which this game's text is written.  This is the
333
     *   RFC3066 language code for the main language of the work.  For
334
     *   example, games written in US English would use 'en-US', while
335
     *   games written in British English would use 'en-GB'.  Note that
336
     *   each language-specific library module should use 'modify' to set
337
     *   this to the default for the library.  
338
     */
339
    languageCode = ''
340
341
    /*
342
     *   The license type for this game.  Most text IF games these days
343
     *   are released as freeware, so we use this as the default.  The
344
     *   GameInfo metadata format defines several other standard license
345
     *   types, including Public Domain, Shareware, Commercial Demo,
346
     *   Commercial, and Other.  Authors should change this if they plan
347
     *   to release under a licensing model other than freeware.
348
     *   
349
     *   Note that the GameInfo metadata format documentation explicitly
350
     *   states that the license type indicated here is advisory only and
351
     *   cannot be considered definitive.  This means that this setting
352
     *   does not take away any of the author's rights to set specific
353
     *   license terms.  Even so, we recommend that you pick an
354
     *   appropriate value here to avoid any confusion.  
355
     */
356
    licenseType = 'Freeware'
357
358
    /*
359
     *   The copying rules for this game.  Most text games these days are
360
     *   released as freeware with minimal restrictions on copying, so we
361
     *   use a default of "nominal cost only." Other values defined in the
362
     *   GameInfo format include Prohibited, No Restrictions, No-Cost Only,
363
     *   At-Cost Only, and Other.  A modifier indicates whether or not the
364
     *   game may be included in compilations (such as those "10,001 great
365
     *   games" CD-R's that people like to sell on auction sites); we
366
     *   indicate that inclusion in compilations is allowed by default.
367
     *   You can change this to "Compilations Prohibited" if you prefer not
368
     *   to allow your game to be distributed in that fashion.
369
     *   
370
     *   Note that the restrictions specified here aren't enforced by any
371
     *   sort of copy-protection or DRM (digital rights management)
372
     *   technology.  This information is entirely for the benefit of
373
     *   conscientious users who want to abide by your wishes and thus need
374
     *   to know what your wishes are.
375
     *   
376
     *   The GameInfo bundle is mostly for the benefit of software that can
377
     *   extract the information from the compiled game.  So, we recommend
378
     *   that you also put a full notice and explanation of your license
379
     *   restrictions somewhere that users can easily find it, such as in a
380
     *   separate LICENSE.TXT file that you distribute with your game, or
381
     *   in the text of the game itself (displayed by a LICENSE or
382
     *   COPYRIGHT command, for example).  
383
     */
384
    copyingRules = 'Nominal cost only; compilations allowed'
385
386
    /*
387
     *   The recommended "presentation profile" for the game.  'Default'
388
     *   means that the interpreter's default profile should be used.
389
     *   (Some interpreters let the user select which profile to use as the
390
     *   default, in which case 'Default' means we'll use that profile.)  
391
     */
392
    presentationProfile = 'Default'
393
394
    /* write our metadata file */
395
    writeMetadataFile()
396
    {
397
        local f;
398
399
        /* 
400
         *   open the file - note that the GameInfo.txt resource is
401
         *   required to be encoded in UTF-8, so open the file with the
402
         *   UTF-8 character set 
403
         */
404
        f = File.openTextFile(gameInfoFilename, FileAccessWrite, 'utf-8');
405
    
406
        /* scan our list of metadata keys and write each one */
407
        for (local i = 1, local len = metadataKeys.length() ;
408
             i + 1 <= len ; i += 2)
409
        {
410
            local key;
411
            local prop;
412
            local val;
413
            
414
            /* get the key name and value property for this entry */
415
            key = metadataKeys[i];
416
            prop = metadataKeys[i+1];
417
            val = self.(prop);
418
419
            /* turn any '\ ' characters into ' ' characters */
420
            val = val.findReplace('\ ', ' ', ReplaceAll);
421
            
422
            /* write out this key if there's a value defined for it */
423
            if (val != nil && val != '')
424
                f.writeFile(key + ': ' + val + '\n');
425
        }
426
427
        /* done with the file - close it */
428
        f.closeFile();
429
    }
430
431
    /* 
432
     *   the GameInfo filename - by default, we write the standard
433
     *   gameinfo.txt file 
434
     */
435
    gameInfoFilename = 'GameInfo.txt'
436
437
    /*
438
     *   The metadata key mappings.  This is a list of key/property pairs.
439
     *   The key in each pair is a string giving a standard GameInfo key
440
     *   name, and the property gives the property (of self) that we
441
     *   evaluate to get the string value for that key. 
442
     */
443
    metadataKeys =
444
    [
445
        'IFID', &IFID,
446
        'Name', &name,
447
        'Headline', &headline,
448
        'Byline', &byline,
449
        'HtmlByline', &htmlByline,
450
        'AuthorEmail', &authorEmail,
451
        'Url', &gameUrl,
452
        'Desc', &desc,
453
        'HtmlDesc', &htmlDesc,
454
        'Version', &version,
455
        'ReleaseDate', &releaseDate,
456
        'FirstPublished', &firstPublished,
457
        'Series', &seriesName,
458
        'SeriesNumber', &seriesNumber,
459
        'Genre', &genreName,
460
        'Forgiveness', &forgivenessLevel,
461
        'Language', &languageCode,
462
        'LicenseType', &licenseType,
463
        'CopyingRules', &copyingRules,
464
        'PresentationProfile', &presentationProfile
465
    ]
466
467
    /* 
468
     *   get today's date, using the GameInfo standard date format
469
     *   (YYYY-MM-DD) 
470
     */
471
    getGameInfoToday()
472
    {
473
        local dt;
474
        local mm, dd;
475
476
        /* get the current date */
477
        dt = getTime(GetTimeDateAndTime);
478
        
479
        /* get the month, and add a leading zero if it's only one digit */
480
        mm = (dt[2] < 10 ? '0' : '') + dt[2];
481
        
482
        /* get the day, and add a leading zero if it's only one digit */
483
        dd = (dt[3] < 10 ? '0' : '') + dt[3];
484
        
485
        /* build and return the full YYYY-MM-DD date string */
486
        return toString(dt[1]) + '-' + mm + '-' + dd;
487
    }
488
;
489
490
491
/* ------------------------------------------------------------------------ */
492
/*
493
 *   Base class for the game's module ID.  This merely sets the listing
494
 *   order to 1 so that the game's credit is listed first.  Normally,
495
 *   exactly one GameID object, called 'versionInfo', is defined in a game,
496
 *   to provide the game's identifying information.  
497
 *   
498
 *   Note that this class is based on GameInfoModuleID, so the library will
499
 *   automatically write out a gameinfo.txt file based on this object's
500
 *   settings.  For full GameInfo data, the game should minimally define the
501
 *   following properties (see GameInfoModuleID and ModuleID for details on
502
 *   these properties):
503
 *
504
 *.  IFID - a random 32-digit hex number to uniquely identify the game;
505
 *.    you can generate one with http://www.tads.org/ifidgen/ifidgen.pl
506
 *.  name - the name of the game
507
 *.  byline - the main author credit: "by so and so"
508
 *.  htmlByline - the main author credit as an HTML fragment
509
 *.  authorEmail - the authors' names and email addresses (in GameInfo format)
510
 *.  desc - a short blurb describing the game, in plain text format
511
 *.  htmlDesc - the descriptive blurb as an HTML ragment
512
 *.  version - the game's version string
513
 *   
514
 *   In addition, you can override the following settings if you don't like
515
 *   the defaults inherited from GameInfoModuleID:
516
 *   
517
 *.  releaseDate - the release date string (YYYY-MM-DD)
518
 *.  licenseType - freeware, shareware, etc.
519
 *.  copyingRules - summary rules on copying
520
 *.  presentationProfile - Multimedia, Plain Text
521
 */
522
class GameID: GameInfoModuleID
523
    /* always list the game's credits before any library credits */
524
    listingOrder = 1
525
526
    /*
527
     *   Show the game's credits.  By default, we'll just show our name and
528
     *   by-line.
529
     *   
530
     *   Typically, authors will want to override this to display the full
531
     *   credits for the game.  Most authors like to show the author or
532
     *   authors, along with notes of thanks to important contributors.
533
     *   
534
     *   Note that libraries generally will not show anything automatically
535
     *   in the credits, to allow the author full control over the
536
     *   formatting of the credits.  Authors are encouraged to give credit
537
     *   where it's due for any libraries they use.  
538
     */
539
    showCredit()
540
    {
541
        /* by default, just show the game's name and by-line */
542
        gLibMessages.showCredit(name, htmlByline);
543
    }
544
545
    /* 
546
     *   show a blank line after the game's version information, to make
547
     *   it stand apart from the list of library and VM version numbers 
548
     */
549
    showVersion()
550
    {
551
        inherited();
552
        "\b";
553
    }
554
;
555
556
/* ------------------------------------------------------------------------ */
557
/*
558
 *   The main TADS 3 library ID.
559
 */
560
moduleAdv3: ModuleID
561
    name = 'TADS 3 Library'
562
    byline = 'by Michael J.\ Roberts'
563
    htmlByline = 'by <a href="mailto:mjr_@hotmail.com">'
564
                 + 'Michael J.\ Roberts</a>'
565
    version = '3.0.18.1'
566
567
    /*
568
     *   We use a listing order of 50 so that, if all of the other credits
569
     *   use the defaults, we appear after the game's own credits
570
     *   (conventionally at listing order 1) and before any extension
571
     *   credits (which inherit the default order 100), but so that
572
     *   there's room for extensions that want to appear before us, or
573
     *   after us but before any default-ordered extensions.  
574
     */
575
    listingOrder = 50
576
;
577
578
579
/* ------------------------------------------------------------------------ */
580
/*
581
 *   An ID module not for the library but for the T3 VM itself.  This
582
 *   doesn't display any credit information, but displays version number
583
 *   information for the VM so that the "version" command shows what
584
 *   version of the interpreter is in use.  
585
 */
586
ModuleID
587
    showVersion()
588
    {
589
        local vsn = t3GetVMVsn();
590
591
        /* 
592
         *   show the version information - note that we must decompose
593
         *   the version number into the standard 3-part dotted string 
594
         */
595
        gLibMessages.showVersion('T3 VM (' + t3GetVMID() + ')',
596
                                 '' + (vsn >> 16) + '.'
597
                                 + ((vsn >> 8) & 0xFF) + '.'
598
                                 + (vsn & 0xFF));
599
        "\n";
600
    }
601
602
    /*
603
     *   Use a very high listing order so that we're the last thing shown.
604
     */
605
    listingOrder = 10000
606
;
607