#charset "Latin1"

/*
* Real NC Debug Actions Module, version 1.1
* Copyright (C) 2002, Nikos Chantziaras.
* Comments and bug reports should be sent to:
* [email protected]
*
* 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