/* * Psychedelic but trivial example world for WorldClass. * This isn't intended to check WorldClass correctness; it just shows * off a few of the neat things WorldClass supports. * * Useful playtesting verbs: * * locate * warpto * gimme * * You can usually answer "all" to disambiguation queries without * causing too much mayhem. :) * */ #include #define NOW #ifdef NOW // // Note: the following coin stuff is only temporary. // It will be replaced by a better example once we fix // a TADS bug. // class Coin: Item isequivalent = true // equivalent to everything else of class Coin sdesc = "coin" location = startroom noun = 'coin' plural = 'coins' ; coin1: Coin noun = 'coin' plural = 'coins' ; coin2: Coin noun = 'coin' plural = 'coins' ; coinproducer: Button, Fixture location = startroom noun = 'button' heredesc = "Inexplicably, there is a button here." doPush(actor) = { local x; x := new Coin; "A coin suddenly appears out of nowhere!"; } ; #endif // // If you want to do your own special preinit tasks, replace // userpreinit. In it, you should do any compile-time housekeeping or // setup. // // Do NOT replace or modify the WorldClass preinit function. // Use this instead. // replace userpreinit: function { } // // If you want to change the standard game startup behavior, // replace userinit, copy the standard code from the WorldClass // version, and make your changes. // // Do NOT replace or modify the WorldClass init function. // Use this instead. // // (The version here is identical to the WorldClass version. // It's only included here to illustrate how you'd make a different // version.) // replace userinit: function { // // Print the intro unless we're restarting. // if (global.restarting = nil) { "\b\b\b\b\b\b\b\b"; intro(); } // // NOTE: We have to set global.lastactor explicitly here, // because no command has been executed yet. (Normally, // global.lastactor is set in the Verb disamiguation code.) // global.lastactor := Me; // // Note funny syntax: // Me.location.enter(Me); } // // Just like with ADV.T, the player character object should be // named "Me". WorldClass doesn't rely on this much, however. // In general, WorldClass makes no distinction between Me and // other actors. // Me: Player // // These three methods must be correctly defined, or // WorldClass will get confused: // location = bed locationtype = 'on' position = 'sitting' noun = 'me' 'self' 'myself' // // To make eating, drinking, and/or sleeping // required for the player's continued survival, // don't make the following methods nil. (See // the definition of the Player class for details.) // starvationcheck = nil dehydrationcheck = nil sleepcheck = nil ; // // A Darkroom has no ambient light -- the player must have a Lightsource // to see. Darkness does not prevent the player from using any verbs // that don't require the "cansee" capability. // // Note that "startroom" is an arbitrary name. You can make it whatever // you want. WorldClass looks at Me.location (see above) to determine // where the player starts. // startroom: Darkroom // // tdesc = "title desc" // This is what gets printed in the status line for the location. // // The reason we want separate sdesc's and tdesc's is that // the tdesc's are usually capitalized. Furthermore, we // might want to implement "look west", which would tell // the player the name of the westward location. We wouldn't // want this name to come out capitalized. // tdesc = "In a Very Boring Room" ldesc = { "This room is pretty boring, but there is a poster here. Head south and you're in another boring room."; } sdesc = "starting room" // // Rooms can have vocabulary too. WorldClass doesn't // allow most verbs to apply to rooms, so naming conflicts // between rooms and objects are not a problem. (See also // the "warpto" playtesting verb.) // noun = 'room' adjective = 'starting' // // Unless we say otherwise, a Room will get the standard // Ground. (More precisely, Ground makes itself appear // in every room for which ground = true, which is the // default.) // // We want to have our own custom ground here, so we set // ground to be our own ground object. // // Note that if we wanted there to be no ground here at // all, we'd set ground to nil. // ground = floor // // Where the player goes from here // goSouth = room2 // // The previous declaration sets the field goSouth to // the *object* room2. WorldClass treats this as // equivalent to the following *code*: // // goSouth(actor) = { return room2; } // // The benefit to using an object instead of code is that // you can change an object at run-time, but you can't // change code at run-time. // // The benefit to using code instead of an object is that // the code form gives you the actor who's travelling. // Note that other actors may traverse the world via // the travel methods too; in fact, future versions of // WorldClass will likely provide code to control actors // that will work just this way. // // Also, WorldClass recognizes verGo methods -- these // are analogous to verDo methods that TADS itself // supports. If you don't define a verGo method at all, // WorldClass assumes you mean // // verGo(actor) = {} // // in other words, that travel in the given direction is // always allowed. This is usually want you want, so the // convention saves typing. However, you may want to limit // passage via certain directions to certain actors; for // example, you might want to allow the player to go west // but not Droyd, the robot that follows the player around. // To do this, you'd define verGoWest accordingly: // // verGoWest(actor) = { // if (actor = Droyd) // "Droyd is too scared to go that way."; // } // // The key point here is that the code to move Droyd can // silently check the verGoWest method and realize that // it shouldn't try to move Droyd west here. Likewise, when // the player explicitly orders Droyd to go west with // // >droyd, west // // the player will get the failure message above. // ; // // Here we define a carryable item. // Note the WorldClass class naming convention: the first letter is // capitalized. No other letters are capialized, and underscore // is never used. // // Note that since this item has locationtype of 'under', // the player will never be able to access it, even though // its location is startroom. (By default, players can't get // to things that are under or behind locations they're in.) // widget: Item sdesc = "widget" noun = 'widget' location = startroom // // When we don't want the default location type of "in", // we have to explicity set the locationtype field to // one of the following strings: // // 'on' // 'under' // 'behind' // // Note that these are *strings*, not numbers. Internally, // WorldClass converts them to numbers, but users should // *never* use the numerical equivalents. // locationtype = 'under' ; // // Two things are illustrated in the next two declarations: // // 1) Use of the Attachable and Attachpoint classes // 2) Importance of correct class ordering in declaration for // correct multiple inheritance. // // 1) // // An Attachable is something that can attach to other things. // The other things it can attach to are given by the attachesto // list. The attachedto list describes which things the Attachable // is *currently* attached to. // // The objects in the attachesto list must be Attachpoints. Attachpoints // are things that other things can attach *to*. They have attachesto // and attachedto lists just like Attachables. // // In this case, both items can be attached *and* attached *to* each // other. But this need not be the case -- one might define a chain // (Attachable) and a fixed hook (Attachpoint) for the chain to attach to. // // An actor can only take an Attachable if the Attachable is takeable and // if everything it is attached to is also takeable. // // 2) // // For multiple inheritance to work properly, you MUST list classes // in the correct order. Since many WorldClass classes are intended // to work with multiple inheritance, this is very important. // // In general, you should put more specific classes to the left of // less specific ones. E.g.: // // fishhook: Item, Attachable WRONG // fishhook: Attachable, Item RIGHT // fishhook: Attachable, Attachpoint, Item sdesc = "fishhook" noun = 'fishhook' 'hook' adjective = 'fish' location = table locationtype = 'on' attachedto = [string] attachesto = [string] ; string: Attachable, Attachpoint, Item sdesc = "piece of string" noun = 'string' 'piece' location = table locationtype = 'on' attachedto = [fishhook] attachesto = [fishhook] tieable = true ; // // A Lightsource provides light, and can therefore illuminate // Darkrooms. There is currently only a binary distinction // between light and dark; there is no concept of lighting *level*. // // A Clothing can be worn. // // Both of these classes have "properties". These properties // are explicitly handled in preinit, and are the names of // methods that print things like "(providing light)", "(being worn)", // etc. Whenever WorldClass prints an object's description, it runs // all the property methods in the object's properties list. This // has the effect of tacking on all the extra text. // hat: Lightsource, Clothing sdesc = "hat" noun = 'hat' location = Me ; // // Here we define an Item that makes noise. This noise description // will be printed in room descriptions. // bomb: Listablesound, Item sdesc = "mysterious black sphere" ldesc = { "A fuse is sticking out of the mysterious black sphere. Hmmm."; } noun = 'bomb' 'sphere' 'ball' adjective = 'mysterious' 'black' location = table locationtype = 'on' // // The description we use when we're listing this item's // sound in a room description. We do *not* include the // subject here, because it may not be known. (For example, // if the player can't see, he'll get "Something is ticking." // instead of "The mysterious black sphere is ticking.") // listlistendesc = { "is ticking."; } // // Like ldesc, but for sound. Printed when the player // does "listen to ___". // listendesc = { "It seems to be making a ticking sound."; } ; // // Here we define a Part. A Part is a component of another // object. It has no location of its own -- it is always located // in the same place as its parent object. Likewise, its locationtype // is the same as its parent's at all times. // // Set the parent object with the partof method. // fuse: Part sdesc = "fuse" ldesc = { "It's just an ordinary fuse. It's sticking out of the mysterious black sphere."; } noun = 'fuse' partof = bomb ; // // This declaration illustrates several things: // // 1) The Key class // 2) The Edible class // 3) Listablesmell class (see above; similar to Listablesound) // 4) Footnoting // kee: Key, Edible, Listablesmell, Item sdesc = "cheez kee" ldesc = { // // Note the use of the note command. // You pass it the object that contains the // footnote method you want to associate with // this numbered footnote. This object is // usually self, but need not be. (You have to // put footnotes somewhere else if you have more // than one in an object.) // // If you do not set footnum to a number, a footnote // number will be assigned at run-time. WorldClass // ensures that no specially numbered footnotes (i.e., // ones for which you've set footnum to a certain // number) will get clobbered by footnotes that get // numbered at run-time. (See the mouse's footnote // for an example of a hand-numbered footnote.) // "The Acme Cheez Kee (tm) is a freak of plastics "; note(self); "."; } noun = 'kee' 'key' adjective = 'cheez' location = table locationtype = 'on' // // These are like listlistendesc and listendesc -- see // the bomb object above. // listsmelldesc = { "smells awful!"; } smelldesc = { "The cheez key smells totally disgusting!"; } // // This method shows how to make takeability // vary according to some condition. // // NOTE that you must NOT change game state in these // methods (istakeable, issmellable, isaudible, etc.) // because, like the standard TADS ver methods, these // are called by WorldClass "behind the scenes." // // In fact, there is a direct connection between verDoTake // and istakeable: In WorldClass, the standard istakeable // method (in Thing) is defined as follows: // // istakeable(actor) = { // Outhide(true); // self.verDoTake(actor); // if (Outhide(nil)) { // self.verDoTake(actor); // return nil; // } // // return true; // } // // In other words, istakeable by default calls verDoTake // and sees if it prints anything. If it does, it turns // output hiding off and calls verDoTake again to actually // display the message to the user. If it doesn't print // anything, it returns true without printing anything. // // This provides a kind of philosophical backwards // compatibility with ADV.T, where we're used to overriding // verDoTake to make things untakeable. // // NOTE: In theory you can print text in istakeable and // still return true. In practice, however, this is a bad // idea since you have no way of knowing how many times // istakeable will be checked. // // The discussion above applies equally to the other is...able // methods, like isvisible, isaudible, issmellable, istouchable, // etc. // complicated = { // check condition here return true; } istakeable(actor) = { if (not self.complicated) { "You fool! Only a madman would try to take a Cheez Kee!"; return nil; } else return true; } // // Footnote text is printed by the footnote method // in the object specified in the note function. // footnote = { "Tiny letters embossed on the Cheez Kee read \"Unlahkx Evreethyng -- Grate four dormz!\""; } ; // // A thermos that's lockable (for some reason) // // A Lockable can only be opened with its key, given by the key field. // // An Openable is a Container that can open and close. A Container // can have things *in* it. When an Openable is closed, it does // not pass sight, smell, sound, etc. through itself. See the // Openable class definition for more info on this. (Try putting // the Lightsource into the thermos, then close the thermos. The // light won't be able to escape, and you won't be able to see.) // // A Qfront can have things *behind* it. The Q denotes that it // won't list the things that are behind it in room descriptions; // only when the player explicitly *looks* behind it with a // "look behind ___" command. Other Holders (things that can // have things in, on, under, or behind them) have "Q" variants too. // thermos: Lockable, Openable, Qfront, Item key = kee sdesc = "plastic thermos" noun = 'thermos' adjective = 'plastic' location = startroom locationtype = 'in' ; // // We'll make the floor vibrate in the starting room. // To call attention to this fact, we'll make the object // a Listabletouch. // floor: Listabletouch, Floor sdesc = "ground" noun = 'ground' 'floor' adjective = 'warm' 'vibrating' location = startroom locationtype = 'in' // // Since this is a Floor, which is in turn // an Everywhere, it will not get listed in // a room description by default. // // When the contents listing function knows it hasn't // listed an object in the visual contents listing, // it won't refer to that object by name. // // This means thatwhen we do a "listen" in the room, // we'll get "Something is vibrating violently here." // instead of "The ground is vibrating violently here." // // To force the contents lister to name the object, // we have to set alwaysname(actor) to return true. // // (Of course, you can make naming the object conditional // on something -- you don't have to return true all // the time.) // alwaysname(actor) = { return true; } touchdesc = "The ground here is warm, and is vibrating." listtouchdesc = "is vibrating violently here." ; // // A Transparent Container always passes light, whether it's open or not. // jar: Transparent, Openable, Item isopen = true sdesc = "jar" noun = 'jar' location = thermos locationtype = 'in' ; lumenberries: Edible, Item sdesc = "lumenberries" isplural = true noun = 'lumenberries' 'berries' adjective = 'lumen' location = thermos locationtype = 'in' ; mouse: Item sdesc = "happy little mouse <>" noun = 'mouse' adjective = 'happy' 'little' location = table locationtype = 'under' touchdesc = { "The mouse is warm and furry."; } // // A hand-numbered footnote. // footnum = 1 footnote = { "Probably looking for some Cheez..."; } ; // // Things can be nowhere. In this case, set location = nil and // locationtype to 'in'. If you don't explicitly set locationtype, // WorldClass will assume it's 'in'. If you move things to nil, // they automatically get put 'in' nil. But style dictates that // you should always do // // obj.movein(nil); // // to move something nowhere. Don't do // // obj.moveunder(nil); // BAD STYLE! // // or // // obj.movebehind(nil); // BAD STYLE! // // or // // obj.moveon(nil); // BAD STYLE! // // (Note the distinction between these move methods in WorldClass, // whereas in ADV.T you always use moveInto. You need to pay attention // to containment types when using WorldClass!) // kee2: Key, Edible, Item sdesc = "spam kee" noun = 'kee' 'key' adjective = 'spam' location = nil ; // // A set of useful standard furniture classes is provided, including: // // Table, Chair, Stool, Ledge, Bed, Desk, Shelf, Door // // Note that we also make the table and chair Fixtures. This just // means that their heredesc's should be printed in room descriptions. // If we didn't make them Fixtures, we'd have to mention them // explcitly in the room's ldesc. // // This table also shows how to use the movingout and movingin methods. // The table just prints a message about each thing taken off of it. // table: Fixture, Table sdesc = "table" heredesc = { "A sturdy table stands in the middle of the room."; } noun = 'table' location = startroom // // As mentioned earlier, overriding verDoTake // is just as good as changing istakeable. This // way (changing verDoTake) will be more readable // to those TADS programmers accustomed to ADV.T code. // verDoTake(actor) = { "The table's bolted to the floor."; } // // Don't let any actor put anything on the table // if the mouse is on the table. // movingout(obj, tolocation, toloctype) = { if (obj.locationtype = 'on') "\^<> <> moving off the table. "; } movingin(obj, loctype) = { if (loctype = 'on') "\^<> <> moving onto the table. "; } ; chair: Fixture, Chair heredesc = { "There is a well-made wooden chair here."; } location = startroom noun = 'chair' // // A Chair is a Nestedroom, and by default Actors // can't reach things in the containing Room when // they're in a (contained) Nestedroom. // // There are two ways to overcome this: // // 1) Define a reachable list and put things in it. // 2) Set reachsurroundings = true // // Option 1 allows you to specify exactly which things // in the containing Room (or anywhere else, for that // matter) are reachable from the Nestedroom. You can // put both objects (e.g., cheez kee) in the reachable // list and Containers. If a Container appears in a // reachable list, everything contained in that Container // will also be reachable. There is currently no way // to restrict the containment type to, for example 'under'. // // In this case, anything in, on, under, or behind the table // will be reachable from the chair. // reachable = [table] ; bed: Fixture, Bed heredesc = { "An old, comfortable-looking bed is off to one side."; } location = startroom noun = 'bed' // // Option 2 in the list described in the definition of // the chair above. // // If you set reachsurroundings = true, then everything // in the Nestedroom's location will be accessible from // the Nestedroom. This is often the simplest solution // since it restricts the player the least, and so is // least likely to cause any playability problems. // reachsurroundings = true ; // // A Decoration is a fixed object (i.e., one that is not takeable) // that is not very important. Unlike ADV.T, WorldClass does not // say "that is not something important" whenever the player manipulates // a Decoration -- the distinction between "Decoration" and "Thing" // is mainly one of intent. // // For objects that really are totally unimportant (i.e., for which // you want WorldClass to tell the player "that is not important"), // use the Unimportant class. // poster: Decoration sdesc = "poster" noun = 'poster' ldesc = "It's just an ordinary poster." location = startroom ; // // Another room // room2: Room tdesc = "Another Stupid Room" ldesc = { "Gee, this place is boring! If you go north, you'll probably wind up in another boring room."; } sdesc = "second room" noun = 'room2' 'room' adjective = 'second' goNorth = startroom ; // // Actors work like they do in ADV.T, but WorldClass Actors // are somewhat more complex. See the Actor class definition // for details. // monk: Male, Actor sdesc = "monk" location = room2 noun = 'monk' actordesc = { "There is an ancient monk here."; } ; elderberries: Item sdesc = "elderberries" isplural = true noun = 'elderberries' 'berries' adjective = 'elder' location = monk ;