Triform 1.2b New Features Over Standard Library ----------------------------------------------- (Please read convert.txt first.) 1 Containers 2 Clothing 3 Light and Darkness 4 Printing Text 5 Actions & NPCs 6 Locks 7 Rooms & Movement 8 Scoring 9 Everything Else == 1 Containers == For Containers, the (hidden_contents) array property controls whether the Things inside/beneath/behind the Container are visible: 0 invisible until the Container is moved somehow (not inside) 1 visible but not mentioned in room descriptions (the player must search the Container to see them) 2 visible and mentioned in room descriptions hidden_contents.&-->0 inside hidden_contents.&-->1 beneath hidden_contents.&-->2 behind (preferred_posture) is the default posture for the Container. If the player types STAND ON CHAIR, for example, he'll stand on the chair. But if ENTER CHAIR is typed, the player could wind up sitting on the chair. Every Person has a (posture) property, which can be: 1 standing (default) 2 sitting 3 lying down IndirectlyContains() will return the (most distant) way in which the object is indirectly contained. This means that if a passport is in a briefcase on top of some luggage under a bed, IndirectlyContains(bed, passport) will return 4. 0 = not indirectly contained 1 = inside 2 = on top 3 = behind 4 = beneath 5 = generically attached 6 = being held by an actor == 2 Clothing == If the constant COMPLEXCLOTHING is defined, a new class Wearable will be provided, replacing the -clothing- and -worn- attributes. Wearables have several new properties: layer min_layer max_layer area_covered clothing_type covered_by covering conceals gendered manipulable_when_locked same_layer skirtlike skirt_covering skirt_layer skirt_maxlayer worn_by == 3 Light and Darkness == If the constant NOFIRSTLOOK is provided, the initial action will not be generated. If the NODARKNESS constant is provided, there will never be darkness for any actor and miscellaneous light-related code and attributes will not be compiled, making for a somewhat smaller game file. It is preferable now to test if two objects are visible to each other rather than in scope to each other. Use the IsVisible() routine to accomplish this: if (IsVisible(batman, joker) == true) ... if (IsVisible(batmobile)) ... The order is not important, but if only one object is provided, as in the second example, the second object will default to the current actor. IsVisible() returns 0 if the two objects are invisible to each other, 1 if they are visible, and 2 if they are semivisible (see the following). An option to implement gradual light changes has been provided. If the constant GRADUAL_LIGHT is defined, the following will be compiled: -quasilight- -demilight- -semilight- -semitransparent- DescribeQuasilight() An object with -quasilight- can be seen in darkness, but does not otherwise affect anything. An object with -demilight- will provide very faint illumination. An object with -semilight- provides illumination somewhere in between. The library provides no rules for how this affects the game other than that it will alert the player when light levels have changed. Anything on the other side of an object with -semitransparent- will receive the next-lowest light level: -light- will drop to -semilight-, etc.; -quasilight- will drop to invisibility. When a Person carries out an action without the presence of full light, the entry point InDarkness() is given a chance to meddle. It may do whatever it likes and then return either true, to interrupt the action, or false to allow it to continue. By default all actions are allowed in darkness except for ##Examine, ##Search, ##LookInside, ##LookOnTop, ##LookBehind, ##LookUnder, ##LookAttached, and ##Consult. To recreate the standard library's treatment of darkness, use the following InDarkness() routine: [ InDarkness person x; if (person == player) { if (noun == 0 || action == ##Go) x++; if (IndirectlyContains(person, noun) || noun == person) { if (action ~= ##Examine or ##Search# or #LookInside or ##LookOnTop or ##LookBehind or ##LookUnder or ##LookAttached or ##Consult) x++; else return L__M(##Examine, 1); } if (~~x) return L__M(##Miscellany, 30); } rfalse; ]; == 4 Printing Text == -male- and -female- can be given even to items which don't have -animate-, and appropriate text will be generated. You may use the past tense by using the language file 3EnglishB.h. The global variable (tense) can be: 1 for present 2 for past You may of course switch back and forth at any time during the game. The property (entered_desc) has been added. If provided, the parser will print it instead of the generic "(in the shark tank)" message. For example: entered_desc " (trapped in Blofeld's shark tank)", (entered_desc) may not be a routine. Note the space before the parentheses begin. (light_desc) has been added to handle changing the "(providing light)" message. You might do this: light_desc "glowing faintly", The (when_dark) property of rooms is used to print the description when there is no light source. The default is "It is pitch dark in here!" (switched_desc) has been added for the "(which is switched on)" message, like so: switched_desc "powered on and humming madly", (worn_desc) has been added for the "(which is being worn)" message: worn_desc "hanging off the shoulder", Andrew Plotkin's implementation of serial commas is now supported; simply define the constant SERIAL_COMMAS. An Epilogue() entry point has been added, to take place between DeathMessage() and ScoreSub(). If provided, this can produce additional text to describe the outcome of the player's actions, rather than just printing the death message and the score. The optional file 3Mistype.h, which has been adapted from Cedric Knight's original mistype.h with only minor compatibility changes, allows the game to be forgiving of typos by the player. == 5 Actions & NPCs == There are four new action properties, (middle), (react_middle), (final), and (react_final). The two (middle) properties are called after the action has been determined to be possible but before it is carried out. The final properties take place after the action has been carried out and messages printed. The two-entry array cant_touch_reason stores the reason that an object could not be touched, heard, or smelled. cant_touch_reason-->0 stores the objec that is blocking the action. cant_touch_reason-->1 stores the reason: 1 = the object must be opened 2 = the actor must exit the object 3 = the actor must disrobe the object 4 = it is being held by another actor The optional file 3Reach.h provides a simplistic way to implement reachability rules. If you would like to have an action occur silently, without printing up any library messages regardless of success or failure, simply set the global variable keep_silent to 2. When taking INVENTORY, items inside a closed opaque container will be listed if they have -topic- (on the presumption that this means the player knows they are there). To implement the inventory change, two changes were made to the handling of WORKFLAG_BIT. First, it now applies at all depths and not only the top level. Second, WORKFLAG_BIT now takes precedence over CONCEAL_BIT. And on a minor note, the behavior of 'REMOVE XXX' has been slighty altered. If the noun is not a child of the player, it will generate a TAKE action rather than a DISROBE action. If an action is unsuccessful, the library will set the global variable failed_action to the library message number printed in response. For example, if the player tries to open something that is locked, then failed_action will be set to 2. A multiple-object action (e.g. TAKE ALL COINS) can be stopped cold by setting failed_action to -1. Every NPC has an npc_number property that the game will automatically set up at the start, unless set manually. Defining the constant TRACK_LOCATIONS before the inclusion of 3Parser will enable the use of a method for tracking the last time the player and each NPC (up to 32) saw each game object. This is accomplished using the properties last_seen and last_seen_npc. last_seen is a four-entry array: last_seen-->0 == the location the item was seen by the player last_seen-->1 == the parent of the item at that moment last_seen-->2 == the IndirectlyContains() result for the item at that moment last_seen-->3 == the time or turns value at that moment last_seen_npc is simpler. It is a 32-entry array, one entry for each of up to 32 NPCs. Each entry stores the location an item was last seen by the npc with the corresponding npc_number property. For example, if Paul's npc_number is 0 and he last saw the precious jewel in his office, then precious_jewel.&last_seen_npc-->0 == office. Finally, the optional library 3goals.h provides an engine for NPCs to set up plans and act independently to achieve defined goals. Its documentation is in its file. == 6 Locks == A more complicated system for keys and locks is available. If the player attempts to lock or unlock something, there are two possibilities: she has either not specified any key, or else she has specified something to use as a key. In the first case the Lockable item's with_key routine is consulted to aid in inferring a possible key. In the second case the with_key property is consulted to test the success or failure of using this particular key. with_key can return: -1: Never auto-try any key. 0: This item is obviously not a key and should not be tried. 1: This item is a key, but does not unlock this item. 2: This item is the correct key, but the actor does not know it is the correct key. 3: This item is not the correct key, but appears to the actor to be a better guess than one which returns 1 or 2. 4: This is the correct key, and is a better guess than one which returns 1 or 2. 5: This is the correct key, but does not appear to be a key at all. 6: This is the correct key, and the actor knows it to be the correct key. For example, imagine a treasure chest and three keys: gold, silver, and bronze. The player does not know which key unlocks the chest. The correct key is the gold key, but inspection of the chest shows it to have a silver lock. Thus until the player discovers that the gold key is the correct one, the silver key will be tried first: Container treasure_chest "treasure chest" class Lockable, with name "chest", adjective 'treasure', with_key [ obj; switch (obj) { gold_key: if (self.gold_key_tried) return 6; else return 1; silver_key: if (self.gold_key_tried) rfalse; else return 2; bronze_key: if (self.gold_key_tried) rfalse; else return 1; default: rfalse; } ], inside_capacity 5, locked true, gold_key_tried 0, silver_key_tried 0, bronze_key_tried 0, description "It has a silver lock."; after [; Lock, Unlock: if (second == gold_key && ~~self.gold_key_tried) self.gold_key_tried = 1; ], has openable; with_key can still be a single object or 0. If 0, no key at all is necessary to lock or unlock the object. It may also be set to -1 if you do not want the game to try keys at random: Container cupboard "cupboard" class Lockable, with name "cupboard", with_key -1, before [; Lock, Unlock: if (self.with_key == -1 && second == cupboard_key) self.with_key = cupboard_key; ], has openable; == 7 Rooms & Movement == Rooms have a number of new features. An (after) routine can now react to an actor leaving. The ##GoneFrom fake action is provided for this: after [; GoneFrom: if (actor == player) print "You walk out of the kitchen.^"; ]; Checking that actor == player is necessary because otherwise NPCs moving about could trigger the message. When the player witnesses NPCs moving about via ##Go actions (probably through NPCAction() calls), the library will print up suitable messages. The content of these messages depend on whether the Room which the NPC enters/leaves has been visited by the player already or not. If it has not, the message may be "The mysterious man walks in from the north." If it has, the message may be "The mysterious man walks in from the garden." Rooms also have (travel_name) properties. This allows you to alter those messages. Thus, the garden's (travel_name) could change from "garden" to "trampled garden". By default the (travel_name) is blank, in which case the message might be "The mysterious man walks in from the Garden." (cant_go) can now be a routine, allowing for more complex behavior. So you could do something like this: cant_go [; if (noun ~= u_obj) "The ladder is the only safe way out."; ], The optional file 3Pathmaker.h, adapted from Jim Fisher's code, provides a way to calculate paths between rooms, or between objects. == 8 Scoring == Roger Firth's suggestion for negatively-scored achievable tasks has been implemented. This means that task_scores must be created as a word array, e.g.: Array task_scores --> 2 2 (-1) 8; It's also easier to set up partially achievable scored tasks. To do so, set up something like this: Array task_scores --> 0 0 0 0; [ PrintTaskName task_number; switch (task_number) { 0: switch (task_scores-->0) { 10: "this task is complete"; default: "this task has been partly completed"; } 1: switch (task_scores-->1) { 7: "this task is complete"; default: "this task has been partly completed"; } 2: "this task is complete"; 3: "this task is complete"; } ]; Then, in your Initialise() routine, put the following lines: notify_mode = 2; for (i=0 : ii = 1; } The first line tells the library that you want to allow partially achievable tasks. The second line is necessary if you want such tasks to be listed in the full score even if they have not yet been begun. Obviously you may need to adapt it to your game. == 9 Everything Else == Adjectives are available. Just give your Things (adjective) properties, like so: Thing denim_jacket with name "jacket", adjective 'blue' 'denim' 'jean', You must use single quotes for adjectives. Note also that adjectives are weak--one can't, in the above example, simply type LOOK AT DENIM and get a description of the jacket. Also, in order to be recognized by the parser they must come before the entries in the (name) property, so that LOOK AT DENIM JACKET will work but not LOOK AT JACKET DENIM. If the constant CHARACTER = XXX is provided, the selfobj will not be compiled. Additionally, you will not have to provide the line "ChangePlayer(XXX)" in your Initialise() routine. The -anatomy- attribute, intended for body parts, has been added. An object with -anatomy- will not be included in inventory descriptions nor count against an actor's (capacity) property (as long as its (attachedtoparent) property is true, because, after all, grisly things may occur). In debugging mode, the TREE command produces slightly more complete text when listing the states of objects -- for example, "(which is closed and locked)". The library keeps track of the actions most recently performed by all actors. Every Person has a (last_action) property, which is an array with three entries. The first entry contains the action; the second the noun (if any); and the third contains the second (if any). You can define a constant NO_MENUS (before including 3Parser) to prevent the compilation of the default method for creating menus. If you don't use menus this will save some memory. ObjectIsUntouchable() may be applied to anyone, not just the current actor. It must be called as ObjectIsUntouchable(item, person, flag1, flag2, flag3). Setting (failed_action) to -1 will interrupt a multiple action. This is currently used to interrupt a PUT ALL action that has no further chance of succeeding because the object to be filled is already full. last_seen->0 = the Room last_seen->1 = the parent last_seen->2 = the parent relationship last_seen->3 = the time By default, the LibraryExtensions object will not be compiled. If your game uses it, define the constant USE_LIBRARY_EXTENSIONS before 3Parser.h. Generally, LibraryExtensions is updated to include the additional functionality in standard Library 6/12. Finally, the optional library 3Who.h provides a way for the player to ask questions about the game world, whether to NPCs or to the narrator.