# modid.t | documentation

    #charset "us-ascii"

    /*
     *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved.
     *
     *   TADS 3 Library - module ID's
     *
     *   This module defines a framework for "module ID's."  The module ID
     *   mechanism allows library modules to identify themselves.  The module
     *   ID framework is modular, in that several libraries can be linked
     *   together without any prior knowledge of one another, and the module ID
     *   framework will be able to find all of their ID's.
     */

    /* include the library header */
    #include "adv3.h"

    /* we also require file access */
    #include <file.h>

    /* ------------------------------------------------------------------------ */
    /*
     *   Module ID.  Each library add-in can define one of these, so that the
     *   "credits" command and the like can automatically show the version of
     *   each library module included in the finished game, without the game's
     *   author having to compile a list of the module versions manually.
     *
     *   An easy way to implement a CREDITS command is to define a ModuleID
     *   object for the game itself, and override the showCredit() method to
     *   display the text of the game's credits.  The module object for the
     *   game itself should usually have a listingOrder of 1, because the
     *   author usually will want the game's information to be displayed first
     *   in any listing that shows each included library module (such as the
     *   VERSION command's output).
     */
    class ModuleID: object
        /* my name */
        name = ''

        /* the "byline" for the module, in plain text and HTML versions */
        byline = ''
        htmlByline = ''

        /* my version number string */
        version = ''

        /*
         *   Show my library credit.  By default won't show anything.
         *   Libraries should generally not override this, because we want to
         *   leave it up to the author to determine how the credits are
         *   displayed.  If a library overrides this, then the author won't be
         *   able to control the formatting of the library credit, which is
         *   undesirable.
         */
        showCredit() { }

        /*
         *   Show version information.  By default, we show our name and
         *   version number, then start a new line.  The main game's module ID
         *   should generally override this to show an appropriate version
         *   message for the game, and any library add-ins that want to
         *   display their version information can override this to do so.
         */
        showVersion()
        {
            gLibMessages.showVersion(name, version);
            "\n";
        }

        /*
         *   Show the "about this game" information.  By default, we show
         *   nothing here.  Typically, only the game's module ID object will
         *   override this; in the game's module ID object, this method should
         *   display any desired background information about the game that
         *   the author wants the player to see on typing the ABOUT command.
         *
         *   The ABOUT command conventionally displays information about the
         *   game and its author - the kind of thing you'd find in an author's
         *   notes section in a book - along with any special instructions to
         *   the player, such as notes on unusual command syntax.  Information
         *   that players will find especially helpful include:
         *
         *   - A list of any unusual command phrasings that the game uses.
         *   Ideally, you will disclose here every verb that's required to
         *   complete the game, beyond the basic set common to most games
         *   (LOOK, INVENTORY, NORTH, SOUTH, TAKE, DROP, PUT IN, etc).  By
         *   disclosing every necessary verb and phrasing, you can be certain
         *   to avoid "guess the verb" puzzles.  (Note that it's possible to
         *   disclose every *required* verb without disclosing every
         *   *accepted* verb - some verbs might be so suggestive of a
         *   particular puzzle solution that you wouldn't want to disclose
         *   them, but as long as you disclose less suggestive alternatives
         *   that can be used to solve the same puzzles, you have a valid
         *   defense against accusations of using "guess the verb" puzzles.)
         *
         *   - A quick overview of the NPC conversation system, if any.
         *   Conversation systems have been slowly evolving as authors
         *   experiment with different styles, and at least three or four
         *   different conventions have emerged.  The default that experienced
         *   players will expect is the traditional ASK/TELL system, so it's
         *   especially important to mention your system if you're using
         *   something else.
         *
         *   - An indication of the "cruelty" level of the game.  In
         *   particular, many experienced players find it helpful to know from
         *   the outset how careful they have to be about saving positions
         *   throughout play, so it's helpful to point out whether or not it's
         *   possible for the player character to be killed; whether it's
         *   possible to get into situations where the game becomes
         *   "unwinnable"; and if the game can become unwinnable, whether or
         *   not this will become immediately clear.  The kindest games never
         *   kill the PC and are always winnable, no matter what actions the
         *   player takes; it's never necessary to save these games except to
         *   suspend a session for later resumption.  The cruelest games kill
         *   the PC without warning (although if they offer an UNDO command
         *   from a "death" prompt, then even this doesn't constitute true
         *   cruelty), and can become unwinnable in ways that aren't readily
         *   and immediately apparent to the player, which means that the
         *   player could proceed for quite some time (and thus invest
         *   substantial effort) after the game is already lost.
         *
         *   - A description of any special status line displays or other
         *   on-screen information whose meaning might not be immediately
         *   apparent.
         */
        showAbout() { }

        /*
         *   My listing order.  When we compile a list of modules, we'll sort
         *   the modules first by ascending listing order; any modules with
         *   the same listing order will be sorted alphabetically by name with
         *   respect to the other modules with the same listing order.
         *
         *   The value 1 is reserved for the game's own ID object.  Note that
         *   the TADS 3 library defines a module ID with listing order 50,
         *   which is chosen so that the main library credit will appear after
         *   the game credits but before any extension credits using the
         *   default order value 100 that we define here.  Extensions are
         *   free, however, to use a number lower than 5 if they wish to
         *   appear before the main library credit.
         */
        listingOrder = 100

        /*
         *   get a list of all of the modules that are part of the game,
         *   sorted in listing order
         */
        getModuleList()
        {
            local lst;

            /* compile a list of all of the modules */
            lst = new Vector(10);
            forEachInstance(ModuleID, { obj: lst.append(obj) });
            lst = lst.toList();

            /*
             *   sort the list by listing order (and alphabetically by name
             *   where listing orders are the same)
             */
            lst = lst.sort(SortAsc, function(a, b) {

                /* if the listings order differ, sort by listing order */
                if (a.listingOrder != b.listingOrder)
                    return a.listingOrder - b.listingOrder;

                /* the listing orders are the same; sort by name */
                if (a.name < b.name)
                    return -1;
                else if (a.name > b.name)
                    return 1;
                else
                    return 0;
            });

            /* return the sorted list */
            return lst;
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   A module ID with metadata.  During pre-initialization, we'll
     *   automatically write out a file with the metadata for each of these
     *   objects.  This is an abstract base class; a subclass must be created
     *   for each specific metadata format.
     */
    class MetadataModuleID: ModuleID, PreinitObject
        /* execute pre-initialization */
        execute()
        {
            /* write out our metadata */
            writeMetadataFile();
        }

        /*
         *   write our metadata file - this must be overridden by each
         *   subclass to carry out the specific steps needed to create and
         *   write the metadata file in the appropriate format for the
         *   subclass
         */
        writeMetadataFile() { }
    ;

    /*
     *   A module ID with GameInfo metadata.  The GameInfo metadata format is
     *   the standard TADS format for descriptive data about the game.  The
     *   usual way to use GameInfo metadata is to create a file called
     *   "gameinfo.txt" for a game, then embed this file directly in the
     *   game's .t3 file using the TADS 3 resource bundler (t3res).  Once the
     *   gameinfo.txt is embedded in the .t3 file, tools will be able to read
     *   the game's descriptive data directly from the .t3 file.  For example,
     *   HTML TADS on Windows can read the information into its Game Chest,
     *   which allows the interpreter to show the full name of the game, the
     *   author, and a blurb describing the game, among other things.
     */
    class GameInfoModuleID: MetadataModuleID
        /*
         *   The IFID - this is a UUID uniquely identifying the game, using the
         *   standard UUID format (xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where
         *   each 'x' is a hexadecimal digit).  You should pick an IFID when
         *   you start each game project, and then keep the same IFID
         *   throughout the game's entire existence, *including* version
         *   updates.  Each new version release of the same game - even major
         *   new versions - should use the same IFID, so that the versions can
         *   all be related to one another as the same game.
         *
         *   If the game has multiple IFIDs, list them here, separated by
         *   commas.  You should NOT *intentionally* create multiple IFIDs for
         *   your game; once you've created an IFID, it should be the unique
         *   and permanent identifier for the game.  In particular, do NOT
         *   create a new IFID for a new version: the whole series of releases
         *   throughout a game's lifetime should be identified by a single
         *   IFID, so that archivists will know that the versions are all
         *   incarnations of the same work.
         *
         *   The reason that multiple IFIDs are allowed at all is that many
         *   older games were not assigned explicit UUID-style IFIDs when
         *   released.  In such cases, the game has an "implied" IFID based on
         *   an MD5 hash of the compiled game file's contents.  Every release
         *   that doesn't contain an explicit IFID will therefore have a
         *   different implied IFID.  So, for example, if you've already
         *   released versions 1, 2, and 3 of your game, and you didn't assign
         *   explicit IFID values to those releases, each version will have a
         *   different implied IFID.  When you release version 4, you should
         *   NOT assign a new UUID-style IFID.  Instead, in the IFID string
         *   here, list ALL THREE of the implied IFIDs from the past releases.
         *   Each of the three IFIDs counts from now on as an IFID for the
         *   work, for all versions collectively.  (By placing the list of
         *   IFIDs in version 4, you prevent version 4 from adding yet another
         *   implied IFID of its own: the explicit IFID list supersedes the
         *   implied IFID.)  See the Babel spec for more information, and for
         *   instructions on how to calculate the implied IFID for a TADS game
         *   that was released without a UUID-style IFID.
         */
        IFID = ''

        /*
         *   The game's headline.  It's become an IF tradition to use a
         *   quasi-subtitle of the sort that Infocom used, of the form "An
         *   Interactive Mystery."  This can be used to define that subtitle.
         */
        headline = ''

        /*
         *   If this game is part of a series, such as a trilogy, these can be
         *   used to identify the name of the series and the position in the
         *   series.  The series name should be something like "The Enchanter
         *   Trilogy"; the series number, if provided, should be a simple
         *   integer string ('1', '2', etc) giving the position in the series.
         *   Note that the series number isn't required even if a series name
         *   is specified, since some series are just groups of works without
         *   any particular ordering.
         */
        seriesName = ''
        seriesNumber = ''

        /*
         *   The genre of the game.  Some games don't fit any particular genre,
         *   and some authors just don't like the idea of having to pigeonhole
         *   their games, so feel free to leave it out.  If there's a good fit
         *   to a well-established genre, though, you can specify it here.  We
         *   recommend you keep this short - one word, maybe two - and use a
         *   genre name that's generally recognized as such.  You might want to
         *   use Baf's Guide as a reference (http://www.wurb.com/if/genre).
         */
        genreName = ''

        /*
         *   The forgiveness level, according to the Zarfian scale propounded
         *   by Andrew Plotkin on rec.arts.int-fiction.  This must be one of
         *   these terms, using the exact capitalization shown: Merciful,
         *   Polite, Tough, Nasty, Cruel.
         */
        forgivenessLevel = ''

        /*
         *   The names and email addresses of the authors, in GameInfo format.
         *   This list must use the following format:
         *
         *   author one <email>; author two <email> <email>; ...
         *
         *   In other words, list the first author's name, followed by one or
         *   more email addresses, in angle brackets, for the first author.
         *   If more than one author is to be listed, add a semicolon,
         *   followed by the name of the second author, followed by the second
         *   author's email address or addresses, enclosing each in angle
         *   brackets.  Repeat as needed for additional authors.  The list
         *   does not need to end with a semicolon; semicolons are merely used
         *   to separate entries.
         */
        authorEmail = ''

        /*
         *   The game's web site, if any.  If specified, this must be an
         *   absolute URL with http protocol - that is, it must be of the form
         *   "http://mydomain.com/...".
         */
        gameUrl = ''

        /*
         *   Descriptive text for the game, in plain text format.  This is a
         *   short description that can be used, for example, in a catalog of
         *   games.  This should be a couple of sentences or so.
         */
        desc = ''

        /*
         *   Descriptive text for the game, as an HTML fragment.  This should
         *   have the same information as the 'desc', but this version can use
         *   HTML markups (including tags and character entities) to embellish
         *   the display of the text.  Any HTML markups should be "in-line"
         *   body elements only, not "block" or head elements, so that this
         *   text can be inserted into a larger HTML document.  For example,
         *   markups like <i> and <b> are fine, but <p> and <table> should not
         *   be used.
         */
        htmlDesc = ''

        /*
         *   The release date.  By default, we compute this statically to be
         *   today's date.  This means this will be set to the date of
         *   compilation.  If the game wishes to override this, note that the
         *   GameInfo format requires this to be of the form YYYY-MM-DD.  For
         *   example, December 9, 2001 would be '2001-12-09'.
         */
        releaseDate = static getGameInfoToday()

        /*
         *   The date of first publication.  This can be just a year in YYYY
         *   format, or a full YYYY-MM-DD date.  This is the original release
         *   date of the original version of the game, which is often of
         *   interest to archivists.  This should *not* be updated when a new
         *   release is made - it's always the date of *original* publication.
         */
        firstPublished = ''

        /*
         *   The language in which this game's text is written.  This is the
         *   RFC3066 language code for the main language of the work.  For
         *   example, games written in US English would use 'en-US', while
         *   games written in British English would use 'en-GB'.  Note that
         *   each language-specific library module should use 'modify' to set
         *   this to the default for the library.
         */
        languageCode = ''

        /*
         *   The license type for this game.  Most text IF games these days
         *   are released as freeware, so we use this as the default.  The
         *   GameInfo metadata format defines several other standard license
         *   types, including Public Domain, Shareware, Commercial Demo,
         *   Commercial, and Other.  Authors should change this if they plan
         *   to release under a licensing model other than freeware.
         *
         *   Note that the GameInfo metadata format documentation explicitly
         *   states that the license type indicated here is advisory only and
         *   cannot be considered definitive.  This means that this setting
         *   does not take away any of the author's rights to set specific
         *   license terms.  Even so, we recommend that you pick an
         *   appropriate value here to avoid any confusion.
         */
        licenseType = 'Freeware'

        /*
         *   The copying rules for this game.  Most text games these days are
         *   released as freeware with minimal restrictions on copying, so we
         *   use a default of "nominal cost only." Other values defined in the
         *   GameInfo format include Prohibited, No Restrictions, No-Cost Only,
         *   At-Cost Only, and Other.  A modifier indicates whether or not the
         *   game may be included in compilations (such as those "10,001 great
         *   games" CD-R's that people like to sell on auction sites); we
         *   indicate that inclusion in compilations is allowed by default.
         *   You can change this to "Compilations Prohibited" if you prefer not
         *   to allow your game to be distributed in that fashion.
         *
         *   Note that the restrictions specified here aren't enforced by any
         *   sort of copy-protection or DRM (digital rights management)
         *   technology.  This information is entirely for the benefit of
         *   conscientious users who want to abide by your wishes and thus need
         *   to know what your wishes are.
         *
         *   The GameInfo bundle is mostly for the benefit of software that can
         *   extract the information from the compiled game.  So, we recommend
         *   that you also put a full notice and explanation of your license
         *   restrictions somewhere that users can easily find it, such as in a
         *   separate LICENSE.TXT file that you distribute with your game, or
         *   in the text of the game itself (displayed by a LICENSE or
         *   COPYRIGHT command, for example).
         */
        copyingRules = 'Nominal cost only; compilations allowed'

        /*
         *   The recommended "presentation profile" for the game.  'Default'
         *   means that the interpreter's default profile should be used.
         *   (Some interpreters let the user select which profile to use as the
         *   default, in which case 'Default' means we'll use that profile.)
         */
        presentationProfile = 'Default'

        /* write our metadata file */
        writeMetadataFile()
        {
            local f;

            /*
             *   open the file - note that the GameInfo.txt resource is
             *   required to be encoded in UTF-8, so open the file with the
             *   UTF-8 character set
             */
            f = File.openTextFile(gameInfoFilename, FileAccessWrite, 'utf-8');

            /* scan our list of metadata keys and write each one */
            for (local i = 1, local len = metadataKeys.length() ;
                 i + 1 <= len ; i += 2)
            {
                local key;
                local prop;
                local val;

                /* get the key name and value property for this entry */
                key = metadataKeys[i];
                prop = metadataKeys[i+1];
                val = self.(prop);

                /* turn any '\ ' characters into ' ' characters */
                val = val.findReplace('\ ', ' ', ReplaceAll);

                /* write out this key if there's a value defined for it */
                if (val != nil && val != '')
                    f.writeFile(key + ': ' + val + '\n');
            }

            /* done with the file - close it */
            f.closeFile();

            /* remember the primary IFID in the globals */
            libGlobal.IFID = rexReplace([',.*$', '<space>+'], IFID, '');
        }

        /*
         *   the GameInfo filename - by default, we write the standard
         *   gameinfo.txt file
         */
        gameInfoFilename = 'GameInfo.txt'

        /*
         *   The metadata key mappings.  This is a list of key/property pairs.
         *   The key in each pair is a string giving a standard GameInfo key
         *   name, and the property gives the property (of self) that we
         *   evaluate to get the string value for that key.
         */
        metadataKeys =
        [
            'IFID', &IFID,
            'Name', &name,
            'Headline', &headline,
            'Byline', &byline,
            'HtmlByline', &htmlByline,
            'AuthorEmail', &authorEmail,
            'Url', &gameUrl,
            'Desc', &desc,
            'HtmlDesc', &htmlDesc,
            'Version', &version,
            'ReleaseDate', &releaseDate,
            'FirstPublished', &firstPublished,
            'Series', &seriesName,
            'SeriesNumber', &seriesNumber,
            'Genre', &genreName,
            'Forgiveness', &forgivenessLevel,
            'Language', &languageCode,
            'LicenseType', &licenseType,
            'CopyingRules', &copyingRules,
            'PresentationProfile', &presentationProfile
        ]

        /*
         *   get today's date, using the GameInfo standard date format
         *   (YYYY-MM-DD)
         */
        getGameInfoToday()
        {
            local dt;
            local mm, dd;

            /* get the current date */
            dt = getTime(GetTimeDateAndTime);

            /* get the month, and add a leading zero if it's only one digit */
            mm = (dt[2] < 10 ? '0' : '') + dt[2];

            /* get the day, and add a leading zero if it's only one digit */
            dd = (dt[3] < 10 ? '0' : '') + dt[3];

            /* build and return the full YYYY-MM-DD date string */
            return toString(dt[1]) + '-' + mm + '-' + dd;
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   Base class for the game's module ID.  This merely sets the listing
     *   order to 1 so that the game's credit is listed first.  Normally,
     *   exactly one GameID object, called 'versionInfo', is defined in a game,
     *   to provide the game's identifying information.
     *
     *   Note that this class is based on GameInfoModuleID, so the library will
     *   automatically write out a gameinfo.txt file based on this object's
     *   settings.  For full GameInfo data, the game should minimally define the
     *   following properties (see GameInfoModuleID and ModuleID for details on
     *   these properties):
     *
     *.  IFID - a random 32-digit hex number to uniquely identify the game;
     *.    you can generate one at http://www.tads.org/ifidgen/ifidgen
     *.  name - the name of the game
     *.  byline - the main author credit: "by so and so"
     *.  htmlByline - the main author credit as an HTML fragment
     *.  authorEmail - the authors' names and email addresses (in GameInfo format)
     *.  desc - a short blurb describing the game, in plain text format
     *.  htmlDesc - the descriptive blurb as an HTML ragment
     *.  version - the game's version string
     *
     *   In addition, you can override the following settings if you don't like
     *   the defaults inherited from GameInfoModuleID:
     *
     *.  releaseDate - the release date string (YYYY-MM-DD)
     *.  licenseType - freeware, shareware, etc.
     *.  copyingRules - summary rules on copying
     *.  presentationProfile - Multimedia, Plain Text
     */
    class GameID: GameInfoModuleID
        /* always list the game's credits before any library credits */
        listingOrder = 1

        /*
         *   Show the game's credits.  By default, we'll just show our name and
         *   by-line.
         *
         *   Typically, authors will want to override this to display the full
         *   credits for the game.  Most authors like to show the author or
         *   authors, along with notes of thanks to important contributors.
         *
         *   Note that libraries generally will not show anything automatically
         *   in the credits, to allow the author full control over the
         *   formatting of the credits.  Authors are encouraged to give credit
         *   where it's due for any libraries they use.
         */
        showCredit()
        {
            /* by default, just show the game's name and by-line */
            gLibMessages.showCredit(name, htmlByline);
        }

        /*
         *   show a blank line after the game's version information, to make
         *   it stand apart from the list of library and VM version numbers
         */
        showVersion()
        {
            inherited();
            "\b";
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   The main TADS 3 library ID.
     */
    moduleAdv3: ModuleID
        name = 'TADS 3 Library'
        byline = 'by Michael J.\ Roberts'
        htmlByline = 'by <a href="mailto:mjr_@hotmail.com">'
                     + 'Michael J.\ Roberts</a>'
        version = '3.1.3'

        /*
         *   We use a listing order of 50 so that, if all of the other credits
         *   use the defaults, we appear after the game's own credits
         *   (conventionally at listing order 1) and before any extension
         *   credits (which inherit the default order 100), but so that
         *   there's room for extensions that want to appear before us, or
         *   after us but before any default-ordered extensions.
         */
        listingOrder = 50
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   An ID module not for the library but for the T3 VM itself.  This
     *   doesn't display any credit information, but displays version number
     *   information for the VM so that the "version" command shows what
     *   version of the interpreter is in use.
     */
    ModuleID
        showVersion()
        {
            local vsn = t3GetVMVsn();

            /*
             *   show the version information - note that we must decompose
             *   the version number into the standard 3-part dotted string
             */
            gLibMessages.showVersion('T3 VM (' + t3GetVMID() + ')',
                                     '' + (vsn >> 16) + '.'
                                     + ((vsn >> 8) & 0xFF) + '.'
                                     + (vsn & 0xFF));
            "\n";
        }

        /*
         *   Use a very high listing order so that we're the last thing shown.
         */
        listingOrder = 10000
    ;

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