WorldClass Author's Manual The WorldClass System Author's manual Copyright 1994 by Paul Gilbert. This manual is the copyrighted property of Paul F. Gilbert. Permission is granted to distribute this material by digital and/or physical means on the provision that the material is distributed in an unmodified form. It is prohibited to redistribute any sub-section from this material separately without the express permission of the author. The author makes no warranty of any kind with respect to this material, and disclaims all warranties, including any implied warranties of merchantability or fitness for any particular purpose, or the continued accuracy of this manual for future versions of the product. I have made every attempt to make this documentation accurate, with the grateful help of David Baggett, but will not be held responsible for any errors. Manual version 2.2 (November 1994) Written by Paul F. Gilbert, c/o REAS Software, PO Box 183, Benalla, Victoria, Australia, 3672. e-mail: s9406702@yallara.cs.rmit.edu.au or: s9406702@minyos.xx.rmit.edu.au if the previous one bounces. If you have any questions or queries about WorldClass, you may either write or email to either me or to the WorldClass mailing list at worldclass@ai.mit.edu WorldClass is Copyright 1994 by David Baggett for Adventions. Refer to the README file for explicit copyright information. Chapter 8: Descriptive Methods and Creating your own Verbs are based on slightly modified versions of short pieces written by David Baggett. Contents Preface About WorldClass 1 A Sample Game 2 Making the Change 3 WorldClass Functions 4 WorldClass Classes 5 Thing 6 Class Reference 7 Senses 8 Advanced WorldClass Techniques 9 Actors Appendix A Playtesting commands Appendix B Where to now Preface =============================================================================== About WorldClass WorldClass is an entire replacement for TADS' default parsing/adventuring system contained within ADV.T. Games which used ADV.T can be changed to run under WorldClass with modification, and receive immediate benefits of the system. WorldClass implements a full sense environment. Objects can now have sounds, smells, tastes, and touch implementations to add realism to your games. Senses can be made to occur in room descriptions, and can even be communicated between rooms and objects. Ever wanted to make a birdcage that whose contents can be examined, smelt, listened to, but not tasted or taken? The Holder class supports all of this, and more. Or perhaps the implementation of more lifelike NPCs to interact with the player? Actor control has been improved, allowing for a rich control of actor commands, as well as a more English-like response to actions carried out by actors. New classes are added to replace the older ADV classes for more efficient handling, and a greater life-like control and handling for unusual commands which the player might input. Added to this are other immediate benefits, such as alleviation of bugs which occurred in TADS 2.1 (and I am unsure whether they still exist in the recently released TADS 2.2), as well as a more efficient grammatical system. On the other hand, using WorldClass will slow down the TADS runtime system (TR) somewhat, due to the more advanced handling of the system ( compare WorldClass's 269Kb for version 1.1.8 [the current WorldClass version as of 11-Nov-94] of code to ADV's 122Kb for TADS 2.2). The slowdown was not great on my 486DX2-66 machine, but on slower computers the greater time may be noticeable. There will also be a greater time period in compilation, due to the increased code of WorldClass which must be recompiled each time, and all the lists which must be set up in preinit, but this should not be a great inconvenience as the final player will never need to compile it repeatedly anyway. A full copy of the WorldClass system, this manual (should you be missing anything) or any other WorldClass material, can be found at Internet location ftp.gmd.de in the /if-archive/programming/tads/worldclass directory There are now two versions of WorldClass available. Version 1.1.7 is now the official 'frozen' version of WorldClass for TADS 2.1 compatibility, and will not be updated further. All future versions, as of 1.1.8 support only the new TADS 2.2 system. ------------------------------------------------------------------------------- About Version 2.0 I have completely revamped version 1.0, which was mainly a reference of classes with explanations to help developers understand all the new classes. Version 2.0 has been rewritten in a form reminiscent of the TADS manual, and includes more examples, and a full introduction to WorldClass section, building a sample mini-adventure in WorldClass. I hope that this manual is the kind of thing Dave was hoping for. Corrections have also been made to several errors which slipped through version 1.0, and clarifications of the classes which I did not understand at the time have also been added. Other errors may have slipped in though, since a great deal of new information has been added. Feel free to report any bug fixes or ideas for improvement to me. References to actors within this manual are made in the masculine form. This is not intended to reflect limitations of actors being male, but rather the fact that I write from a male perspective. This manual is being distributed as a postscript file and a plain text version. This is the text version of the manual. It is recommended that you print out the postscript version if possible as it contains all the formatting that was not possible to add into this text version. This manual was written in PC Word for Windows 6.0 ------------------------------------------------------------------------------- About Version 2.2 I have fixed up a few bugs which were in the sample program. I was a bit rushed when I did it, so unfortunately a few errors managed to creep in. Also as of version 2.2, each chapter will be included in a separate postscript file. Thus if I make any major changes to a chapter, one could easily just print out the pertinent chapter (although admittedly, it will render the previous page numbering obsolete). In this revision, I've included a new chapter on methods of interest in the Thing class, and revised the Senses chapter. I've also split the information on actors in Advanced WorldClass Techniques into a separate chapter. Dave intends to eventually implement some actor templates into WorldClass, rendering the provided examples unnecessary in the possible future. I've also inserted some information relevant to TADS 2.2 here and there within the manual, and tidied up some of the other sections. I've also expanded the Class listing chapter in places to provide more specific information for each class. This new version should go further towards providing an easy complete reference for the WorldClass system. ------------------------------------------------------------------------------- Typographical Conventions The postscript version of the manual contains several typographical conversions which could not be ported over into text. ------------------------------------------------------------------------------- Acknowledgments Thanks are given to David Baggett, who helped me out with some of the finer points of his WorldClass system, and gave me positive encouragement. Without him, this manual for his excellent piece of software would not have been as complete as it is. Chapter 1 =============================================================================== A Sample Game This chapter introduces some of the concepts behind writing a WorldClass program by going through the steps of creating a small sample game under WorldClass. While being very simple, the game is a complete, fully-working WorldClass program. To show differences between WorldClass and standard TADS, we will start off by constructing a WorldClass version of the simple game presented in Chapter 1 of the TADS 2.0 manual before adding new things onto it. This will hopefully give you a general overview, and help in later sections to know how various classes can fit into a game. The game will be broken up into sections, each adding on more to the game as we go along. You should already be familiar with the process of entering TADS scripts into a word processor and saving in ASCII format for compilation by TC. In general, modifications will be made using the modify keyword. A full version of the sample game can be found in SAMPLE.T ------------------------------------------------------------------------------- A Very Simple Game Let's start off by defining the two rooms in which the mini-adventure is comprised, and giving a basic definition for the player. #include /* read in the WorldClass system */ path: Outside /* Note the use of Outside rather than Room */ sdesc = "Outside cave" ldesc = "You're standing in the bright sunlight just outside of a large, dark, foreboding cave, which lies to the north." goNorth = cave /* Note that go is used now */ ; cave: Room /* Note that the Room class is capitalised */ sdesc = "Cave" ldesc = "You're inside a dark and musty cave. Sunlight pours in from a passage to the south." goSouth = path ; Me: Player /* Create a player controlled actor */ location = path /* Give it an inital loaction */ locationtype = 'in' /* the player will be "in" the path room */ position = 'standing' /* the player will be standing */ ; Lets now walk through the basic changes that WorldClass makes. The #include line now uses #include , instead of referencing ADV.T and STD.T. WorldClass is a complete replacement for the two standard TADS include files, and games which use WorldClass should not include these files. WorldClass does not have a separate file for final polishing up (as STD.T has been defined for). All changes to the system behaviour will have to be done by replace and/or modify. WorldClass also renames many of the classes. In addition to this, it capitalises the first letter of all class names, and removes capitals from within the classes. Thus the room class is now Room, the item class is now Item, and the actorDesc is actordesc. Later sections will provide details on just what classes have been renamed, and which classes provide equivalent functionality. Outside was used instead of Room in the path room. For realism, three objects are defined by WorldClass: Floor, Walls, and Ceiling. They will be discussed later in the manual, but briefly the Outside class defines that there will be no walls or ceiling present. Thus if the player types 'look at ceiling' whilst on the path, the response will be that there is no ceiling. You will also notice from the script that no startroom object is provided. The reason for this is that WorldClass does not look for a default room, but rather requires that you specify a starting room location explicitly when you instantiate the Me actor. Me is the actor which you control, and must be defined in all WorldClass games. Me uses the Player class, which is the rough equivalent of ADV's basicMe. In addition to specifying a starting location, you may also specify a location type and position. Normally, you can leave the location type (locationtype) as it's default 'in' (unless you want the player to start under, on, or behind an object), and leave the players position (held in the position field) to the default of 'standing', unless you wish the player to start off 'sitting' or 'lying'. So if you wanted the player to have just woken up in bed, you would set the location to the bed, locationtype to 'on' and position to 'lying'. Directions are now given using the go, as in goNorth, goSouth, goNortheast, goUp, etc. As with the standard verb handling, verGo support is also given. Thus, you could include a verGoSouth method in the path room definition as follows: verGoSouth(actor) = { "Adventure beckons; you have no wish to leave."; } This would take effect if you tried to walk south on the path, preventing you from leaving the mini-game. Since an actor parameter is provided, you can easily define certain exits which are only available to certain actors if you wish. ------------------------------------------------------------------------------- Adding Items to the Game Now we can make the game a little bit more interesting by adding some objects that the player can manipulate, rather than just leaving him to walk between the two rooms. We'll add a solid gold skull, and a pedestal which the skull can be put upon. pedestal: Fixture, Surface noun = 'pedestal' heredesc = { "A pedestal stands in the center of the cave."; } sdesc = "pedestal" ldesc = "A large bone-white pedestal rests in the cave center. It provides no clues as to it's original purpose." location = cave ; goldSkull: Item noun = 'skull' 'head' adjective = 'gold' sdesc = "skull" ldesc = "The skull is of the purest gold, and sparkles in that special way that only gold sparkles like." location = pedestal locationtype = 'on' ; Here two objects have been defined, pedestal and goldSkull. The pedestal belongs to two classes, Fixture and Surface. Fixture defines the object to be a fixture type. Fixtures have the special property that rather than having their object printed along with all the other objects a room contains, it prints it's own custom message, printed out in the heredesc property, as shown above. The Surface class is similar to ADV's surface class. It defines that the object can have things placed on it. The skull belongs to the Item class, which enables it to be taken and manipulated, just as in the old Item class. It specifies that it's location will be the pedestal. Note that we also have to set the locationtype to 'on'. Otherwise the object would take the default location type of being 'in' the pedestal, which we do not want. Any object which is to be 'on', 'under', or 'behind' will have to set locationtype to that string. ------------------------------------------------------------------------------- Making the Items Do Something The game as it is is rather bland, so lets make a small puzzle which is close to that in the TADS manual. We'll assume that whoever left it there rigged up the pedestal that a trap will go off if the weight on the pedestal falls below a value of 3 units. This can only occur when an object is taken from the pedestal, so we can concentrate our checking there. We'll implement a doTake method using the weight concept, rather than just checking for the presence of a rock, as is shown in the TADS manual. You are probably aware that every object can have a weight property value, which specifies how heavy the object is, and a bulk property, which specifies how big, or bulky, an object is. Containers have an accompanying maxweight and maxbulk, which specifies how much weight and bulkiness the object can take. We'll use this feature to implement a weight trap on the pedestal. For the purposes of this simple game, we won't need to give the pedestal and maximums, but we will need to redefine all our items to have a definite weight. For speed, lets just redefine the Item class to give every item a default weight of 3 units: modify Item weight = 3 ; Now every Item derivative will have a set weight. Keep in mind, however, that the default maxweight defined in Player (which is used to derive Me), is only 20, so in full adventures you may wish to increase the size when you instantiate Me. Now comes the interesting part; how to form a trap on the pedestal which will kill the player if they take any item from the pedestal which causes the sum of the weights on the pedestal to fall below 3 (for this small adventure no objects). We'll do this by modifying the doTake method of Item to check if the object is contained by the pedestal, and if so check to see if the weight removed from the pedestal would cause it to fall below 3: modify Item doTake( actor ) = { if ( ( self.location = pedestal ) and ( pedestal.contentsweight( actor, ['on'] ) - self.weight ) < 3 ) { "\bAs you lift the << self.subjsdesc >>, a volley of poisonous arrows is shot from the walls! You try to dodge the arrows, but they take you by surpise!"; die(); } pass doTake; } ; Note the usage of subjsdesc instead of sdesc. WorldClass provides a slew of grammatical methods to enable correct grammar in responses, as well as handling reflexive commands. These are divided into two groups, the subj for the subject in a command, and obj, for the object of commands. See Chapter 8 for more details. It is also important to note that this example was player specific. In a game where several actors could be controlled by the actor, formatting strings would need to be applied, and an alternative method to die() provided if you wanted the actor to die, rather than the player. See Chapter 8 for more information. This modification makes use of a useful Holder (which defines containers) method, contentsweight, which gets passed in the actor doing the action and a list comprising of the location types to add into the weight total. In this case, we just want the total weight of all those objects 'on' the pedestal. After subtracting the object's weight, we see if it falls below 3. If it does, we perform the killing, or otherwise simply pass on to the inherited code. We now have all the handling necessary to implement the trap, except for one thing: another item which can be placed onto the pedestal, thus putting the weight over the threshold of the trigger. Thus shall we define a small rock on the cave floor. [Note: Although it was just said 'on the cave floor', the rock will be contained 'in the cave', so we just stick with the default locationtype of 'in]: smallRock: Item sdesc = "small rock" ldesc = "It's a rock; sedimentary to be precise. It's smooth and rounded as if it has been in a river for a long period of time." noun = 'rock' adjective = 'small' location = cave ; We now have an object to keep the pedestal weighed down whilst we take the skull. Since we placed the code in the Item.doTake method, the trap will function for all objects placed upon it. Thus we can not pick up the rock again without placing the skull back on the pedestal. ------------------------------------------------------------------------------- Implementing Interaction So far we have implemented a small puzzle whereby you can get the skull, but it has been, in the basic tradition of Adventure, strictly an object manipulation exercise. What every good adventure game should have is some form of interaction between the player and other people, monsters, toasters, or pink furry little creatures from Alpha Centauri. Lets implement a secondary puzzle. An immovable pile of bones lies outside the cave, and when the player leaves the cave with the skull, the bones will form into a skeleton who'll want the skull, and be able to accept it from the player, striding off into the sunset. Firstly, we'll implement a basic skeleton object: bones: Fixture sdesc = "bones" ldesc = "A large jumbled pile of bones, the remains of some poor adventuer no doubt, lie bleaching in the sun." heredesc = { "A large pile of bones lie bleaching in the sun."; } isplural = true noun = 'bones' 'skeleton' location = path ; This object will act as a backdrop for the time before the player brings the skull out. This is a useful way to construct actors which will initially be inactive, and not obvious as actors. You wouldn't want the player to try interacting with the pile of bones, for example, and have a response which necessitated an alive actor capable of interacting with you to be valid. Now we need to create an actor for the skeleton, who will initially not be present. Below is a basic definition for the skeleton, who will be wanting the skull: skeleton: Actor sdesc = "skeleton" ldesc = "A large skeleton, minus a skull, stands before you, arm outstretched and waiting, as if for you to give it something." noun = 'skeleton' actordesc = { self.ldesc; } ; Now of course, we need some way to swap the bones with the skeleton when you bring out the skull. This is most easily done by modifying the enter method, which is analogous to the enterRoom method of ADV, printing out any text when the player enters the room and making the contents known to the actor. The following modification to the path room causes the swap when you enter the outside path room with the skull. modify path enter(actor) = { if ( self.cansee (goldSkull, goldSkull.locationtype, true) and bones.location <> nil ) { P(); I(); "As you arrive, the bones suddenly start jiggling with increasing speed until they suddenly rise and form themselves into a skeleton, which is conspicuously missing it's skull."; P(); bones.movein(nil); skeleton.movein(path); } pass enter; } ; Here we encounter another of the different names WorldClass uses. Rather than EnterRoom, WorldClass calls the method enter when a location is entered. You will also note that moveInto was not used. This is because under WorldClass it also takes in the location type to move the object to (ie 'in', 'on', etc.). It is easier to use the methods movein, moveon, moveunder, and movebehind, which accurately match the parameters of ADV's moveInto. Also you will notice the use of cansee. cansee is one of the functions associated with the senses mechanism defined in the Sensor class, and determines if a line of sight would be available between itself and the passed object. You may wonder why we don't just check the skull's location to see if it is in the Me player. The reason is that the skull might conceivably be nested in another container which still permitted sight (such as 'on' a plate) or in a container which didn't permit sight (such as, to borrow from the WorldClass test game, a closed thermos). cansee takes care of recursively checking visibility and working out if they have connected visibility. For this simple mini-adventure, we didn't have any such contains, so a method such as isin (which determines if an object is in a given object; such as the skull in the path room) could be used. But the principle of sight should not be ignored, especially for a realistic definition of an actor's actions. You will also notice that the bones.location is checked to see if the bones are still in the room. Since the bones are moved out when the skeleton is moved in, this effectively insures that the skeleton only appears once. The functions P() and I() are used to print a paragraph separator and paragraph indentation respectively, depending on their enable state. We now have everything except handling for giving the skull to the skeleton. Let's add that in now. modify skeleton ioGiveto(actor, dobj) = { if (dobj = goldSkull) { "The skeleton accepts your gift of the skull, and after attaching it in some manner to it's neck, strides off into the sunset."; skeleton.movein( nil ); goldSkull.movein( nil ); } } ; And that's it. In this chapter we've gone through the basics of creating a small mini-game under WorldClass. In later sections, we'll explore all of the classes defined in WorldClass. You should also note that included with the WorldClass library is another mini-adventure, TEST.T, which demonstrates many of the new classes in WorldClass; you may find it useful to refer to that for a demonstration on how to use them. As an added exercise, try adding a leave method which causes the skeleton to collapse again if you leave with the skull. Hint: you can use goldSkull.isin(actor) to determine whether the actor leaving the room has the skull. Chapter 2 =============================================================================== Making the Change The previous chapter introduced writing a WorldClass adventure game, with some references to the new features WorldClass offers to the adventure game programmer. This chapter introduces aspects what is required of a WorldClass game, and what work will be necessary to convert your game from ADV. ------------------------------------------------------------------------------- Required Objects and Functions WorldClass requires several objects and functions to be defined in a game. This allows a greater control of these objects, but like ADV, the names are fixed by the system. The required objects are listed below: ------------------------------------------------------------------------------- Me Me: The player's actor. This is the actor defaulted to when no actor is specified in a player command. WorldClass does not make much differences between the player's actor and other actors apart from this. The class Player is provided which provides, like ADV's basicMe, reasonable modifications to the Actor class for the Me actor. This includes, in main, modification of many of the properties which print messages referring to the actor (eg. sdesc) to print reflexive messages (such as "you", "yourself", etc.) In your instantiation of Me using the Player class, you should also provide a location, a locationtype (from 'in', 'on', 'under', 'behind'), and a position (from 'standing', 'sitting', 'lying'). The locationtype and position are optional if the player is start in a room, standing. ------------------------------------------------------------------------------- userinit userinit(): The WorldClass system requires the usage of the init() function to perform the initialisation of daemons for the turn counter, and actor starvation, dehydration, and lack-of-sleep checks. Under no circumstance should preinit() be overridden, or your game will not work. Instead, use userinit() to do any init code that your game needs to perform. WorldClass provides a default userinit which prints the introduction if the game is not restarting, and moves the player into the first location, depending on Me's location, locationtype, and position. You should copy the userinit function from the WorldClass program and add it into your replacement userinit function, when you need to do so. ------------------------------------------------------------------------------- userpreinit userpreinit(): The WorldClass system requires the usage of the preinit() function to preform the creation of global lists and the proper setting up of object hierarchies. Under no circumstance should preinit() be overridden, or your game will not work. Instead, use userpreinit() to do any preinit code that your game needs to perform. WorldClass provides a default empty userpreinit(), so you will need to replace it with your own code ------------------------------------------------------------------------------- Updating ADV games to WorldClass To convert an existing game to run under WorldClass will require some changes to be made to your game. This sections details exactly what things you will be required to change to have your game merely run under WorldClass. ------------------------------------------------------------------------------- Change your preinit and init functions. This is a requirement of any TADS adventure which makes use of a preinit or init function. WorldClass defines its own routines which must not be overridden. To enable games to have their own init and preinit routines, at the end of preinit a procedure named userpreinit is called, and at the end of init a procedure named userinit. WorldClass provides skeletons for both preinit and userinit. You should therefore use the replace command to make your own replacement function as necessary. The userinit function contains some code which is necessary to the start of your program, so you will probably wish to copy the default userinit into the replacement userinit you define. ------------------------------------------------------------------------------- Preserve the preparse() function The preparse() function is used for the processing of entered lists. If your game needs to use preparse() for special functioning, you should copy the existing preparse() to the start of your replacement function and add your code after it. ------------------------------------------------------------------------------- Setting Me as an instance of Player Since WorldClass games do not use STD.T, the programmer will have to manually set the Me as an instance of the Player class, as in: Me: Player; WorldClass does not rely on the "Me" object much, making few distinctions between the Player and other actors, except for directing commands with no actor specified to the Me actor. You shouldn't use Player for anything other than the instantiation of the Me actor. When you instantiate Me, you should provide a location for the player to start in, and optionally an accompanying locationtype and position. ------------------------------------------------------------------------------- Converting Classes WorldClass has redefined many of game objects which were used in ADV.T, so you will need to redefine the classes used by your objects. The following table shows the classes which have changed, and the name of the WorldClass replacement class. ADV WorldClass ADV WorldClass hider unsupported fixeditem Decoration, Part, behindHider Qfront Unimportant, Fixture underHider Qover basicMe Player searchHider Qsurface buttonitem Button fooditem Edible dialItem Dial chairitem Chair or Stool switchItem Switch beditem Bed doorway Door keyedLockable Lockable lockableDoorway Use: Lockable, Door lockable Lockable (1) keyItem Key vehicle nestedroom (2) transparentItem Transparent basicNumObj Number movableActor ItemActor basicStrObj String clothingItem Clothing floatingItem Floating distantItem Distant (1) lockable in ADV.T could be used for an object which could be locked/unlocked. WorldClass's Lockable differs in that it requires a key or otherwise the object will be unlockable [although the Door class does have the lock/unlock when mykey is nil]. If you want a special purpose container which can be locked/unlocked [such as a jack-in-the-box with a latch, for example], you'll need to make your own derivate of Openable with lock/unlock handling and modify 'open' to check the lock status. (2) vehicle is currently unimplemented by WorldClass, so a new class derived from nestedroom will have to be created, incorporating board/unboard, enter exit, etc. Future versions of WorldClass are likely to have a vehicle class added. Also, all those classes not mentioned in the table which begin with a lowercase letter have now been capitalised, and uppercase during the names have been removed. Since TADS is case-sensitive, all object definitions in your game will need to be changed (such as changing 'item' to 'Item' and any verDoAskAbout to VerDoAskabout [note that no verb has its second word capitalised in the verb definitions]). If in doubt, or if a method you use is unrecognised by the compiler system, you can always check the class reference to get particulars on the specific class under WorldClass. ------------------------------------------------------------------------------- Converting Function calls Finally, there are some functions which have been renamed under WorldClass or, in the case of TADS internal functions, new functions provided to interface better to the internal functions. The table below shows the function name and replacement function which WorldClass provides. ADV WorldClass rand rnd (1) or rndchance outhide Outhide (1) scoreRank score_and_rank preinit userpreinit (2) init userinit (2) (1) the indicated ADV function is an internal TADS function, but the equivalent WorldClass function should be used instead, as it provides a better interface to the old function. (2) WorldClass's init and preinit functions automatically call these. Chapter 3 =============================================================================== WorldClass Functions This chapter is intended to introduce the general functions WorldClass provides, and a discussion on how to modify some of the not-so-general functions to provide specific behaviour. ------------------------------------------------------------------------------- init and preinit As the section detailing converting existing games to WorldClass documents, WorldClass requires exclusive use of these two functions for setting up information for correct functionality of the game. As the WorldClass source file clearly indicates: DON'T REPLACE OR CHANGE PREINIT! DON'T REPLACE OR CHANGE INIT! Instead, two functions userpreinit and userinit are supplied, which are called at the end of the respective function, to set up information which the game programmer would have placed in init/preinit. In the current version of WorldClass shells of these two functions are provided, so you will need to replace them. userpreinit currently has an empty body, so you can simply replace it (using replace of course) with the preinit code you wish the compiler to preform. userinit, on the other hand, contains some code which should be preserved if you replace the userinit. Currently, it: 1) prints the introduction if the game is not being restarted 2) sets global.lastactor to Me. This must be explicitly done, since no command has yet been entered (this being normally set during verb disambiguation). This is necessary for step 3 to work properly. 3) Does: Me.location.enter(me) Which basically causes the preset location which Me should start in to be described, and the contents made known to the player. Note that there is no setting of Me.location to startroom. WorldClass no longer uses startroom as a default room for starting the game in. You must explicitly declare in your Me instantiation from Player the following: starting location (eg. location = bed) location type (eg. locationtype = on) position (eg. position = 'lying') where the location type can be ('on', 'in', 'under', 'behind'), and the position can be ('standing', 'lying', 'sitting'). All three statements should be included at the end of your userinit code. ------------------------------------------------------------------------------- Footnotes The note() function in WorldClass implements a convenient footnote system. For those unfamiliar with the term in interactive fiction, a footnote can appear in any description, represented as a highlighted/bracketed number. Typing 'FOOTNOTE number' or 'NOTE number' prints out the footnote associated with that number. Footnotes generally contain coincidental information or comments. In most IF game systems, a footnote only becomes available once it's reference number has been printed once (although The Hitchhiker's Guide is a good counterexample, with it's FOOTNOTE 11). To implement a footnote, simply make a call to the function note(), in the exact position in a description where you wish the footnote notice to appear, passing it an object. The object (which will normally be self), should contain a footnote method to print out the text. note() will automatically assign a footnote number to the object in order which the footnotes appear in the game. Henceforth uses it even when note is called again with the same object. If a complicated object has more than one footnote to be associated with it, simply define some dummy objects with only a footnote method, which can be passed to note() Should you wish to predefine footnotes at the start of the game to allow them to be accessible, even before the actual footnote appears (such as in the Hitchhiker's Guide), WorldClass allows you to implicitly set a footnote number by setting footnum in the object which contains the footnote to the desired footnote number. Then you can later call note() when the footnote number is to be printed, and it will automatically print the predefined number. WorldClass will also automatically skip any such 'used' footnote numbers should you call note() later to create new footnotes (ie. passing objects which have no predefined footnote number). ------------------------------------------------------------------------------- versioninfo() WorldClass now uses a function to display version information, whereas ADV merely defined the versionVerb to print the sdesc of the object version. versioninfo() is also called by the intro(), just as STD.T's init() function printed the version.sdesc as well. The versioninfo() checks to see, for backward compatibility with ADV, if a version.sdesc exists, and if so prints its. Otherwise, it prints a generic version message, which includes the copyright information regarding WorldClass. It finally, regardless of the presence of version.sdesc, calls classlibrary.info to print the message which details the major, minor, and patchlevel version of WorldClass which was used. For your own games, you have two choices then for making version information: 1) Declare a version.sdesc message which contains the information. WorldClass already defines a dummy version object, so you will need to use replace or modify. Remember too, that the WorldClass version message from classlibrary will be printed after it. 2) Replace the versioninfo() function with your own, which prints the version information for your product. Under the terms of the licensing agreement, you must include the following lines line in your version information text: This product includes portions of WorldClass, a TADS class library developed by David M. Baggett for (ADVENTIONS). WorldClass is Copyright (C) 1994 by David M. Baggett. following it with a call to classlibrary.info to print the current WorldClass version. ------------------------------------------------------------------------------- Random Numbers WorldClass provides two functions for providing games with random numbers. rnd(n) is passed in an upper limit, and returns a random number between 1 to n inclusive. rndchance(n) is passed in a percentage value, and returns true if a random n% chance probability is satisfied. These functions test the value of global.nondeterministic to see if it is set. If it is, the functions behave normally, but if set to nil, the rnd() function always returns 1, and rndchance() always returns true. This allows for easy testing of games without random occurrences happening. A playtesting verb 'deterministic' is provided which sets global.nondeterministic to nil to provide easy regression testing. ------------------------------------------------------------------------------- Output hiding WorldClass provides a function Outhide(state), which can be used to hide output and determine whether anything would have been output. Pass it a true to enable output hiding: Outhide( true ); Then call it with a nil to reenable text output, as in: result := Outhide( nil ); although the assignment is optional. Outhide returns, when nil is passed to it, true if any text would have been output between it and the last Outhide( true ). This can therefore be used for such things as checking whether a ver will print messages without having the messages displayed. Under TADS 2.2, there is a new function outcapture(state) which correctly works in nesting situations. In general, it is probably easier to use this function when you only need to test if a method will produce output, and outcapture(state) when you wish to examining the text generated, or print the text if you wish the text to be printed. ------------------------------------------------------------------------------- Ending the game Several functions are provided for use in game endings. The first is end(), which causes the game to end without explanation. It is called by die() after printing that you have died, but it can also be called during demos when the player has finished the demo section and you want the game to end. The die() function is provided to handle the death of an actor. Although it is has theoretical handling for the deaths of multiple actors, by not using Me directly, it should only be used when you wish the player to be killed. After printing the death message, via the actor's diemessage property, it then calls end() to end the game, and force a restore/restart/undo/quit Currently, the starvationcheck, dehydrationcheck, and sleepcheck methods in the Player definition call this when the player should die. For games where multiple actors are in threat of dying you will have to make a general actor method for moving the actor into nil, and dynamically creating a 'dead body' object to replace the actor, adding on any grammar details specific to the killed actor (such as 'female' to female corpses). The last function of interest, terminate(), is called just as the run-time system halts, and should be used to print any good-bye messages as the player returns to the system. ------------------------------------------------------------------------------- Game Saving WorldClass, like ADV, supports the standard 'SAVE' command, which can optionally take in a double-quoted string of the filename to save as (following the file naming conventions of the platform used on). In addition to this, an extra function called warnsave(). warnsave() was written to provide a function which could be called at the commencement of exceptionally dangerous parts of a game. It checks to see if the player has saved his game in less than five turns, and if he hasn't, then to prompt whether he wishes the game to be saved, and if the reply is yes, then to save the game. ------------------------------------------------------------------------------- The Turn Counter The turn counter function turncount() is called every turn by being set up as a daemon. Firstly it calls incturn(), as is required by the TADS system for correct timing of fuses. It then adds on to the global.turns (which holds the number of turns elapsed so far) the number contained in global.turnspertick. WorldClass defines that a tick is always one command long, whilst several turns can go by for one command (tick). Thus, to define that the turn counter should go at a faster rate (such as if you are in a waiting room), you should redefine the global.turnspertick to reflect the number of turns to increment by each "turn". ------------------------------------------------------------------------------- Scores The default function for changing the score in ADV, incscore(), was to merely add on the passed amount to global.score, and then to call scoreStatus to update the score/turns. WorldClass's incscore prints out a message as the score is changed, as long as global.silentscore = nil (allowing you to hide score changes). It can recognise both the score going up and down, and correctly prints out if only a single point is being added/subtracted to score. It then updates the score and status line. All changes to scores within your program should be made with this function. ------------------------------------------------------------------------------- Other Useful functions There are also several other useful functions WorldClass provides, as listed below: maxinlist: is passed in a list of numbers and returns the largest number. mininlist: is passed in a list of numbers and returns the smallest number. I(): this should be used to indent a new paragraph instead of \t. It checks the value of global.indent, and prints the \t if is set (turned on). WorldClass provides a command 'INDENT', which toggles the state of global.indent P(): this should be used to separate paragraphs. It checks the value of global.paragraphspacing, and prints \b if it is set, or \n if it is not. Chapter 4 =============================================================================== WorldClass Classes In this chapter some of the classes which can be grouped together under general categories will be introduced. For a more complete examination of each individual class, see Chapter 6: Class Reference. ------------------------------------------------------------------------------- Furniture The follow list shows the general furniture types from which objects can be derived. Bed: Defines a bed, which the actor can sit, lie, or stand on, as well as have objects placed on it, and hidden under it. Desk: Defines a desk type, which can have things put on or in it. For many situations you may wish to define your own Desk type directly from Surface if you do not wish the desk to directly contain objects (such as if you wish to add a drawer). Chair: Defines a chair which you can sit in (or on), as well as stand on it. You can disable standing on it by setting standable to nil. Stool: Defines a stool which you can sit on (but not in), as well as stand on it. As with Chair you can turn off standing by setting standable to nil. Shelf: Defines a shelf-type surface which can hold objects on it. Ledge: Defines a nestedroom upon which you can sit, stand, lie upon, or hold objects in. ------------------------------------------------------------------------------- Containers The basic container type is defined by Holder, from which all other containing types are derived. Objects should not be derived directly from Holder, but rather from one of the classes detailed below. The Holder class defines an object which has contents, be it on, in, under, or behind. Using the defaults inherited from Thing, the maximum weight and bulk which any of the following classes can hold is mapped directly to maxweight and maxbulk. Since there will be cases where more than one container type is used in an object (such as in Desk, which uses a surface 'on' and a container 'in'), you will need to override the properties mentioned for each container type for realistic effect. By default both maxweight and maxbulk are the maximum value possible. It is also important to understand the method by which the maximum weight/bulks work in nested containers. The current version of WorldClass declares that an object's weight and bulk will only be checked against the maximum bulk and weight of it's containing object. Thus it is possible to put heavier things into a smaller container. Say you implemented a table which had a maximum weight of 10. Should you place a rock of weight twenty in a container which could hold it, but only itself had a weight of 5, the container would incorrectly be able to rest on the table. You should therefore be aware of the effects, and be careful in your programming accordingly. This inconsistency is likely to be fixed up in future WorldClass versions. Container, Qcontainer: A container is an object which contain things. It uses inmaxweight and inmaxbulk to specify the maximum weight and bulk. Surface, Qsurface: A surface is an area upon which objects can be placed. It uses onmaxweight and onmaxbulk to specify the maximum weight and bulk. Over, Qover: An over is a class which can have things placed under it (such as beds). It uses undermaxweight and undermaxbulk to specify the maximum weight and bulk. Front, Qfront: A front is an object which can have objects placed behind it (such as for example a weird statue which has a hidden doorway behind it). It uses behindmaxweight and behindmaxbulk to specify the maximum weight and bulk. There is also an additional modifier class Transparent, which specifies that the resulting container derived from it (requiring that it be first in the inheritance list) and a descendant of Holder will be transparent, enabling the contents to be seen but not touched, smelt, listened to, or tasted. An example class definition using Transparent might be: class Bottle: Transparent, Container; WorldClass objects have a locationtype which works in tandem with location. It can be 'in', 'on', 'under', or 'behind'. Thus an object placed in a Qover will have the Qover as its location and have it's locationtype set to 'under'. For objects contained in a room, they are only accessible if the object is 'in' the room. You should also note that the hiding mechanism has been confined exclusively to the containers themselves. Items therefore do not need to be declared as hiddenItem to prevent themselves being printed. You need only to derive from the correct Q class and place the items in them (using the appropriate locationtype). The iscontentslisted is the general check to see if the contents of a container should be listed, and is defaulted to true for Containers and nil for Qcontainers. There are several sense passing methods defined in holder for easy passing/ blocking redefinition of the holder object. These can be easily replaced for those holder objects which will pass only certain senses. See Chapter 7 for details on senses. And finally, the container classes use the method acceptsput, which gets passed in the actor and location type, to determine whether the actor can put an object into that particular location type. You can override this to only allow certain actors access to a container or if for some reason you need to hand-code your own container type. ------------------------------------------------------------------------------- Attachables Attachables are an entirely new concept introduced by WorldClass as an a organised way to implement objects which be connected, attached, plugged, or tied to another object. WorldClass defines two classes for this purpose, Attachable and Attachpoint. An Attachable can be attached to an appropriate Attachpoint, but not vice versa. For those objects which can be done either way (such as a hook and a piece of string from the WorldClass demo TEST.T), both objects must inherit both Attachable and Attachpoint, as follows [extract from TEST.T]: fishhook: Attachable, Attachpoint, Item Note that is important for the two classes to come before Item in the inheritance list. To set up an Attachable, you can firstly set the properties tieable or pluggable to true to allow tying and plugging. maxattachments is set to the number of things that the Attachable can be attached to. This defaults to one (as in a plug), but can be increased (say to two for a piece of string). Finally, set the property attachesto to a list of objects to which the object can be attached, and attachedto to a list of any objects (which must still be listed in attachesto) that the object is initially attached to. For the Attachpoint, you need to set maxattachments if you want more than one item to be able to attach to it at once. Then place in attachesto a list of the appropriate Attachable(s), plus a list of any initial attachments in the attachedto list. ------------------------------------------------------------------------------- The bare necessities Unlike ADV.T, in which only the player could starve, dehydrate, or collapse from lack of sleep, WorldClass implements these checks for all actors, although by default this checking is turned off. To enable checking, you will need to place your own code in the actor's starvationcheck, dehydrationcheck, and sleepcheck. WorldClass will then automatically initialise the checks as daemons and call them every turn. An actor's initial levels for hunger, thirst, and sleep are set to the values contained in mealtime, drinktime, and sleeptime. Two classes are provided to deal with hunger and thirst: the ingestible classes Edible and Drinkable implement food and drinks. The Edible class has a property foodvalue which indicates the number of turns to be added onto the actor's turnsuntilstarvation, the number of turns until the actor starves, and is by default set to the maximum number of turns the actor can go before starving, mealtime. The same occurs with Drinkable, using drinkvalue to add onto the actor's turnsuntildehydration count, and is by default set to the actor's maximum time before dehydration, drinktime. These classes are designed primarily for the quenching of hunger / thirst, and are simple derivatives of Item. For those items which you do not wish to act in this way ( say a poisonous liquid within a bottle ), it will be easier simply to use a derivate of Item, and make your own verDoDrink and doDrink properties. Sleep requires that the actor 'sleep' when his turnsuntilsleep nears zero. The default Player sleepcheck causes the player to collapse if it reaches zero, and calls the sleep method (which is also called when 'SLEEP' is entered), to determine what happens while the player sleeps [Things could be added here, such as scattering some of the player's object if he is not in a bed]. By default, it doesn't do much. ------------------------------------------------------------------------------- Fixed item Classes There are several fixed-item type classes which WorldClass implements, each of which has different characteristics of usage, as explained below. Whilst any object directly derived from Thing is immovable, you should only need to use these classes, deriving from Decoration as a bare minimum. Decoration: is merely a exact derivative of Thing (with no modifications). Any objects intended to be just decorations can use this class. Unimportant: is used for those decoration objects which you want to be referrable to, but clearly unimportant, and not to be bothered with. An Unimportant derived objects will not allow any verbs except 'inspect' give much, all others will be responded to with a message that the object is unimportant. Distant: works in much the same way as Unimportant, except that it replies that object is too far away for all verbs which are not in a list contained in allowedverbs (by default the allowedverbs list only contains inspectVerb). Part: is used for those things which are part of another object, and go wherever it goes. Instead of specifying a location, however, you set the property partof to the object the Part is part of. Parts also contain a setpartof method used for setting the parent object when changing a part's partof property, although the partof can be manually set initially, as init will take care of setting up dependency lists. Part also returns the same location, locationtype, and isknownto status as it's parent object. Fixture: is used for objects which are to be visible, but described separately from items in room descriptions, giving their own custom description. ie: [ "An enormous mahogany desk stands in the center of the room" vs. "There is an enormous mahogany desk here." ]. This is accomplished by setting the heredesc property with an appropriate message, which will be printed in the room's description. The default heredesc property prints and one of two standard messages, depending on whether the object has been determined or not. ------------------------------------------------------------------------------- Multiple Location Classes There are two classes for objects which appear in multiple rooms at once, or over the course of the game: Everywhere and Floating. A Floating object can have, as its location, a method rather than an a parent object, and should return a location value. Floating objects are never made known to the player directly, as they are not placed in contents lists. This class is useful for such things as streams, for example, which might be needed to be present in many rooms in the game, without defining separate stream sections for each location. The Everywhere class is a specialised derivative of Floating. Any object which uses Everywhere will automatically be in every room. To limit the object to only certain rooms (such as the ceiling to only those rooms which are indoor), Everywhere provides a property roomprop. If set to nil (default), the object is in every room, but if it is set to the property address, the object will only appear in those rooms with that property it points to. What this means is that in the Ceiling object (predefined by WorldClass) for those rooms which are indoors (such as those rooms derived from Room), the property ceiling is set to true, and in the ceiling object itself, the roomprop is set to &ceiling. The Everywhere location code then automatically checks to ensure that the ceiling property is true for any room or else the object won't be present there. ------------------------------------------------------------------------------- Room Classes This section describes the room types which WorldClass makes available to the programmer. Room One of the first useful additions to Room is the addition of global additions to the status line. It checks to see if a global.statusline method exists, and if so executes it. The global.statusline should print text as deemed appropriate, and the text will be automatically added to the text of the current room name. This could be useful for display short status remarks that the game wants the player to notice quickly, or be continuously updated, such as notifying the player is poisoned, or even for printing current hit-points during a battle (assuming of course that a hit-point combat system was in use). To handle the presence of light sources, Room firstly has a property ambientlight, which is set to true to indicate no light sources are required. Two methods are provided, darken and lighten, which set ambientlight to the appropriate value, so you should use these methods for special Darkroom situations, such as turning on/off lights depending on a switch in another room. islit is used to determine if any light-source can "see" the room, thus illuminating it. Darkoom Darkroom is different from Room only in that it sets the ambientlight to nil. A darkroom will be illuminated by any object derived from Lightsource, or the Room methods lighten and darken can be used to manually set the ambientlight status. Outside Is a simple derivate of room which sets the ceiling and walls properties to nil. These are the properties which the Everywhere objects Ceiling and Walls use to determine whether to appear in that room, so thus they will not appear in rooms defined as Outside. Nestedroom A nested room is a room which is, of course, nested within another room. Nestedrooms are useful for objects which are to be enterable; the basis of which forms the bed, chair, etc. classes. WorldClass defines that full sense passing occurs between the outside Room and the Nestedroom; including Listablesenses, sight, and, if included in the reachable list, taste, touch, and smelling. This also includes nestedrooms which are inside nestedroom; senses came in all the way from the top level. However, nested nestedrooms should be avoided if possible, as it creates status lines which are too long. Nestedroom is descended from Container (indirectly through Room), but by default disables putting things into them. If a nestedroom is to behave as a container as well, the particular surface type(s) should be declared before the nestedroom in an object declaration, or the class Ledge can be used. It also provides better handling for when a Nestedroom changes location while the actor is in it, making the new room contents known to the actor. Setting up of the Nestedroom follows the same procedure as used in ADV, using the reachable list to specify reachable objects, and adds a property reachsurroundings which should be set if all objects within the room are to be accessible. ------------------------------------------------------------------------------- Memory Memory is one of the more interesting aspects of WorldClass. Memory can, in this context, be defined as those objects which an actor knows about, either inherently (ie. from the start of the game), or from coming into contact and seeing it. Every object contains two methods, isknownto and makeknownto, which each take in the actor doing the action. Each object, therefore, must maintain a list of those actors which have seen it. This list is stored in the knownto field, and is by default an empty string []. isknownto returns true if the actor knows about the object (ie. the actor is in it's isknownto list), and makeknownto makes the object known to the actor. Should you need actors to know about some objects at the start of the game, you should add the actor's name to the knownto list. By default, actors will know of every object that you know about, being flagged by the actor's knowsall property. If you do not wish this to occur, you will need to set knowsall to nil. Then explicit calls will need to be made to an object's makeknownto or makecontentsknownto, with the actor passed to them, to make those objects and/or contents known to an actor. There are, however, problems with this. See Topic in Chapter 6 for more information and a workaround. Chapter 5 =============================================================================== Thing The Thing class is the single most important class defined by WorldClass. It contains all of the code necessary for the class system to operate, and is indeed so large that it needed to be split into two sections (the second one using 'modify Thing') in order for the parser to accept it. Because most of the methods are contained in Thing, making them available to all classes, it deserves a special chapter on it's own to give a listing of the methods available for usage. This chapter provides a listing of all methods of interest within thing. It does not include the internal methods WorldClass uses. The listing of methods is organised into groups, with a preceding method index for quick reference below of those methods which are listed (and postscript page numbers removed). In the list *X* represents a location type (in, on, under, behind), *Y* represents either nothing or a listable sense (sound, smell, touch), and *Z* represents an action type (see, touch, take, smell, hear, speakto, putinto). aamessage listtastedesc acceptsput listtocuhdesc actorAction makecontentsknownto adesc makeknownto can*Z* move*X* can*Z*contents multisdesc contentsX noticable grammer methods passcan*Z* isatopic passcan*Z*across isaudable passcan*Z*in isbehind passcan*Z*out isin recursivecontents isknownto sdesc islistable setlocation islistable*X* setlocationtype ison smelldesc islistener tastedesc issmellable tdesc istakeable thedesc istouchable topcontents isunder touchdesc isvisible verb methods itemcontents *X*contentslisted*Y* ldesc *X*maxbulk listendesc *X*maxweight listlistendesc *Z*path listsmelldesc ------------------------------------------------------------------------------- Actor Methods aamessage(verb, dobj, prep, iobj) The aamessage method is a default response method for when actors are commanded. This code will probably never get called for non actors. It prints out a message that the object isn't likely to carry out your whims. In your actor definitions, you can override this to print out specific messages depending on the passed verb, dobj, prep, and iobj which represent the command that the actor was commanded to preform. actorAction(verb, dobj, prep, iobj) The actorAction method is called by the TADS run-time system when a command has been parsed. As a default, the provided definition simply passes the command information to aamessage to print out a response. This method should be used in your actors to trap any commands the actor is not to perform for the player, by printing out a suitable denial response and executing an 'exit' statement. If no exit statement is encountered, the actor will attempt to carry out that command (per the restrictions placed upon doing that action). ------------------------------------------------------------------------------- Contentairs Several methods are provided for allowing custom determination of whether the specified object will be listed in the contents list for this object in room descriptions for a given containment type and sense. Each should return true if it's given combination is valid for that object. By default, they all return true. incontentslisted(obj) incontentslistedsmell(obj) incontentslistedsound(obj) incontentslistedtouch(obj) oncontentslisted(obj) oncontentslistedsmell(obj) oncontentslistedsound(obj) oncontentslistedtouch(obj) undercontentslisted(obj) undercontentslistedsmell(obj) undercontentslistedsound(obj) undercontentslistedtouch(obj) behindcontentslisted(obj) behindcontentslistedsmell(obj) behindcontentslistedsound(obj) behindcontentslistedtouch(obj) recursivecontents(actor,loctypes) This method returns a list of the contents of the object. It is passed in the actor and a list comprising of all the top-level locationtypes to be included in the searching. For all lower levels, all location types are used. itemcontents(actor,loctypes) This method returns a list of the contents of the object. It is passed in the actor and a list comprising of all the top-level locationtypes to be included in the searching. For all lower levels, all location types are used. This method differs from recursivecontents in that it only passes out Items in the list, and only recurses through objects which are not Items. Remember that Holder objects are by default not items, but if you declare one to be an item, it will not be recursed into by this method. topcontents(actor,loctypes) This method returns a list of the top-level contents of the object for the given location types. This method does not recurse; only the objects directly contained by the object will be returned. acceptsput(actor,loctype) This is the method used by Holders to determine whether a given location type can have objects placed into it. The default response is always nil, thus preventing the placement of anything into, onto, behind, or under the object. canXcontents(obj,silent,loctype) methods This group of methods returns true if the object can sense the contents of the specified object. If loctype is nil, then a list of valid location types is returned, or nil if no location type specified is valid. The available methods are: canseecontents, cantouchcontents, cantakecontents, cansmellcontents, canhearcontents, canspeaktocontents, canputintocontents. These methods are defined primarily for derivative classes, as by default Things can't see. isX(obj) methods There are four methods provided which determine whether the specified object is in, on, under, or behind the thing. They are respectively: ison, isin, isunder, isbehind Each take in the object to check, and return true if the if the object is contained in the that manner. The exact working out is a bit tricky. We say that A is contained *in* B or if A is either directly contained *in* B or if A is *in*, *on*, *under*, or *behind* something that is directly contained *in* B. ------------------------------------------------------------------------------- Grammar Thing provides the definition for all the grammar methods available for use to print correct grammar. See Chapter 8 for a more detailed look into the use of the grammar methods. is subjsdesc objsdesc(actor) isnt subjthedesc objthedesc(actor) does subjadesc objadesc(actor) doesnt subjprodesc objprodesc(actor) has doesnthave youll youre possesivedesc reflexivedesc ------------------------------------------------------------------------------- Knowledge isknownto(actor) Returns true if the object is known to the given actor. It makes use of the knownto list to store all the actors which know about the object. In addition, all objects will know about themselves, and if the object has it's knowsall field set, the object will know about everything that the player knows about. Due to current disambiguation methods this does not get called properly. See Chapter 7 under Topic for a workaround. makeknownto(actor) This method is called to make the object known to the specified actor. It automatically checks the knowsall flag, and terminates if it is set. makecontentsknownto(actor) This method makes the contents of the object known to the specified actor. Anything that is visible, following the standard rules of visibility, becomes known to the specified actor. isatopic(obj, loctype, silent) This method checks the state of the object's istopic flag, and returns true if it is set, or otherwise prints a message that the object is not a productive topic. The default istopic value is nil. Topic sets it to true, and Item inherits from it. Thus items are legal topics. ------------------------------------------------------------------------------- Moving objects movein(obj) Moves the object into the specified object. This is somewhat similar to ADV's moveInto. moveon(obj) Moves the object onto the specified object. moveunder(obj) Moves the object under the specified object. movebehind(obj) Moves the object behind the specified object. moveto(obj, loctype) Moves the object to the specified object, using the given location type. ------------------------------------------------------------------------------- Object descriptions sdesc The default object sdesc provided prints "thing" or "things", depending on whether the object is plural. multisdesc The multisdesc method is called by the system whenever the object's name is being printed as part of a list. As in: >get all rug: The rug is too heavy for you to move it. The rug's multisdesc is called to print the object's name, rather than sdesc. The default WorldClass multisdesc merely prints out the object's sdesc highlighted. ldesc Provides a default which prints out a message that the object looks ordinary, providing correct grammar for both when the object is singular or plural. thedesc The default provided prints the object's sdesc, preceding it with a "the" if the object has not yet been determined. adesc The default provided prints the object's sdesc, preceding it with a "a" if the object has not yet been determined. tdesc The default provided for object's getting printed on the status-line, prints out the object's sdesc. ------------------------------------------------------------------------------- Sense Availability isvisible(actor) Returns true if the object is visible to the given actor. The default method calls the object's verDoInspect with output hiding to see if any objection would be raised to examining the object. If yes, then the visibility fails, and it prints out the verDoInspect message and returns nil. istouchable(actor) Returns true if the object is touchable by the given actor. The default method calls the object's verDoTouch with output hiding to see if any objection would be raised to the actor touching the object. If yes, then the touching fails, and it prints out the verDoTouch message and returns nil. istakeable(actor) Returns true if the object is takeable by the given actor. The default method calls the object's verDoTake with output hiding to see if any objection would be raised to the actor taking the object. If yes, then the takeability fails, and it prints out the verDoTake message and returns nil. issmellable(actor) Returns true if the object is smellable by the given actor. The default method calls the object's verDoSmell with output hiding to see if any objection would be raised to the actor smelling the object. If yes, then the smellability fails, and it prints out the verDoSmell message and returns nil. isaudible(actor) Returns true if the object is audible to the given actor. The default method calls the object's verDoListento with output hiding to see if any objection would be raised to the actor listening to the object. If yes, then the audibility fails, and it prints out the verDoListento message and returns nil. islistener(actor) This method is called to determine whether the object can listen to things spoken to it (ie. commands). By default, this simply returns nil to disallow the player giving commands to inanimate objects. ------------------------------------------------------------------------------- Sense Listing listlistendesc This method is called to print the sound description of the object. By default it prints that the object isn't making any noise. listsmelldesc This method is called to print the smell description of the object. By default it prints that the object doesn't smell unusual. listtastedesc This method is called to print out an object's taste status. Since there is no way for pervasive tastes, there is no accompanying Listable class. listtastedesc is called only by the default tastedesc. It by default prints that the object tastes normal. listtouchdesc This method is called to print the touch description of the object. By default it prints that the object doesn't feel unusual. islistablesmell(actor) This method is should return when an object is to actively print smells in a room description (via listsmelldesc). Only Listablesmell objects have this method called, as a list of all listable smells is set up by preinit. islistablesound(actor) This method is should return when an object is to actively print sounds in a room description (via listlistendesc). Only Listablesound objects have this method called, as a list of all listable sounds is set up by preinit. islistabletouch(actor) This method is should return when an object is to actively print a touch in room descriptions (via listtouchdesc). Only Listabletouch objects have this method called, as a list of all listable touches is set up by preinit. islistable(actor) This method returns true if the object should be listed in room and/or content listings. By default this returns nil, making the object invisible. noticable(actor) This method should return true if the object is allowed to be noticed. This is distinct from being listable in that islistable only applies when the contents listing is a room description. listendesc This method is called when the object is listened to (as in 'LISTEN TO dobj'). By default it prints the object's name and then calls listlistendesc to print the sound description of the object. smelldesc This method is called when the object is smelt (as in 'SMELL dobj'). By default it prints the object's name and then calls listsmelldesc to print the smell description of the object. tastedesc This is called when the object is tasted (as in 'TASTE dobj'). By default it prints the object's name and then calls listtastedesc to print the taste description of the object. touchdesc This is called when the object is touched (as in 'TOUCH dobj'). By default it prints the object's name and then calls listtouchdesc to print the touch description of the object. ------------------------------------------------------------------------------- Sense Passing Several methods are provided for determining whether or not the object allows the various senses to pass into, out of, and through it. The passcanX methods are the default methods to which both the passcanXin, passcanXout, and passcanXacross map to. Using these methods, you can defining default sense passing for an object. The available methods are passcansee passcantouch passcantake passcansmell passcanhear passcanspeakto Each of these methods take in (actor, obj, loctype), where obj is the object trying to sense in/out/through the object, silent is set if the check is to be done silently, and loctype is the location type being used (or nil all location-types are to be checked for validity, in which case it should return true if any location-type is allowed). The passcanXin methods can be used to determine whether the specified object can sense into the object. The available passcanXin methods are: passcanseein passcantouchin passcantakein passcansmellin passcanhearin passcanspeaktoin As with the passcanX methods, they take in (actor, obj, loctype) for determining sensing in. The passcanXout methods can be used to determine whether the specified object can sense out of the object. The available passcanXout methods are: passcanseeout passcantouchout passcantakeout passcansmellout passcanhearout passcanspeaktoout As with the passcanX methods, they take in (actor, obj, loctype) for determining sensing out. The passcanXacross methods are used to determine whether the specified object contained in the thing can sense other object's contained in the thing (ex: the player seeing the objects in a room). The following methods are used: passcanseeacross passcantouchacross passcantakeacross passcansmellacross passcanhearacross passcanspeaktoacross The passcanXacross methods take in four parameters (actor, obj, selfloctype, objloctype). The selfloctype and objloctype are not necessarily the same as self.locationtype and obj.locationtype -- there may be other objects between the thing and the obj, and the lowest common ancestor between them. ------------------------------------------------------------------------------- Sense Paths seepath(obj,loctype,silent) Determines whether a given line of sight exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account the visibility of the object. The Sensor method cansee is more general purpose for line-of-sight checks touchpath(obj,loctype,silent) Determines whether a clear touch path exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account whether the object is actually touchable. The Sensor method cantouch is more general purpose for touchability checks takepath(obj,loctype,silent) Determines whether a clear take path exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account whether the object is actually takeable. The Sensor method cantake is more general purpose for takeability checks smellpath(obj,loctype,silent) Determines whether a given line of smelling exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account whether the object is actually smellable. The Sensor method cansmell is more general purpose for smellability checks hearpath(obj,loctype,silent) Determines whether a given line of hearing exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account whether the object is actually hearable. The Sensor method canhear is more general purpose for line-of-sight checks speaktopath(obj,loctype,silent) Determines whether a given line of speaking exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account the listening ability of the object (via islistener) of the object. The Sensor method canspeakto is more general purpose for speakability checks putintopath(obj,loctype,silent) Determines whether a given line of placement exists between the object and the specified one (where silent should be set to true if no messages are to be displayed). This does not take into account whether the object actually accepts objects. The Sensor method canputinto is more general purpose for placability checks cansee(obj,loctype,silent) This provides a default non-sight message. Use Sensor if you want sensing capabilities. cantouch(obj,loctype,silent) This provides a default non-touch message. Use Sensor if you want sensing capabilities. cantake(obj,loctype,silent) This provides a default non-take message. Use Sensor if you want sensing capabilities. cansmell(obj,loctype,silent) This provides a default non-smell message. Use Sensor if you want sensing capabilities. canhear(obj,loctype,silent) This provides a default non-hear message. Use Sensor if you want sensing capabilities. canspeakto(obj,loctype,silent) This provides a default non-speaking message. Use Sensor if you want speaking capabilities. canputinto(obj,loctype,silent) This provides a default non put-into message. Use Sensor if you want sensing capabilities. ------------------------------------------------------------------------------- Setting the Location setlocation(loc) This can be called to set the location of an object. It checks the current location value of the object, and only sets it to the new location value if the object's location is not a method (ie. code). setlocationtype(loctype) This can be called to set the location-type of an object. It checks the current locationtype value of the object, and only sets it to the new location-type specified if the object's locationtype is not a method. It also sets the locationtypenum field, which is the internal WorldClass numerical representation of the location-type. If for some reason, you need to explicitly change locationtype at run-time, you should use this method. ------------------------------------------------------------------------------- Verb methods Thing contains a listing of all the default verbs defined under WorldClass. By default, most verbs fail on a Thing. A method is provided, verDoX(actor, v), which takes in the actor doing the action, and the verb used, and prints a cant-do message using the verb's description. Almost all verb ver methods call this method. If you add in your own verbs, you should provide a default modification to Thing to call verDoX, to disallow your verb on anything you do not explicitly modify. The verbs which WorldClass defines are, using the verb name defined and ignoring equivalent words (ie. the inspect has "examine" and "x" as equivalent verbs): With only direct object passed: board, break, clean, climb, close, detach, drink, drop, eat, enter, exit, fasten, flip, follow, get in, get off, get on, get out, get out from behind, get out from under, get under, get behind, hit, inspect, kick, lie in, lie on, listen to, look behind, look in, look on, look through, look under, move, move N, move E, move S, move W, move NE, move NW, move SE, move SW, open, poke, pull, push, read, screw, search*, sit in, sit on, smell*, stand, take, taste*, tie to, touch*, turn, turn off, turn on, unfasten, unplug, untie, unscrew, unwear, wear. With an indirect object passed: ask about, ask for, attach to, attack with, clean with, detach from, dig with, give to, hit with, lock with, move with, plug in, put in, put on, put under, put behind, put through, screw with, shoot at, shoot with, show to, take from, take off, take out, take under, take behind, tell about, tell to, throw at, throw behind, throw through, throw to, throw under, touch with, turn to, type on, unlock with, unplug from, untie from, unscrew with. ------------------------------------------------------------------------------- Weight and Bulk contentsX(actor, valueprop, loctypes) Returns the total weight or bulk of the contents of the object. It is passed in the actor, valueprop (which should be a property pointer to either contentsweight or contentsbulk), and loctypes, which should be a list of the top-level location types to measure (or nil for all location types). weight and bulk maximum values Each location-type can have it's only specific maximum weight and bulk associated to it. By default, however, they all map onto the object's maxweight and maxbulk. The following can thus be overridden to provide specific limits for the appropriate location type. inmaxbulk onmaxbulk undermaxbulk behindmaxbulk inmaxweight onmaxweight undermaxweight behindmaxweight Chapter 6 =============================================================================== Class Reference This chapter gives a comprehensive guide, where possible, to each of the classes provided by WorldClass. Basic fields for classes, such as ldesc, sdesc, location, etc., will not be included but assumed. ------------------------------------------------------------------------------- Inheritance Diagram The following tree shows the class inheritance structure under WorldClass. Classes which have multiple direct ancestors are connected in the tree to their first defined ancestor parent, and use the notation (x/y), where x is the precedence of the class's ancestor (where 1 has the greatest precedence), and y is the total number of classes which that class inherits from. The names enclosed in stars represent special purpose objects defined by WorldClass rather than a class. object -+ +- *classlibrary* +- *global* +- Prep +- Thing (continued below) +- Verb --+ | +- Systemverb | +- Debugverb | +- Soloverb -- Travelverb +- *version* Thing -+ +- Button +- Decoration +- Dial +- Distant +- Female +- Fixture (2/2) +- Floating --+ | +- Everywhere --+ | | +- *Ceiling* | | +- Floor (1/2) -- *Ground* | | +- *Walls* | +- Part +- Holder --+ | +- Container --+ | | +- Actor (4/4) | | +- Connection | | +- Desk (2/2) | | +- Openable | | +- Qcontainer | | +- Room --+ | | +- Darkroom | | +- Nestedroom --+ | | | +- Chair | | | +- Ledge (2/2) | | | +- Stool | | +- Outside | +- Front -- Qfront | +- Over -- Qover --+ | | +- Table (2/2) | | +- Bed (2/2) | +- Surface --+ | +- Desk (1/2) | +- Ledge (1/2) --+ | | +- Bed (1/2) | | +- Floor (2/2) | | | +- Qsurface | +- Shelf | +- Table (1/2) +- Item (3/3) +- Key +- Knowntopic +- Listable --+ | +- Fixture (1/2) -- Actor (1/4) --+ | | +- Follower | | +- Itemactor (2/2) | | +- Player | +- Item (1/3) --+ | +- Attachable | +- Attachpoint | +- Clothing | +- Drinkable | +- Edible | +- Itemactor (1/2) | +- Lightsource +- Listablesmell +- Listablesound +- Listabletouch +- Listobj -- listObj +- Lockable +- Male +- Me +- NIL +- Number -- numObj +- Obstacle -- Door +- Readable +- Sensor -- Actor (2/4) +- String -- strObj +- Switch +- TOP +- Topic --+ | +- Actor (3/4) | +- Item (2/3) +- Transparent +- Unimportant ------------------------------------------------------------------------------- Actor: Fixture, Sensor, Topic, Thing The Actor class defines a creature who can interact with the player within the context of the game, and can be given a pseudo-reality of life by programming actions for the actor to perform. The actor is arguably the most complex of the WorldClass classes, as it needs to be since Player is derived directly from it. By default, the starvationcheck, dehydrationcheck, and sleepcheck are set to nil, so an actor will not die. The knowsall property is also set to true, so the actor will automatically know all that you know about [see Topic for a discussion on the limitations of knowsall]. Much of the ADV.T definitions of actors pertains to WorldClass actors. Actors have maxweight and maxbulk, and the actorAction method is passed in the standard form of (verb, d-object, prep, i-object) and should either print a message and exit if the command to the player is disallowed, or simply ignore the command if it is valid, allowing WorldClass to cause the actor to carry out the command. The default method ignores all commands except the helloVerb command, in which case it cases the actor to nod his/her head at you. [You will have therefore have to modify this in any headless horseman actors you do]. Each sets the method islistener(actor) to return true, signalling that the actor can listen/hear things. They also have a speech_handler(s) method which is called when 'SAY "string"' is entered. Since by default all actors will ignore all but the Hello command, only the player will be able to SAY things; all others actors will simply ignore your command to say "string". Also a series of default responses are provided for commands like 'TAKE do FROM actor' , which by default cause the actor to be uncommunicative and not accept / give any objects, and not respond much. Making a good actor will require you to replace them (see the Actor definition in the WorldClass source to get an idea of the methods you'll need to replace). One final addition of interest is the concept of travelhooks. This information is given in the source code regarding travel hooks: Set travelhook to a list of the form [object1 &method1 object2 &method2 objectn &methodn] and the travelto method will call object1.(method1)(self, oldloc, oldloctype, newloc, newloctype) object2.(method2)(self, oldloc, oldloctype, newloc, newloctype) ... objectn.(methodn)(self, oldloc, oldloctype, newloc, newloctype) in order, where oldloc is the actor's location prior to the travel, and newloc is the actor's location after the travel. oldloctype and newloctype are the location types ('in', 'on', etc.) for the old and new locations respectively. This works properly with obstacles too. Note that these actually get called from inside the newlocation method. What this means is that whenever the actor moves into a new location, the specified object:methods are called. It is important to note that these methods are only called when the actor 'travels'. The method newlocation takes care of giving control to each object/method in turn, and newlocation is only used by the travelto method. Thus an actor going N will activate this delegation process, but merely standing up off a chair won't. This is an easy way for implementing conditions which occur when an actor moves about. For instance, you might have a remote control device tuned to a particular actor which will start beeping when the actor enters particular areas, which would have been defined using a derivate class of Room with, say, restricted = true. (in much the same way as Outside is defined). ------------------------------------------------------------------------------- Attachable: Item The Attachable class defines objects which can be attached to other objects. To set up an Attachable object derivative, you can firstly set the properties tieable or pluggable to true if you wish to allow tying and plugging. maxattachments is set to the number of things that the Attachable can be attached to. This defaults to one (as in a plug), but can be increased (say to two for a piece of string). Finally, set the property attachesto to a list of objects to which the object can be attached. The objects in this list must all be derivatives of Attachpoint. You can also set the attachedto property to a list of the starting attached Attachpoints if you wish. ------------------------------------------------------------------------------- Attachpoint: Item The Attachpoint class defines objects to which Attachable derived objects can attach themselves to. The only thing you need to do in the Attachpoint is to explicitly set maxattachments if you want more than one item to be able to attach itself to it at once, and have the in the attachesto list of the legal Attachable objects. You can also set the attachedto property to list a list of the starting attached Attachable objects. ------------------------------------------------------------------------------- Bed: Ledge, Qover The Bed class defines a basic bed type. An actor can sit, lie, or stand on it, and objects can be placed on it. It also acts as an over hider, that is that objects can be placed underneath it and will not be displayed unless the bed is specifically looked under. Since Ledge has Nestedroom as an ancestor, all properties pertaining to nestedrooms, such as the reachable list, reachsurroundings flag, etc. can pertain to Bed. ------------------------------------------------------------------------------- Button: Thing The Button class defines a basic button. A button is an object that can be pushed, and will return to it's previous position when released. As with ADV's buttonItem class, you are expected to provide your own doPush method to control what happens when the button is pushed. The Button class has an expansion to allow for proper handling of touch.You can set the touchpress property to true if you wish 'touch button' to equate to 'press button', or if it is set to nil, the inherited touch response is called. ------------------------------------------------------------------------------- Ceiling: Everywhere The Ceiling object is defined by WorldClass to give a reasonable implementation of a ceiling in the game areas. The ceiling property should be set to true for all rooms within your game which have a ceiling. The default Room definition has this, and an additional class Outside is provided which the ceiling will not be present in (or Walls). You can use either of these classes when defining rooms. Should you want to have the examination of the ceiling print a custom message in a certain room, you can provide a ceilingdesc method in that room. The Ceiling object will automatically detect the presence of ceilingdesc, and automatically print that rather than the standard output message. Should you wish to define your own message for large areas of rooms, simply make a new room class which has the appropriate ceilingdesc added. Also, should you wish to have a custom Ceiling object in a room, you should create a ceiling object from Everywhere, and set the room's ceiling field to the new ceiling object for that room, rather than just true. Ex: Suppose you wanted to create a pit. For realism, it should have walls and a floor, but no ceiling. You could therefore define a new class called Pitroom as follows: class Pitroom : Room ground = true // We want ground walls = true // We want walls ceiling = nil // We dont wont ceiling. ; ------------------------------------------------------------------------------- Chair: Nestedroom The Chair class is derived from nestedroom. You sit in (or on which is equivalent), as well as stand on it. As with Nestedroom, by default all objects in the outside room are unreachable unless the object is listed in the reachable list or by setting reachsurroundings to true to reach everything. You can disable standing on it by setting standable to nil. ------------------------------------------------------------------------------- classlibrary: object The classlibrary object contains an info method which prints out the current major, minor, and patchlevel version of the WorldClass system. This method is called in the versioninfo() function after the game version information has been printed. ------------------------------------------------------------------------------- Clothing: Item The Clothing class provides for those objects which can be worn, providing an isworn flag which signals whether it is being worn or not, and adding the "(being worn)" attribute in inventory listings when it is worn. It provides wear and unwear methods, as welling as handling automatically taking off the clothing when being dropped or placed somewhere. ------------------------------------------------------------------------------- Connection: Container The Connection class is provided for connecting together locations so that objects and senses available in one room become available in the other(s). That is, several rooms which describe parts of a larger physical room can be connected so that objects in one room can be looked at, smelled, taken, etc. This is done by creating a derivative object from Connection, which is basically a container of rooms. All rooms to be connected sense-wise should have their location property set to the object, rather than nil (which init() then changes to TOP). Ex for TEST.T: room12connection: Connection; modify startroom location = room12connection; modify room2 location = room12connection; From room 2 you would then be able to smell the kee and even take it, even if it is in the other room. One good use for the Connection would be in the definition of a small make-up room which has a large table in the northern half. For some obscure reason, you wanted being near the table to be in a seperate sub-room to the north. However, the current implementation of WorldClass does not allow listable smells to travel between locations, or the viewing of one location from another. Connection is likely to be updated in the future, so it is advisable not to use this this class or risk making your code incorrect in future versions. ------------------------------------------------------------------------------- Container: Holder The Container class is used for holding things inside it. That is, it is a holder which only allows objects to be placed in it with a locationtype of 'in'. It uses inmaxweight and inmaxbulk to specify the maximum weight and bulk that it can handle. By default these two point to the object's maxweight and maxweight. ------------------------------------------------------------------------------- Darkroom: Room The Darkroom class is identical to the Room class except that it has no ambient light (ambientlight is set to nil). Two methods inherited from Room are provided, darken and lighten, which set ambientlight to the appropriate value, so you should use these for turning on or off ambient light, such as in response to flipping a switch in another room. Any light-source brought into the room will override the ambientlight, and will automatically illuminate the room. ------------------------------------------------------------------------------- Decoration: Thing The Decoration is an unmodified descendant of Thing. It is provided for things which will just be decorations. Since Decoration has Thing as it's direct superclass, any objects which derive from Decoration will not be printed in room descriptions. ------------------------------------------------------------------------------- Desk: Surface, Container The Desk class defines a desk type, which acts as both a surface (you can place things on it) and as a container (things can be put in it). For many situations you may wish to derive your own desk type directly from Surface if you do not wish the desk to directly contain objects (such as if you wish to add a drawer to desk, rather than just assuming it). It also provides dummy open/close methods which make the desk act as if it can open and close. ------------------------------------------------------------------------------- Dial: Thing The Dial class implements a dial which can be set to different integer positions. While still having the maxsetting (defaulted to 10) for the maximum dial position like ADV had, it now also has minsetting, the minimum position the dial can take. The setting property holds the current setting of the dial, defaulted to 1. A default ldesc is provided, which will print the range the dial can take, and the current dial position (stored in setting). The doTurnto method should be overridden in your derivative objects to call the inherited code and then perform an action based on the value of setting. ------------------------------------------------------------------------------- Distant: Thing The Distant class defines things which are in the room with the actor but physically too far away to reach. The property allowedverbs contains a list of those verbs which are allowed on the object (by default only inspectVerb). For those verbs it acts normally, but to all other verbs it prints a message that the object is too far away to do that. ------------------------------------------------------------------------------- Door: Obstacle The Door class implements the most common type of obstacle. It provides a method for printing the open/closed and lock/unlock status, opendesc. (Indeed, it defines the default ldesc to merely print the opendesc). It provides handling for automatic door opening if possible, as well as lock / unlock using mykey (or without key if it is set to nil). As with ADV's doorway class, you should set doordest to the destination location of the door, and otherside to the door in the other room. ------------------------------------------------------------------------------- Drinkable: Item The Drinkable class is used for deriving those items which you want to be able to be drunk by an actor. It has a property drinkvalue, which indicates the number of turns to be added onto the actor's turnsuntildehydration, the number of turns until the actor dehydrates, and is by default set to the maximum number of turns the actor can go before dehydrating, drinktime. When the drinkable is drunk, the method adddrinkvalue(actor) is called to update the actor's drink level before removing the object. By default adddrinkvalue simply adds on the object's drinkvalue to the actor's turnsuntildehydration. The method drinkdesc(actor) is then called to print out the response to drinking, which is by default that it quenches your/the actor's thirst. The init() function automatically checks through all the actors to find those which have a dehydrationcheck method. If any are found, they are set up as a daemons. The default Player dehydrationcheck is likely to be the only dehydrationcheck, as not many games will have actors besides the player which can die of thirst. For those special-case edible objects which you do not want to have an alleviating affect on the actor's thirst, you will find it easier to simply make a derivative of item and supply your own verDoDrink and doDrink methods. ------------------------------------------------------------------------------- Edible: Item The Edible class is used for deriving those items which you want to be edible by an actor. It has a property foodvalue, which indicates the number of turns to be added onto the actor's turnsuntilstarvation, the number of turns until the actor starves, and is by default set to the maximum number of turns the actor can go before starving, mealtime. When the edible is eaten, the method addfoodvalue(actor) is called to update the actor's food level before removing the object. By default addfoodvalue simply adds on the object's foodvalue to the actor's turnsuntilstarvation. The method eatdesc(actor) is then called to print out the response to eating, which is by default "Delicious!" The init() function automatically checks through all the actors to find those which have a starvationcheck method. If any are found, they are set up as a daemons. The default Player starvationcheck is likely to be the only starvationcheck, as not many games will have actors besides the player which can starve. For those special-case edible objects which you do not want to have an alleviating affect on the actor's hunger, you will find it easier to simply make a derivative of item and supply your own verDoEat, doEat methods. ------------------------------------------------------------------------------- Everywhere: Floating The Everywhere class is a specialised derivative of Floating. Any object which uses Everywhere will automatically be present in every room. To limit the object to only certain rooms (such as the ceiling to only those rooms which are indoors), Everywhere provides a property roomprop. If set to nil (default), the object appears in every room. If it is set to a property pointer, the Everywhere object is in the room if that property is set to true. It also includes a property roomdescprop. If set to nil then the object's ldesc is printed when the Everywhere is examined. If the roomdescprop is set to a property pointer, then if the specified property is defined in the room, then it is printed instead of the ldesc, thus allowing for special features only in certain rooms. Ex: Ground sets roomprop = &ground and roomdescprop = &grounddesc. The ground will thus be present in every room that has ground = true, and if a grounddesc property exists in the room, it will be printed instead of the Ground's lsdesc when the ground is examined in that room. ------------------------------------------------------------------------------- Female: Thing The Female class contains all the grammer modifications necessary to make an actor female. It should be defined before Actor in an actor's inheritance list. Ex: priestess: Female, Actor ------------------------------------------------------------------------------- Fixture: Thing The Fixture class is used for those fixed objects which you want to be specially mentioned in room descriptions, as opposed to the standard Item "There is an xxxx here." This is accomplished by setting the heredesc property to some code to print out the desired custom description. The default heredesc checks to see if the object has been determined yet, and if so prints "The xxxxx is here", or if not prints "There is a xxxxx here." (with appropriate calls to determine correct pronunciation). ------------------------------------------------------------------------------- Floating: Thing The Floating class is used for those objects which are to appear in multiple locations within the game; having as it's location a method (rather than a room/container) which will return a location value. Floating objects are never made known to the player directly, as they are not placed in contents lists. This class is useful for such things as streams, for example, which might be needed to be present in many rooms in the game, without defining separate stream sections for each location. Since the Floating object is never implicitly in a single room (ie. connected to that room via the room's contents list), floating objects are, by default, known to all actors. You can turn this off by setting known to true in your object definition. ------------------------------------------------------------------------------- Floor: Everywhere, Ledge The Floor class implements the floor or ground in a location, and is able to handle things like "PUT object ON FLOOR". The Ground object handles most cases of the ground already. You will only really need to override the ground in a location with a Floor object if you want the ground to do something unusual, such as making the ground vibrate, in which case you would create a floor object derived from Listabletouch and Floor, and put ground = floor_object in the desired room. The Floor class is inherited by Ground, which defines roomprop = &ground and roomdescprop = &groundesc, as well as giving noun definitions and a sdesc. Floor gives a standard definition for putting things onto itself and taking them (redirecting to the outer room), lying or sitting, or 'getting on the floor' to exit a Nestedroom. ------------------------------------------------------------------------------- Follower: Actor The Follower class implements a special actor-type that can "shadow" the movements of another actor. It is designed to stay one room behind the character it follows; thus allowing the player to follow the actor when it leaves a room by using the "follow" command. All the vocabulary for a follower should match that of the actor it is shadowing, and the myactor should contain the actor which the follower is shadowing. ------------------------------------------------------------------------------- Front: Holder The Front class is used for holding things behind it. That is, it is a holder which only allows objects to be placed in it with a locationtype of 'behind'. It uses behindmaxweight and behindmaxbulk to specify the maximum weight and bulk that it can handle. By default these two point to the object's maxweight and maxweight. ------------------------------------------------------------------------------- global: object The global object contains all those variables which are global to the game. All values, such as score, turns, status, and internal WorldClass lists are stored here. Below is a list of some of the more interesting fields in global. Many of the fields are used for internal working, and are of no concern to you: actorlist Set during preinit() to a list of all the actors indent Flags whether paragraph indentation, performed in I(), should be done. The command 'INDENT' toggles this state. lastactor Set to the actor during disambiguation lastdo Set to the direct object during disambiguation lastio Set to the indirect object during disambiguation lastprep Set to the preposition during disambiguation lastverb Set to the verb during disambiguation. maxscore Used to hold the maximum game score paragraphspacing Flags whether blank lines should be inserted between paragraphs, as done by the P() function. playtesting Flags whether the game is in playtesting mode. If this is set, then all the playtesting commands are enabled [See Appendix A]. score Used to hold the current score searchisexamine Set this to true if you want 'SEARCH do' to equal 'EXAMINE do'. By default this is set to true. silentincscore Flags whether score changes should be done silently. By default set to nil so that score changes are printed. statuslinewidth Is set to the maximum width of the status line. If a status line exceeds this width, as much of the status line as possible is printed, ending with a "..." to indicate that there is more that was not displayed. turns Contains the current number of turns expired in the game. turnspertick WorldClass defines that one command entered is a tick. One tick, however, may be several turns (status line turns). Set this at any point to increase the rate at which turns elapse. undowarning Used to hold the number of undoes that can be executed before a warning is given. ------------------------------------------------------------------------------- Ground: Floor The Ground object is defined by WorldClass to give a reasonable implementation of a floor in the game areas. The ground property should be set to true for all rooms within your game which have a floor. The default Room definition has this. Should you want to have the examination of the floor print a custom message in a certain room, you can provide a grounddesc method in that room. The Ground object will automatically detect the presence of ceilingdesc, and automatically print that rather than the standard output message. Should you wish to define your own message for large areas of rooms, simply make a new room class which has the appropriate grounddesc added. Also, should you wish to have a custom Ground object in a room, you should create a ceiling object from Floor, and set the room's ground field to the new floor object for that room, rather than just true. Ex: [TEST.T] suppose you wished to create a vibrating floor in one of your rooms. The first step would be to define a replacement Floor object: floor: Listabletouch, Floor // Define basic vocabulary and location sdesc = "ground" noun = 'ground' 'floor' adjective = 'warm' 'vibrating' location = startroom locationtype = 'in' // Since the floor isn't a listable item, it won't be actively mentioned // in room descriptions, and thus won't become known to the player. // Thus, when printing the listable touch, it would print // "Something is vibrating", rather than "The floor is vibrating". Since // it should be obvious that the floor is vibrating, we'll define the // alwaysname(actor) method to return true, to force the floor to always // be named. alwaysname(actor) = { return true; } // Provide touch descriptions. touchdesc = "The ground here is warm, and is vibrating." listtouchdesc = "is vibrating violently here." ; Then you would only need to add the line: ground = floor in the startroom definition. ------------------------------------------------------------------------------- Holder: Thing The Holder class is the base type from which is derived all the container types: Container ('in'), Surface ('on'), Over ('under'), Front ('behind'), where the strings indicate the locationtype which is accepted for objects being placed into the holder. The methods movein, moveon, moveunder, movebehind are defined in Thing which are analogous to ADV's moveinto. They are passed the container to move into, and then pass that onto the general moveinto routine, which has an extra parameter for the locationtype. Only in those location types which an object inherits will objects be placeable. Two properties, maxweight and maxbulk provide the default maximum weight and bulk for all the surface types. Individual weight/bulks for each surface within an object can be obtained by overriding inmax, onmax, undermax, or behindmax, where is either weight or bulk. There are several sense passing methods defined in Holder for easy passing/ blocking redefinition of the holder object, and can be easily replaced in the objects you derive from the Holder descendants, for those holder objects which you wish to pass only certain senses (the birdcage being a good example, passing all the senses but touch). The methods for action passing are passcansee, passcantouch, passcantake, passcansmell, passcanhear, passcanspeakto. They are each passed the parameters: actor The actor doing the action. obj The specific object within the holder being referenced, thus allowing for sense blocking for only some objects. Makes for easy implementation for such puzzles as a chest which makes gold invisible for sneaking past a treasury guard. loctype The loctype is the location type being examined. Looking into a container, for example, would pass the 'in' location type. Useful for objects which comprise of more than one location type, each which varying properties. In addition to the passcanX methods, there are the passcanXin, passcanXout, and passcanXacross methods. In the cases of passcanXin and passcanXout methods, they take the same parameters: (actor, obj, loctype), and indeed by default simply remap to the passcanX methods. passcanXacross on the other hand, is used to determine whether a sense is available across the Holder for the specified actor to the object. The across methods take in four parameters, (actor, obj, selfloctype, objloctype) respectively. The Darkroom class makes use of the across classes to conditionally allow sight across depending upon the presence of illumination. While these methods determine whether the different senses can act into, out of, and through a container, a container can also specify whether it wants its different location types listed for smells, sounds, touches, or just sight. To do this, a series of boolean methods XcontentslistedY are defined, each of which should return true if a passed object should be listed in that sense and location type. The legal methods are: incontentslisted(obj) incontentslistedsmell(obj), incontentslistedsound(obj) incontentslistedtouch(obj), oncontentslisted(obj) oncontentslistedsmell(obj) oncontentslistedsound(obj) oncontentslistedtouch(obj) undercontentslisted(obj) undercontentslistedsmell(obj) undercontentslistedsound(obj) undercontentslistedtouch(obj) behindcontentslisted(obj) behindcontentslistedsmell(obj) behindcontentslistedsound(obj) behindcontentslistedtouch(obj) Ex: Say you defined an actor whose contents you didn't want to have listed (the default is for you to see an actor's possessions. You would simply to need to add to his definition the statement: incontentslisted(obj) = { return nil; } ------------------------------------------------------------------------------- Item: Listable, Topic, Thing The Item class defines an object which can be picked up and manipulated by an actor. It has a weight and bulk property which represent, respectively, the weight (heaviness) and bulkiness (how big) in units. ------------------------------------------------------------------------------- Itemactor: Item, Actor The Itemactor class defines an actor which can be taken by the player. It's isactor method only prints out true (and hence prints the actor's custom description) when the Itemactor is in a room, rather than being held by an actor or contained in something. ------------------------------------------------------------------------------- Key: Item The Key class defines an object that is a key. It supports the use of unlock onto the direct object to which it is applied. Door objects which set mykey should point it to an instantiated object of this class. ------------------------------------------------------------------------------- Knowntopic: Topic The Knowntopic class is used for those Topic objects which are to be known from the start of the game, without the player having to actually "see" it. An object's isknownto method is used to determine from it's kownto list which actors know about it. Knowntopic redefines isknownto to return true always, so that the object is known to all actors. ------------------------------------------------------------------------------- Ledge: Surface, Nestedroom The Ledge class is similar to the Bed class, in that it defines a flat nestedroom surface upon which you can sit, stand, or lie on, and can also hold objects on it. ------------------------------------------------------------------------------- Lightsource: Thing The Lightsource class defines an object which provides light. A lightsource will illuminate a room which has ambientlight set to nil as long as it's line of sight to the room is not blocked (ie. it is not put in a closed container which is not transparent). It's islit method is set to true by default to provide constant illumination. This can be overriden to provide only conditional illumination, or illumination that will run out. ------------------------------------------------------------------------------- Listable: Thing The Listable class is a modifier class which causes an object to be listed in room descriptions. Item derives from this class, so all non-hidden items are listed in room descriptions. Listable should be placed at the left in the inheritance list of the inheritance list of an object. Ex: gizmo: Listable, Thing ------------------------------------------------------------------------------- Listablesmell: Thing The Listablesmell class is used for those objects which are to actively have smells in room descriptions and 'SMELL' without a specified object. The islistablesmell is called when smell descriptions are being displayed, and should return true if the object is to print a smell, or nil otherwise. If true is returned, WorldClass then calls the object's listsmelldesc to print out a description of the smell. It should not include the name of the object, but only the smell itself, as in the kee defintion from TEST.T: listsmelldesc = { "smells awful!"; } There is also a standard method smelldesc which is inherited from Thing. smelldesc should print out a complete smell message about the object, and is called when 'SMELL object' is used, as in: smelldesc = { "The cheez key smells totally disgusting!"; } It is important to note that WorldClass does not call islistablesmell before calling smelldesc, so your code should do it manually if the smell is only to be intermittent, as in: if ( self.islistablesmell(global.lastactor) ) "A rose by any other name... "; else "The rose is no longer smelling."; smelldesc does not require Listablesmell to print a smell. For objects which you only want to smell when smelled (such as, for example, a rose), you should merely redefine the smelldesc in your object, be it a derivative of Fixture, Item, etc. For those objects whose smell is very noticeable, you should in general use Listablesmell, ------------------------------------------------------------------------------- Listablesound: Thing The Listablesound class is used for those objects which are to actively have sounds in room descriptions and 'LISTEN' without a specified object. The islistablesound is called when sound descriptions are being displayed, and should return true if the object is to print a sound, or nil otherwise. If true is returned, WorldClass then calls the object's listlistendesc to print out a description of the sound. It should not include the name of the object, but only the smell itself, as in: listsmelldesc = { "is going cookoo, cookoo. "; } There is also a standard method listendesc which is inherited from Thing. listendesc should print out a complete sound message about the object, and is called when 'LISTEN TO object' is used, as in: listendesc = { "The cookoo clock is going cookoo, cookoo!"; } It is important to note that WorldClass does not call islistablesound before calling listendesc, so your code should do it manually if the sound is only to be intermittent, as in: if ( self.islistablesound(global.lastactor) ) "The clock is going cookoo (and so are you)."; else "The clock is wisely keeping silent."; listendesc does not require Listablesound to print a sound. For objects which you only want to hear when listened to (such as a TV perhaps), you should merely redefine the listendesc in your object, be it a derivative of Fixture, Item, etc. For those objects whose sound is very noticeable, you should in general use Listablesound. ------------------------------------------------------------------------------- Listabletouch: Thing The Listabletouch class is used for those objects which are to actively have touches in room descriptions. The islistabletouch is called when touch descriptions are being displayed, and should return true if the object is to print a touch, or nil otherwise. If true is returned, WorldClass then calls the object's listtouchdesc to print out a description of the touch. It should not include the name of the object, but only the touch action itself, as in: listtouchdesc = { "feels hot... very hot!"; } It is important to note that Listabletouch should only be used for those objects the player would automatically touch whilst in the room, such as a wall-to-wall carpet, since touch is not transmitted through the air. There is also a standard method touchdesc which is inherited from Thing. touchdesc should print out a complete touch message about the object, and is called when 'TOUCH object' is used, as in: touchdesc = { "The porcupine feels like your average pincushion."; } It is important to note that WorldClass does not call islistabletouch before calling listendesc, so your code should do it manually if the touch is only to be intermittent, as in: if ( self.islistabletouch(global.lastactor) ) "You feel tiny bumps moving around beneath the surface."; else "The ball's surface is smooth all over."; touchdesc does not require Listabletouch to print a touch. For objects which you only want to be able to manually touch (and not automatically when in the same room), you should merely redefine the touchdesc in your object, be it a derivative of Fixture, Item, etc. For those objects whose touch is unavoidable when in the same room, you should in general use Listabletouch. ------------------------------------------------------------------------------- Listobj: Thing The Listobj class defines handling of lists. It is instantiated into listObj. The preparse() function automatically scans the entered command to see if a list was entered. A list in a command line should be a series of numbers separated by either a comma or a space, and the entire list be delimited by a [ ] or a ( ). ie: [5, 10, 15, 20] Valid [5 10 15 20] Valid (5, 10, 15, 20) Valid (5 10 15 20) Valid (5, 10, 15, 20 ( Invalid preparse() translate the entered list into an internal list, and stores it in strObj.value, just as in numObj and strObj. Since preparse() is used in this manner, you will need to preserve the code if you need to add your own preparse function. Verbs can then make use of the lists just as they would strings or numbers. See Chapter 8 for details on making your own verbs. ------------------------------------------------------------------------------- listObj: Listobj The listObj object is an instantiation of the Listobj class, and is used for holding an entered list, just as numObj is used for holding a number, and strObj for a string. The list can then be read off from the value field. See Listobj for more information. ------------------------------------------------------------------------------- Lockable: Thing The Lockable class is a modifier class which causes the container to need to be unlocked before it can be opened. It should be declared to the left of the container in the inheritance declaration. Ex: thermos: Lockable, Container or maindoor: Lockable, Doorway ------------------------------------------------------------------------------- Male: Thing The Male class contains the grammer modifications necessary to make an actor male. It should be defined before Actor in an actor's inheritance list. Ex: priest: Male, Actor ------------------------------------------------------------------------------- Me: Player The Me actor, whilst not explicitly defined in WorldClass, must be defined in all your games. It represents the actor which the player will control. All commands entered which do not explicitly declare a recipient actor, as in: "Floyd, turn on the planet smashing explosive and eat it." will automatically be applied to Me. Me should derive from the Player class, and must define a location to point to the starting location. The property locationtype can also be to set to either ('in', 'under', 'on', or 'behind') if you want the player to start in some special position (such as hiding behind a curtain at the start of a game), and the position property can be set to ('standing', 'sitting', or 'lying') respectively. See Actor or Topic for more information on the nature of actors. ------------------------------------------------------------------------------- Nestedroom: Room The Nestedroom class defines a room which is contained (nested) within another room. Nestedrooms are useful for objects which are to be enterable; the basis of which forms the bed, chair, etc. classes. WorldClass defines that full sense passing occurs between the outside Room and the Nestedroom; including Listablesenses, sight, and, if included in the reachable list, taste and touching. This also includes nestedrooms which are inside nestedroom; senses came in all the way from the top level. However, nested nestedrooms should be avoided if possible, as it creates status lines which are too long. Nestedrooms are descended from Container (indirectly through Room), but they disable putting things into them. If a nestedroom is to behave as a container as well, the particular surface type(s) should be declared before the Nestedroom in an object declaration. It also provides better handling for when a Nestedroom changes location while the actor is in it, making the new room contents known to the actor. Setting up of the Nestedroom follows the same procedure as used in ADV, using the reachable list to specify reachable objects, and adds a property reachsurroundings which should be set if all objects within the room are to be accessible. ------------------------------------------------------------------------------- NIL: Thing The NIL object is similar to TOP. Anything which is nowhere is taken to be in NIL. NIL itself has TOP as it's location, so all objects trace their ancestry either through a room into TOP, or through NIL into TOP. NIL itself is thus defined only to make the job of blocksreach easier. You should never actually set something to NIL. ------------------------------------------------------------------------------- Number: Thing The Number class defines handling of numbers entered in the command line. It is instantiated into numObj. The internal parser automatically replaces a number entered with a reference to numObj and sets numObj.value to the number entered. ------------------------------------------------------------------------------- numObj: Number The numObj object is an instantiation of the Number class, and is used for holding an entered number, just as listObj is used for holding a list, and strObj for a string. The number can then be read off from the value field. ------------------------------------------------------------------------------- Obstacle: Thing The Obstacle class implements an obstacle between two locations. It is basically unchanged from the ADV.T definition. It has a destination property which specifies the room that is reach if the obstacle is negotiated successfully or prints a message and returns nil if the negotiation fails. The most classic obstacle is a doorway. ------------------------------------------------------------------------------- Openable: Container The Openable class implements a container which can be opened and closed. This class can be used for things like bottles, boxes, etc. ------------------------------------------------------------------------------- Outside: Room This is a derivative class of Room, and sets the walls and ceiling properties to nil, and the ground property to nil. These three properties are used for determining whether the Everywhere objects ground, walls, and ceiling should be present in the room. Obviously, you shouldn't have either a wall or a ceiling outside. ------------------------------------------------------------------------------- Over: Holder The Over class is used for holding things under it. That is, it is a holder which only allows objects to be placed in it with a locationtype of 'under'. It uses undermaxweight and undermaxbulk to specify the maximum weight and bulk that it can handle. By default these two point to the object's maxweight and maxweight. ------------------------------------------------------------------------------- Part: Floating The Part class is used for those things which are part of another object, and go wherever it goes. Instead of specifying a location, however, you set the partof property to the object the Part is to be part of. Parts also contain a setpartof method used for setting the parent object when changing a part's partof, although the partof can be manually set initially, as init() will take care of setting up dependency lists. Part also returns the same location, locationtype, and isknownto status as it's parent object. ------------------------------------------------------------------------------- Player: Actor The Player class makes the modifications to the grammar and methods of the basic Actor class to make it useable for the player's character. Player should be used strictly for defining the Me character. ------------------------------------------------------------------------------- Qcontainer: Container A Qcontainer is just like a Container except that any objects which are in it are not printed in room descriptions. ------------------------------------------------------------------------------- Qfront: Front A Qfront is just like a Front except that any objects which are behind it are not printed in room descriptions. ------------------------------------------------------------------------------- Qover: Over A Qover is just like an Over, except any objects which are under it are not printed in room descriptions. ------------------------------------------------------------------------------- Qsurface: Surface A Qsurface is just like a Surface except any objects which are on it are not printed in room descriptions. ------------------------------------------------------------------------------- Readable: Thing The Readable class defines an object which can be read, and supplies "READ" handling. The readdesc property should be set to print the text which results when the object is read. ------------------------------------------------------------------------------- Room: Container The Room class is a general definition of a room within the game. It provides handling for actors to be within the room, as well as objects to be dropped in the room, and the passing of senses within the confines of the room. Rooms can also have noun/adjective properties, so they can be referred to by the playtesting commands [see Appendix A]. Directions are now performed using go, where direction is the exit direction, as in goSouth, goNorth, goNortheast, goUp, etc. This new style of directions is not required, since the default go properties for Room automatically check for the existence of the old style direction properties, and will use them if no new-style direction is provided. Ex: goSouth = room2 which would connect the southern exit to room2. Note that the goSouth is set to the *object* room2. This is treated by WorldClass the same as the following code: goSouth(actor) = { return room2; } The benefit of being able to using an object is that you can change the destination during the game just by changing the value of the property (in the previous case goSouth). The benefit of using code is that you get in the actor attempting to move in that direction. Actors may move through the game areas via the travel methods too, and it is likely that future versions of WorldClass will provide code to control actors in this manner. WorldClass also recognises verGo methods, which are similar to the verb methods verDo that TADS supports. The default verGo(actor) is a null body, so the movement in any direction is allowed. These can be overridden to prevent movement by certain actors in that direction. Should a direction be attempted for which there is no accompanying go or method, WorldClass will check to see if that room defines a noexit method, which should return a destination room or nil (and optionally print a message). If no noexit method is defined, the room is checked to see if a goNowhere(actor) method is defined. If so, it is called with the actor doing the moving. goNowhere is the preferred method to be used. Should neither method be defined, a default message that the actor can't go that way is printed. Another useful additions to Room is the addition of global additions to the status line. The statusLine method checks to see if a global.statusline method exists, and if so executes it. The global.statusline should print text as deemed appropriate, and the text will be automatically added to the text of the current room name. This could be useful for display short status remarks that the game wants the player to notice quickly, or be continuously updated, such as notifying the player is poisoned, or even for printing current hit-points during a battle (assuming of course that a hit-point/combat system was in use). To handle the presence of light sources, Room firstly has a property ambientlight, which is set to true to indicate no light sources are required. Darkroom is different from Room only in that it sets this to nil. Two methods are provided, darken and lighten, which set ambientlight to the appropriate value, so you should use these methods for special Darkroom situations, such as turning on/off lights depending on a switch in another room. islit is used to determine if any light-source can "see" the room, thus illuminating it. ------------------------------------------------------------------------------- Sensor: Thing The Sensor class is a base for things which can sense other things, and forms a base for "fundamental game actions/awareness". The most common use for this is, of course, Actors, although there are other conceivable things which might need to sense what is happening around them. It provides several methods for determining whether a given sense/action is available from the sensor to a specified object: the cansee, cantouch, cantake, cansmell, canhear, canspeakto, and canputinto methods, all of which take in three parameters: obj (the object), loctype (the location type), and silent (to disable output). The method checks the validity of: "Can my Sensor the object *right now*?" For the most part, each of these can methods checks its own "identity" method (ex. for cansee, this is isvisible) to see if the object in question could *ever* be sensed. If this check passes, it then does a "sense reach check" by calling the blocksreach method. Here is where most of the real work gets done -- blocksreach determines which objects are between the Sensor and the target object in the "containment hierarchy." The path through the containment hierarchy from the Sensor to the target will go "up" through some objects (objects that only the Sensor is contained in), "across" one object (the lowest object in the containment hierarchy that contains BOTH the Sensor and the target), and "down" some objects (objects that only the target is contained in). Blocksreach queries the appropriate passcan method of each container along the path -- this verifies that there is a clear "line of sense" between the Sensor and the target object. Each of the can methods use an accompanying: seepath, touchpath, takepath, smellpath, hearpath, speaktopath, and putintopath to determine if a path between the two is available for that action, these methods having been inherited from Thing. ------------------------------------------------------------------------------- Shelf: Surface The Shelf class is a simple derivate of Surface, and is designed to be used for deriving shelf-like surfaces. ------------------------------------------------------------------------------- Stool: Nestedroom The Stool class is much like the Chair class except that you can 'sit in' or 'sit on' a chair object whilst you can only 'sit on' a stool object. As with the Chair class you can disable standing on it by setting standable to nil. ------------------------------------------------------------------------------- String: Thing The String class defines handling of strings entered in the command line. It is instantiated into strObj. The internal parser automatically replaces a double-quoted string in the command line with a reference to strObj and sets strObj.value to the string entered. ------------------------------------------------------------------------------- strObj: String The strObj object is an instantiation of the String class, and is used for holding a string entered on the command line, just as listObj is used for holding a list, and numObj for a number. The list can then be read off from the value field. ------------------------------------------------------------------------------- Surface: Holder The Surface class is used for holding things on it. That is, it is a holder which only allows objects to be placed in it with a locationtype of 'on'. It uses onmaxweight and onmaxbulk to specify the maximum weight and bulk that it can handle. By default these two point to the object's maxweight and maxweight. ------------------------------------------------------------------------------- Switch: Thing The Switch class implements a switch object which can be turned on or off, flipped, switched, etc. The biggest difference between the older ADV switchItem class is that that you can manipulate them even when in the dark, so long as it is already known to you (ie. the room it is contained in has been illuminated previously). The property isactive is true if the switch is on, and nil if the switch is off. The methods doTurnon, doTurnoff, and doSwitch are used to set the value of isactive appropriately. For many applications, you can simply override these methods with code that calls the inherited code, and then does an action depending on the new value of isactive. ------------------------------------------------------------------------------- Table: Surface, Qover The Table class implements a basic table type. Objects can be placed upon it, and hidden under it. ------------------------------------------------------------------------------- Thing: object The Thing class is the basic class for all other classes used within the game. It contains all the functionality of ADV's Thing, and adds in new methods to handle sense control, text formatting, and much more. ------------------------------------------------------------------------------- TOP: Thing Under ADV, it was the usual practise for rooms to have no location, since they were not contained by anything, except, in a sense, the game itself. Under WorldClass, however, all rooms (or derivatives of Room, such as Nestedroom) which have a nil location are set to point to TOP in preinit. If, however, they do have a location (such as would be the case for a group of rooms sharing a Connection), the location property is left untouched. TOP can be thought of as the top of the hierarchy of containment. In the test game, for example, the jar is contained IN the thermos which is contained ON the table, which is contained IN the startroom, which is contained IN TOP. Since TOP is the parent of all rooms, anything which relates to inter-room activities, such as sense passing for example, could be contained in TOP. TOP might be programmed (as mentioned by WorldClass) to find the shortest path between two rooms and pass hearing if the two rooms are within a certain distance from each other. ------------------------------------------------------------------------------- Topic: Thing The Topic class acts as a parent for those objects which an actor can ask or tell about to another actor. It sets the property istopic to true to signal that the derived object can be asked about/told about. Item inherits from Topic, so all objects are by default valid topics. Setting an item's istopic to nil will signal that it is not a valid topic, causing all ask about/tell abouts to print a default message that it isn't a productive topic. By default, actors will know of every object that you know about, being flagged by the actor's knowsall property. If you do not wish this to occur, you will need to set knowsall to false. Then explicit calls will need to be made to an object's makeknownto or makecontentsknownto, with the actor passed to them, to make those objects and/or contents known to an actor. Ex: Suppose you were defining an actor to only know about things you show to it. You would first set isknown = nil, to turn off automatic knowing of what you know, and then implement erDoShowto and doShowto methods, making a call to makeknownto [and makecontentsknownto if the object is a container] At present there is a problem with WorldClass, in that the actor specified in an ask about command does not call the indirect object's isknownto to determine whether it knows about the object; even if it's knowsall property is set to nil. This is because the actor is disambiguated first in 'ASK actor ABOUT topic/object'. At that time, the topic/object has not yet been disambiguated, so it cannot be called to determine whether the actor is known to it. The knowsall property is thus immaterial; other actors will "seem" to know about every topic. To solve this, you should currently set knowsall to nil (for future compatibility with WorldClass should a solution be found), and make your verDoAskabout call the object's isknown, passing it the actor, to determine whether the actor knows about it. Ex: verDoAskabout(actor, io) = { if ( not io.isknownto( self ) ) "The monk shakes his head, not having seen the object to which you refer."; } ------------------------------------------------------------------------------- Transparent: Thing The Transparent class is a modifier class that acts upon descendants of the Holder class (containers, fronts, surfaces, etc). It causes the holder to be transparent, and the contents visible even if the holder is blocking the view (such as closing a container). This should be specified in the inheritance list of an object to the left of the holder type used. Ex: bottle: Transparent, Container ...bottle definition... ------------------------------------------------------------------------------- Unimportant: Thing The Unimportant class is used for those decoration objects you want to make absolutely clear to player are unimportant, and not to be bothered with. An Unimportant derived object will not allow any verbs except for inspectVerb. All other verbs will cause a message that the object is unimportant. ------------------------------------------------------------------------------- version: object The version object handling is provided for backward compatibility with TADS. Under WorldClass, a new function versioninfo() is called to print the version information it checks to see if the version object has a sdesc defined, and if so prints that rather than the generic game header. Since WorldClass predefines the version object as an empty object, you will need to modify it to add on your game description, or directly modify the versioninfo() function. ------------------------------------------------------------------------------- Walls: Everywhere, Thing The Walls object is defined by WorldClass to give a reasonable implementation of walls in the game areas. The walls property should be set to true for all rooms within your game which have walls. The default Room definition has this, and an additional class Outside is provided which the walls will not be present in (or Ceiling). You can use either of these classes when defining rooms. Should you want to have the examination of the walls print a custom message in a certain room, you can provide a wallsdesc method in that room. The Walls object will automatically detect the presence of wallsdesc, and automatically print that rather than the standard output message. Should you wish to define your own message for large areas of rooms, simply make a new room class which has the appropriate wallsdesc added. Also, should you wish to have a custom wall object in a room, you should create a wall object from Everywhere, and set the room's walls field to the new walls object for that room, rather than just true. Ex: say you were implementing an underground section. You could define a Caveroom class with custom messages as follows: class Caveroom: Room grounddesc = { "The cave floor is bare rock, with an occasional stalactite (or is that stalagmite? You never could tell)"; } wallsdesc = { "The cavern walls are solid looking limestone rock."; } ceilingdesc = { { "The cavern roof is close to your head. You pray that you aren't a claustrophobia."; } ; Chapter 7 =============================================================================== Senses One of the most powerful features of WorldClass is it's handling of the senses/actions. This chapter is aimed at describing the workings of the senses and how to make use of them in your games, delving into their default nature and how they can be modified. ------------------------------------------------------------------------------- The basic sense descriptions At the lowest level are senses used when examining an objects. An object, as well as being looked at, can now be touched, smelled, tasted, and listened to. This is done by creating touchdesc, smelldesc, tastedesc, or listendesc methods to print out appropriate response when the particular sense is applied to the object. The default methods inherited from Thing print messages notifying that nothing unusual is sensed. Since such sense methods are so new (that is, not fully implemented in most IF works), many adventure gamers will not be used to using them. Therefore, it would be good practise to include any noticeable senses (such as the disgusting smell of a refuse object) in the object's ldesc. If the sense is not noticeable (such as with a rose's smell), you needed worry. ------------------------------------------------------------------------------- The Listable senses The next step is to make pervasive senses appear in room descriptions. Three classes, Listablesmell, Listablesound, and Listabletouch are provided for implementing smells, sounds, and touches [sight already being covered, and ignoring taste, since it requires an active usage of the tongue, rather than being passive.] These three classes, which are derivates of Thing, provide islistablesmell, islistablesound, and islistabletouch methods respectively, which always return true, signalling an everpresent sense. As long as any of an object's islistable methods return true and there is a direct connection of that sense between the player and the object (more of this anon), the object's sense lister is called, one of the listsmelldesc, listlistendesc, and listtouchdesc. The list-sense methods should not print the object name, but rather only the sense itself, as in: listsmelldesc = { "smells awful!"; Before calling it, WorldClass first calls the method alwaysname, which gets passed the actor. This should return true if the actor by default knows about the object without seeing it. Ex: such as inside a spaceship: you would hear the engines, yet you would you to read "The engines are roaring." rather than "Something is roaring." Then if the alwaysname method returns nil, it checks out to see if the object is known to the actor, via isknownto. If both tests fail, then it prints "Something" [or "Some things"], or otherwise the sdesc name is printed normally. The listdesc is then called to print out the remainder. ------------------------------------------------------------------------------- Intermittent senses So far, we've looked into both Listable and the desc properties. It was assumed that the sense was always present. Let's now look into cases where the sense is only intermittently present, or dependant on some condition in order to be present. To control printing of a sense in room descriptions (or explicit use of 'SMELL' or 'LISTEN' for smells and sounds), the appropriate method islistablesmell, islistablesound, or islistabletouch should be overridden to return true if the sense is to be listed. Even though the Listable classes only differ from Thing in that they define this method to always return true, you must still specify it in the object's inheritance list, as preinit() forms lists of every object which use these classes, and will ignore any others. For good practise, the desc method should also call the islistable to determine whether the sense should be printed, since this is not handled by the system. Ex: Suppose we wanted to implement a bomb monitor, a scanning device which would start beeping wildly when brought into the presence of the bomb in TEST.T. We could implement it as follows: monitor: Sensor, Listablesound, Item sdesc = "bomb monitor" ldesc = { "This is a deceptively simple looking device. It consists of a small rectangular box on which is mounted a small video camera and a speaker"; if ( self.islistablesound(global.lastactor) ) " which is currently chirping wildly"; "."; } noun = 'monitor' 'box' adjective = 'bomb' location = room2 islistablesound(actor) = { return ( self.cansee(bomb, bomb.locationtype, true) ); } listlistendesc(actor) = "is chirping wildly!" listendesc = { if ( self.islistablesound(global.lastactor) ) "The small speaker on the monitor is chirping wildly!"; else "There is no sound emerging from the monitor."; } ; You will notice the general style of the object. The listendesc calls islistablesound to determine whether to print the sound, as does the object's ldesc, which additionally prints the sound for the benefit of the player. The islistablesound method itself makes use of the cansee method (which will be discussed anon) to determine whether the monitor can "see" the bomb. In addition to islistablesound, islistablesmell, and islistabletouch, there is also an islistable method, which acts upon the visibility of an object. For an item, for example, it will return true, so that the object can be seen. Just as can make a conditional return for the other three three islistableX methods, so too can you do the same with the isvisible method. Ex: Suppose you were writing a fantasy game and needing to implement a "sword-weilding stud" actor. You could give him a sword with particular properties pertinant to a combat system. Yet you wouldn't want the sword displayed all the time. You could therefore either make the actor's possession invisible (see Holder in chapter 6), but since he would be unlikely to have pockets, this is a poor answer. Instead, simply make the sword's isvisible return nil if it being held by the warrior, as in: sword: Item islistable(actor) = { if (self.isin(warrior)) return nil; else return ture; } ; ------------------------------------------------------------------------------- Sense Properties of Objects At the lowest level, you may wish to disable a sense/action completely for a certain object. There are several methods, each associated with one of the actions, which decide whether the object in question is inherently accessible in the various ways. This is different from whether a given sense is available at a given time (as in islistablesmell); it makes the sense never available. The methods are listed in table below. To maintain compatibility with older code, all methods but islistener check the appropriate ver method, allowing an explanation to be printed as to why the action isn't available: Method name ver method Comment isvisible verDoInspect determines if the object is ever visible istouchable verDoTouch determines if the object is touchable istakeable verDoTake determines if the object is ever takeable issmellable verDoSmell determines if the object can be ever smelled isaudible verDoListento determines if the object is ever audible islistener -------- determines if the object can listen Each of these methods are passed the actor being checked, thus allowing actor specific customisation. You may wish to override one for specific disallowing of one of the actions. Ex: Thing by default sets islistener to return nil (objects can't listen). Actor redefines it to return true (actors can hear things) Be wary of modifying these, especially the isvisible. If an object is declared to be totally invisible to the player, it will never become 'known' to him. In the same manner, Floating objects provide a known property to make the object known, since having no explicit starting location, it is never explicitly in a location for the player to see. ------------------------------------------------------------------------------- Sensors You will notice that the bomb monitor example was defined as a sensor, and made use of the cansee method. You might wonder why this was necessary, since we logically should only have to check the bomb's location, right? The answer is no. Let's consider briefly why: 1) The bomb and monitor would not be likely to have the same location when brought together. One of them would be contained by an actor (most likely the player), so we could not simply compare location values, as this would not work unless they were both explicitly in the same room [/container]. 2) We could institute a loop to find the room both are contained in, by going up through the locations until an object can been reached which has the Room class but not the Nestedroom class [using isclass] (excluding Nestedroom since we would want the outermost room) and compare these. In fact, we can do this nicely with the isin method. But what if the bomb were placed in the thermos, and it was closed? A video monitor could not be expected to know about it (unless it was infrared, in which case isin would be acceptable). Thus we need a routine which can simulate the visual perceptions of the eye, and determine whether the bomb is in fact "visible". Lo and behold, we have a routine available: The Sensor class provides a cansee method. Ostensibly designed for actors, it provides the same "sight" functionality that we need. The Sensor class is a base for things which can sense other things, and forms a base for "fundamental game actions/awareness". The most common use for this is, of course, actors, although there are other conceivable things which might need to sense what is happening around them. It provides several methods for determining whether a given sense/action is available from the sensor to a specified object: the cansee, cantouch, cantake, cansmell, canhear, canspeakto, and canputinto methods, all of which take in three parameters: obj (the object), loctype (the object's location type), and silent (set to disable output). The method checks the validity of: "Can my Sensor the object *right now*?" For the most part, each of these can methods first check to see if a path exists between the object by calling one of the following methods: seepath, touchpath, takepath, smellpath, hearpath, speaktopath, and putintopath to determine if a path between the two is available for that action (these methods having been inherited from Thing). If it is, the methods then call their own "identity" method (the properties listed in the previous section; for cansee, this is isvisible) to see if the object in question could *ever* be sensed. If this check passes, they return true to signal that the action is valid. The previous bomb example, thus, used the return value to decide whether to start beeping. ------------------------------------------------------------------------------- Sense Passers: passcanX Before looking into how the seepath, touchpath, takepath, smellpath, hearpath, speaktopath, and putintopath methods determine if a path is available, lets look into the primary routines used in containers to determine the passing of senses. The methods can be grouped into three sections, characterised by the following rules (where sense is either cansee, cantouch, cantake, cansmell, canhear, or canspeakinto): Method category Rule passin true iff object A, not in the container, can object B, in the container. passout true iff object A, in the container, can object B, not in the container. passacross true iff object A, in the container, can object B, in the container. The passin methods are used for determining if a given sense can be passed in through a container. The standard Container, for example, blocks all of these if the container is closed, and the Transparent class makes a container transparent by defining the passcanseein method to return true (as well as passcanseeout and passcanseeacross). The passout methods are used for determining if a given sense is passable out a container. You could thus override passcanseeout to return nil for an enclosed Nestedroom which you want the player to not able to see into, but not out of. Each of these two categories get passed in three parameters: actor, obj, and loctype. actor specifies the Sensor (most likely an actor) doing the sensing, obj specifies the object inside the container the actor is attempting to sense ( or the object outside the actor is attempting to sense from inside ), and loctype is the location type within the container of obj. The passacross methods are used for determining if objects in the same container can pass senses to each other. Room, for example, defines these methods to return true, thus allowing senses to pass freely with a room, and it is made use of in Darkroom to ensure nothing can be sensed whilst the room is dark. The passacross methods get passed in four parameters: actor, obj, selfloctype, and objloctype. actor is the Sensor doing the sensing, obj is the object being sensed in the same container (self), and selfloctype and objloctype specify the location types of both the object and the container itself. These are not necessarily the same as self.locationtype and obj.locationtype, however -- there may be other objects intervening between self and obj and the lowest common ancestor between them. There is an important point to note. These methods are usually called exclusively by the blocksreach method (explained later), who job is to find a route between the sensor and object, repeatedly calling passin, passout, and passacross where appropriate to determine if the sense can pass all the way between the two. The problem is that you might think you could use the actor to implement a container which could only act visible to certain types: ie: A container who only cheez creatures can see into. The answer is no, you can't. Due to efficiency constraints in the blocksreach function does not fully utilise the passcanX methods in the current WorldClass version. You should only make global redefinitions to these methods (ie, returning always true or always nil). ------------------------------------------------------------------------------- blocksreach The blocksreach method is the main method used by senses for determining if sense S can pass from object B to object A. This sounds easy, but it actually quite complicated, since it effectively means that continuous sense checking must be performed from object A, up through any containers one at a time, across whatever area the two objects share in common, and back down through the containers to object B. blocksreach is called by the methods seepath, touchpath, takepath, smellpath, hearpath, speaktopath, and putintopath. Each method passes a set of pointers to the appropriate passin, passout, and passacross methods to blocksreach. These are used by the blocksreach method to determine at each stage whether the given sense is passable. blocksreach must, as it recursively goes through each containers along the path, make sure that the given sense can be passed. To do this it makes use of the passcanX methods, as previously discussed. Ex: The following output was produced by blocksreach with REACHDEBUG turned on [See Appendix A]: Blocksreach: you: [ (bed) (starting room) (thing) (TOP) ] blocksreach: jar: [ (plastic thermos) (starting room) (thing) (TOP) ] Least common ancestor is starting room Child of LCA (self) is bed Child of LCA (obj) is plastic thermos Reach trace: OUT of [bed] ACROSS [starting room] IN [plastic thermos] You'll have to open the plastic thermos first. (*) End of reach trace. You'll have to open the plastic thermos first. What happens is that reachdebug first finds the highest object in the object inheritance list which both objects have in common, in this case the starting room. It can checks the sense passing out of the bed (ie. calling passout), then checks sense passing across the bed (calling passacross), then checks sense passing into the thermos (calling passin), and fails here because the thermos is closed, and it's pass methods define that whilst closed, it can not pass the senses in or out. You should also notice the use of TOP. All rooms which do not specify a location get set to TOP in the preinit code. Thus blocksreach continues up until it reaches TOP, and then works backward to find the lowest common ancestor. Also, any objects which have a nil location are assumed to point to NIL, which itself is a member of TOP. See the previous chapter for a listing of both NIL and TOP. Thus are the sense actions transmitted between objects. ------------------------------------------------------------------------------- Sense Passing between Rooms Senses are by default blocked between rooms. In the current version of WorldClass, inter-room passing has not yet been implemented fully. The Connection class is ostensibly provided for connecting together rooms so that objects and senses available in one room become available in the other(s). The class is merely a direct-derivative of Container. Since Container defines the passXacross methods to return true, this works to a degree. That is, objects in one room which has it's location set to a Connection derived object become available in any other rooms which have their location set to the same Connection derived object. There are, however, several pitfalls to be aware of: i) The other rooms to which a current room is connected to (via the connection) are not described. Thus objects in the connected room are not made known to the actor (via makeknownto). ii) Only the passive sense/actions will be passed through. That is, a Listable will not pass through the connection from it's starting room to any other room. One good use for the Connection might be in the definition of a small make-up room which has a large table in the northern half. For some obscure reason, you wanted being near the table to be in a seperate sub-room to the north. Defining a connection between the two rooms, which would both have mention of the rooms contents in both, would allow the easy manipulation of objects in either "room". Even here though you would need to be careful. Objects dropped whilst "near the table" would be therefore invisible to the outer room. It is probably better to define your rooms using other more conventional means, and leave Connection alone. Future versions of WorldClass are likely to change the definition of Connection, so you are advised to avoid using it if possible. Chapter 8 =============================================================================== Advanced WorldClass Techniques So far, we've concentrated on concepts of WorldClass. Much of your adventure games will be able to be constructed using your already familiar background with TADS applied to the new classes provided, without much work. However, you'll certainly reach a stage where you want to add in new classes, special occurrences, and handling. These will likely be complicated definitions which will require a good knowledge of how WorldClass operates. This chapter is designed to present some of the more complicated and abstract areas of the WorldClass system and methods by which different constructs can be created. This should, at the very least, give you a better idea of how to implement your ideas. But before we begin, should you create any classes and/or object/constructs which you think are excellent and useful for other adventures and you would like to share them, then by all means post them to the WorldClass mailing list at worldclass@ai.mit.edu if you have e-mail access, thus allowing other WorldClass users to share your good idea. [See Appendix B for details of the mailing list] ------------------------------------------------------------------------------- Descriptive Methods WorldClass provides a whole new slew of text-formatting functions for making your classes/objects more grammatically correct. This section provides a list and explanation of the functions provided for this purpose. WorldClass keeps track of case and number. So instead of only sdesc and thedesc, we now have the following expanded list: Method name Example subjsdesc "coins" subjadesc "coins" subjthedesc "the coins" subjprodesc "they" objsdesc "coins" objadesc "coins" objthedesc "the coins" objprodesc "them" possessivedesc "their" reflexivedesc "themselves" As can be seen, some of these will often be the same. Furthermore, these methods don't need to be explicitly defined in your code. For most cases, the above methods will print correct grammar as long as you correctly define the following methods correctly: sdesc Same as in ADV.T adesc Same as in ADV.T isplural Set to true if the object is plural (ie. if the object represents more than one phsyical object). isdetermined Returns: true = object should not have "the" appended to the desc. eg. "Elvis" and objects whose sdesc have "the" already, as in sdesc = "The Q Continuum" Generally only actors require special treatment, and you needn't worry about that, since the classes Male and Female are provided from which to derive actors, and the Player definition takes care of making your descriptions reflexive. Note that although you don't usually need to define the different methods yourself, you must take care to use them specifically. Code built on WorldClass should never include things like: "\^<> doesn't want <>."; // WRONG! Instead, you should take care to use the new methods so that the responses generated are grammatically correct: "\^<> doesn't want <>."; Note that the objX methods take in the subject of the sentence. This gives the obj methods the information required to enable them to know if the reflexive case should be used. In the example above, if (actor = o), WorldClass will correctly generate: Elvis doesn't want himself. If we didn't have the extra parameter, we'd get: Elvis doesn't want Elvis. which is marginal. In some cases, this is catastrophic. Consider, for example, Elivs doesn't want to talk to Elvis. Huh? Are there two Elvises? (What if there are two Elvises? Then this will be totally confusing.) Using the actor parameter to objthedesc, we'll "magically" get Elvis doesn't want to talk to himself. which is correct. Note that when there is no subject, or when you want to suppress the reflexive in all cases, you can safely pass nil to the obj... method. So remember: subj... methods: no actor parameter! obj... methods: require actor parameter! You will also probably find when creating grammar that there are certain verbs for which no WorldClass verb exists (ie. 'hear'). For handling whether an 's' should be printed, use the following standard expression: if ( actor.isplural ) "hear"; else "hears"; Since, for this very usage, the Player class defines that the player's actor should have it's isplural set, the correct grammatical response should be generated whether the player, an NPC actor, or multiple NPC actors (defined by one TADS actor) should be passed in the actor variable. ------------------------------------------------------------------------------- Creating your own Verbs Verbs under WorldClass are much more complicated than was the case in ADV.T. This is due to the fact that "verb requirements" are implemented which make use of the sense reaching methods to test for sense requirements in both the direct and indirect objects applied to a verb. These sense requirements are simply a special list of canX method names that must be true for the verb to succeed (ie. the target objects are reachable with the given senses). This plays a role in disambiguation. Whereas ADV.T assumes than a player can never access an object that isn't in the current location or isn't visible, WorldClass has no such requirement. It's only requirements is that the verb requirements pass. Since Rooms by default do not pass senses in or out, this effectively accomplishes what ADV.T explicitly assumes -- that the player can only access things in the same room. It is important to note that this need not be the case. The syntax for specifying the requirements is complicated, so lets present a sample verb, throwVerb, and go the explanation of it's content: throwVerb: Verb sdesc = "throw" verb = 'throw' 'toss' prepDefault = atPrep ioAction(atPrep) = 'Throwat' ioAction(toPrep) = 'Throwto' ioAction(throughPrep) = 'Throwthrough' ioAction(underPrep) = 'Throwunder' ioAction(behindPrep) = 'Throwbehind' ioAction(inPrep) = 'Putin' ioAction(onPrep) = 'Puton' dorequires = [[&cantake]] iorequires = [ [&cansee] inPrep onPrep [&canputinto] ] ; The requirements for the direct object are given in the dorequires field, and the indirect object requirements are given in the iorequires field. Let's handle the iorequires definition since it's the most complicated. The second line, inPrep onPrep [&canputinto] says that if the prep used in the command is either "in" or "on", the requirements are given by the list [&canputinto] Since no other preps are specifically mentioned, then if the player uses any other preposition, the requirements will be given by the first list: [&cansee] The end result of this is that commands like >throw do at io >throw do to io >throw do under io >throw do behind io simply require the io to be visible to the player -- ie., you can only throw something at something you can see. On the other hand, the commands >throw do in io >throw do on io will be treated completely differently. For these, the io will have to be "putinto-able" by the player. The requirements lists can be as long as you want. Eg., we could require that the io in commands like >throw do at io also be "touchable". In this case, we'd need only add &cantouch to the appropriate requirements list: inPrep onPrep [&canputinto &cantouch] Note that the order of requirements is significant, because WorldClass will check them in the order you list them, and will print a "can't do that" message as soon as a sense reach check fails. These failure messages can be set to "long explanations" of the form "You can't do that, because you can't the object." In these cases, you should be sure to list the requirements in order of importance -- the more fundamental the requirement, the earlier it should appear. (A minor point, perhaps, but worth noting.) Usually requirements lists can be very simple, like so: dorequires = [[&cantake]] Be SURE not to write dorequires = [&cantake] // WRONG! because WorldClass isn't smart enough to realise that you don't "need" a doubly nested list in these simpler cases. A common case where you'll need to specify preps in the verb is in cases like the 'shoot' verb In English you can say either >shoot the gun at John or >shoot John with the gun where changing the prep effectively swaps the do and io. In these cases, your dorequires and iorequires lists will be "mirror images" of each other, like so: dorequires = [ [&cansee] atPrep [&cantake] ] iorequires = [ [&cantake] atPrep [&cansee] ] For such commands where the direct-object and indirect-object are interchangeable, you would define two handling methods, such as Shootwith and Shootat which are called depending on whether the preposition 'with' or 'at' was used. One could easily redirect to the other, swapping over the direct-object and indirect-object. The table below shows a list of the basic &tests you can choose from when making your verb. &cansee can see object &canhear can hear object &cansmell can smell object &cantouch can touch object &cantake can take object &canspeakto can speak to object &canusealpha can use a string &canusenumber can use a number &canusealphanum can use a number or a string &canusenumberorlist can use a number or a list There is however, no reason why you could not create new testing methods, and use them. Remember that each test method is passed the obj, loctype, and silent variables. Verbs also have an allok property, which should be set to true if "all" is allowed on that verb. Inspect, for example, disallows this to prevent "examine all". ------------------------------------------------------------------------------- Special Constructs This section is devoted to some examples of some of the things you can do with WorldClass. This may, at least, give you a better insight on how to better implement your own special objects. ------------------------------------------------------------------------------- Reading the Command During the disambiguation of a command, the properties lastactor, lastverb, lastdo, lastprep, and lastio within global are set to the actor, verb, direct-object, preposition, and indirect object of the command that has been entered. This can be extremely useful in implementing objects which must know about the command which referenced them. Ex: Suppose you wanted to supply a goNowhere method for a room which had the same destination for all directions, except for up and down. You would have had to implement your own methods for goUp and goDown so that goNowhere would not be called. But thanks to the stored properties, global.lastverb can simply be checked in the goNowhere method to easily disallow those directions, as follows: goNowhere(actor) = { if ( global.lastverb <> upVerb and global.lastverb <> downVerb) return forest; else { "\^<> have to get <> \ <> first."; return nil; } } In the WorldClass code, global.lastactor is the most often used of the command fields, as there are many routines which print actor dependant correct grammar, which can easily make use of the actor without having to have it explicitly passed in. ------------------------------------------------------------------------------- Creating objects dynamically One of the most long-awaited concepts was put into place with TADS 2.2. That of dynamic object creation at run-time. To demonstrate the effectiveness of this new concept, let's consider an example. A cupcake dispenser, which dispenses cupcakes whenever you press a button, but has only ten cupcakes inside for giving. The first thing to be aware of is that as of yet WorldClass has not implemented the dynamic object capabilities, as Dave was contrained to writing the code in TADS 2.1 to allow the public to make use of it. Thus there is no support for isEquivalent; all cupcakes in a room will be listed seperately, rather than saying 'there are xx cupcakes here'. Anyway, the first step is create a cupcake class. We will need to include our own construct and destruct methods, as the Thing class does not provide these either. A decent class is provided below. class Cupcake: Edible isEquivalent = true sdesc = "cupcake" ldesc = "This is a rather unremarkable, but delicious looking, cupcake." noun = 'cupcake' 'cake' plural = 'cupcakes' doEat(actor) = { self.eatdesc(actor); self.addfoodvalue(actor); delete self; } eatdesc(actor) = { "The cupcake tastes absolutely delicious."; } construct = { self.movein(dispenser.location); } destruct = { self.movein(nil); } ; You will note that the doEat method is redefined to destroy the item when eaten, rather than just moving it into nil. This is important, as all objects created remain in memory or disk, taking up room, even if they are moved into nil. The plural is also set to 'cupcakes', allowing them all to be addressed together. Next an appropriate dispenser must be implemented, with handling for a limit of cupcakes. This can be done as follows: dispenser: Fixture sdesc = "cupcake dispenser" ldesc = "This is a classic cupcake dispenser with a big red button for dispensing cupcakes." heredesc = { "A rather battered looking cupcake dispenser rests against the northern wall."; } noun = 'dispenser' 'machine' adjective = 'cupcake' location = startroom ; button: Part, Button partof = dispenser sdesc = "button" ldesc = "It's big, it's red, it's round. Yep, it's a button." noun = 'button' adjective = 'red' numcupcakes = 10 doPush(actor) = { if (numcupcakes > 0) { local x = new Cupcake; "The dispenser grinds for a second, and then dumps a cupcake onto the floor through a small hatch."; numcupcakes--; } else "Click! Nothing happens."; } ; You will note that the variable numcupcakes is used to count down how many cupcakes the dispenser has left. It is kept in the button, since it is the button which makes use of it (ie. The dispenser acts merely as a backdrop). The format is simple. We declare a local variable x to be assigned to a new cupcake. Since the construct method moves a reference to the object into the room's contents list, we don't have to save x. And that's all there is to it. You could direcly add these two code fragments to TEST.T and use it straight away. Indeed, from this base, more complicated dynamic handling could be built, such as a liquid system which uses it's bulk against the maximum bulk of a container to determine how much can go into it, and more than one liquid be mixed in the one container. The possibilities are endless. ------------------------------------------------------------------------------- Keyholes One source of good realism in your games can be the presense of keyholes within doors. Lets characterise the behaviour we would want the keyhole to be able to perform: i) The keyhole should be able to have 'unlock' applied to it, and affect the door. ii) The player should be able to look through the keyhole, and view the room on the other side, so long as no object is in the keyhole. iii) The player should be able to place any single small object inside the keyhole. For simplicity, we shall ignore the possibility that the shape of a key might not fit into the lock, although making it a container will give it maximum bulk handling which will partially do the job. iv) Should a key (or other object) already be in on the opposite side of the lock, we should be able to push it out. A good example of this is the doormat puzzle in Zork II, although in this case the object will fall onto the floor in the other room. With this in mind, the keyhole class can be defined as follows. class Keyhole: Part, Container sdesc = "keyhole" ldesc = { "<> is currently "; if (self.contents <> [] ) "blocked by <>, which is in it."; else { if (self.partof.otherside.parts[1].contents <> []) "blocked by an object placed in the other side of the keyhole."; else "empty."; } } verDoLookthrough(actor) = {} doLookthrough(actor) = { if ( self.contents <> [] ) "\^<> is blocking %your% view through the keyhole."; else if ( self.partof.otherside.parts[1].contents <> [] ) "Something on the other side is blocking %your% view through the keyhole."; else { local dest := self.partof.doordest; local oldloc := actor.location; local oldloctype := actor.locationtype; // Here we do a bit of a hack. We directly move the actor into the // destination room so that he can see the objects, making them known // to him, as well as taking care of Darkrooms when the actor has a // light source. actor.location := dest; actor.locationtype := 'in'; // go through the steps that Room.enter does. We won't actually // call the method, since we want to always print the room description dest.makecontentsknownto(actor); dest.makeknownto(actor); dest.lookaround(actor, true); dest.isseen := true; // Move the actor back into his old location actor.location := oldloc; actor.locationtype := oldloctype; } } // Restrict the keyhole to holding only one object. verDoPutin(actor,io) = { if ( self.contents <> [] ) then "%You% can't, because there is already something in <>."; } doPutin(actor,io) = { if ( self.partof.doordest.parts[1].contents <> [] ) { "As %you% <> <> into <>, %you% "; if ( actor <> Me or actor.isplural = nil ) "hears"; else "hear"; " a soft thunk on the other side."; self.partof.doordest.parts[1].contents[1].movein( self.partof.doordest ); // Hide output so "done." isn't printed. Outhide(true); inherited.doPutin(actor,io); Outhide(nil); } else inherited.doPutin(actor,io); } // This section basically redirects locks/unlocks to the parent door. verDoLock(actor) = { self.partof.verDoLock(actor); } doLock(actor) = { self.partof.doLock(actor); } verDoUnlock(actor) = { self.partof.verDoUnlock(actor); } doUnlock(actor) = { self.partof.doUnlock(actor); } verDoLockwith(actor,io) = { self.partof.verDoLockwith(actor,io); } doLockwith(actor,io) = { self.partof.doLockwith(actor,io); } verDoUnlockwith(actor,io) = { self.partof.verDoUnlockwith(actor,io); } doUnlockwith(actor,io) = { self.partof.doUnlockwith(actor,io); } ; It is a derivative of Part, and should have a door as it's partof object parent, and specify a noun. Just as doors must be defined for two locations, this class requires that both doors have a keyhole connected to them. It defines the full set of lock/unlock verbs, so a key can be placed in the lock and then used to unlock the lock/door. Using the keyhole, an actor can look through it and view the other-side room, and see it's contents. Thus the contents become known to the actor without the need for actually visiting the room. Note the use of the conditional testing of the isplural state of the actor for printing "hear". This is done because no WorldClass verb has that verb as it's description (ie. the closest is the 'listen to/listens to', which wouldn't quite work). There are actually three different grammatical conditions for printing the verb: i) When the player is doing the action, as in: "you hear a soft thunk." ii) When another actor is doing the action, as in: "he hears a soft thunk." iii) When multiple actors represented as a single actor do the action, as in: "they hear a soft thunk." As can be seen, it would require testing of both the isplural flag in an actor, and a test to see if the player's actor was doing the action. To simplify this process, the Player class defines that the player's character is plural (ie. isplural is set). This will then produce correct grammar for the player's action. It is important to note that is semantical only. Should you make a construct which makes use of actors' isplural flag, you should be aware of this special circumstance for the Me actor. actor being Me or plural. This is to ensure that when the player performs the action: "As you put the ...." will be, printed, and if the actor is plural, then: "As they put the ...." But when the actor is singular, and not the player the correct 's' will be added: "As he puts the ...." ------------------------------------------------------------------------------- Behind rooms Whilst the Holder class has been discussed, the potential for nested rooms have not been detailed. This shows how to create a custom nestedroom which is not 'in', but rather one of the other location types of 'on', 'under' or 'behind' Say for example, you wanted to implement something similar to the Jack actor described in Chapter 7 of the TADS 2.0 Authors Manual (page 157). In this, the actor enters the room, and if the actor is hidden, opens the safe. In the example, which was demonstrating how to define an actor in terms of a state machine, the fact of whether or not the player was hidden, was left open, using only a flag Me.ishidden to signal if the player was hidden. Let's look at how we could implement this in WorldClass, in terms of creating nestedrooms. A good solution to the problem of hiding would be to provide a curtain. The player could hide behind it, and ideally be able to see the outside room from it but not the other way around. Thus the Jack actor could come in, see no-one around, and carry on to unlock the lock. To start with, an appropriate verb is already supplied by WorldClass which can be used, the getbehindVerb. It has verbs of "stand behind", "get behind", and "go behind". For this example we'll want to add another verb combination of "hide behind". modify getbehindVerb verb = 'hide behind' ; Now that the go-behind verb has been expanding to allow hiding, we need to create a nested room class which we can "hide behind". The definition below shows a standard definition which can be used for this situation: class Qbehindroom: Nestedroom tdesc = { if (self.location) self.location.tdesc; else caps(); ", <> \ <> \ <>"; } verDoExit(actor) = { if (not actor.iscontained(self, 'in')) "\^<> not in <>."; } doExit(actor) = { actor.moveto(self.location, self.locationtype); actor.position := 'standing'; "%You% "; if ( actor.isplural ) "leave"; else "leaves"; " <>."; } verGoOut(actor) = { self.verDoExit(actor); } goOut(actor) = { actor.position := 'standing'; "%You% "; if ( actor.isplural ) "leave"; else "leaves"; " <>.\b"; return self.location; } verDoGetbehind(actor) = {} doGetbehind(actor) = { actor.movebehind(self); actor.position := 'standing'; "\^<> now standing behind <>."; } passcanseein(actor, obj, loctype) = { return nil; } passcantouchin(actor, obj, loctype) = { return nil; } passcantakein(actor, obj, loctype) = { return nil; } ; The stage is set. The Qbehindroom is defined as a behind-type nested room which we can "hide behind", yet still see out of. Should another actor come into the outer room he would not be able to see the player. Thus could the situation in the TADS manual be achieved. You will also notice that only passcanseein, passcantouchin, and passcantakein were overridden. This is because sounds and smells should not be stopped by the curtain. As long as the entering NPC had code to call the appropriate canX methods, then sounds in the behind-room (such as the player carrying a cookoo clock), would be noticed, and appropriate action could be taken (such as the NPC finding the actor). Chapter 9 =============================================================================== Actors This chapter is included as an extension to the Advanced WorldClass Techniques. It looks into ways the user can implement actors that can move about. Eventually, WorldClass will implement a general set actor classes for moveable actors and goal driven actors. This chapter is thus only intended as a guide for mechanics of basic movement. ------------------------------------------------------------------------------- Actors Actors can be one of the most important aspects of a game. They give the game depth and help make the player feel he is actually in the portrayed world. A good adventure should include NPCs who can not only communicate with the actor, but can move about or do things or their own. It is therefore essential that you have a good understanding of how actors work. You should approach this section with a good understanding of how actors work. Pages 147 to 161 of the TADS 2.0 is a good place to start if you are in doubt. ------------------------------------------------------------------------------- Moving About One of the new improvements added by WorldClass is the verGo, analogous to the vers of objects. To enable this to work to full potential however, you must define movement controllers which call the verGo appropriately, and disallow illegal movements. You can no longer simply just use moveInto to shift an actor to a new location. In order to implement this lets first design an intelligent routine which can tell if a given direction is allowed, by returning a destination room if it is. This routine must be able to check (for compatibility) the old style movement directions, and be able to handle the verGo. First of all, we'll need to define a few lists for direction handling. To make these available to all moving actors, we'll add them to global, as shown. modify global dirstringlist = ['north' 'northeast' 'east' 'southeast' 'northwest' 'west' 'southwest' 'south'] olddirlist = [&north &ne &east &se &nw &w &sw &s] verdirlist = [&verGoNorth &verGoNortheast &verGoEast &verGoSoutheast &verGoNorthwest &verGoWest &verGoSouthwest &verGoSouth] dirlist = [&goNorth &goNortheast &goEast &goSoutheast &goNorthwest &goWest &goSouthwest &goSouth] ; Now lets define a new actor class called Mover, which contains two methods: movevalid and getdestination. Both will be passed in a direction string (one of those contained in the dirstringlist). movevalid will check to see if the direction is passible. It does this by calling the verGo with the actor to see if any text is printed. If it is, it will be assumed that the direction is obviously impassible (as in a brick wall offering no means of passage), and nil will be returned, or otherwise true to indicate that the direction is passible. Should you want a direction to be conditional passible, it uses a diroklist, which should contain of list of direction strings for those locations you wish to be considered "possible exits". getdestination checks with movevalid and the appropriate verGo (if it finds one present) and passes out the destination if a valid destination exists. The complete code for the two routines is shown below: class Mover: Actor diroklist = [] movevalid(direction) = { local dir := find( global.dirstringlist, direction ); local dest; // Check to see if the given direction is in the dirok list. // If it is, then return true immediately. if ( find(self.diroklist, direction) ) return true; // Check to see if a verGo method exists, and if so call it // to see if it outputs anything. If it does, the test fails if (defined(self.location, global.verdirlist[dir])) { Outhide(true); self.location.(global.verdirlist[dir])(self); if (Outhide(nil)) return nil; } // Check to see if a go is defined, then get the destination, // remembering that it can be both a method or set to an object. // Then check to see if destination is not nil, and if so output true. if (defined(self.location, global.dirlist[dir])) { if (proptype(self.location, global.dirlist[dir])=6) dest := self.location.(global.dirlist[dir])(self); else dest := self.location.(global.dirlist[dir]); if ( dest ) return true; else return nil; } else { if (defined(self.location, global.olddirlist[dir])) return true; else return nil; } } getdestination( direction ) = { local dir := find( global.dirstringlist, direction ); // Check to see if a verGo method exists, and if so call it // to see if it outputs anything. If it does, the test fails if (defined(self.location, global.verdirlist[dir])) { Outhide(true); self.location.(global.verdirlist[dir])(self); if (Outhide(nil)) return nil; } // Check to see if a go is defined, then get the destination, // remembering that it can be both a method or set to an object. // Then check to see if destination is not nil, and if so output true. if (defined(self.location, global.dirlist[dir])) { if (proptype(self.location, global.dirlist[dir])=6) return self.location.(global.dirlist[dir])(self); else return self.location.(global.dirlist[dir]); } else { if (defined(self.location, global.olddirlist[dir])) return self.location.(global.olddirlist[dir]); else return nil; } } ; You will notice that the two methods are much alike, but the real difference lies in the handling of the diroklist / verGo. By calling movevalid you can determine whether a given direction has a possible exit. Then getdestination can be called to get the destination if the movement in that direction by the actor is valid. ------------------------------------------------------------------------------- Wanderers Now that we have a basic class for getting directions, we can expand that to allow actors to wander about at will. By default, a wanderer will automatically avoid an exit which prints a verGo message, but you can easily set a room's diroklist to make the wanderers try to exit. We'll define a new class Wanderer from which wandering actors can be derived. As with Everywhere, we'll define a roomprop which can contain a property pointer to a property which must be set for any room the actor is to wander into. Thus you can limit an actor to only certain rooms. We'll also define a pauseturns variable, which indicates the number of turns the actor is to pause in each room for. class Wanderer: Mover roomprop = nil pauseturns = 3 leavemessage(dir) = { "\b\^<> "; if (self.isplural) "leave"; else "leaves"; " to the <>.\n"; } entermessage(dir) = { "\b\^<> "; if (self.isplural) "enter"; else "enters"; " from the <>.\n"; } attemptedleave(dir) = { "\b\^<,self.subjthedesc>> starts to the <>. "; } pausectr = 0 moveDaemon = { local tries; pausectr += global.turnspertick; if (pausectr > pauseturns) { for (tries:=1; tries<50; tries++) { local dir := global.dirstringlist[rnd(8)]; local seen; local dest; if ( self.movevalid(dir) ) { dest := self.getdestination(dir); seen := Me.cansee( self.location, self.locationtype, true ); if (dest = nil ) { if ( seen ) { self.attemptedleave(dir); self.location.(global.verdirlist[dir])(self); } return; } else { if ( not self.roomprop or ( self.roomprop and dest.(self.roomprop) ) ) { if ( seen ) self.leavemessage(dir); Outhide(true); self.travelto( dest ); Outhide(nil); if ( Me.cansee( self.location, self.locationtype, true ) ) self.entermessage(global.dirstringlist[9 - find(global.dirstringlist, dir)]); pausectr := 0; return; } } } } } } ; The Wanderer implements an easily customizable wandering system. The wanderer will wander anywhere (unless it's roomprop is set, in which case it is limited to those specified rooms). It automatically distinguishes, with help from the Mover class, if a direction is possible, and should a direction be seemingly possible but not actually possible to that particular actor, it has a custom message as well. The responses when the actor enters, leaves, or attempts to leave the current room are contained in separate methods for ease of customisation. In the case of attemptedleave, the method is first called, and then the verGo, which should print out a response as to why the actor could not go in that direction. The only requirement then would be to add code to the userinit() function to cycle through all objects and for those objects which have a moveDaemon, set up the method as a daemon. ------------------------------------------------------------------------------- Moving-track actors By using the same mechanism we can implement an actor who moves on a track, by simply providing of a list of directions to move in. The Trackactor class, defined below, provides an implementation for track movement of an actor. The track should be provided in the derived actor's track list, in the form of strings of the direction to move in at each stage. As with the Wanderer class, pauseturns should be set to the number of turns the actor is to remain in each room class Trackactor: Mover track = [] pauseturns = 3 pausectr = 0 trackctr = 1 leavemessage(dir) = { "\b\^<> "; if (self.isplural) "leave"; else "leaves"; " to the <>.\n"; } entermessage(dir) = { "\b\^<> "; if (self.isplural) "enter"; else "enters"; " from the <>.\n"; } attemptedleave(dir) = { "\b\^<,self.subjthedesc>> starts to the <>. "; } moveDaemon = { pausectr += global.turnspertick; if (pausectr > pauseturns) { if ( length(self.track) = 0 ) { pausectr := 0; return; } else { local dir := self.track[trackctr]; local seen; local dest; if ( self.movevalid(dir) ) { dest := self.getdestination(dir); seen := Me.cansee( self.location, self.locationtype, true ); if (dest = nil ) { if ( seen ) { self.attemptedleave(dir); self.location.(global.verdirlist[dir])(self); } return; } else { if ( not self.roomprop or ( self.roomprop and dest.(self.roomprop) ) ) { if ( seen ) self.leavemessage(dir); Outhide(true); self.travelto( dest ); Outhide(nil); if ( Me.cansee( self.location, self.locationtype, true ) ) self.entermessage(global.dirstringlist[9 - find(global.dirstringlist, dir)]); pausectr := 0; trackctr += 1; if ( trackctr > length(tracklist) ) trackctr := 1; return; } } } } } } ; You then need only to add on default actor characteristics, and the actor will happily go around his specified track. ------------------------------------------------------------------------------- Commandable actors Then comes the case of those actors which you want the player to be able to control during the game. The following class Controllable presented below shows how an actor's actorAction can be updated to handle movement commands. class Controllable: Mover leavemessage(dir) = { "\b\^<> "; if (self.isplural) "leave"; else "leaves"; " to the <>.\n"; } entermessage(dir) = { "\b\^<> "; if (self.isplural) "enter"; else "enters"; " from the <>.\n"; } attemptedleave(dir) = { "\b\^<,self.subjthedesc>> starts to the <>. "; } actorAction(v, d, p, i) = { if (isclass(v, Travelverb)) { local dir := global.dirstringlist[ find( global.dirlist, v.doprop ) ]; local dest; local seen; if ( self.movevalid(dir) ) { dest := self.getdestination(dir); seen := Me.cansee( self.location, self.locationtype, true ); if (dest = nil ) { if ( seen ) { self.attemptedleave(dir); self.location.(global.verdirlist[dir])(self); exit; } } else { if ( not self.roomprop or ( self.roomprop and dest.(self.roomprop) ) ) { if ( seen ) self.leavemessage(dir); else "Done."; Outhide(true); self.travelto( dest ); Outhide(nil); if ( Me.cansee( self.location, self.locationtype, true ) ) self.entermessage(global.dirstringlist[9 - find(global.dirstringlist, dir)]); exit; } } } } else if (v <> helloVerb) { self.aamessage(v, d, p ,i); exit; } } ; And thus is implemented a base actor class which can be commanded to move around. actorAction is usually reserved for printing out objections to a command given to an actor. In this situation, I placed the handling inside it, since if I didn't the automatic travelling of the actor would print a description of the room he had entered into, and the player shouldn't be told that. And that it for moving actors around. You may wish to build on these base, such as adding to the movement routines: One addition you might wish to add is storing the last direction a wanderer has travelled, and try to avoid going back that same way. Or you may wish to implement only partial controllability for some actors. Its up to you. Appendix A =============================================================================== Playtesting commands Several commands are available for playtesting games you create with WorldClass. The global.playtesting is set by default to true to indicate that playtesting verbs are allowed. You will certainly want to modify global to set this to nil before distributing your finished games. The Verb.playtestcheck takes care of checking whether a verb is for playtesting. Any playtesting verbs which you add should inherit debugVerb, thus allowing automatic disabling under commercial versions. Below is a brief description of each of the play-testing commands. ------------------------------------------------------------------------------- knowVerb know Makes the specified object(s) known to the player (ie. puts the player in the object's knownto list). Normally an object becomes known to an actor only when that actor sees it. Using this command automatically makes the specified object known to the player (Me), without needing to see it normally. ------------------------------------------------------------------------------- locateVerb locate The locate verb allows you to see where objects are located. When an object is specified, it prints out the location of the object and location type, recursively continuing downwards through locations until it reaches either TOP or nil. Ex: The bomb in TEST.T, which is on the table, would print: mysterious black sphere ON table IN starting room IN TOP ------------------------------------------------------------------------------- listcontentsVerb listcontents [object], contents [object], or list [object] The listcontents verb is much like the locate verb. It's purpose is for recursively listing the contents of objects. If the optional parameter is not specified, it lists the contents of the actor's current location. If it is, the contents of that object are recursively listed. Ex: Typing 'contents' in TEST.T whilst still on the bed would produce: you ON bed IN starting room IN TOP hat IN you ON bed IN starting room IN TOP since you are contained by the bed, and you contain the hat. Typing 'contents table' would produce: fishhook ON table IN starting room IN TOP piece of string ON table IN starting room IN TOP mysterious black sphere ON table IN starting room IN TOP fuse ON table IN starting room IN TOP cheez kee ON table IN starting room IN TOP happy little mouse UNDER table IN starting room IN TOP ------------------------------------------------------------------------------- gimmeVerb gimme This command gives the player the specified object, no matter whether or not it is accessible or known to the actor. This is very useful (in tandem with the warp to command) for bypassing parts of your game that have already been debugged. Since every recompile makes previous save games useless, it saves time in having to go through all the puzzles you have already tested. ------------------------------------------------------------------------------- warptoVerb warp to This warps the player to the specified room. To enable this to work, each of your room/nestedrooms should have a noun and optionally an adjective defined. Since WorldClass doesn't allow most verbs to apply to rooms, there isn't much worry about naming conflicts. You can only warp to rooms or nestedrooms. The warpto also automatically stands you up if you are not already doing so. Thus if you warp to a nestedroom which only allows sitting (such as a chair with standable = nil), you will still be standing. This is not a problem, since a simple sit puts you in the correct position. ------------------------------------------------------------------------------- hungryVerb hungry This checks the actor's turnsuntilstarvation. If it less than 40, it resets it up to the actor's starting value, mealtime. If it is greater than 40, it sets it down to 40. This is useful for checking proper functionality of hunger messages. ------------------------------------------------------------------------------- thirstyVerb thirsty This checks the actor's turnsuntildehydration. If it less than 40, it resets it up to the actor's starting value, drinktime. If it is greater than 40, it sets it down to 40. This is useful for checking proper functionality of thirst messages messages. ------------------------------------------------------------------------------- sleepyVerb sleepy This acts much the same as hungry, except that it acts on turnsuntilsleep. ------------------------------------------------------------------------------- lightroomVerb lightroom Calls the current room's lighten method to set the room's ambientlight to true, then looks around. It thus makes a Darkroom visible. ------------------------------------------------------------------------------- darkroomVerb darkroom Calls the current room's darken method to cause the lights to go out, by setting ambientlight to nil. The will, though, still be visible as long as you have a light source. ------------------------------------------------------------------------------- qVerb q This is included to allow the user to quickly quit back to the system without getting queried by the computer. Rather than using Debugverb as a parent, this verb manually checks global.playtesting to see whether playtesting is on. If it is, it drops back to system, or if not redirects to the standard quit verb. There is also an alternate quit command build into the run-time system itself. Should your game cause an error which causes inputs to not be acted upon (such as running out of memory even after garbage collection), you can force termination of the game by typing $$ABEND ------------------------------------------------------------------------------- dieVerb die This causes the die() function to be called to killed of the player. the 'q' command is usually an easier method if you just wish to quit the game. This function is, however, useful for testing death sequences, or if you ever create situations where several actors can die. ------------------------------------------------------------------------------- debugreachVerb debugreach, or reachdebug This is provided for remote debugging of reachability of an object. Unlike the others, this is not a DebugVerb, so is available in production versions for remote debugging of problems. When used, it toggles the state of the debug tracing. Whilst on, any attempts to get objects which are known about but unreachable will provide a full debug report on the results. Ex: To try it out in TEST.T, try typing: 'DEBUGREACH', 'KNOW JAR' then 'GET JAR'. ------------------------------------------------------------------------------- debugtraceVerb debugtrace This command toggles the parser debug reporting introduced in TADS 2.2. When turned on, the parser will generate a number of information messages as it anyalyses a player's command, as is intended to provide information on how it interprets the command words. ------------------------------------------------------------------------------- debugVerb debug This command is not a playtesting command as such. When entered, if the user is currently executing the program through the TADS Debugger it drops the user back into the code/command window. Otherwise it simply prints the somewhat humorous "I don't see any bugs here." The TADS Debugger comes with the registered version of TADS, so if you haven't registered, I can highly recommend doing so. ------------------------------------------------------------------------------- weightVerb weigh Gives the weight of the object specified, as given by object.weight ------------------------------------------------------------------------------- bulkVerb bulk Gives the bulk of the object specified, as given by the object's bulk property. Appendix B =============================================================================== Where to now If you're interested in finding out more about WorldClass or TADS, or to contribute to general discussions on the system, there are several things you can do: 1) Get a fully registered version of TADS if you don't already. The manual is a great reference even if you don't use ADV.T, and you also receive the TADS Debugger (a must when tracing some tricky WorldClass execution) and the MakeTRX for making standalone WorldClass (or TADS) games. TADS can be registered by: Telephone: credit card order by (415)493-2430 weekdays from 10 AM to 6 PM Pacific Time. High Energy BBS: Set your modem to 14,400 bps (or any lower speed), N-8-1, and dial 415-493-2420. The BBS is open to the public -- you don't need to be a registered user of any our products to access the system. Feel free to browse the message conferences and libraries on the BBS; you may be able to find the answer to your question without having to wait for a reply. Address specific questions to SYSOP. PCPursuit users: the High Energy BBS can be reached from the San Jose (CASJO) outdial. Internet: support@hinrg.starconn.com Compuserve: 73737,417 GEnie: M.ROBERTS10 Mail: High Energy Software PO Box 50422 Palo Alto, CA 94303 2) Check out ftp.gmd.de in the directory /if-archive/programming/tads/worldclass 3) And questions about WorldClass can be mailed to the WorldClass mailing list worldclass@ai.mit.edu Requests to be added to the mailing list should be sent to dmb@ai.mit.edu 4) If you have Web access, I have set up a TADS/WorldClass URL which will contain links to all available WorldClass modules, files, and documentation. It is currently located at http://yallara.cs.rmit.edu.au/~s9406702/tads During the summer holidays of December-February, it may be shifted to http://minyos.xx.rmit.edu.au/~s9406702/tads so you can try there if the first URL bounces. 5) Subscribe to the newsgroup rec.arts.int-fiction This group if for general discussion of the concepts and programming behind designing interactive fiction, and you can find help on any TADS problem you might have. For WorldClass specific problems, it would be better to join the mailing list and discuss your problems on it, keeping TADS questions to the newsgroup. My personal metaquotation for writing interactive fiction: "Life is like a door. Before you can open it, you must build it."