/* The annotated source code for * Roadside Adventure, by Kevin Venzke * stepjak@yahoo.fr, http://nodesiege.tripod.com/if/ * Programmed Oct 13-15, 2004. * Annotated Oct 24, 2004. * * I hope this may be of interest to people who are * curious about replacing adv.t and std.t, but who * aren't sure where to start. * * For precise information, you will want to look through * the TADS manual, which is what I would have to do * anyway were someone to ask me a question. Also, I * haven't used some things that could be useful, such * as the "validActor" property, or the "cantReach" for * verbs. * * The idea behind this game was just to cram something * into a 10k .GAM. It's not the best game ever, so * consider yourself warned! * * To get a 10k game out of this, I "Compile for Release" * under version 2.5.7. You may need to go into * Build/Settings and remove the "gameinfo.txt" resource. * The extension of this file should be ".t". */ /* _rand. Copied from adv.t. You don't need this, but I felt it was * worth it for this game. It is supposed to be more random than simply * using rand(). */ _rand: function(x) { return (((rand(16*x)-1)/16)+1); } /* "can't" and "already done" are errors that come up pretty often in * this game, so they got their own functions to save space. */ cant: function { "You can't do that. "; } alrdone: function { "That's already done! "; } /* die() is used for winning and dying. You don't have to define * a function called "die." */ die: function { timeDelay(1000); // a pause. timeDelay() is built-in. "Enter to restart:\ "; input(); // input() is built-in. restart(); // restart() is built-in. } /* In adv.t, usually Me.travelTo(destination) is used. In attempt * to save space, and since Roadside Adventure only uses "Me" for * the player, I made a function travelto() to handle moving the player. */ travelto: function( p ) { Me.loc:=p; // This game doesn't use the "location" property, // in attempt to avoid "contents" being contained // in the .GAM (to save space). // // In hindsight, I think this was pointless, except // to save a few keystrokes. I believe the // "contents" lists are filled at runtime. p.look; // "room.look" is the equivalent of adv.t's // room.lookAround(verbosity). } /* indanger() returns whether the player is in a "roadside" room. */ indanger: function { local l; l:=Me.loc; return (l=roadside or l=roadside2 or l=roadside3); } /* tigername() prints one of three tiger descriptions. */ tigername: function { switch(_rand(3)) { case 1: "baby "; break; case 2: "queen "; break; case 3: "worker "; break; } "tiger"; } /* "tod" stands for "time of day." Affects some room descriptions. */ tod: function { if(norman.loc=nil) "hot"; else "midnight"; } /* init() is a function you must provide. TADS runs it first thing. * If you wanted to try replacing the TADS parser, you could do so * by never returning from init(). */ init: function { clearscreen(); // This is a built-in function. randomize(); // So is this. "\bWhat rotten luck! En route to an important bachelor party, your lousy truck decides to die on the side of a desert road, miles and miles from civilization... "; "\b\b\(Roadside Adventure\)\nA 10k TADS Adventure by Kevin Venzke\nType ABOUT for information.\b\b"; notify(tigers,&prowl,0); sr.look; // Display the start room description. } /* You don't have to define a class called "thing," but you will * almost certainly want to. */ class thing: object loc = nil // This game uses "loc" instead of "location." adesc = "a <>" thedesc = "the <>" // You will want to define the above. The parser may call // them itself, for instance in: // "I don't know how to use THE ITEM." heredesc = "You see <> here. " // My room.look calls heredesc if the thing is present. adjective = 'the' // This is my lame way of making "the" understood: It's // an adjective matching everything. See adv.t if you // want to implement articles in the standard way. actorAction(v,d,p,i) = { cant(); exit; } // If objects lack the above line, the player will most // likely be able to give successful instructions to // inanimate objects, like "SHOES, WEAR THE SUNGLASSES." // What I have done here is display an error, and just // "exit" to stop the command. cantReach(a) = (cant()) // The parser will call this when the player refers to // "it" or "him" (etc.) when the thing referred to is // no longer valid. So you should include something // like this; otherwise the game will just print a // blank line. // // Also, if you provide "isVisible(vantage)," (which this // game does not,) and it returns true, but the object // is not valid for the verb, then "cantReach" is called. // // If you don't provide isVisible, it's the same as // providing it but having it always return nil. // Validity trumps visibility. verDoLook(a) = {} doLook(a) = (self.ldesc) ldesc = "Nothing special. " // In adv.t, it's not "Look" but "Inspect." // Take and Drop: // By default, things are not fixed. You don't have to use // a property called "fixed." verDoDrop(a) = { if(self.loc<>a) alrdone(); else if(self.fixed) cant(); } doDrop(a) = { "Dropped. "; self.loc:=a.loc; // adv.t uses moveInto() in order to keep // the contents lists up-to-date. But contents // is not used in this game. } verDoTake(a) = { if(self.loc=a) alrdone(); else if(self.fixed) cant(); } doTake(a) = { "Taken. "; self.loc:=a; } ; /* Note that you don't have to have a "room" class. You'll probably * want one, though, since rooms usually contain information about what * rooms they connect to. Not so in this case, since I thought it * would take up too much space. */ class room: object look = { local o; "\nYou're <>.\n"; for(o:=firstobj(thing);o<>nil;o:=nextobj(o,thing)) { if(o.loc=self and o<>Me and o<>tigers) { o.heredesc; "\n"; } } } // Note that my rooms don't have an sdesc and don't use the status // bar. Instead of looking at the contents list, "look" cycles // through ALL things, and asks present things to print a "heredesc" // unless they are Me (the player) or the tigers. It would be less // messy if Me and tigers just had a flag meaning, "Don't ask me to // display a heredesc," much like setting "isListed" to nil with // adv.t. ; /* You will probably want to define a basic verb class, since most verbs * will share validDo and validIo conditions. * (They will likely also share doDefault methods, but this game doesn't * use those. This means that this game will never try to guess which * object you meant to do something to, and "ALL" is never meaningful except * in response to a "Which did you mean, the X or the Y?" prompt.) */ class deepverb: object validDo(a, o, s) = (o.loc=a or o.loc=a.loc) validIo(a, o, s) = (self.validDo(a,o,s)) // adv.t usually checks whether an object isVisible or isReachable. // To save space, this game avoids that, and says simply that an // object is valid for a verb if the player either has the object, // or the object is in the same room as the player. // // If an object is not valid for a verb, the game usually tells // you "I don't see any X here." So this isn't the place to // check whether the object makes SENSE with the verb. ; /* The game's seven rooms. In this game, the only useful property is * the ldesc. */ roadside: room ldesc = "on the side of a <> desert road, with <<(norman.loc=nil)?"cars buzzing by":"no cars in sight">>" ; roadside2: room ldesc = "on the side of a <> desert road" ; roadside3: room ldesc = "on the side of a <> desert road, with an abandoned building to the east" ; bldg1: room ldesc = "just inside a dark, abandoned research facility, with rooms to south and east" ; bldg2: room ldesc = "in a darkened lounge. You can go north" ; bldg3: room ldesc = "in a robot storage room, with an exit to the west"; /* "sr" is a room as well as a thing. This works out because, as a thing, * the ldesc is not used (doLook doesn't display it). */ sr: thing, room ldesc = "in your truck, on the side of the road" fixed = true // The truck can't be taken. sdesc = "truck" loc = roadside noun = 'truck' 'back' 'car' 'cars' verDoUse(a) = {} doUse(a) = { // "get in" and "enter" mean "use." travelto(sr); } doLook(a) = { if(gloves.loc=nil) { // if gloves are nowhere, find them. gloves.loc:=roadside; gloves.heredesc; setit(gloves); } else "Your truck won't start. You need a ride. "; } ; dress: thing sdesc = "dress" noun = 'dress' loc = Me verDoDrop(a) = { if(norman.loc<>nil or not indanger()) "You can't, it's too cold here! "; else pass verDoDrop; } doDrop(a) = { "You strip off your lone garment and jiggle freely in the hot sun and traffic.\b"; pass doDrop; } ldesc = "Tiny and lavender. " ; gloves: thing loc=nil noun = 'gloves' 'glove' 'pair' adjective = 'rubber' sdesc = "rubber gloves" adesc = (self.sdesc) isThem = true // isThem, isHer, etc., are built-in. heredesc = { "A pair of rubber gloves is "; if(loc=roadside) "in the back of the truck. "; else "here. "; } ; tigers: thing fixed = true // Can't take the tigers. noun = 'tiger' 'tigers' 'clan' adjective = 'queen' 'baby' 'worker' 'hungry' sdesc = "tiger" loc = { return (indanger() ? Me.loc : roadside); } // The tigers are basically a floatingItem. ldesc = "The tigers lick their fat blood-drenched lips hungrily. " isThem = true health = 7 verDoKill(a) = {} doKill(a) = { askio(quitVerb); } verDoKillWith(a,i) = {} // Note that "quitVerb" also contains my prepositions. What // doKill(a) is doing is asking the player what he wants to // kill tigers with. prowledyet = nil prowl = { local a; if(not indanger()) return; a:=_rand(12); if(a>5) return; if(not prowledyet) { prowledyet:=true; "\bYou hear a casual, throaty roar, the sign of hungry tigers. A glance around you reveals a clan of dozens, sizing you up and licking their ivory fangs. "; setit(self); return; } switch(a) { case 1: "\bA <> claws at you lazily, catching a heap of flesh."; break; case 2: "\bYou wrestle your head out of the dank maw of a <>."; break; case 3: "\bA <> head rams into you, crisply snapping some ribs."; break; case 4: "\bPart of your limb flies off in the teeth of a <>."; break; case 5: "\bA mean kick from a <> splatters your brains to the ground."; break; } health--; if(health=0) { "\bSorry, you died, you were eaten by \(tigers\)!\b"; die(); // Note that die() is neither built-in nor required. } } ; robotleg: thing loc = bldg2 knowwhatitis = nil sdesc = { if(knowwhatitis) "robot leg"; else "strange bar"; } noun = 'leg' 'bar' adjective = 'strange' 'robot' doLook(a) = { if(norman.knowname) { "It's a robot leg! "; knowwhatitis:=true; } "Bends in the middle; good for whacking! "; } verDoUseWith(a,i) = {} verIoKillWith(a) = {} ioKillWith(a,d) = { if(d<>tigers) { cant(); return; } tigers.loc:=nil; unnotify(tigers,&prowl); "You feel your health surge as you smack a baby tiger in the face! The clan reassesses you, and slinks back off into the night... "; } ; norman: thing fixed = true // Can't take the robot. loc = bldg3 knowname = nil hasleg = nil sdesc = { if(knowname) "robot"; else "dormant robot"; } noun = 'robot' 'norman' adjective = 'dormant' isHim = true ldesc = { "It's a goofy-looking robot that can be turned on. "; if(knowname and not hasleg) "Sparks spit distractingly from where its leg should be. "; } verDoKill(a) = "The robot can't be turned off. " verDoKillWith(a,i) = (self.verDoKill(a)) // "Turn off" means "kill." verDoUse(a) = { if(knowname) alrdone(); } doUse(a) = { knowname:=true; "The robot bubbles and flickers to life. \"Hi, I'm Norman! Oh dear, me missing a leg!\"\ he enthuses, \"Will you pwease find my leg and put it back on me?\"\b You start to object, but Norman interrupts you. \"Quick, go now!\" "; notify(self,&follow,0); } // "Turn on" means "use." verIoUseWith(a) = {} //put leg on robot ioUseWith(a,d) = { if(d<>robotleg or not self.knowname) { cant(); return; } if(gloves.loc<>Me) { "Sorry, you died, you were \(electrocuted\)!\b"; die(); } "You fit the leg into Norman with a click. He leaps to his feet, ecstatic. \"Now I follow you everywhere!\"\ he informs you, \"You friend forever!\" "; robotleg.loc:=nil; hasleg:=true; } // "Put X on Y" means "use X with Y." follow = { if(not hasleg) return; if(tigers.loc=nil and _rand(25)=1) { "\bSorry, you died, it's too \(cold\)!\b"; die(); } if(self.loc<>Me.loc) { self.loc:=Me.loc; "\bNorman bounces after you<<(self.loc=sr)?", careful to buckle his safety belt":"">>. "; if(tigers.loc=self.loc) { "\b\"Oh no, not tigers!\"\ Norman squeals. \"You run, I will distract them!\"\ he offers.\b The tigers tear into his feeble plastic casing, making a hearty feast of his integrated silicon innards as you look on in disbelief. They eat their fill and flee into the darkness.\b You spend the night next to Norman's flaming corpse, a warm comforting bonfire that gave its life to protect you. When you awaken, the flame is extinguished, and the sun has risen...\b"; unnotify(self,&follow); unnotify(tigers,&prowl); self.loc:=nil; gloves.loc:=tigers; tigers.loc:=nil; travelto(roadside); } return; } switch(_rand(8)) { case 1: "\b\"Pzzzzzt! Vort ort!\"\ says Norman. "; break; case 2: "\b\"Oooooooo \(na\)!\"\ says Norman. "; break; case 3: "\b\"Prrrrr zzzzt! Joo cho cho!\"\ Norman suggests. "; break; case 4: "\bNorman reminisces about the time he pushed his friend robot into a volcano. "; break; case 5: "\bNorman absent-mindedly recites the first four letters of the word \"hippo.\" "; break; } } ; /* You must provide the next four objects, and also takeVerb. */ pardon: function // usually this prints "I beg your pardon?" or "Eh?" { } numObj: object; strObj: object; againVerb: deepverb verb = 'again' 'g'; takeVerb: deepverb verb = 'take' 'get' 'wear' 'put on' doAction = 'Take' sdesc = "take"; // This game doesn't distinguish between "take" and "wear." dropVerb: deepverb verb = 'drop' 'remove' 'take off' doAction = 'Drop' sdesc = "drop"; // No distinction between "drop" and "take off." useVerb: deepverb verb = 'use' 'enter' 'get in' 'turn on' 'put' sdesc = "use" doAction = 'Use' ioAction(quitVerb) = 'UseWith'; // Several actions are treated as a single "use" verb. // Note that "quitVerb" contains the games' prepositions. killVerb: deepverb verb = 'kill' 'hit' 'turn off' 'whack' sdesc = "kill" doAction = 'Kill' ioAction(quitVerb) = 'KillWith'; // "quitVerb" contains the game's prepositions. waitVerb: deepverb verb = 'wait' 'z' action(a) = "Time passes... " // I didn't provide an sdesc, since the game never asks you, // "What do you want to wait?" etc. ; /* In adv.t, lookVerb is separate from inspectVerb. Here they are the * same. This also permits me to avoid providing an sdesc. */ lookVerb: deepverb verb = 'look' 'l' 'x' 'look at' 'search' 'look in' action(a) = { a.loc.look; // No good if a.loc ever were nil. } doAction = 'Look' ; /* The inventory verb is pretty straight-forward. */ invVerb: deepverb verb = 'i' 'inventory' action(a) = { local i, o; i:=0; "You have:\ "; for(o:=firstobj(thing);o<>nil;o:=nextobj(o,thing)) { if(o.loc=a) { i++; if(i<>1) ", "; o.adesc; } } if(i=0) "nothing"; ". "; } ; /* You will probably want a class specifically for travel verbs (like * "north," "in," etc.). I did so in this game so that I could handle * all the room connections in one method. The result is pretty ugly, * as you can see. */ class travelVerb: deepverb action(a) = { local l; l:=a.loc; if(l=roadside3 and (self=inVerb or self=eVerb)) { travelto(bldg1); return; } if(indanger() and self<>inVerb and self<>outVerb) { if(l=roadside) travelto(roadside2); else if(l=roadside2) travelto(roadside3); else travelto(roadside); return; } if(self=nVerb) { if(l=bldg2) travelto(bldg1); else cant(); return; } if(self=sVerb) { if(l=bldg1) travelto(bldg2); else cant(); return; } if(self=eVerb) { if(l=bldg1) travelto(bldg3); else cant(); return; } if(self=wVerb) { if(l=bldg1) travelto(roadside3); else if(l=bldg3) travelto(bldg1); else cant(); return; } if(self=outVerb) { if(l=sr) travelto(roadside); else if(l=bldg1) travelto(roadside3); else cant(); return; } cant(); } ; /* None of the actual travel verbs handle their own actions: */ nVerb: travelVerb verb = 'n'; sVerb: travelVerb verb = 's'; eVerb: travelVerb verb = 'e'; wVerb: travelVerb verb = 'w'; inVerb: travelVerb verb = 'in'; outVerb: travelVerb verb = 'out' 'get out' 'exit' 'leave'; /* You must provide a Me object. In this game, it isn't implemented * as a thing; you can't refer to "me" or "myself." In fact, the Me * object is here implemented as the "hitchhike" verb, in order to * save space. */ Me: object roomCheck(v)=true // If you don't have this, all of the player's commands will fail, // printing a blank line. loc=sr // You start out in the truck. // And the code handling the verb portion: verb = 'hitchhike' action(a) = { if(not indanger()) { cant(); } else if(norman.loc<>nil) { "There are no cars! "; } else if(dress.loc=Me) { "All the cars seem to ignore you! "; } else { "Nudity always provokes charitable hormones. A seedy bearded fellow in a rusty brown car pulls up and gladly takes you the rest of the way to the bachelor party.\b \(Congratulations, you have won the game!\)\b [Inspiration:\ \"Cattus Atrox\" by David Cornelson, \"Planetfall\" by Steve Meretzky, \"Interstate Zero\" by Adam Cadre, \"Moonglow\" by Dave Bernazzani. Thanks to Trisha, inky, and mcp.]\b"; die(); } } ; /* You must have a quitVerb. Since quitVerb doesn't need its sdesc (you * are never asked, "What do you want to quit?"), it doubles here as a * preposition. */ quitVerb: object verb = 'quit' action( actor ) = { quit(); } // Preposition code: preposition = 'at' 'on' 'off' 'in' 'out' 'with' sdesc = "with" ; /* These last two verbs are also optional. */ restartVerb: object verb='restart' action(a)={ restart(); } ; aboutVerb: object verb = 'about' action(a) = { "Kevin Venzke, stepjak@yahoo.fr, made this Oct\ 13-15, 2004. Version 3. Enjoy. (And try my other game, \(Kurusu City\).) "; abort; } ;