EXTEND.T - An Extension Set for TADS by Neil deMause (neild@echonyc.com), 3/26/96 In the course of writing two games using TADS (MacWesleyan/PC University and Lost New York) I've compiled a collection of modifications to the basic ADV.T library that make programming a lot less painful. (Okay, some programming a lot less painful.) Included are many new verbs, a couple of new object classes, some useful functions, and a couple of fixes for parser oddities in ADV.T. Pick and choose from among these as you like; they're all for public consumption. All questions or comments regarding this code should be directed to me at the above address. This code is freeware. Do with it as you will. NOTIFY - Notifying when you earn points --------------------------------------- This adds the verb "notify", which toggles back and forth between on and off. When on, the message You have just gained X points. is printed each time incscore() is run. Notification is off by default; add the line notified=true to the global object if you want the default to be notification on. replace incscore: function( amount ) { global.score := global.score + amount; scoreStatus( global.score, global.turnsofar ); global.addthis:=amount; if (global.notified) notify(global,&tellscore,1); } notifyVerb:deepverb sdesc="notify" action(actor)= { if (not global.notified) { "Notification turned on."; global.notified:=true; } else { "Notification turned off."; global.notified:=nil; } } verb='notify' ; modify global I also make the default mode for my games "verbose", just because I like it that way. verbose = true tellscore={"\bYou have just gained <> points.";} ; ISINSIDE - Search an object's entire contents hierarchy ------------------------------------------------------- This function enables you to determine if one object contains another, even if the contained object is buried several levels deep. Actually, it works from the bottom up -- cycling through the contained item's location hierarchy until it either hits the desired container, or nil, in which case it stops. Here's how to use it: Say you have a puzzle where carrying a gun through an airport metal detector will set off an alarm. Obviously, you want this to occur even if the player is carrying the gun in their bag, or their pocket, or even hidden inside a hollowed-out book in a secret compartment in their briefcase. To check on this, include the following code: if (isinside(gun,Me)) alarm.ring; isinside() returns true if the item is anywhere within the location, nil otherwise. isinside: function(item,loc) { if (item.location=loc) return(true); else if (item.location) return(isinside(item.location,loc)); else return(nil); } MOVEFROMTO - Bulk relocation ---------------------------- Dan Shiovitz deserves all the credit for this one; I was looking for a way to move the entire contents of one object to another, and he came up with this nifty code. moveFromTo: function (from, to) { local l, i; l := from.contents; for (i := 1; i <= length(l); ++i) { l[i].moveInto(to); } } DISABLING "ALL" --------------- Another one that isn't my doing, though I've unfortunately forgotten who on rec.arts.int-fiction provided this code, long ago. I've changed the defaults for take, drop, and put to allow the use of "all" (which seems logical); adding "allowall=true" to other verbs will let you use "all" with them as well. modify deepverb doDefault (actor, prep, iobj) = { if (self.allowall=nil) { if (objwords(1) = ['A']) { global.allMessage := 'You can\'t use "all" with this verb.'; return []; } pass doDefault; } else pass doDefault; } ; parseError: function (str, num) { // if there's an allMessage waiting, use it instead of the default if (global.allMessage <> nil) { local r; r := global.allMessage; global.allMessage := nil; return r; } else return nil; } modify takeVerb allowall=true ; modify dropVerb allowall=true ioAction(onPrep)='PutOn' //while we're at it... ; modify putVerb allowall=true ; PLATFORMITEM - Neither chair nor bed... --------------------------------------- I once beta-tested a game where if you sat on the toilet then tried to leave, you got the response "You're not going anywhere until you get out of the toilet!" If that toilet had been a platformItem, much embarrassment could have been avoided. (See also doUnboard under "modify thing".) class platformItem:chairitem statusPrep='on' noexit = { "%You're% not going anywhere until %you% get%s% off of <>. "; return( nil ); } ; VERBS! - I got a million of 'em... ---------------------------------- These are some of the verbs I use the most often, along with new ioActions for some verb- preposition pairs that ADV.T doesn't recognize, and the prepositions "for" and "against", which ADV.T inexplicably omits. modify throwVerb ioAction(thruPrep) = 'ThrowThru' ioAction(onPrep) = 'PutOn' ; liftVerb:deepverb verb='lift' 'raise' sdesc="lift" doAction='Lift' ; smellVerb:deepverb verb='smell' sdesc="smell" doAction='Smell' ; modify openVerb ioAction(withPrep)='OpenWith' ; modify class openable doOpenWith(actor,io)= { "I don't know how to open <> with <>."; } ; modify inVerb verb='jump in' ; modify climbVerb ioAction(thruPrep)='ClimbThru' ; againstPrep:Prep preposition='against' sdesc="against" ; forPrep:Prep preposition='for' sdesc="for" ; modify askVerb ioAction(forPrep)='AskFor' ; listenverb:deepverb verb='listen' sdesc="listen" action(actor)={Me.location.listendesc;} //add a listendesc ; //for any location //where "listen" should get a specific response listentoverb:deepverb verb='listen to' sdesc="listen to" doAction='ListenTo' ; "Empty" requires a modification for the container class, using moveFromTo() emptyVerb:deepverb verb='empty' sdesc="empty" doAction='Empty' ; modify container verDoEmpty(actor)={} doEmpty(actor)= { if (not self.isopen) "\^<> is closed."; else { "You empty the contents of <> onto the ground."; moveFromTo (self, Me.location); } } ; Of course, now we need to code in default responses for many of these new verbs... modify thing verDoSmell(actor)={} doSmell(actor)={self.smelldesc;} smelldesc="\^<> doesn't smell like anything in particular." Fixes a TADS bug that creates responses like "Okay, you're no longer in the toilet." doUnboard( actor ) = { if ( self.fastenitem ) { "%You%'ll have to unfasten "; actor.location.fastenitem.thedesc; " first. "; } else { "Okay, %you're% no longer <> "; self.thedesc; ". "; self.leaveRoom( actor ); actor.moveInto( self.location ); } } verDoTouch(actor)={} doTouch(actor)=self.touchdesc touchdesc="It feels just like <>." listendesc={"You don't hear anything.";} verDoListenTo(actor)={} doListenTo(actor)={"<>";} verDoFind(actor)={"You'll have to find that on your own.";} verIoAskFor(actor)={} ioAskFor(actor,dobj)= { dobj.doAskFor(actor,self); //redirects the action to the } //person you're asking ; UNLISTEDITEM - Not fixed, but not listed ---------------------------------------- Often you (well, I) want to have an item that you can take, but that is included in the room description rather than listed separately. This item is unlisted until you take it, after which it behaves like a regular item. (But be sure to include code in your ldesc removing it from the room description once it's taken as well.) class unlisteditem:item isListed=nil doTake(actor)={self.isListed:=true; pass doTake;} ; INTANGIBLE - For things like smells, sounds, etc., a special ------------------------------------------------------class. class intangible:fixeditem verDoTake(actor)={"That can't be taken.";} verDoTakeWith(actor,io)={"That can't be taken.";} verDoMove(actor)={"That can't be moved.";} verDoTouch(actor)={"That can't be touched.";} verDoTouchWith(actor,io)={"That can't be touched.";} ldesc="That's not visible." verDoLookbehind(actor)="That's not visible." verDoAttack(actor)={"That can't be attacked.";} verDoAttackWith(actor)={"That can't be attacked.";} verIoPutOn(actor)={"You can't put anything on that.";} ; A whole bunch of modifications to the basic Actor class. modify Actor This automatically translates "ask actor for object" as "actor, give object to me," which can avoid a lot of unnecessary coding. verDoAskFor(actor,io)={} doAskFor(actor,io)={self.actorAction(giveVerb,io,toPrep, Me);} Likewise, this translates "actor, tell me about item" as "ask actor about item." actorAction(v,d,p,i)={if (v=tellVerb and d=Me and p=aboutPrep) {self.doAskAbout(i); exit;}} listendesc="\^<> isn't saying anything!" disavow="\^<> looks confused." ldesc="\^<> looks just like <>." verDoLookin(actor)={"I don't know how to look in <>.";} verDoSearch(actor)={"How rude!";} ioGiveTo(actor, dobj) = { "\^<> doesn't want it."; } ; Another ADV.T bug - currently, asking someone about a distantItem gives the odd response "It's too far away." modify distantItem dobjGen(a, v, i, p) = { if (v <> inspectVerb and v <> askVerb and v <> tellVerb) { "It's too far away."; exit; } } ; FULL - Giving a detailed score ------------------------------ Inform has (built-in, I believe) an easy way to give a listing of all the actions that have earned you points. The following code adds the same functionality to TADS. fullVerb:deepverb verb= 'full' action(actor)= { if (global.score=0) "You have no points."; else { "You have earned the following:\b"; In here is where you insert the list of actions that can earn the player points. For example, if the player gets 5 points for finding the magic carrot peeler, and 1 points for each carrot they peel with it, you would insert the following: if (global.scPeeler) "\n5 points for finding the carrot peeler"; if (global.scCarrots>0) "\n<> points for peeling carrots"; You also, naturally, need to add the appropriate code in the place where the actual puzzle is solved -- so that at the same time you call incscore(5) for finding the carrot peeler, you also set scPeeler:=true. (I just do a search for "incscore" through the entire game once I'm done, and insert the proper code next to each instance.) "\bTotal score: <>"; } } ; - o -