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