+-------------------------------+ A TADS Tip Sheet, version 0.3b +-------------------------------+ TADS, Michael J. Roberts' Text Adventure Development System, is a very powerful and flexible programming system for developing text adventures. (interactive fiction) However with that power comes a good deal of complexity. Here are some tips that I've come up with over the past four years of TADS authoring. And I thought that there may well be beginning TADS authors out there who could use some of these tips. Feel free to mail me with any suggestions for new tips or, indeed, corrections. I can be reached at tela@tela.bc.ca. Note that this tip sheet was originally written prior to the publication of the TADS 2.2 Release Notes. Some of the material in this tipsheet is actually explained, in far more detail, in those notes, especially the rewritten Book of the Parser. - N. K. Guy, tela design. (http://www.tela.bc.ca/tela/) RESTRICTIONS: You may distribute, modify and copy this file as much as you like, but it cannot be bought or sold. Please include this notice and a list of any changes you made if you choose to redistribute it. STANDARD PARANOID ANTI-LITIGATION STUFF: I believe that the information contained in this document is reasonably accurate. However, I make *no* guarantees of *any kind* that the document is error free or will meet your needs or anything like that. You assume full risk for using or not using any information in this file. If you find something wrong, please mail me a correction and I'll fix it. I hope that's all that needs to be said on this point. Also, any mention made within this document of commercial or shareware products is purely for informational purposes, and does not imply an endorsement of any kind by anybody. VERSION HISTORY: Version 0.1. Written 4/26/93 N. K. Guy (tela@tela.bc.ca) updated to 0.1a. (banana tree example code fixed) 3/18/94 updated to 0.2 to reflect TADS 2.2.1.0 Freeware 10/19/96 updated to 0.3. Added the #pragma C+ and setit() issues, TADS Page URL. 10/28/96 updated to 0.3a. Added the numberedObject stuff. 1/15/97 updated to 0.3b. Added Ditch Day, hiddenItem/obstacle and moveInto bits. 12/23/97 WHAT IS TADS? TADS, the Text Adventure Development System, is a particularly groovy system for programming text adventure games (sometimes called "interactive fiction") on a variety of computer platforms. TADS and Graham Nelson's Inform are the two most powerful and most popular development systems for writing text adventures currently available at time of writing. TADS was originally a shareware package written and published by High Energy Software. However, in mid 1996 TADS author Michael J. Roberts publicly announced that TADS was to become a freeware product and that High Energy Software would be shutting down the bulletin board system that it used to support the program. He has made good on his promise, and has even freely released the source code that makes up the program. You no longer have to pay anyone to use TADS - it's totally free! However, Mike Roberts retains copyright to the TADS system, so you can't go and change it or try (operative word here is try) and make lots of money off it without his permission. For more information on TADS have a look at the unofficial TADS page: http://www.tela.bc.ca/tela/tads/ WHERE CAN I GET IT? You can find the latest and greatest version of TADS in a variety of places, including, if you have access to Internet FTP: ftp://ftp.gmd.de/if-archive/programming/tads/ This site, the Interactive Fiction Archive (if-archive) is at GMD, the German National Research Centre for Information Technology. It's run by volunteer maintainers Volker Blasius and David Kinder, to whom we all owe a debt of gratitude. Note that if you don't live in Germany and find the connection to be rather slow, or if you want to be a good net.citizen and minimize your use of overtaxed international links, you can access the if-archive at a number of mirror sites. For example, US users may want to try Washington University's mirror at wu-archive: ftp://ftp.wustl.edu/doc/misc/if-archive/programming/tads/ European users outside Germany could try the Finnish University and Research Network's mirror: ftp://ftp.funet.fi/pub/misc/if-archive/programming/tads/ Additionally, if you have access to Usenet news and you're interested in discussions on the topic of writing interactive fiction, check out the newsgroup: news:rec.arts.int-fiction. For discussions on the topic of playing interactive fiction games, check out the newsgroup: news:rec.games.int-fiction. +---------+ Some Tips +---------+ READ THE MANUAL. TADS has a great manual. A wondrous manual. Michael J. Roberts clearly poured half his soul and an awful lot of time into writing it. Read it. Worship it. This may be something of an obvious tip, but spending time with it will minimize time wasted later on figuring out why something won't work. The manual is available in two forms at time of writing. The first is typeset using TeX, the powerful and deeply arcane typesetting language popular within the academic community. If you have a program that can display TeX files (or, more accurately, that can convert the TeX files into DVI files for display purposes) then you might want to get that. The TeX files can be found at: ftp://ftp.gmd.de/if-archive/programming/tads/manuals/ The second is an HTML (HyperText Markup Language) translation of the TeX files for Web browsers, converted by yours truly. This version is on the Web at: http://www.tela.bc.ca/tela/tads-manual/ I'll probably upload it to the GMD if-archive as well. And I'm sure the TeX files will be converted into all kinds of other formats over the next while. READ THE TADVER.* FILES. Your copy of TADS should have come with a file called TADSVER.xxx, where xxx is the operating system you use. This file is extremely handy - it lists all the new features and fixed problems with the latest versions of TADS. There are many useful new features that have been added to TADS since the version 2 manual was released, and this file documents them. No TADS user should be without it! Note that there are two versions of the TADSVER file - one that lists all of the new features up to version 2.1 or so, (TADSV200.xxx) and another file that lists the changes from 2.1 onwards. I've put the most recent version of the Macintosh TADSVER file online at: http://www.tela.bc.ca/tela/tads-manual/tadsver-mac.html READ SOME SOURCE CODE. One of the best ways to learn how to program is, in my opinion, to find some well-written source code and play with it for hours on end. You can learn a lot through doing this. Tearing someone else's source apart and modifying it for your own ends is a very educational activity. TADS used to ship with the full source for a small game, Ditch Day Drifter, which illustrates many important basic features of TADS. Unfortunately somewhere along the way the Ditch Day code got left out of the standard TADS distribution. If you never got a copy, there's one at this URL: ftp://ftp.gmd.de/if-archive/games/tads/ditchday.zip And if you are on the Internet and have access to the treasure trove of stuff in the if-archive at ftp.gmd.de, check out the sample source code available there. For instance, there is an Examples directory in the TADS programming area that contains a number of small source code examples written by many TADS authors. A lot of these examples are written specifically to teach people about various principles of game design. Others are written to implement commonly-used features. For example, I wrote a little set of modules that I find very useful for testing games in progress. That file, wizard.t, is available in the examples directory. Another useful file is bugs.t by Stephen Granade, which provides patches for some minor bugs in the adv.t TADS libraries. ftp://ftp.gmd.de/if-archive/programming/tads/examples/ In addition to the mini code examples there is original TADS source to a number of complete full-length games on ftp.gmd.de as well. For example, Dave Baggett, in a deeply admirable selfless gesture, has ported the classic Colossal Cave (ie: "Adventure") game to TADS and published the source code in order to help train future generations of TADS authors. He's also made the source to his epic game "Legend" available. 1995 Interactive Fiction Contest winner Magnus Olsson has put the source to his winning entry "Uncle Zebulon's Will" in the same directory, and TADS author Michael J. Roberts has put his games "Deep Space Drifter" and "Perdition's Flames" online as well. ftp://ftp.gmd.de/if-archive/games/source/tads/ There are also a lot of other files in the if-archive that, although not directly related to TADS, nevertheless contain extremely useful information. For example, Graham Nelson's Inform compiler, which generates games compatible with the classic Z-machine system designed by Infocom, has a well-written manual that contains a lot of useful tips that can be used by any text adventure author. Check out his fascinating "Craft of Adventure" document. ftp://ftp.wustl.edu/doc/misc/if-archive/info/ Now let's get into some of the technical tips. *NEVER* MODIFY GAME STATE IN VERIFY METHODS. This is a very very common problem. The way TADS uses the "verify" method in a verb can be quite confusing, and it can lead to a lot of problems. Verify methods are object methods that start with "ver". Basically, remember that TADS doesn't use the verify method solely to see if an item can be verbed appropriately. It also uses the *same method* as part of its disambiguation routine. When it does so it silently calls the method. That means it calls it, but suppresses any text that the message may try to produce. If any text is produced and hidden then the game knows that the item in question can't be verbed by the verb. The upshot of all this is that if you change the state of the game somehow and don't simply display text you can really get strange things happening when that verify method is triggered inadventently by a disambiguation routine. Here's a concrete example. This is bad: badMagicTurkey: item sdesc = "magic turkey" [ etc. etc. ] verDoTouch( actor ) = { "Good heavens! The magical turkey vanishes in a cloud of tangerine-coloured vapour! "; self.moveInto( nil ); } ; The code is bad because the disambiguation function might call that verify method sometime, and if it does so it'll suppress the text but modify the game state by moving the turkey into nil. Thus our turkey may suddenly vanish for no readily apparent reason, and the player won't be notified when it does. The fix? Simply do this: goodMagicTurkey: item [ etc. etc. ] verDoTouch( actor ) = {} doTouch( actor ) = { "Good heavens! The magical turkey vanishes in a cloud of tangerine-coloured vapour! "; self.moveInto( nil ); } ; Since our verify method has simply an empty method the runtime just moves on to the actual verb method and executes it. No problems. Check out pages 36-39 of the TADS manual (a crucial section, by the way) and particularly page 39 for more details. This is the section titled "Disambiguation" in Chapter 4. *NEVER* SET OBJECT LOCATIONS MANUALLY AT RUNTIME. This is actually explained in chapter 4 of the manual, but I feel it's worth repeating here. It's OK to specify an item's location manually in the initial game definition. For example, this is cool, insofar as euphoniums can possibly be considered to be cool: Euphonium: item location = bandRoom ; What you *don't* want to do is set an item's location manually like that within code that is executed during game play. Doing so totally messes things up, because then the contents property of the container within which the item is located is not updated correctly. So the following code is bad: Euphonium.location := Wastebasket; Instead, you want to use the moveInto() method, like this: Euphonium.moveInto( Wastebasket ); That keeps everything neatly synched up. COMMON COMPILER ERRORS. Some of the most common compile-time errors result from forgetting to add a semicolon or not closing parentheses properly. As explained on page 201 of the manual, the compiler will try to skip ahead to the next code object if it encounters a problem. Thus if you end up with an endless string of compile-time errors appearing on your screen when you try and compile you can usually safely ignore most of the errors - it was likely the first one that caused all the problems. Another common problem is accidentally adding a semicolon after an if statement, thus: if ( elvis.isDead ); { say( 'No kidding. ' ); } This will cause a compile-time error as the semicolon after the if(); statement will tell the compiler that the if statement is complete. Yet another easily made error is to name a local variable the same name as a function. You can't do that. COMMON RUNTIME ERRORS. If you forget to return a value from a method or function you'll get the dreaded 1010 error. This could also mean that you've asked the game to evaluate a property that doesn't exist. For instance, if you have something like this: if ( Me.location.isUpstairs ) and Me.location = nil, you'll have problems because TADS will try to figure out the isUpstairs property of a non-existent object. You'd be better off doing this: if ( Me.location and Me.location.isUpstairs ) Another common problem involves the number of arguments you send to a function. Let's say you've invented a nice function like this: superDuperFunction: function( parm1, parm2, parm3 ) { // ingenious code in here. } Now if you call your function using this code: superDuperFunction( nil, true, nil, nil ); you'll get the annoying TADS-1026 error. The same error will occur if you call a method with the wrong number of arguments. TO MODIFY ADV.T OR NOT TO MODIFY ADV.T? That is the question. This is one of those perennial Difficult Decisions that TADS authors have to face. There are essentially three basic approaches as I see it. 1) Don't touch adv.t at all. The only changes made are done through TADS 2.1's "modify" and "replace" features. Pros: game doesn't break when a new version of adv.t is released. Cons: as your game gets complex it gets confusing tracking all the patches you've made to adv.t. 2) Modify adv.t as needed, brazenly ignoring "modify" and "replace." Pros: the game can include a myriad features that go beyond the fairly basic adv.t defaults. Cons: it's a lot more work to maintain as every single time a new version of adv.t is released you've got to go through manually and check every bit of code to make sure it still works. 3) Skip adv.t altogether and use WorldClass instead. Pros & Cons: See next section. Either way it's good practice to document thoroughly any changes you make to either adv.t or std.t or both. TAKE A LOOK AT WORLDCLASS. David Baggett, half of the Adventions team famous for the Unnkulian Unventures, has designed a complete replacement class library system for TADS. WorldClass replaces the standard adv.t and std.t TADS libraries altogether. WorldClass is an extremely powerful and flexible class library system that lets you do some pretty impressive stuff. It's much more consistent in structure and naming, and supports a lot of very useful concepts. For instance, it lets you treat all five senses equally in a game, and can handle abstractions like knowledge of items. There are two disadvantages to WorldClass as I see it, however. The first is that it's considerably slower than adv.t on old machines. If you have a reasonably modern computer, this won't be an issue, but if you have a really ancient clunker (eg: Mac Plus, IBM PC AT) then you'll find WorldClass games take a long time thinking between moves. This is because all items in the game are considered, rather than just those in your immediate surroundings. This means that cool stuff like knowledge classes are supported, but does slow things down. Second, WorldClass is quite a bit more complicated than adv.t. It lets you do a lot more than adv.t, but does have a steeper learning curve as a result. If you want to knock off a quick mini game, you might want to stick with adv.t. But if your ambitions are a bit higher, consider spending the time to learn what WorldClass can do for you! WorldClass, including Paul Gilbert's excellent manual, is in the if-archive at: ftp://ftp.gmd.de/if-archive/programming/tads/worldclass/ Or you can check the WorldClass Programming Page: http://www.df.lth.se/~mol/progtadsworldclass.html CASE IS NORMALLY SIGNIFICANT. When you *play* a TADS game you can enter commands in upper, lower or mixed case and everything works fine. However, TADS *code* is normally case sensitive. If you're having problems with a variable or object name not being recognized double-check to make sure that the capitalization is consistent. Common problems involve properties like "islit" or "isseen" and methods like "verDoTake" and the like. WorldClass is considerably more consistent than adv.t on this score. Newer versions of the compiler do permit case insensitivity, but you'll likely find your game won't compile with this option turned on unless you check your entire source code for case issues. CHECK OUT THE TADS DEBUGGER. One of the coolest things about TADS is that it has a fabulous source code debugger. This was one of the major perks for buying the shareware version of TADS back when it was shareware. And now that TADS has gone freeware the debugger is freely available at your disposal. At least, if you use Macintosh or MS-DOS computers it is. At time of writing the debugger wasn't available for any other platforms, but since the source code is available I'm sure busy hands are at work as we speak, porting the program to every platform under the sun. So be sure to check the thing out if you can get your hands on it. It lets you run a game and step and trace through the source, making it really easy to find bugs. You can even go in and change variables and stuff, making it quite simple to test your code. The Mac version is particularly nice, as it uses multiple windows to display the game, the command line for the debugger and the source code. In fact, at time of writing the Macintosh is the best platform for writing TADS games with (in my opinion) for this very reason. DON'T USE "THING". Never create an object of type "thing". You'll get a runtime error if the player tries to pick it up. Always create an object of type "item" if you want something small and takeable. An item with "thing" as its class is, of course, just fine. LOCAL STATEMENTS. Local statements must be the first statements to appear in a block of code. Otherwise you get a "general syntax error." As mentioned in the manual on page 71, the only kind of code that can precede a local statement is another local statement. Thus, this next code segment won't compile because its local statement is preceded by something else: superFunction: function { "Hello there! "; local burp := true; } This (admittedly rather pointless) function will work flawlessly if you put the local statement at the top, before any other statements within the brackets. WHY THE HECK WON'T FLOATING ITEMS, HIDDEN ITEMS OR OBSTACLES WORK? In all likelihood you've defined something in the game as being a floating item, hidden item or obstacle without also defining it as being a member of whatever class it needs to be. In other words, this won't work: tree: floatingItem but this will: tree: fixeditem, floatingItem (of course, it may sound like a bit of a contradiction in terms to have a tree that's both fixed and floating, but there you go.) If you look in adv.t you'll notice that floatingItems and hiddenItems and obstacles are all defined as objects rather than items. That means they contain no code of any kind. The class is being used as a marker class rather than one from which code is inherited. Note that if you've coded your stuff correctly and it still doesn't work then it's possible that you neglected to include the preinit() code that sets up the list of all floating and hidden items in the game. WHENCE DOUBLE-QUOTED STRINGS? By "double-quoted" I mean strings of text that are delineated by quotation marks rather than apostrophes. There's a crucial difference. TADS displays the former whenever it sees them but won't display the latter unless you explicitly use the say() function. In other words, this won't work: if ( self.colour = "red" ) self.colour; The runtime will automatically display the word "red" whenever it evaluates this piece of code. You probably don't want that. Instead you should do this: if ( self.colour = 'red' ) say( self.colour ); Why does TADS have this unusual double-quoted string concept? Well, because it's very useful. Adventure games are constantly displaying text. It'd be extremely tedious to have to say something like "printf( 'blah blah' );" or whatever all the time just to get words on the screen. Double-quoted TADS strings are a useful shortcut. This is explained on page 21 of the printed manual. "OF" IS A SPECIAL WORD IN OLDER TADS VERSIONS. As described on page 32 of the manual, the word "of" is a special word that gets removed by the parser in older versions of TADS. If you want to use it in some other context (say, "accuse Ronald of murder") then be sure to upgrade to version 2.2, which lets you use "of" as a preposition and removes the previously hardcoded restriction. PLURAL AND SINGULAR PROBLEMS. Let's say you have an object somewhere with a noun of "blinds". Let's say you also have another object elsewhere with a plural of "blinds". Now you won't be able to refer to the item with only a noun set to "blinds" - you'll get the "I don't see that here" message. Both have to be either noun only or plural only. CONDITIONAL VOCABULARY. TADS doesn't permit conditional vocabulary in noun and adjective definitions. It sets up a table of vocabulary words at compile time. So something like this is not legal: noun = { if ( self.isBig ) return( 'big' ); else return( 'small' ); } However, starting with version 2.2 you can add and delete words at will using the addword() and delword() built-in functions. These don't let you set up conditional code like the example above, but do let you add and delete vocabulary words at runtime. Very handy feature! (note however that there is a bug in the Macintosh version of the TADS runtime at time of writing. If you add a word using addword() and then undo that move then the word is not removed as it should be.) SPACES AFTER STRINGS. It's usually good form to add an extra space after a string of text, thus: ldesc = "It's an ordinary turnip. " instead of: ldesc = "It's an ordinary turnip." Why? Well you never know what text might be displayed next. If, say, a daemon displays some text without first printing a "\b"; sequence to add a blank line you'll get your sentences running together. Adding a blank space prevents this from happening. TADS IS OBJECT-ORIENTED. It really is! Don't code up objects with endless case statements and if-else statements when you can inherit properties and methods from classes. You'll find the code is much more elegant and easy to understand and often takes up less room as well. Thus, this following piece of code is a Really Clunky Way to do things: bananaTree: fixeditem, floatingItem sdesc = { switch( Me.location ) { case Forest: "big"; break; case Greenhouse: "small"; break; case Jungle: "enormous"; break; } " banana tree"; } [ etc etc ] locationOK = true location = { if ( Me.location = Forest or Me.location = Greenhouse or Me.location = Jungle ) return( Me.location ); } ; This is a somewhat better way to do the same sort of thing: bananaTree: fixeditem, floatingItem sdesc = { Me.location.bananaTreeString; " banana tree"; } [ etc etc ] locationOK = true location = { if ( Me.location.hasBananaTree ) return( Me.location ); } ; Then you could set the hasBananaTree property to nil in the "room" class, so that all other rooms would inherit this nil value. Special locations could have the property set to true. Likewise, those special locations could have their bananaTreeString properties set to display the appropriate message. What's the advantage of doing it this way? Well, putting special case code into special rooms means you don't have to hardcode a whole pile of unwieldy conditional coding into the floating item. Also, special case stuff (ie: the forest having a tree or whatever) is associated with special case locations. (note: this isn't the world's greatest piece of sample code as it has one major inconsistency in it. That is, unless you've also modified the chair item class in adv.t to pass the hasBananaTree value through, the tree will mysteriously vanish if you sit down on anything. Just something to keep in mind!) BREAKING OUT OF THE RUNTIME. Sometimes TADS gets stuck and only displays an error message instead of accepting input. Other times you type something but absolutely nothing is displayed - the game just returns you to the > prompt. These usually indicate a bug in your TADS code. In either case typing the special command $$ABEND in the runtime window should force the runtime to quit. BEWARE CONTROL CHARACTERS. It seems that accidentally embedding a control character in your TADS source will often cause the compiler to bomb. If you're getting mysterious compiler errors from a piece of code that looks perfectly legitimate, you might have accidentally typed an invisible control character into your code - maybe your finger slipped off the shift key or something. In cases like this it's a good idea to run your source through a filter program to eliminate any possible control characters before trying to figure out why an otherwise reasonable-looking piece of code isn't working. For example, Macintosh users of BBEdit can use that text editor's "Zap Gremlins" feature to toast all control characters. BEWARE OF #pragma C+ Remember that TADS supports two different styles of operators - its own style, and the style used in C. There are some similarities and some differences between these two styles. TADS lets you choose which style of operator you want. If you include the header "#pragma C+" at the start of each source code file then the compiler will use C style operators. If you don't include this header or include the header "#pragma C-" then TADS will use its default style. The thing to beware of is that some examples of TADS coding out there use the traditional style and some the C style. The vast majority of the code I've seen uses the traditional style, but I've noticed a handful of code examples (usually written by Mike Roberts in his documentation) that use the C style. This can lead to problems if you copy a chunk of code that uses one style operator and paste it into another file that uses the other style. So, let's say you see some code that includes this line: if ( v == inspectVerb ) That was written in the C style. The traditional TADS operator style would have read like this: if ( v = inspectVerb ) Likewise, you might see some code that looks like this: if ( self.value != nil ) The traditional TADS way to code that is: if ( self.value <> nil ) The other common problem operator is the assignment operator. In the C style it's this: trombone.noiseValue = true; but in the traditional TADS style it's this: trombone.noiseValue := true; So be sure to check which operator method the code you're using is written in. If the code contains the line #pragma C+ at the start then you know for sure it's written in the C style. However if it doesn't have anything then you're best off checking the code carefully to make sure that it is, in fact, written in the traditional TADS style. Some of the operators are compatible between styles - but others are not and will cause the compiler to choke. The C style operators are described in detail in the TADS 2.2 Release Notes. ADD IN THE DEBUG FUNCTIONS The standard debugTrace() function simply lets you enter the debugger's command line mode when you're running it. It's useful, but recent versions of TADS support a much niftier extension to the function. This extension turns on a diagnostic mode when you're playing a game. Even more usefully, this diagnostic mode works with the standard runtime as well as with the debugger program. The diagnostic mode works like this. Let's say it's turned on and you type the command: >examine the iron bench The runtime then displays the following extra information: . Checking words: ... examine (verb) ... the (article) ... iron (adj) ... bench (noun) . Checking for actor . Reading noun phrase ... iron (treating as adjective) ... bench (treating as noun) ... found objects matching vocabulary: ..... white wooden bench ..... cast iron bench . executing verb: examine .. setting it: cast iron bench The runtime then displays the normal response to the command; in this case: You're looking at the cold cast iron frame of an old-fashioned park bench. It seems to have been painted recently with glossy black enamel. Note all the useful information. The game breaks down the command, telling you which words it's treating as nouns or adjectives, then lists all the objects in the game that match the supplied input. In this case my game contains two items that have 'bench' as a noun, but the game disambiguates further and only selects the cast iron one. It also performs a setit() on the bench item. To add this handy feature to your game just add the following verbs: debugonVerb: sysverb verb = 'debugon' action( actor ) = { if ( debugTrace( 1, true ) ) "Debug diagnostic mode engaged. "; abort; } ; debugoffVerb: sysverb verb = 'debugoff' action( actor ) = { if ( debugTrace( 1, nil ) ) "Debug diagnostic mode disengaged. "; abort; } ; This problem with setit() is due to be fixed in the next version of the TADS runtime. "it" IS A PROBLEM WITH numObj AND strObj OBJECTS Here's an obscure one. Let's say you have a method in a numObj or strObj object. Here's a silly example of this: narfVerb: deepverb verb = 'narf' sdesc = "narf" doAction = 'Narf' ; numObj: basicNumObj; verDoNarf( actor ) = {} doNarf( actor ) = { local i, len; len := self.value; if ( len = 0 ) "No narf. "; else { "\b\t"; for ( i := 1 ; i <= len ; ++i ) "Narf! "; } } This pointless piece of code works like this: >narf 4 Narf! Narf! Narf! Narf! The problem is that TADS will then set the value of "it" to numObj. Which means that if the player types, say, "examine it" after issuing the "narf" command, nothing will happen: >narf 3 Narf! Narf! Narf! >examine it > This occurs because the numObj object hasn't got any verb methods defined for any common verbs. Exactly the same thing will happen with strObj objects, thus: >say "hello" You say "hello." >eat it > The easiest way to deal with this problem is to put the line setit( nil ); into your doNarf method. This resets the value of "it" to nil, safely avoiding the whole mess. A more complex solution is to create a universally accessible floating item that has no vocabulary words associated with it, then setting 'it' to be that object. This allows for fancy tricks like this: >say "hello" Okay. You say "hello." >x them I'm not sure what you're referring to by "them," because the last thing you referred to was the text "hello." WHERE IS THE NUMBEREDOBJECT CODE? The code for handling numbered objects seems to be missing from the 2.2.1 adv.t distribution. Here it is. /* * numbered_cleanup: function * This function is used as a fuse to delete objects created by the * "numberedObject" class in reponse to calls to its newNumbered * method. Whenever that method creates a new object, it sets up a fuse * call to this function to delete the object at the end of the turn in * which it created the object. */ numbered_cleanup: function( obj ) { delete obj; } /* * numberedObject: object * This class can be added to a class list for an object to allow it to * be used as a generic numbered object. You can create a single object * with this class, and then the player can refer to that object with * any number. For example, you can create a single "button" object * that the player can refer to with "button 100'' or "button 1000'' * or any other number. If you want to limit the range of acceptable * numbers, override the "num_is_valid" method so that it displays * an appropriate error message and returns "nil" for invalid numbers. * If you want to use a separate object to handle references to the object * with a plural ("look at buttons"), override "newNumberedPlural" to * return the object to handle these references; by default, the original * object is used to handle plurals. */ class numberedObject: object adjective = '#' anyvalue( n ) = { return n; } clean_up = { delete self; } newNumberedPlural( a, v ) = { return self; } newNumbered( a, v, n ) = { local obj; if ( n = nil ) return self.newNumberedPlural( a, v ); if ( not self.num_is_valid( n ) ) return nil; obj := new self; obj.value := n; setfuse( numbered_cleanup, 0, obj ); return obj; } num_is_valid( n ) = { if ( n = 0 ) { "There aren't zero "; self.pluraldesc; " here! "; return nil; } else if ( n > self.maxNum ) { "There aren't that many "; self.pluraldesc; "! "; return nil; } else return true; } dobjGen( a, v, i, p ) = { if ( self.value = nil ) { "You'll have to be more specific about which one you mean. "; exit; } } iobjGen( a, v, d, p ) = { self.dobjGen( a, v, d, p ); } maxNum = 10 ; +--------------+ Macintosh Tips +--------------+ Here are some tips for the users of the Macintosh version of TADS. I don't normally do compilation on the DOS or UNIX or other flavours of TADS so I'm not going to include any tips for those platforms unless someone sends me some. COMPILER BOMBS. If you have a very large game and you're having problems with the compiler crashing when you try to compile, it's possible you don't have enough memory allocated. Try increasing the amount of memory set aside for the compiler by changing the setting in the "Get Info" box. 1500 K is usually enough for even a big game. MULTIFINDER OR SYSTEM 7.x. Don't forget that TADS can run in the background under MultiFinder or under System 7.x. However once TADS has finished loading in the files and starts the actual compilation it'll lock up your Mac for the duration of the compile. TEXT EDITOR. If you're struggling with editing code on a word processor or something, take a look at some of the freeware and shareware text editors out there. There are quite a few, including Edit II and Alpha. Probably one of the best (IMHO) is BBEdit, however. BBEdit itself is now payware, but there's still a freeware "Lite" (sic) version available. Any version is ideal for editing TADS code. They do bracket and brace balancing, semi-automatic formatting, GREP search and replace, no wordwrap and a thousand and one other features. Try to find version 2.2.2 which, unlike the newer BBEdit Lite, can do file comparisons. This function, analogous to a "diff" utility, is an extremely nice way to compare older versions of source with the new. Of course obsessive hackers can check out the port of emacs... COMMAND-PERIOD. Don't forget that TADS can now break out of endless loops by hitting command-period, like all good Mac programs do. Note that the HTML version of this file normally lives at: http://www.tela.bc.ca/tela/tads/authoring/tads-tip-sheet.html