#charset "Latin1"

/*
 * Real NC Debug Actions Module, version 1.1
 * Copyright (C) 2002, Nikos Chantziaras.
 * Comments and bug reports should be sent to:
 * realnc@lycos.de
 *
 * The copyright holder hereby grants the rights of usage, distribution
 * and modification of this software to everyone and for any purpose, as
 * long as this license and the copyright notice above are preserved and
 * not modified.  There is no warranty for this software.  By using,
 * distributing or modifying this software, you indicate that you accept
 * the terms and conditions of this license.
 */

/*
 * The documentation of this module is based on the documentation for
 * Neil K. Guy's and Neil deMause's "wizard.t" module for Tads 2.
 * Although this module is inspired by "wizard.t", no code from it is
 * used here, which is natural, since this is Tads 3 code.
 */

/*
 * Sometimes, when debugging an adventure, it's useful to have a small
 * set of special verbs that let you do things the player can't
 * ordinarily do.  These shortcuts can be extremely useful in cutting
 * down test time, as they help you skip sections of a game that you
 * know are fine.  Here are a few verbs that I've found quite helpful
 * for testing games.
 *
 * "Snarf" lets you pick up any takeable object, regardless of whether
 * it's visible and in scope or not.  "Zarvo" reports on the location
 * of any specified object.  "Pow" transports the player directly to
 * any location in the game.  And "Mega" and "Unmega" turn the player
 * into a glowing superhuman, or revert the player to normal.
 *
 * To use this module, just compile it together with your game's source
 * file(s).  Don't forget to compile for debugging (by using the
 * compiler's -d option).  When you compile for release, the module
 * won't be included in your game.
 *
 * Implemented and tested with Tads v3.0.5 using a GNU/Linux system.
 */

/*

Version history
===============

1.1
---

 - None of the actions can now be directed to actors other than the
   player character.  This is especially important for the pow action;
   pow would result in a run-time error if directed to an NPC.

 - Fixed the pow action.
   Powing to a location now calls the travelerLeaving() method of the
   current location and the travelerArriving() method of the
   destination.  Also, the powing is done with moveIntoForTravel(), not
   with moveInto().

 - Fixed the snarf action.
   The snarf action wasn't able to remove an object from a closed
   container if the player character and the container were both in the
   same location.

 - Intangible and Distant items are now allowed with pow and zarvo.

 - Fixed som stoopid end very embarasing dokumentation typoes and
   speling mistacles, althu meny ar steel thear. ;-)

 - Added version history.

1.0
---

 - ???

*/


#ifdef __DEBUG


#include <adv3.h>
#include <en_us.h>


/* --------------------------------------------------------------------
 * Snarf
 *
 * This verb lets you pick up any takeable item, even if it's not
 * accessible to you - say it's in a different room or inside a closed
 * container or whatever.
 *
 * I gave it the silly name "snarf" as "snarf" is not a verb I'm likely
 * to use in the actual game for anything.  You can, of course, change
 * this to something more serious.
 *
 * Essentially, all this verb does is call the dobjFor(Take) handler.
 * The reason I have it do that instead of, say, moving the object
 * directly into the player's inventory, is because this way I can rely
 * on the standard Take checks to ensure that the player isn't trying
 * to pick up a fixed or decoration object or whatever.  Also, the
 * player can't pick up more items than he or she normally can (unless
 * the "mega" verb has been used; see below.)
 *
 * However, "snarf" differs from the normal take verb in that instead
 * of checking to see if the object in question is in scope or not, it
 * simply lets you take any object that's anywhere in the game.  It
 * does this by overriding the objInScope() method of TAction.  Snarf
 * also doesn't care if the object is in a closed container.  This is
 * done by modifying the checkMoveViaPath() method of Container.
 * Furthermore, the only preconditions for snarf are objNotWorn (the
 * object must not currently being worn) and roomToHoldObj (there must
 * be enough room for the object in the PC's inventory).
 */

DefineTAction(Snarf)
	// We want all objects to be in scope, so we always return true
	// here.
	objInScope( obj ) { return true; }

	execAction()
	{
		if (!gActor.isPlayerChar) {
			libMessages.systemActionToNPC();
			exit;
		}
		inherited;
	}
;

VerbRule(Snarf)
	'snarf' dobjList
	: SnarfAction
	verbPhrase = 'snarf/snarfing (what)'
;

modify Thing {
	dobjFor(Snarf)
	{
		// We do not require the object to be touchable to snarf it.
		// But we still require that it's not being worn, and that the
		// PC has enough room in his inventory to hold it.
		preCond = [objNotWorn, roomToHoldObj];

		// Everything else works the same as with the Take action.
		verify() { verifyDobjTake(); }
		remap() { return remapDobjTake(); }
		check() { checkDobjTake(); }
		action() { actionDobjTake(); }
	}
}

modify Container {
	// checkMoveViaPath() is defined in the Thing class, but the
	// Container class overrides this method to check if the container
	// is closed and, if it is, disallows the action.  Since snarf must
	// be able to take objects even from closed containers, we need to
	// modify this method.
	checkMoveViaPath( obj, dest, op )
	{
		// Allow the operation if the current action is snarf.
		if (gActionIs(Snarf)) return checkStatusSuccess;

		// Otherwise, do whatever was the default.
		return inherited(obj, dest, op);
	}
}


