Summary of Platypus release 4+ ============================== Send questions, comments, and bug reports to platypushome@yahoo.com. This documentation assumes at least a basic familiarity with Inform and its standard library. It primarily covers differences between the standard library and Platypus. The Reference provides a somewhat more thorough listing of attributes, properties, and so forth. Conventions followed in this document: Attribute: -light- Class, or member: [Actors] Global variables: =actor= Obsolete: lockable* Property: +description+ Property routine: +parse_name()+ Routine: InDark() Rewind: [<<] Of course, you should not type any of these symbols when actually using these identifiers, except for the underscores and the parentheses after a routine call. (And you can't use obsolete things at all.) [Square brackets] also indicate optional parameters in routine calls. I apologize in advance for any omissions or inaccuracies, however, for legal reasons I disavow any and all responsibility for any resulting apocalyptic mayhem. -1. Acknowledgements My gratitude to everyone who has given me feedback on Platypus, and specifically John Bytheway, Nate Cull, Dmitry Ferentsev, Aponar Kestrel, Michael A. Krehan, Andrew MacKinnon, Iain Merrick, A. O. Mu–iz, Taro Ogawa, Dan Schmidt, and especially Daniel Barkalow and Gary Poster. Andrew Plotkin performed the dark, ancient rituals which summoned forth the mighty Glulx and created the biplatform library. He also created Glk, but I'll try to forgive him. And to Graham Nelson, for Inform and its standard library, without which we'd all be using TADS. Ewwwww. We now return you to your regularly scheduled documentation. 0. Contents 1. Attributes 2. Properties 3. Rooms 4. Actors and Actions 5. Containers 6. Finding Paths 7. Tasks and Footnotes 8. Scoring 9. Gizmos and Cogs 10. The Runtime Dictionary 11. Useful Routines 12. Parsing 13. List-writing 14. New and Modified Commands 15. Things Not Yet Covered 1. Attributes a) Some attributes have been eliminated: Attribute Instead, --------- -------- absent* move the object out of FloatingHome, or create it in Storage door* just give it a +door_to+ property lockable* just give it a +with_key+ property scenery* use -concealed- and -static- scored* give it a +points+ property b) Some have been added: Attribute This object... --------- -------------- -activedaemon- has a running daemon. -activetimer- has a running timer. -hider- can hold things -under- it. -inside- is inside its parent. -known- is known to the player. -quotedmode- has first-person action output. -secret- is permanently unknown to the player. -upon- is on top of its parent. -under- is underneath its parent. c) The enterable* attribute has become a property routine, +allow_entry()+. See below. d) A complete list of attributes can be found in the Reference. 2. Properties a) All of the direction properties (n_to*, w_to*, etc.) have been replaced by +dirs+ (see below.) Three other properties have also been removed: life* (use +respond()+ or +respond_indirect()+ instead), capacity* (see +carrying_capacity+, +inside_capacity+, +upon_capacity+, and +under_capacity+ below), and door_dir*, which is not needed. b) Many have been added: Property Use -------- --- +adjective+ Holds adjectives (dictionary words) describing object. (See also section 12g below.) +allow_entry()+ Called with -upon-, -inside-, or -under- as a parameter. Should return true if the object can be entered in the specified way. +allow_push()+ Called with a direction object parameter, should return true if object can be pushed out that exit. +allow_take()+ (For animates) Can return true to allow this creature to be held. +carrying_capacity+ (For [Actors]) how much can be carried. +disambiguate()+ Called when parser is trying to choose an object based on player input. Takes no parameters, but can look at global =action_to_be=. +dirs+ / +dirs()+ (For [Rooms]) This property, which can be an array or a routine, replaces the direction properties (see section 3c below). +perform()+ (For [Actors]) Called to cause the actor to perform the specified action. +fpsa+ (For [Rooms]) Used by FindPath() (see section 6 below). +guide_path()+ (Primarily for [Actors]) called when FindPath() is about to look for a path for this object. By setting +fpsa+ property of [Rooms] to 0, can exclude them from consideration. +inside_capacity+ (For containers) How much this object can hold -inside-. +join_scope()+ Used by [ScopeCogs] (see below). +location+ (For [Actors]) current location (see below). +messages()+ (For [Actors] or [MessageCogs]) Provides action messages. (see section 4f below). +moveYN()+ (For floating objects) Takes a room as a parameter. If provided and returns false, is not present in the given room (in spite of that room's +shared+). Note that as of release 4, floating objects are no longer moved around, making the name of this property archaic. +points+ Number of points awarded for entering room, holding object, or accomplishing task. +possessive+ Holds possessives (dictionary words) describing possessed (held) objects (e.g. 'fred^s'). +shared+ (For [Rooms]) list of floating objects present here. +startup()+ Called when the game first starts. Used to initialize this object (or anything else). +upon_capacity+ (For supporters) How much this object can hold -upon- it. +under_capacity+ (For hiders) How much this object can hold -under- it. +words()+ Can be used instead of the +name+ and +adjective+ properties. Called with a word and should return 0 if it does not match the object, +name+ if it is a name of the object, and +adjective+ if it is an adjective. c) There are ten reaction properties, organized into groupings based on when they are called, and for what objects: Stage 1 Stage 2 Stage 3 ------- ------- ------- meddle_early (Action is meddle respond_early determined to be respond respond_early_indirect possible) respond_indirect Stage 4 ------- (Action occurs) Stage 5 Stage 6 Stage 7 ------- ------- ------- meddle_late (Action result meddle_late_late respond_late is printed) (always called) respond_late_indirect The four meddle properties are called for all objects in scope. The respond properties are called for the direct object, and the respond...indirect properties are called for the indirect object. When an action is invoked, the following sequence is observed. The library stops executing the action immediately when any of the routines returns true, jumping to step 17: 1. GamePreRoutine() is called (if provided). 2. If the =actor= is the =player=, the player's +orders()+ routine is called (if provided). 3. +meddle_early()+ routines are called for all objects in scope. (This is equivalent to react_before*.) 4. +respond_early()+ is called for the direct object (if any). (This is equivalent to before*). 5. +respond_early_indirect()+ is called for the indirect object (if any). 6. The library determines whether the action is possible (for example, testing whether ##Take is being called for a -static- object). Implicit actions may be invoked at this point. If the action is not possible, a suitable message is printed and the library stops here. 7. GameOnRoutine() is called (if provided). 8. +meddle()+ routines are called for all objects in scope. 9. +respond()+ is called for the direct object (if any). 10. +respond_indirect()+ is called for the indirect object (if any). 11. The library performs the default behavior for the action. For example, moving an object to the =actor= for a ##Take action. 12. +meddle_late()+ routines are called for all objects in scope. (This is equivalent to react_after*.) 13. +respond_late()+ is called for the direct object (if any). (This is equivalent to after*.) 14. +respond_late_indirect()+ is called for the indirect object (if any). 15. GamePostRoutine() is called. If =deadflag= is set by this point, the library stops here. 16. The library prints the appropriate default (or +messages()+ provided) message for the completed action. 17. +meddle_late_late()+ routines are called for all objects in scope. d) Some less-used non-additive properties have been made individual rather than common. e) You may have any number of active timers and daemons. The -activetimer- and -activedaemon- attributes indicate that an object's timer and/or daemon are running. (Thus, "give thing activedaemon" is the same as calling StartDaemon(thing).) f) add_to_scope has been made additive. g) A more complete list of properties can be found in the Reference. 3. Rooms a) Rooms should be ofclass [Rooms]. b) [Rooms] can now be in scope. +name+ and +adjective+ properties should contain the actual names and adjectives of the room. Note that +respond_early()+ and +respond_late()+ affect only actions performed on the room itself (unlike the before* and after* properties of the standard library). However, you can use +meddle_early()+, +meddle()+, and +meddle_late()+ to trap any action in the room, even if the room is not in scope (e.g. in the dark, or when the character is inside an opaque container). Declaring constant DONT_SCOPE_ROOMS prevents rooms from being placed in scope except for GO TO and meddle-property reasons. c) Direction properties such as n_to* and sw_to* are gone. Use the +dirs+ property instead, which takes two forms: As an array, +dirs+ contains a list of one or more direction objects (ndir, sedir, outdir, etc.), each of which is followed by the room object in that direction (or a door object, or a string). For example: dirs udir At_Complex_Junction wdir In_Bedquilt edir At_Witts_End; You can also list more than one direction object before a particular location: dirs ndir udir Foyer edir Narrow_Space; In the above example, both ndir and udir lead to Foyer. As a routine, +dirs()+ accepts one parameter, a direction object, and returns a room, 0, 1, or a string. If 1 is returned, it is assumed that the situation has been handled and the library silently cancels the action. 0 results in a "can't go that way" message (either generic or provided by +cant_go+). If a string is returned, it is printed if and only if the =player= is actually trying to go that way (and not some other actor). Make sure to check =finding_path= and =actor= when appropriate (see sections 4c and 6a). Example: dirs [ d; if (d == udir) return At_Complex_Junction; if (d == wdir) return In_Bedquilt; if (d == edir) return At_Witts_end; ]; d) Darkness is no longer handled by moving the =player= object into the "thedark" object. If the player is sitting in a teacup on the mantel in the library when the lights go out, the player is still sitting in a teacup on the mantel in the now-darkened library rather than hovering in an inky void nestled somewhere amidst the roots of Yggdrasil. The usual constraints of darkness still apply. InDark(player) will return true if the player is in darkness. 4. Actors and Actions The ghost who returns to haunt his murderer need not be surprising; most of us play interactive FICTION game as a representation of reality. - Markovian insight a) If you do not want to use the standard player object, set the constant PLAYER_OBJECT to your new player object prior to #including "Middle.h". This is not absolutely necessary; you can change the =player= at any time via ChangePlayer(). Setting the PLAYER_OBJECT constant to some other object has the effect of preventing the default player object from being compiled at all. b) Actors should be ofclass [Actors]. An "actor" is any character (that is, object) who will perform actions via +perform()+ (see following). The [Actors] class also provides the properties an object must have if you want to call FindPath() for it (see section 6). Otherwise, it is more efficient to simply use -animate-. c) Non-meta verbs can be used by any actor. To have an actor perform an action call: xxxx.perform(##Action, noun, second), where xxxx is, of course, the actor. If you will be making action calls for NPCs, be sure to keep that in mind when programming +respond()+ routines and the like, since these are triggered no matter who the current actor is. Check the global =actor= to see who is performing the action. d) Take player proximity into consideration when an NPC is performing an action. That is, you will not want to print "Fred opens the can of peanut brittle, and a snake leaps out!" in a +respond()+ routine if Fred is in the attic and the player is in the basement. Use TestScope(Fred) to see if the player can see Fred. Standard action messages ("Fred takes the widget.") are automatically muted in such circumstances. e) +location+ is now a property of [Actors], NOT a global variable. Look, but don't touch. Use MoveTo(actor, new location[, position]) to relocate [Actors] (or anything else, for that matter). Exception: you can define an actor with +location+ already set to the initial position of an actor, but remember to give the actor the -upon-, -inside-, or -under- attribute if appropriate. You can presupply any object as an initial +location+, but once the game is underway, +location+ will always hold the room the actor is in (and not, for instance, the chair she is sitting in). This is the same way that the location* global variable works in the standard library. f) [Actors] have a +messages()+ property which is responsible for the text displayed when an actor performs an action. This works just like the +messages()+ property of a [MessageCogs] object (see section 9d) except that it only applies to that actor. Generic, default messages are provided by the [Actors] class for standard actions. However, the *player's* default messages are currently still stored in the LanguageLM routine in English.h. (Actors::messages() returns false if the actor is the player, which causes the library to "fall down" to the LanguageLM() routine.) g) When creating your own actions, take advantage of the +messages()+ property and call ActionMessages(action[, number[, object]]) in order to print the correct message based on the current actor. Example: [ WhistleSub; if (OnRoutines()) rtrue; ActionMessage(##Whistle, 1); ]; Actors Fred "Fred" with name 'fred', messages [; Whistle: "Fred whistles that song you can't stand."; ]; This system ensures that no message will be printed if the action takes place where the player cannot perceive it. To provide a default message, either create your own subclass of [Actors], or a [MessageCogs] object (see section 9d below). h) Actions such as ##Scream, which are not dependant on sight, can be perceived (that is, will have their messages printed) and reacted to (via +meddle()+, etc.) even in darkness. i) Strings printed by +messages()+ properties can make use of certain codes by enclosing them between #s. For example: with messages [; Glorp: switch(lm_n) { 1: "#Actor# glorp#s# #obj# with #second#."; 2: "#Obj# #is# not glorpable."; ]; The codes are as follows: #actor# : If the =actor= is not the =player=, and the =actor= or #a# has not been named yet this turn, prints the name of the =actor=. Otherwise, prints an appropriate pronoun. #his# or #its# : Prints possessive pronoun fitting named object. (The "named object" is the last object specified by an "#actor#", "#obj#", "#noun#", or "#second#" print code. If no such code has been used, the named object defaults to the =actor=.) #him# or #ito# : Prints objective pronoun fitting... #he's# or #it's# : Print contraction... #himself# or : Prints reflexive pronoun... #itself# #is# or #are#, : Print verb form... #has# or #have# #s#, #es# : Prints verb ending if appropriate, as in "take#s#" or "toss#es#". #obj# or #o# : Prints (the) lm_o, that is, the object that was passed to ActionMessage(). #noun# or #n#, : Print (the) noun or (the) second. #second# or #d# #b#, #r#, : Activate bold, roman, underline, and fixed-width #u#, #f# font modes. Note that if a code begins with an uppercase letter, the resulting output will also be capitalized. N.B.: In some cases, the last named object may not be the one you want. For example, the message: "#Actor# #has# to put #second# down before #actor# can put things on top of #ito#." might result in: You have to put the tray down before you can put things on top of yourself. the last named object before #ito# was the actor, resulting in the above glitch. To get around this, you can change the "named object" without actually printing its name by extending the print code with "-a" for actor, "-o" for object (lm_o), "-n" for noun, or "-s" (or "-d") for second. So, the above message could be changed to: "#Actor# #has# to put #second# down before #actor# can put things on top of #ito-d#." resulting in: You have to put the tray down before you can put things on top of it. because the named object is changed to =second= before the code is translated. These extensions cannot be used with the "naming" codes: #actor#, #obj#, #noun#, and #second#. Or rather, -o and -s can be used, but they do something different. The library performs pronoun substitution on naming codes, for example, substituting "He" or "She" for #Actor# when the actor has just been named. In order to use the correct pronoun, it is necessary to know whether the thing named is subject or object. That is, whether to use a nominative pronoun such as "he" or an accusative one, such as "him". By default, a nominative pronoun is used if the print code begins with an uppercase letter (and is therefore presumably at the beginning of a sentence), and an accusative pronoun otherwise. This will not always work: But #actor# can't do that. might yield But him can't do that. To fix this, there are three code extensions that can be used with the naming codes: -s for subject (forcing nominative pronouns), -o for object (forcing accusative or reflexive pronouns), and -x to prevent pronoun substitution altogether, always printing the name of the object. (Exceptions: if the object is the player and player_perspective is not THIRD_PERSON, or the object is the =actor= and has -quotedmode-, pronoun substitution will still take place.) So: But #actor-s# can't do that. If you wish to use print codes in text other than that printed by messages, you can call HoldX(), print the text, and then call PrintX(). Alternately, you can call PrintX([string]). Calls to HoldX() are not nested; once PrintX() is called, the text is no longer being buffered. Alternately, you can call PrintX(string[, object]) to print the given string using print codes. If object is supplied, it signifies the object to use for #object# and related codes. If you actually need to print an # in PrintX()-filtered text, you can do so by doubling it: ##. If you need to use accented characters in PrintX()-filtered text, you must use a slightly different syntax, always beginning with &: &< = put a circumflex on the next letter: a,e,i,o,u,A,E,I,O,U &' = put an acute accent on the next letter: a,e,i,o,u,y,A,E,I,O,U,Y &` = put a grave accent on the next letter: a,e,i,o,u,A,E,I,O,U &: = put a dieresis on the next letter: a,e,i,o,u,A,E,I,O,U &c = put a cedilla on the next letter: c,C &- = put a tilde on the next letter: a,n,o,A,N,O &/ = put a slash on the next letter: o,O &o = put a ring on the next letter: a,A &ss = German sz &<< &>> = continental European quotation marks &ae &AE &oe &OE = ligatures &th &Th &et &Et = Icelandic accents &LL = pound sign &!! = inverted (Spanish) exclamation point &?? = inverted (Spanish) question mark &ct = ^ ct is for "caret" &bs = \ bs is for "backslash" &at = @ at is for "a (squiggly) thing" &td = ~ td is for "tilde" && = & Note that the accented character codes should *not* be placed in #...#. If a code after & is not recognized, an asterisk is printed instead. If the code is recognized, but can't be printed, a question mark is printed. j) The +allow_take()+ property, if provided by an [Animates] object, will allow the current =actor= to pick the animate up if it returns true. 5. Containers In any coherent world, things are generally where they are not, there is a sort of theme park maintained by Witt & Co. - Markovian insight a) Objects can now have things inside, on top of, and underneath them, possibly all three at the same time. The -upon-, -under-, and -inside- attributes indicate which position an object is in. However, objects which are merely in a room (i.e., on the ground) have none of these, nor do objects which are carried by an actor. Remember that -upon- and -on- are two completely different attributes. b) If an object is a child of another object (container) which is neither a room nor ofclass [Actors], and it does not have an appropriate attribute (-upon-, -inside-, or -under-), it is not in scope, and will be completely inaccessible. When creating objects inside (or upon or under) other objects remember to give them the appropriate attribute. (However, see 11a). c) The enterable* attribute has been replaced by the +allow_entry()+ property. If provided, it will be called with the -upon-, -inside-, or -under- attribute. It should return true to indicate that the object can be entered in the specified fashion. It will only be called with an attribute appropriate to any containment class(es) it belongs to, so the parameter can be ignored if, for example, the object is a -supporter- but not a -container- or -hider-. If only certain [Actors] can enter the object, it is best to return true here and handle individual exclusions via +respond()+. d) This space intentionally left blank. e) Items which are -under- hiders do not show up in room descriptions unless the -hider- is -transparent-. f) If a -hider- is picked up, anything -under- it is left behind. 6. Finding Paths a) The FindPath(starting room, destination, actor[, maximum moves]) routine will find the shortest route between two [Rooms] for the given actor. The actor's +guide_path()+ property will be called first. By setting the +fpsa+ property of [Rooms] to 0, it can exclude them from consideration (so they won't be allowed in the path). If the routine returns false, no path could be found. The =finding_path= global will be set to the actor for whom the path is being found. Since this routine "probes" exits, it is essential to check the =finding_path= global in your +dirs()+ methods to prevent spurious messages or worse from happening for seemingly no reason. For example, if trying to go through a particular exit causes a trap to go off, you must check the =finding_path= global to make sure the trap is not triggered by FindPath(). b) You can return strings from +dirs()+ methods rather than printing them directly. They'll only be printed if the =player= is actually trying to go through the exit, and will not be printed for FindPath() or NPCs. c) The path found, if any, will be stored in the actor's +path_moves+ (as direction objects) and +path_rooms+ property arrays. The number of steps in the path will be stored in the actor's +path_length+ property. +path_rooms+ contains the expected destinations that result from following the directions. Thus, if an NPC is following a path and a step does not result in the expected room, then something has gone wrong with the path (or the NPC has been blocked). 7. Tasks and Footnotes a) To create a task, just make an Object with a +points+ property and a name (not +name+, but a "short name"). Example: Object opened_gate "opening the mysterious gate" with points 5; Then call Achieved(opened_gate) at the appropriate time. b) By giving your task objects +number+ properties, you can control the order in which they appear in the full score. Tasks with lower numbers will appear before tasks with higher numbers. Tasks with the same number appear in the order in which they are Achieved(). Since +number+ defaults to 0, you can give a task a negative number to force it to appear at the top of the list, for example. c) Also see Scoring, below. d) If you wish to use footnotes, you must #Include "footnotes.h" and your footnote objects must be ofclass [Footnotes]: Footnotes example_footnote "Such as, ~If you shoplift something, they seldom want it back.~"; Then call Note(example_footnote) or use it as a print specifier: print "Peter prattles incessantly about the benefits of living in a nudist colony.",(note) example_footnote; e) [Footnotes] provides the +number+ property. If you preset this (to a positive value), you can "fix" the reference numbers for some or all of your footnotes. Otherwise, they are numbered in the order in which they are revealed to the player. (Obviously, you should not assign the same number to more than one footnote. In fact, to do so is to precipitate the age of Ragnarok. Or possibly the age of Aquarius. I forget which. Better to play it safe.) f) If you give FootnoteGizmo -general-, note references will no longer appear once their associated notes have been read. g) NOTE (number) or FOOTNOTE (number) are used by the player to view notes. h) NOTES will list all of the notes the player has previously read. i) The library sets the -general- attribute of [Footnotes] whose references have been displayed, and the -light- attribute of notes that have been read. 8. Scoring a) The MAX_SCORE constant has been replaced by the =maximum_score= global. This is automatically calculated at startup based on the sum of all positive +points+ properties in the game. You can easily override this by setting maximum_score = 100 (or whatever) in your Initialise routine or a +startup()+ method. b) If you give a takeable object a +points+ property (with a number), the player will receive the points upon first holding the item. If you give a room a +points+ property, the player will receive the points upon first entering the room. c) The "finding sundry items" and "visiting various places" lines (which appear if the player receives +points+ from a taking an item or entering a room with +points+) are treated as tasks numbered 30000 and 30001, respectively. The +number+ properties of the finding_items and visiting_places objects can be changed (e.g. during startup) to relocate them in the list of achievements. 9. Gizmos and Cogs a) [Gizmos] and cogs are used to extend certain library functions. For instance, the SHOWOBJ command can be extended with a [ShowObjCogs] object, or the definition of scope can be extended with a [ScopeCogs] object. b) A [ShowObjCogs] object is used to tell ##ShowObj how to print the values contained in a new property. For example: ShowObjCogs with knows_property [ prop; if (prop == bibble) rtrue; ], print_property [ prop val; print (name) val; ]; This cog "teaches" ShowObj that the contents of the (fictional) "bibble" property should be printed as an object. +knows_property()+ is passed one parameter, a property, and should return true if this cog knows how to display it. +print_property()+ is passed two parameters, a property and a value, and should print the value in the format appropriate to the property. c) A [ScopeCogs] object is used to bring into scope objects which would normally be out of scope. For example: ScopeCogs with join_scope [ act; if (act == Wibble) PlaceInScope(Wobble); ]; This cog ensures that Wobble is always in scope to Wibble. +join_scope()+ is called with one parameter, an object (usually an actor) for which scope is being determined. It can use PlaceInScope(object) to add things to scope, and if it returns true, the cog itself is also put into scope. d) A [MessageCogs] object is used to override library messages. This is only useful for the player's messages, or for new actions, because the +messages()+ property of an actor will supersede it. It works like the standard library's LibraryMessages object, except that you must use the +messages()+ property instead of before*. (See section 4f). e) Cogs only function when they are in the associated [Gizmos] object. The classes listed above are automatically moved into their associated [Gizmos] at startup, except those created as children of Storage. You can move cogs into and out of their [Gizmos] at any time to enable or disable them. For instance, if you need to change many of the default messages at a certain point in your IF, you could swap [MessageCogs] in and out. The [Gizmos] have matching names ending in Gizmo, e.g. MessageGizmo, ShowobjGizmo, etc. f) [Gizmos] is a class, however there is no "Cogs" class to which all cogs belong. g) Not all "cog" classes have names ending in "Cogs". For example, the [Footnotes] class provided by the "footnotes.h" add-on is a cog class. (That is, all [Footnotes] objects are moved into FootnoteGizmo at startup, where they must be in order to be used.) 10. The Runtime Dictionary a) If you need to add words to the program's vocabulary while it is running, you can use the Runtime Dictionary for this. Set the constant RUNTIME_DICTIONARY_MAX_WORDS at the start of your program to the maximum number of words you need to add to the dictionary. Then, call AddWord(address, length) where address is the place in memory the word is located (in characters) and length is the number of characters in the word. For example, if you had: Array newWord -> 'c' 'a' 'r' 'g' 'o'; And called: AddWord(newWord, 5); Then 'cargo' would become a dictionary word. If AddWord returns false, there is no room left in the Runtime Dictionary. Otherwise, it returns the address of the new word. Note that if you try to add a word already in the dictionary (initial or Runtime), AddWord returns the address of the existing word. b) "nameable.h" makes use of the Runtime Dictionary to allow the player to give objects names during play. 11. Useful Routines a) SetDefaultObjectPositions() will go over all of the objects in the game and automatically set the -upon-, -inside-, or -under- attribute for any object which is in a -supporter-, -container-, or -hider- and does not have any of the three attributes set. This is primarily intended to be called at startup, and is simply to save the effort of manually creating every object with an appropriate attribute. If the parent object has more than one containment attribute (e.g. -container- and -supporter-), -upon- takes precedence over -inside-, which takes precedence over -under-. If the parent object is ofclass [Gizmos], [Class], or [Rooms], then no attribute will be set for its children even if it also belongs to a containment class. SetDefaultPosition(object) works the same way, for the specified object only. b) MoveTo(object, destination[, position[, look flag]]) is the easiest way to move objects around during play. MoveTo(object, destination) will move the given object to the destination object, clear the -upon-, -inside-, and -under- attributes, and call SetDefaultPosition(object). If the object is ofclass [Actors], its +location+ will be set. The position attribute can also be specified as a third parameter, if the destination has (or may have) more than one containment attribute. Example: MoveTo(Fred, Freds_bed, upon). The "look flag" is only useful if moving the =player=. If 1, no location description is printed at all after moving the =player=. If 2, the usual description the player would get upon walking into a room (which may be shortened if moving to a room that has -visited-) is printed. Note that this is a fourth parameter; if you wish to set the look flag and do not want to specify a position attribute, use 0 as the third parameter, e.g. MoveTo(player, bedroom, 0, 2). c) InDark(object) returns true if the given object is in the dark. d) OffersLight() is no longer available. It is no longer possible to answer the question of whether an object "offers light" in general or not, since it might offer light to something -upon- it but not to things -inside- it. You can instead use InDark() (see above), or HasLightSource() in conjunction with TestScope(), if necessary. e) DrawCompass(x-position) will draw a compass in the status line, which must be at least 3 lines tall. =finding_path= is set to ExitsSub when DrawCompass() is checking exits. f) IndirectlyContains(object1, object2) returns 0 if object2 is not a descendant of object1 (unless they are the same; see below). Otherwise, it returns the position of the child of object1 from which object2 is descended. For example: table !-----------------------! book (upon) box (under) ! kitten (inside) Then IndirectlyContains(table, kitten) == under. If the child of object1 has no position attribute, or object1 and object2 are the same, then the return value is -1. g) Also see the list of routines in the Reference. 12. Parsing But it's a lot easier for YOU to parse English than it is for a machine, because you've had a lot more practice at it, and you started life with a LALR grammar and one-token look-ahead. - Markovian insight a) Descriptors have been reworked. There is now an entry point routine ParseDescriptors() which can be used to add to them. For example: [ ParseDescriptor obj wd fl; switch(wd) { 'sparkling', 'enchanted': if (obj has magic) rtrue; default: return -1; } rfalse; ]; This creates a new descriptor (which is essentially a universal adjective) which applies to any object with the (hypothetical) "magic" attribute. The ParseDescriptor() routine takes three parameters: an object, a word, and a flag. Allow for the flag, but ignore it. The routine should return 1 (or true) if the word is a descriptor which does apply to the object, 0 (false) if it's a descriptor that doesn't apply, and -1 if it doesn't know the word at all. (Study the example above.) b) Standard (already-defined) descriptors are 'open'/'opened', 'closed', 'worn', 'unworn', 'empty', 'my'/'mine'/'this'/'these', 'your', 'that'/'those', 'his', 'her', 'its', and 'their'. c) Early descriptors (which must come before the name of the object, not as part of it) are 'one'...'twenty', 'the', 'a'/'an', 'any', 'all'/'each'/'every'/'everything', 'both', 'another'/'other', and 'some'. d) 'himself', 'herself', 'itself', and 'themselves' are understood in certain contexts, e.g.: ASK SHEILA ABOUT HERSELF is the same as ASK SHEILA ABOUT SHEILA. e) +parse_name()+ and ParseNoun() have been changed a bit. They can now return a count of adjectives as well as names, by multiplying the adjectives by 100 before adding them to the count. Adjectives are treated as "weaker" than names when the parser is trying to determine which object is being referred to. Also, if you add 10000 to the return value, this result is final: the parser will not attempt to match any further descriptors to the object, nor allow positional description (as described in the next entry). For more information on +parse_name()+ or ParseNoun(), see their respective entries in the Reference. f) Objects can be identified by their location, e.g.: >EXAMINE THE BOX ON THE TABLE >EXAMINE THE BAG IN THE BOX ON THE TABLE >PUT THE MARBLE IN THE BAG IN THE BOX ON THE TABLE and so forth. g) By default, it is possible to refer to objects using only adjectives (although names will take priority over them, so that 'orange' alone might refer to a piece of fruit over an orange bowl, for instance). By setting the constant WEAK_ADJECTIVES, you can prevent this, thus requiring at least one +name+, TADS-fashion. 13. List-writing a) WriteListFrom() takes parameters in the form (object, style bitmap, depth, attribute) where attribute is an attribute that is required for an object to be listed. (This is used for -upon-, -under-, etc.) If the attribute is -workflag-, it will be checked only for depth 0. b) The WORKFLAG_BIT is no longer available. Use the attribute parameter instead (see preceding entry). c) The NEWSTYLE_BIT produces a "new-style" list, e.g.: a sack (which is empty) and a box. In the box is a telescope. where contents are listed in separate sentences. d) The SORT_BIT will cause the list to be printed in alphabetical order by the objects' printed names. 14. New and Modified Commands a) The GO TO command can be used by the player to be automatically walked back to a previously-visited room. Only rooms the player has -visited- are allowed on the path. The CONTINUE command will repeat the last GO TO command, and is useful if the player was interrupted while trying to get there. ##GoToRoom only prints a message if called for an NPC. Note that GoToRoomSub() makes use of FindPath() (see section 6). b) The EXITS command will give the player a list of exits. =finding_path= is set to ExitsSub when it is checking exits. The list will indicate where a given exit goes, if the destination has the -visited- attribute. c) Inventory and Places lists are now sorted into alphabetical order. This can be changed for inventories by altering =invtall_style= and =invwide_style=. d) (Debugging command) DBDISTANCE will indicate how many moves away a given room is, using the shortest path FindPath() can produce, e.g.: >DBDISTANCE LIBRARY 3 rooms e) See also section 7h, i above. 15. Things Not Yet Covered Chess is an example of a game that breaks the coherence of its fictional world as a work of IF. - Markovian insight a) The [Sacks] class takes the place of the old SACK_OBJECT. You can have as many as you like. (Note that these work for all [Actors] and not just the =player=.) Note that objects will only be put -inside- the sack, not -upon- it, even if it is also a supporter. Also, you can forgo the [Sacks] class altogether if you like and #Replace the IsASack() routine, which returns true for any object that... you know. Just be sure the object has -container-! b) In case you missed it, note that the direction objects now have names ending in "dir" like ndir, swdir, etc. c) =action= is set to ##WhichOne when the player is asked "Which do you mean..?" This might be useful if you want a +short_name()+ routine to print "elvish sword" instead of "sword", for example. d) Instead of "@output_stream 3 xxxx;" and "@output_stream -3", you should call OpenBuffer(xxxx) and CloseBuffer(). These calls can be nested to a maximum depth of MAXIMUM_OPEN_BUFFERS (defaults to 5). You should not, however, nest calls to the same buffer (i.e. the "xxxx" above). e) "Narrative" mode (for lack of a better name) alters the way the results of multiple-object commands are displayed. Instead of: >TAKE ALL table: The table is fixed in place. orange shirt: Taken. orange shirt: Taken. sack: Taken. red box: Taken. blue box: Taken. blue box: Taken. orange hat box: Taken. if the global =narrative_mode= is set to true, the output would be: You take four boxes (the orange hat box, the two blue boxes and the red box), the sack and the two orange shirts, but the table is fixed in place. This feature is a work in progress. First limitation: In order for it to work correctly, the meddle and respond property families (+meddle_early+, etc.) must return message codes instead of printing directly for any action that allows multiple objects. For example, do NOT code: respond [; Take: "You wouldn't touch that with a ten-foot pole!"; ]; But instead: respond [; Take: return ActionMessage(##Take, 9999); ]; MessageCogs with messages [; Take: if (lm_n == 9999) "You wouldn't touch #obj# with a ten-foot pole!"; ]; Note that the example code given above is not NPC-friendly, because it assumes the player ("You") will be the one trying to take the object. (You could replace this with "#Actor#".) If the code were attached to the red box, the previous example would become: You take three boxes (the orange hat box and the two blue boxes), the sack and the two orange shirts, but the table is fixed in place, and you wouldn't touch the red box with a ten-foot pole. The exclamation point is lost in this case, but would have been printed if the player had entered TAKE THE RED BOX, as narrative mode would not apply. Second limitation: If any implicit actions are generated by the command, the output will be, er, wrong. Thus, implicit actions for standard multiple-object verbs are disabled when narrative mode is on. Third limitation: If the player is moved in the middle of the command, there will be no output from the command at all. You can test "if (multiflag)" to determine when a multiple-object command is being processed, and thus when narrative mode is in effect (assuming narrative_mode == true). (Most likely, you would use this test in a reaction or +messages()+ property.) f) The global variable =player_perspective= can be set to FIRST_PERSON, SECOND_PERSON, or THIRD_PERSON to determine how the PC is referred to. 16. Things *Really* Not Yet Covered