/*
 * 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;

	//
	// Note funny syntax:
	//
	Me.location.enter(Me);
}

//
// Just like with ADV.T, the player character object should be
// named "Me".  WorldClass doesn't rely on this much, however.
// In general, WorldClass makes no distinction between Me and
// other actors.
//
Me: Player
	//
	// These three methods must be correctly defined, or
	// WorldClass will get confused:
	//
	location = bed
	locationtype = 'on'
	position = 'sitting'

	noun = 'me' 'self' 'myself'

	//
	// To make eating, drinking, and/or sleeping 
	// required for the player's continued survival,
	// don't make the following methods nil.  (See 
	// the definition of the Player class for details.)
	//
	starvationcheck = nil
	dehydrationcheck = nil
	sleepcheck = nil
;

//
// A Darkroom has no ambient light -- the player must have a Lightsource
// to see.  Darkness does not prevent the player from using any verbs
// that don't require the "cansee" capability.
//
// Note that "startroom" is an arbitrary name.  You can make it whatever
// you want.  WorldClass looks at Me.location (see above) to determine
// where the player starts.
//
startroom: Darkroom
	//
	// tdesc = "title desc"
	// This is what gets printed in the status line for the location.
	//
	// The reason we want separate sdesc's and tdesc's is that
	// the tdesc's are usually capitalized.  Furthermore, we
	// might want to implement "look west", which would tell
	// the player the name of the westward location.  We wouldn't
	// want this name to come out capitalized.
	//
	tdesc = "In a Very Boring Room"
	ldesc = {
		"This room is pretty boring, but there is a poster 
		here. Head south and you're in another boring room.";
	}
	sdesc = "starting room"

	//
	// Rooms can have vocabulary too.  WorldClass doesn't
	// allow most verbs to apply to rooms, so naming conflicts
	// between rooms and objects are not a problem.  (See also
	// the "warpto" playtesting verb.)
	//
	noun = 'room'
	adjective = 'starting'

	//
	// Unless we say otherwise, a Room will get the standard
	// Ground.  (More precisely, Ground makes itself appear
	// in every room for which ground = true, which is the
	// default.)
	//
	// We want to have our own custom ground here, so we set
	// ground to be our own ground object.
	//
	// Note that if we wanted there to be no ground here at
	// all, we'd set ground to nil.
	//
	ground = floor

	//
	// Where the player goes from here
	//
	goSouth = room2

	//
	// The previous declaration sets the field goSouth to
	// the *object* room2.  WorldClass treats this as
	// equivalent to the following *code*:
	//
	// 	goSouth(actor) = { return room2; }
	//
	// The benefit to using an object instead of code is that
	// you can change an object at run-time, but you can't
	// change code at run-time.
	//
	// The benefit to using code instead of an object is that
	// the code form gives you the actor who's travelling.
	// Note that other actors may traverse the world via
	// the travel methods too; in fact, future versions of
	// WorldClass will likely provide code to control actors
	// that will work just this way.
	//
	// Also, WorldClass recognizes verGo<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 Transparent Container always passes light, whether it's open or not.
//
jar: Transparent, Openable, Item
	isopen = true
	sdesc = "jar"
	noun = 'jar'
	location = thermos
	locationtype = 'in'
;
lumenberries: Edible, Item
	sdesc = "lumenberries"
	isplural = true
	noun = 'lumenberries' 'berries'
	adjective = 'lumen'
	location = thermos
	locationtype = 'in'
;
mouse: Item
	sdesc = "happy little mouse <<note(self)>>"
	noun = 'mouse'
	adjective = 'happy' 'little'
	location = table
	locationtype = 'under'

	touchdesc = {
		"The mouse is warm and furry.";
	}

	//
	// A hand-numbered footnote.
	//
	footnum = 1
	footnote = {
		"Probably looking for some Cheez...";
	}
;

//
// Things can be nowhere.  In this case, set location = nil and
// locationtype to 'in'.  If you don't explicitly set locationtype,
// WorldClass will assume it's 'in'.  If you move things to nil,
// they automatically get put 'in' nil.  But style dictates that
// you should always do 
//	
//	obj.movein(nil);
//
// to move something nowhere.  Don't do
//
//	obj.moveunder(nil);	// BAD STYLE!
//
// or
//
// 	obj.movebehind(nil);	// BAD STYLE!
//
// or
//
//	obj.moveon(nil);      	// BAD STYLE!
//
// (Note the distinction between these move methods in WorldClass,
// whereas in ADV.T you always use moveInto.  You need to pay attention
// to containment types when using WorldClass!)
//
kee2: Key, Edible, Item
	sdesc = "spam kee"
	noun = 'kee' 'key'
 	adjective = 'spam'
	location = nil
;

//
// A set of useful standard furniture classes is provided, including:
//
// 	Table, Chair, Stool, Ledge, Bed, Desk, Shelf, Door
//
// Note that we also make the table and chair Fixtures.  This just
// means that their heredesc's should be printed in room descriptions.
// If we didn't make them Fixtures, we'd have to mention them
// explcitly in the room's ldesc.
//
// This table also shows how to use the movingout and movingin methods.
// The table just prints a message about each thing taken off of it.
//
table: Fixture, Table
	sdesc = "table"
	heredesc = {
		"A sturdy table stands in the middle of the room.";
	}
	noun = 'table'
	location = startroom

	//
	// As mentioned earlier, overriding verDoTake
	// is just as good as changing istakeable.  This
	// way (changing verDoTake) will be more readable
	// to those TADS programmers accustomed to ADV.T code.
	//
	verDoTake(actor) = {
		"The table's bolted to the floor.";
	}

	//
	// Don't let any actor put anything on the table
	// if the mouse is on the table.
	//
	movingout(obj, tolocation, toloctype) = {
		if (obj.locationtype = 'on')
			"\^<<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.";
	}
;

elderberries: Item
	sdesc = "elderberries"
	isplural = true
	noun = 'elderberries' 'berries'
	adjective = 'elder'
	location = monk
;