/* --------------------------------------------------------------------
 * Zarvo
 *
 * Another useful verb with a silly name.  Zarvo lets you find an
 * object anywhere in the game without touching it.  Zarvoing an object
 * simply prints that object's current location's `name' property.
 */

DefineTAction(Zarvo)
	objInScope( obj ) { return true; }

	execAction()
	{
		if (!gActor.isPlayerChar) {
			libMessages.systemActionToNPC();
			exit;
		}
		inherited;
	}
;

VerbRule(Zarvo)
	('zarvo' | 'v') dobjList
	: ZarvoAction
	verbPhrase = 'zarvo/zarvoing (what)'
;

modify Thing {
	dobjFor(Zarvo)
	{
		preCond = [];

		verify() {}
		check() {}

		action()
		{
			"\^<<self.theName>> <<self.verbToBe>> ";
			if (!self.location) {
				"floating around in the nil outer space. ";
			} else {
				"in the location called <q><<self.location.name>></q>. ";
			}
		}
	}
}

/* We want Decoration, Intangible and Distant items to work with zarvo,
 * so we also define dobjFor(Zarvo) handlers for them.
 */
modify Decoration {
	dobjFor(Zarvo)
	{
		// Just do what we defined in Thing.
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}

modify Intangible {
	dobjFor(Zarvo)
	{
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}

modify Distant {
	dobjFor(Zarvo)
	{
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}


/* --------------------------------------------------------------------
 * Pow
 *
 * This is a particularly useful verb when testing that zaps you
 * directly to a specific location as though you'd teleported there.
 * Saves a lot of walking around.  To use it, simply type "pow"
 * followed by the location to which you wish to pow, or by an object
 * that is contained either directly or indirectly in this location, so
 * that typing "pow item" for non-room objects will take you to that
 * object's location.  This means that you can "pow" to any room with
 * at least one object in it, even if you haven't taken the step of
 * assigning nouns to your rooms.  If the specified object is nested
 * inside multiple containers, we simply traverse the locations
 * upwards, until we find one that can contain actors.  Only
 * BasicLocation objects can do that, so we simply pow the PC to the
 * first BasicLocation we found.
 *
 * We require that the player isn't seated (by specifying the
 * actorStanding precondition) to avoid problems.  This means that the
 * PC will attempt to stand up before we pow him to the new location.
 * Then we transport them directly to the chosen location by using the
 * actor's moveInto() method.  In homage to the classic "Adventure", we
 * surround the player with a nice orange cloud of smoke.
 *
 * There's one more time-consuming thing that you, as implementor, have
 * to do to be able to use this verb directly with rooms.  And that is
 * you have to assign appropriate nouns and adjectives to each room you
 * want to be powable.  If you don't, then the only way to pow to a
 * location is by using an item in that location as the direct object
 * of the verb.  This can be a bit awkward, since interactive
 * disambiguation is sometimes impossible with some objects, most
 * probably with decoration and fixed objects ("Which door do you mean,
 * the door, the door, the door, or the door?").
 *
 * So, how should you assign vocabulary words to your rooms?  They
 * shouldn't be available in the release version of your game, only in
 * the debug version.  Tads 3 defines the preprocessor symbol "__DEBUG"
 * when it compiles a game with debug information.  You could simply
 * define a macro like this:
 *
 *   #ifdef __DEBUG
 *     #define dbgNoun(x) vocabWords_ = x
 *   #else
 *     #define dbgNoun(x)
 *   #endif
 *
 * And assign nouns to your rooms like this:
 *
 *   kitchen: Room {
 *     'Kitchen'  'the kitchen'
 *     "You are not in front of a white house, but in the kitchen of a
 *     yellow one. "
 *     dbgNoun('kitchen-room');
 *   }
 *
 * Now you can pow to the kitchen like this:
 *
 *   >POW KITCHEN-ROOM
 */

DefineTAction(Pow)
	objInScope( obj ) { return true; }

	execAction()
	{
		if (!gActor.isPlayerChar) {
			libMessages.systemActionToNPC();
			exit;
		}
		inherited;
	}
;

VerbRule(Pow)
	'pow' singleDobj | 'pow' 'to' singleDobj
	: PowAction
	verbPhrase = 'pow/powing to (where)'
;

modify Thing {
	dobjFor(Pow)
	{
		// We must be standing to be able to pow.
		preCond = [actorStanding];

		verify() {}

		check() {}

		action()
		{
			// Destination.
			local dest = gDobj.ofKind(BasicLocation) ? gDobj : gDobj.location;

			// We do a "location rewind" until we find a location that
			// can contain actors.
			while (dest) {
				if (dest == gPlayerChar.location) {
					// The location we found is the same as the PC's
					// current location.  No need to pow.
					"You are already there. ";
					return;
				}
				if (dest.ofKind(BasicLocation)) {
					// We found a BasicLocation.  Let's pow there.
					"You are engulfed in a cloud of orange smoke.  Coughing and gasping, you
					emerge from the smoke and find that your surroundings have changed... ";

					// Remember my original location - this is the
					// location where the PC was before he powed.
					local origin = gPlayerChar.location;

					// Tell the old room we're leaving.
					if (origin) origin.travelerLeaving(gPlayerChar, dest, nil);

					// Move to the destination.
					gPlayerChar.moveIntoForTravel(dest);

					// Tell the new room we're arriving, if we changed
					// locations.
					if (gPlayerChar.location)
						gPlayerChar.location.travelerArriving(gPlayerChar, origin, nil, nil);

					// We're done.
					return;
				}
				// One level up.
				dest = dest.location;
			}
			// We didn't find a BasicLocation.  We can't pow.
			"I can't pow you there. ";
		}
	}
}

/* We want Decoration, Intangible and Distant items to work with pow,
 * so we also define dobjFor(Pow) handlers for them.
 */
modify Decoration {
	dobjFor(Pow)
	{
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}

modify Intangible {
	dobjFor(Pow)
	{
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}

modify Distant {
	dobjFor(Pow)
	{
		preCond() { return inherited; }
		verify() { inherited; }
		check() { inherited; }
		action() { inherited; }
	}
}


/* --------------------------------------------------------------------
 * Mega
 *
 * Sometimes, in a game, you need to pick up and carry a whole plethora
 * of items, as the standard definition for the player character object
 * has set limits as to the number of objects (bulkCapacity), the
 * maximum bulk of any one object (maxSingleBulk) and the total weight
 * of objects (weightCapacity) the player can carry.  This is obviously
 * important from both a realism standpoint and also a puzzle-making
 * one.  However, it can be a total pain in the neck when testing.
 *
 * One of the features of the mega verb is that it increases the
 * strength and carrying ability of the player to superhuman levels.
 * Use the unmega command to restore the player to his or her normal
 * abilities.
 *
 * The other feature of the verb involves dark rooms.  Sometimes, when
 * playing, it's a hassle to have to carry a torch or other light
 * source all the time.  However, through the miracle of the mega verb,
 * the player can set him or herself literally glowing, thereby
 * obviating the light source problem.  People who've played any of the
 * Infocom fantasies will recognize this as a kind of "frotz me"
 * command.  Again, unmega makes the player normal once more.
 */

class MegaAction: IAction {
	// Is the PC currently in Mega mode? (Class property)
	playerIsMega = nil;

	// We save the old values so that we can restore them later with
	// the Unmega action. (Class properties)
	oldBulkCapacity   = gPlayerChar.bulkCapacity;
	oldMaxSingleBulk  = gPlayerChar.maxSingleBulk;
	oldWeightCapacity = gPlayerChar.weightCapacity;
	oldBrightness     = gPlayerChar.brightness;

	execAction()
	{
		if (!gActor.isPlayerChar) {
			libMessages.systemActionToNPC();
			exit;
		}

		if (MegaAction.playerIsMega) {
			"You are already imbued with superhuman abilities. ";
		} else {
			MegaAction.oldBulkCapacity   = gPlayerChar.bulkCapacity;
			MegaAction.oldMaxSingleBulk  = gPlayerChar.maxSingleBulk;
			MegaAction.oldWeightCapacity = gPlayerChar.weightCapacity;
			MegaAction.oldBrightness     = gPlayerChar.brightness;

			gPlayerChar.bulkCapacity   = 65535;
			gPlayerChar.maxSingleBulk  = 65535;
			gPlayerChar.weightCapacity = 65535;
			gPlayerChar.brightness     = 4;

			MegaAction.playerIsMega = true;

			"Phreeeow! You suddenly have superhuman strength, and you\'re surrounded by a
			strange ethereal glow which permits you to enter darkened places with impunity! ";
		}
	}
}

VerbRule(Mega)
	'mega'
	: MegaAction
	verbPhrase = 'mega/transforming you to a glowing superhuman'
;


/* --------------------------------------------------------------------
 * Unmega
 *
 * Reverses the effects of Mega.
 */

class UnmegaAction: IAction {
	execAction()
	{
		if (!gActor.isPlayerChar) {
			libMessages.systemActionToNPC();
			exit;
		}

		if (!MegaAction.playerIsMega) {
			"You\'re already a mere mortal! ";
		} else {
			gPlayerChar.bulkCapacity   = MegaAction.oldBulkCapacity;
			gPlayerChar.maxSingleBulk  = MegaAction.oldMaxSingleBulk;
			gPlayerChar.weightCapacity = MegaAction.oldWeightCapacity;
			gPlayerChar.brightness     = MegaAction.oldBrightness;

			MegaAction.playerIsMega = nil;

			"Phreeeow! You\'re a puny human once more! You\'re also no longer doing that human
			lightbulb thing. ";
		}
	}
}

VerbRule(Unmega)
	'unmega'
	: UnmegaAction
	VerbPhrase = 'unmega/transforming you to a puny human'
;


#endif // __DEBUG

