Game Development Community

dev|Pro Game Development Curriculum

Advanced AI for TGE 1.4

by Jeff Loveless · 08/17/2006 (12:15 pm) · 15 comments

Download Code File

Ok here's my first contribution back to the commuinity. I can't take very much credit other than for dusting off an old resource and updating it to work with the current build of TGE. I did however add a little to the behavior of the AI Players...

If you have a path on your map, a bot will exist as soon as the mission loads, its name is Korky, it is not your friend.
You can also spawn a bot with Ctrl-B (as in the original resource)

(its fairly cut and dry to copy all the functions in the aiplayer.cs script and change the names to add different classes of bots with different models, speeds, attributes, etc..)

Once ctrl-b is pressed a bot will be spawned on a path in your mission and proceed to follow it, if you have more then one path it will randomly choose from the paths available before spawning.

Once a bot "sees" you it will engage in combat, it will battle you to the death, grabbing health or more ammo has not yet been implimented. If it wins the battle it will return to its path and continue following it. Feel free to add to this and please update it with more features, remember to give back to the community.

Thanks to JimOMighty, Beffy, Ace-NoESCape, for the work they did on this before I found it.

You'll need to add the .cc and .h files to your project and add the .cs to your scripts in client and server directories, re-build, delete DSO's and you're done! I'll also include all the code here in the resource for those that want to see it first.

Either overwrite the files or theres a very very handy program for just this occasion called WinMerge, its pretty self explanitory.

SDK/Engine/Game/
AiPlayer.cc
//-----------------------------------------------------------------------------
// Torque Game Engine
// 
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "game/aiPlayer.h"
#include "console/consoleInternal.h"
#include "core/realComp.h"
#include "math/mMatrix.h"
#include "game/moveManager.h"

// added for Jimomighty stuff...
#include "game/gameConnection.h"

IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);

/**
 * Constructor
 */
AIPlayer::AIPlayer()
{
   mMoveDestination.set( 0.0f, 0.0f, 0.0f );
   mMoveSpeed = 1.0f;
   mMoveTolerance = 0.25f;
   mMoveSlowdown = true;
   mMoveState = ModeStop;

   mAimObject = 0;
   mAimLocationSet = false;
   mTargetInLOS = false;
   mAimOffset = Point3F(0.0f, 0.0f, 0.0f);

   mTypeMask |= AIObjectType;

	// Jimomighty
	mScanningPlayers = false;
	mScanningPlayersDistance = 100.0f;
	mFollowTargetInLOS = false;
	mFollowTargetObject = NULL;
	mAttackTargetInLOS = false;
	mAttackTargetObject = NULL;
	mJump = true;
	mModeFire = false;
	mModeJump = false;
}

/**
 * Destructor
 */
AIPlayer::~AIPlayer()
{
}

/**
 * Sets the speed at which this AI moves
 *
 * @param speed Speed to move, default player was 10
 */
void AIPlayer::setMoveSpeed( F32 speed )
{
   mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));  
}
//ace
void AIPlayer::setSite( F32 site ) 
{
mScanningPlayersDistance = site;
}
//endace
/**
 * Stops movement for this AI
 */
void AIPlayer::stopMove()
{
   mMoveState = ModeStop;
}

/**
 * Sets how far away from the move location is considered
 * "on target"
 * 
 * @param tolerance Movement tolerance for error
 */
void AIPlayer::setMoveTolerance( const F32 tolerance )
{
   mMoveTolerance = getMax( 0.1f, tolerance );
}

/**
 * Sets the location for the bot to run to
 *
 * @param location Point to run to
 */
void AIPlayer::setMoveDestination( const Point3F &location, bool slowdown )
{
   mMoveDestination = location;
   mMoveState = ModeMove;
   mMoveSlowdown = slowdown;
   //trying to fix bot aim locations following paths
   //if (!mAimLocationSet) {
      mAimLocationSet = true;
      mAimLocation = location;
   //}
}

/**
 * Sets the object the bot is targeting
 *
 * @param targetObject The object to target
 */
void AIPlayer::setAimObject( GameBase *targetObject )
{   
   mAimObject = targetObject;
   mTargetInLOS = false;
   mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}

/**
 * Sets the object the bot is targeting and an offset to add to target location
 *
 * @param targetObject The object to target
 * @param offset       The offest from the target location to aim at
 */
void AIPlayer::setAimObject( GameBase *targetObject, Point3F offset )
{
   mAimObject = targetObject;
   mTargetInLOS = false;
   mAimOffset = offset;
}

/**
 * Sets the location for the bot to aim at
 * 
 * @param location Point to aim at
 */
void AIPlayer::setAimLocation( const Point3F &location )
{
   mAimObject = 0;
   mAimLocationSet = true;
   mAimLocation = location;
   mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}

/**
 * Clears the aim location and sets it to the bot's
 * current destination so he looks where he's going
 */
void AIPlayer::clearAim() 
{
   mAimObject = 0;
   mAimLocationSet = false;
   mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}

/**
 * This method calculates the moves for the AI player
 *
 * @param movePtr Pointer to move the move list into
 */
bool AIPlayer::getAIMove(Move *movePtr)
{
   *movePtr = NullMove;

   // Use the eye as the current position.
   MatrixF eye;
   getEyeTransform(&eye);
   Point3F location = eye.getPosition();
   Point3F rotation = getRotation();

   // Orient towards the aim point, aim object, or towards
   // our destination.
   if (mAimObject || mAimLocationSet || mMoveState == ModeMove) {

      // Update the aim position if we're aiming for an object
      if (mAimObject)
         mAimLocation = mAimObject->getPosition() + mAimOffset;
      else
         if (!mAimLocationSet)
            mAimLocation = mMoveDestination;

      F32 xDiff = mAimLocation.x - location.x;
      F32 yDiff = mAimLocation.y - location.y;
      if (!isZero(xDiff) || !isZero(yDiff)) {

         // First do Yaw
         // use the cur yaw between -Pi and Pi
         F32 curYaw = rotation.z;
         while (curYaw > M_2PI)
            curYaw -= M_2PI;
         while (curYaw < -M_2PI)
            curYaw += M_2PI;

         // find the yaw offset
         F32 newYaw = mAtan( xDiff, yDiff );
         F32 yawDiff = newYaw - curYaw;

         // make it between 0 and 2PI
         if( yawDiff < 0.0f )
            yawDiff += M_2PI;
         else if( yawDiff >= M_2PI )
            yawDiff -= M_2PI;

         // now make sure we take the short way around the circle
         if( yawDiff > M_PI )
            yawDiff -= M_2PI;
         else if( yawDiff < -M_PI )
            yawDiff += M_2PI;

         movePtr->yaw = yawDiff;

         // Next do pitch.
         if (!mAimObject && !mAimLocationSet) {
            // Level out if were just looking at our next way point.
            Point3F headRotation = getHeadRotation();
            movePtr->pitch = -headRotation.x;
         }
         else {
            // This should be adjusted to run from the
            // eye point to the object's center position. Though this
            // works well enough for now.
            F32 vertDist = mAimLocation.z - location.z + 1.0f;
            F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
            F32 newPitch = mAtan( horzDist, vertDist ) - ( M_PI / 2.0f );
            if (mFabs(newPitch) > 0.01) {
               Point3F headRotation = getHeadRotation();
               movePtr->pitch = newPitch - headRotation.x;
            }
         }
		// for Jimomighty's functions
		if (mModeFire) {
			const char *WeaponSlot = Con::getVariable("$WeaponSlot");
			S32 slot = dAtoi(WeaponSlot);

			if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
				if (getMountedImage(slot)) {
					
					if (!movePtr->trigger[slot]) {
						// shoot trigger
						movePtr->trigger[slot] = true;
					}
				}
			}
		}
		if (mModeJump) {
			if (canJump()) {
				if (!movePtr->trigger[2]) {
					movePtr->trigger[2] = true;
				}
			}
		}
		// end Jimomighty stuff

      }
   }
   else {
      // Level out if we're not doing anything else
      Point3F headRotation = getHeadRotation();
      movePtr->pitch = -headRotation.x;
   }

   // Move towards the destination
   if (mMoveState == ModeMove) {
      F32 xDiff = mMoveDestination.x - location.x;
      F32 yDiff = mMoveDestination.y - location.y;

      // Check if we should mMove, or if we are 'close enough'
      if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) {
         mMoveState = ModeStop;
         throwCallback("onReachDestination");
      }
      else {
         // Build move direction in world space
         if (isZero(xDiff))
            movePtr->y = (location.y > mMoveDestination.y)? -1 : 1;
         else
            if (isZero(yDiff))
               movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
            else
               if (mFabs(xDiff) > mFabs(yDiff)) {
                  F32 value = mFabs(yDiff / xDiff);
                  movePtr->y = (location.y > mMoveDestination.y)? -value : value;
                  movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
               }
               else {
                  F32 value = mFabs(xDiff / yDiff);
                  movePtr->x = (location.x > mMoveDestination.x)? -value : value;
                  movePtr->y = (location.y > mMoveDestination.y)? -1 : 1;
               }

         // Rotate the move into object space (this really only needs
         // a 2D matrix)
         Point3F newMove;
         MatrixF moveMatrix;
         moveMatrix.set(EulerF(0, 0, -(rotation.z + movePtr->yaw)));
         moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0 ), &newMove );
         movePtr->x = newMove.x;
         movePtr->y = newMove.y;

         // Set movement speed.  We'll slow down once we get close
         // to try and stop on the spot...
         if (mMoveSlowdown) {
            F32 speed = mMoveSpeed;
            F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
            F32 maxDist = 5;
            if (dist < maxDist)
               speed *= dist / maxDist;
            movePtr->x *= speed;
            movePtr->y *= speed;
         }
		 else {
            movePtr->x *= mMoveSpeed;
            movePtr->y *= mMoveSpeed;
		 }

         // We should check to see if we are stuck...
         if (location == mLastLocation) {
            throwCallback("onMoveStuck");
            mMoveState = ModeStop;
         }
      }
   }

   // Test for target location in sight if it's an object. The LOS is
   // run from the eye position to the center of the object's bounding,
   // which is not very accurate.
   if (mAimObject) {
      MatrixF eyeMat;
      getEyeTransform(&eyeMat);
      eyeMat.getColumn(3,&location);
      Point3F targetLoc = mAimObject->getBoxCenter();

      // This ray ignores non-static shapes. Cast Ray returns true
      // if it hit something.
      RayInfo dummy;
      if (getContainer()->castRay( location, targetLoc,
            InteriorObjectType | StaticShapeObjectType | StaticObjectType |
            TerrainObjectType, &dummy)) {
         if (mTargetInLOS) {
            throwCallback( "onTargetExitLOS" );
            mTargetInLOS = false;
         }
      }
      else
         if (!mTargetInLOS) {
            throwCallback( "onTargetEnterLOS" );
            mTargetInLOS = true;
         }
   }

   // Replicate the trigger state into the move so that
   // triggers can be controlled from scripts.
   // beffy - commented out for Jimomighty stuff!
   //   delete beffy's comment if it still doesn't work
   //for( int i = 0; i < MaxTriggerKeys; i++ )
   //   movePtr->trigger[i] = getImageTriggerState(i);

   return true;
}

/**
 * Utility function to throw callbacks. Callbacks always occure
 * on the datablock class.
 *
 * @param name Name of script function to call
 */
void AIPlayer::throwCallback( const char *name )
{
   Con::executef(getDataBlock(), 2, name, scriptThis());
}


// --------------------------------------------------------------------------------------------
// Console Functions
// --------------------------------------------------------------------------------------------

/**
 * Stop the player
 */
ConsoleMethod( AIPlayer, stop, void, 2, 2, "ai.stopMove();" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   ai->stopMove();
}

/**
 * Stop the player aiming
 */
ConsoleMethod( AIPlayer, clearAim, void, 2, 2, "ai.clearAim();" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   ai->clearAim();
}

/**
 * Sets the move speed for an AI object
 */
ConsoleMethod( AIPlayer, setMoveSpeed, void, 3, 3, "ai.setMoveSpeed( float );" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   ai->setMoveSpeed( dAtof( argv[2] ) );
}
ConsoleMethod( AIPlayer, setSite, void, 3, 3, "ai.setSite( float );" ) // Sabrecyd
{
AIPlayer *ai = static_cast<AIPlayer *>( object );
ai->setSite( dAtof( argv[2] ) );
}

/**
 * Tells the AI to move to the location provided
 */
ConsoleMethod( AIPlayer, setMoveDestination, void, 3, 4, "ai.setMoveDestination( \"x y z\" );" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   Point3F v( 0.0f, 0.0f, 0.0f );
   dSscanf( argv[2], "%g %g %g", &v.x, &v.y, &v.z );
   bool slowdown = (argc > 3)? dAtob(argv[3]): true;
   ai->setMoveDestination( v, slowdown );
}

/**
 * Returns the point the AI is set to move to
 */
ConsoleMethod( AIPlayer, getMoveDestination, const char *, 2, 2, "ai.getMoveDestination();" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   Point3F movePoint = ai->getMoveDestination();

   char *returnBuffer = Con::getReturnBuffer( 256 );
   dSprintf( returnBuffer, 256, "%g %g %g", movePoint.x, movePoint.y, movePoint.z );

   return returnBuffer;
}

/**
 * Tells the AI to aim at the location provided
 */
ConsoleMethod( AIPlayer, setAimLocation, void, 3, 3, "ai.setAimLocation( \"x y z\" );" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   Point3F v( 0.0f,0.0f,0.0f );
   dSscanf( argv[2], "%g %g %g", &v.x, &v.y, &v.z );

   ai->setAimLocation( v );
}

/**
 * Returns the point the AI is aiming at
 */
ConsoleMethod( AIPlayer, getAimLocation, const char *, 2, 2, "ai.getAimLocation();" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   Point3F aimPoint = ai->getAimLocation();

   char *returnBuffer = Con::getReturnBuffer( 256 );
   dSprintf( returnBuffer, 256, "%g %g %g", aimPoint.x, aimPoint.y, aimPoint.z );

   return returnBuffer;
}

/**
 * Sets the bots target object
 */
ConsoleMethod( AIPlayer, setAimObject, void, 3, 4, "ai.setAimObject( obj, [Point3F offset] );" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   Point3F off( 0.0f, 0.0f, 0.0f );
      
   // Find the target
   GameBase *targetObject;
   if( Sim::findObject( argv[2], targetObject ) )
   {
      if (argc == 4)
         dSscanf( argv[3], "%g %g %g", &off.x, &off.y, &off.z );

      ai->setAimObject( targetObject, off );
   }
   else
      ai->setAimObject( 0 );
}

/**
 * Gets the object the AI is targeting
 */
ConsoleMethod( AIPlayer, getAimObject, S32, 2, 2, "ai.getAimObject();" )
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   GameBase* obj = ai->getAimObject();
   return obj? obj->getId(): -1;
}


// --------------------------------------------------------------------------------------
// Jimomighty stuff
// --------------------------------------------------------------------------------------

// beffy - added to have constant updates
void AIPlayer::processTick(const Move* move)
{
		// Jimomighty stuff
		doActivities();
		if (getAttackTargetObject())
			attackTarget();

		if (getFollowTargetObject())
			followTarget();


	Parent::processTick(move);
}


/**
 * Do basic activities.
 */
void AIPlayer::doActivities() { // Jimomighty

	if (getStateName() == "Dead") {
		clearAllTargets(); // HACK: This should be directly called when player dies.

		return;
	}

	checkTargetLOS();
	scanningPlayers();
}

/**
 * Returns the follow target object
 *
 * @return Object bot is following
 */
S32 AIPlayer::getTargetDistance() const { // Jimomighty
	ShapeBase *targetObject;
	VectorF v1(0,0,0), v2(0,0,0);
	v1 = getPosition();

	if (getAttackTargetObject() && Sim::findObject( getAttackTargetObject(), targetObject ) ) {
		
		v2 = targetObject->getTransform().getPosition();
	} else if (getFollowTargetObject() && Sim::findObject( getFollowTargetObject(), targetObject ) ) {
		v2 = targetObject->getTransform().getPosition();
	} else {
		return -1;
	}

	VectorF v = v2 - v1;
	float dist = mSqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));

	return dist;
}

/**
 * Clears all primary targets the bot has.
 */
void AIPlayer::clearAllTargets() { // Jimomighty
	setScanningPlayers(false);
	clearAttackTarget();
	clearFollowTarget();

   // Fail-case:
	stopMove(); // Stop Moving
	clearAim(); // Clear any aiming.
}

/**
 * Clears all action targets the bot has.
 */
void AIPlayer::clearActionTargets() { // Jimomighty
	clearAttackTarget();
	clearFollowTarget();

   // Fail-case:
	stopMove(); // Stop Moving
	clearAim(); // Clear any aiming.
}

/**
 * Clear attack target.
 */
void AIPlayer::clearAttackTarget() { // Jimomighty
   mAttackTargetInLOS = false; // Reset LOS set.
   mAttackTargetObject = NULL; // Reset target.

	fireWeapon(false); // Make sure the AI has stoped fireing.
}

/**
 * Clear follow target.
 */
void AIPlayer::clearFollowTarget() { // Jimomighty
   mFollowTargetInLOS = false; // Reset LOS set.
   mFollowTargetObject = NULL; // Reset target.
}

/**
 * Checks all LOS on targets.
 */
void AIPlayer::checkTargetLOS() { // Jimomighty

	RayInfo RayEvent;
	U32 mask = TerrainObjectType | InteriorObjectType | VehicleObjectType | StaticShapeObjectType | PlayerObjectType;
	ShapeBase *targetObject;

	// Check the follow target LOS.
	if (mFollowTargetObject) {
		if( Sim::findObject( getFollowTargetObject(), targetObject ) ) {
			Player* objPlayer = static_cast<Player*>(targetObject);

			MatrixF playereyeTransform;
			getEyeTransform(&playereyeTransform);
			MatrixF targetObjecteyeTransform;
			targetObject->getEyeTransform(&targetObjecteyeTransform);

			// We do two ray casts. We try to see if we can see two points either the top or bottom of the target.
			disableCollision(); // We want to exclude the bot.

			if (getContainer()->castRay( playereyeTransform.getPosition(), targetObjecteyeTransform.getPosition(), mask, &RayEvent )) { // Check if we see the bottom.
				if ( RayEvent.object == mFollowTargetObject ) {
					// We can see him.
					if (!mFollowTargetInLOS) {
						mFollowTargetInLOS = true;
						throwCallback( "onFollowTargetEnterLOS" );
					}

				} else if (getContainer()->castRay( playereyeTransform.getPosition(), targetObject->getTransform().getPosition(), mask, &RayEvent )) { // Check if we see the top.
					if ( RayEvent.object == mFollowTargetObject ) {
						// We can see him.
						if (!mFollowTargetInLOS) {
							mFollowTargetInLOS = true;
							throwCallback( "onFollowTargetEnterLOS" );
						}

					} else {
						// We can't see him.
						if (mFollowTargetInLOS) {
							mFollowTargetInLOS = false;
							throwCallback( "onFollowTargetExitLOS" );
						}

					}
				} else {
					// We can't see him.
					if (mFollowTargetInLOS) {
						mFollowTargetInLOS = false;
						throwCallback( "onFollowTargetExitLOS" );
					}
				}
			} else { // There is no players so we can't see him!

				if (mFollowTargetInLOS) {
					mFollowTargetInLOS = false;
					throwCallback( "onFollowTargetExitLOS" );
				}

			}
			enableCollision();

			// We should clear this.
			objPlayer = NULL;
		}
	}

	// We should clear this.
	targetObject = NULL;

	// Check the attack target LOS.
	if (mAttackTargetObject) {
		if( Sim::findObject( getAttackTargetObject(), targetObject ) ) {
			Player* objPlayer = static_cast<Player*>(targetObject);

			MatrixF playereyeTransform;
			getEyeTransform(&playereyeTransform);
			MatrixF targetObjecteyeTransform;
			targetObject->getEyeTransform(&targetObjecteyeTransform);

			// We do two ray casts. We try to see if we can see two points either the top or bottom of the target.
			disableCollision(); // We want to exclude the bot.

			if (getContainer()->castRay( playereyeTransform.getPosition(), targetObjecteyeTransform.getPosition(), mask, &RayEvent )) { // Check if we see the bottom.
				if ( RayEvent.object == mAttackTargetObject ) {
					// We can see him.
					if (!mAttackTargetInLOS) {
						mAttackTargetInLOS = true;
						throwCallback( "onAttackTargetEnterLOS" );
					}

				} else if (getContainer()->castRay( playereyeTransform.getPosition(), targetObject->getTransform().getPosition(), mask, &RayEvent )) { // Check if we see the top.
					if ( RayEvent.object == mAttackTargetObject ) {
						// We can see him.
						if (!mAttackTargetInLOS) {
							mAttackTargetInLOS = true;
							throwCallback( "onAttackTargetEnterLOS" );
						}

					} else {
						// We can't see him.
						if (mAttackTargetInLOS) {
							mAttackTargetInLOS = false;
							throwCallback( "onAttackTargetExitLOS" );
						}

					}
				} else {
					// We can't see him.
					if (mAttackTargetInLOS) {
						mAttackTargetInLOS = false;
						throwCallback( "onAttackTargetExitLOS" );
					}
				}
			} else { // There is no players so we can't see him!

				if (mAttackTargetInLOS) {
					mAttackTargetInLOS = false;
					throwCallback( "onAttackTargetExitLOS" );
				}

			}
			enableCollision();

			// We should clear this.
			// objPlayer = NULL;
		}
	}

	// We should clear this.
	// targetObject = NULL;

	// When adding more, uncomment above!
}

/**
 * Tells AI (not)to scan.
 * if true it will hunt and kill every player that comes into sight :P
 */
void AIPlayer::setScanningPlayers(bool enable) { // Jimomighty
	if (enable) {
		mScanningPlayers = true;
	} else {
		mScanningPlayers = false;
	}
}

/**
 * Tells the AI to scan for targets.
 */
void AIPlayer::scanningPlayers() { // Jimomighty
   // SCAN!
		if (mScanningPlayers && !mAttackTargetObject) {
			F32 tagDistance = mScanningPlayersDistance;
			Player *tagPlayer = NULL;

			VectorF v1(0,0,0), v2(0,0,0);
			v1 = getPosition();

			SimGroup *g = Sim::getClientGroup();
			SimGroup::iterator j;
			for (j = g->begin(); j != g->end(); j++)
			{
				GameConnection *client = static_cast<GameConnection*>(*j);
				Player *player = NULL;
				if (! client->getControlObject())
					continue;
   
				player = dynamic_cast<Player*>(client->getControlObject());   
				if (! player)
					continue;

				// Istead of follow it should be team but thats not done yet.
				//if (mPlayer != player && mFollowTargetObject != player) {
				if (mFollowTargetObject != player) {
					v2 = player->getTransform().getPosition();

					VectorF v = v2 - v1;
					float dist = mSqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));

					if ( dist <= mScanningPlayersDistance && dist < tagDistance) {
						tagDistance = dist;
						tagPlayer = player;
					}
				}
			}

			// We have a player?
			if (tagPlayer) {
				clearAttackTarget();
				setAttackTargetObject( tagPlayer );
			}
		}
}

/**
 * Sets the object the bot is targeting
 *
 * @param targetObject The object to target
 */
void AIPlayer::setFollowTargetObject( ShapeBase *followtargetObject ) { // Jimomighty
	clearActionTargets();

   mFollowTargetObject = followtargetObject;
}

/**
 * Returns the follow target object
 *
 * @return Object bot is following
 */
S32 AIPlayer::getFollowTargetObject() const { // Jimomighty 
   if( bool( mFollowTargetObject ) )
      return mFollowTargetObject->getId();
   else
      return -1;
}

/**
 * Tells the AI to follow the target.
 */
void AIPlayer::followTarget() { // Jimomighty
   // We have to have a follow target object.

	if (!mAttackTargetObject) // Can't work without a player!
		if (mFollowTargetObject != NULL) {
			// Find the target
			ShapeBase *targetObject;
			if( Sim::findObject( getFollowTargetObject(), targetObject ) ) {
				Player* objPlayer = static_cast<Player*>(targetObject);
				if (objPlayer->getStateName() == "Dead") {
					throwCallback( "onFollowTargetKilled" );

					clearFollowTarget();
					return;
				}

				Point3F location;
				MatrixF const &objectTransform = targetObject->getTransform();
				MatrixF const &myTransform = getTransform();

				objectTransform.getColumn( 3, &location );

				if ( mFabs ( myTransform.getPosition().x - location.x ) > 15 || mFabs ( myTransform.getPosition().y - location.y ) > 15 ) {
					if (myTransform.getPosition().x > location.x)
						location.x += 4.5f;
					else
						location.x -= 4.5f;

					if (myTransform.getPosition().y > location.y)
						location.y += 4.5f;
					else
						location.y -= 4.5f;

					setAimLocation( objectTransform.getPosition() );
					setMoveDestination( location, false );
				}
			}
		}
}

/**
 * Sets the object the bot is targeting
 *
 * @param targetObject The object to target
 */
void AIPlayer::setAttackTargetObject( ShapeBase *attacktargetObject ) { // Jimomighty
	clearActionTargets();

   mAttackTargetObject = attacktargetObject;
}

/**
 * Returns the attack target object
 *
 * @return Object bot is attacking
 */
S32 AIPlayer::getAttackTargetObject() const { // Jimomighty 
   if( bool( mAttackTargetObject ) )
      return mAttackTargetObject->getId();
   else
      return -1;
}

/**
 * Tells the AI to attack the target.
 */
void AIPlayer::attackTarget() { // Jimomighty
   // We have to have a attack target object.

	if (mAttackTargetObject != NULL) {
		// Find the target
		ShapeBase *targetObject;
		if( Sim::findObject( getAttackTargetObject(), targetObject ) ) {
			Player* objPlayer = static_cast<Player*>(targetObject);
			if (objPlayer->getStateName() == "Dead") {
				throwCallback( "onAttackTargetKilled" );
				clearAttackTarget();
				return;
			}

			Point3F location;
			MatrixF const &objectTransform = targetObject->getTransform();
			MatrixF const &myTransform = getTransform();

			objectTransform.getColumn( 3, &location );

			// Not to far.
			if ( mFabs ( myTransform.getPosition().x - location.x ) > 20 || mFabs ( myTransform.getPosition().y - location.y ) > 20 ) {
				fireWeapon(false);

				if (myTransform.getPosition().x > location.x)
					location.x += 10.0f;
				else
					location.x -= 10.0f;

				if (myTransform.getPosition().y > location.y)
					location.y += 10.0f;
				else
					location.y -= 10.0f;


				setAimLocation( objectTransform.getPosition() );
				setMoveDestination( location, false );
			} else {
				// Not too close.
				if ( mFabs ( myTransform.getPosition().x - location.x ) < 1 || mFabs ( myTransform.getPosition().y - location.y ) < 1 ) {
					if (myTransform.getPosition().x > location.x)
						location.x += 10.0f;
					else
						location.x -= 10.0f;

					if (myTransform.getPosition().y > location.y)
						location.y += 10.0f;
					else
						location.y -= 10.0f;

					setAimLocation( objectTransform.getPosition() );
					setMoveDestination( location, false );
				} else { // Fix this later (moving back and shooting.
					if (mAttackTargetInLOS)
						fireWeapon(true);
					else
						fireWeapon(false);

					setAimLocation( location );
					setMoveDestination( location, false );
				}
			}
		}
	}
}

/**
 * Tells the AI to fire.
 */
void AIPlayer::fireWeapon(bool enable) { // Jimomighty
   // Fire the seclected weapon.
	
	if(enable)
	{
		mModeFire = true;
		throwCallback( "onShoot" );
	}
	else
	{
		mModeFire = false;
		throwCallback( "onUnshoot" );
	}
}

/**
 * Tells the AI to jump.
 */
void AIPlayer::doJump(bool enable) { // Jimomighty
   // Fire the seclected weapon.
	
	if(enable)
	{
		mModeJump = true;
		mJump = true;
		throwCallback( "onJump" );
	}
	else
	{
		mModeJump = false;
		mJump = false;
		throwCallback( "onUnJump" );
	}
}

void AIPlayer::doJump() { // Jimomighty
	doJump(true);
}


/**
 * Clear the bots targets.
 */
ConsoleMethod( AIPlayer, clearAllTargets, void, 2, 2, "ai.clearAllTargets();" ) {
   AIPlayer *ai = static_cast<AIPlayer*>( object );
   ai->clearAllTargets();
}

/**
 * Clear the bots attack target.
 */
ConsoleMethod( AIPlayer, clearAttackTarget, void, 2, 2, "ai.clearAttackTarget();" ) {
   AIPlayer *ai = static_cast<AIPlayer*>( object );
   ai->clearAttackTarget();
}

/**
 * Clear the bots follow target.
 */
ConsoleMethod( AIPlayer, clearFollowTarget, void, 2, 2, "ai.clearFollowTarget();" ) {
   AIPlayer *ai = static_cast<AIPlayer*>( object );
   ai->clearFollowTarget();
}

/**
 * Tells AI to scan.
 */
ConsoleMethod( AIPlayer, setScanningPlayers, void, 3, 3, "ai.scanningPlayers(bool);" ) {
	// argc;
   AIPlayer *ai = static_cast<AIPlayer *>( object );
	ai->setScanningPlayers(dAtob(argv[1]));
}

/**
 * Sets the bots follow target object
 */
ConsoleMethod( AIPlayer, setFollowTargetObject, void, 3, 3, "ai.setFollowTargetObject( obj );" ) {
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   
   // Find the follow target
   ShapeBase *followtargetObject;
   if( Sim::findObject( argv[2], followtargetObject ) )
      ai->setFollowTargetObject( followtargetObject );
   else
      ai->setFollowTargetObject( NULL );
}

/**
 * Gets the object the AI is following
 */
ConsoleMethod( AIPlayer, getFollowTargetObject, S32, 2, 2, "ai.getFollowTargetObject();" ) {
   AIPlayer *ai = static_cast<AIPlayer *>( object );

   return ai->getFollowTargetObject();
}

/**
 * Gets the distance from target
 */
ConsoleMethod( AIPlayer, getTargetDistance, S32, 2, 2, "ai.getTargetDistance();" ) {
   AIPlayer *ai = static_cast<AIPlayer *>( object );

   return ai->getTargetDistance();
}

/**
 * Sets the bots attack target object
 */
ConsoleMethod( AIPlayer, setAttackTargetObject, void, 3, 3, "ai.setAttackTargetObject( obj );" ) {
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   
   // Find the attack target
   ShapeBase *attacktargetObject;
   if( Sim::findObject( argv[2], attacktargetObject ) )
      ai->setAttackTargetObject( attacktargetObject );
   else
      ai->setAttackTargetObject( NULL );
}

/**
 * Gets the object the AI is attacking
 */
ConsoleMethod( AIPlayer, getAttackTargetObject, S32, 2, 2, "ai.getAttackTargetObject();" ) {
   AIPlayer *ai = static_cast<AIPlayer *>( object );

   return ai->getAttackTargetObject();
}

ConsoleMethod( AIPlayer, doJump, void, 3, 3, "ai.doJump(bool)")
{
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   ai->doJump(dAtob(argv[2]));
}

AiPlayer.h
//-----------------------------------------------------------------------------
// Torque Game Engine
// 
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------

#ifndef _AIPLAYER_H_
#define _AIPLAYER_H_

#ifndef _PLAYER_H_
#include "game/player.h"
#endif


class AIPlayer : public Player {

	typedef Player Parent;

public:
	enum MoveState {
		ModeStop,
		ModeMove,
		ModeStuck,
	};

private:
		MoveState mMoveState;
		F32 mMoveSpeed;
		F32 mSite; // ace
		F32 mMoveTolerance;                 // Distance from destination before we stop
		Point3F mMoveDestination;           // Destination for movement
      Point3F mLastLocation;              // For stuck check
      bool mMoveSlowdown;                 // Slowdown as we near the destination

		SimObjectPtr<GameBase> mAimObject; // Object to point at, overrides location
		bool mAimLocationSet;               // Has an aim location been set?
		Point3F mAimLocation;               // Point to look at
      bool mTargetInLOS;                  // Is target object visible?

      Point3F mAimOffset;

      // Utility Methods
      void throwCallback( const char *name );
public:
		DECLARE_CONOBJECT( AIPlayer );

		AIPlayer();
      ~AIPlayer();

		virtual bool getAIMove( Move *move );

		// Targeting and aiming sets/gets
		void setAimObject( GameBase *targetObject );
      void setAimObject( GameBase *targetObject, Point3F offset );
		GameBase* getAimObject() const  { return mAimObject; }
		void setAimLocation( const Point3F &location );
		Point3F getAimLocation() const { return mAimLocation; }
		void clearAim();

		// Movement sets/gets
		void setMoveSpeed( const F32 speed );
		F32 getMoveSpeed() const { return mMoveSpeed; }
		void setSite( const F32 site ); // ace
		F32 getSite() const { return mSite; }//ace

		void setMoveTolerance( const F32 tolerance );
		F32 getMoveTolerance() const { return mMoveTolerance; }
		void setMoveDestination( const Point3F &location, bool slowdown );
		Point3F getMoveDestination() const { return mMoveDestination; }
		void stopMove();


		// updating - beffy
		void processTick(const Move* move);
		// ---Scanning // Jimomighty
		bool mScanningPlayers;
		F32 mScanningPlayersDistance;
		void scanningPlayers();
		void setScanningPlayers(bool enable);
		void setScanningPlayersDistance( const F32 distance );

		// ---Following // Jimomighty
		bool mFollowTargetInLOS;
		SimObjectPtr<ShapeBase> mFollowTargetObject;

		void setFollowTargetObject( ShapeBase *followtargetObject );
		S32 getFollowTargetObject() const;
		void followTarget();
		F32 getFollowTargetLOS() const { return mFollowTargetInLOS; }

		// ---Attacking // Jimomighty
		bool mAttackTargetInLOS;
		SimObjectPtr<ShapeBase> mAttackTargetObject;

		void setAttackTargetObject( ShapeBase *attacktargetObject );
		S32 getAttackTargetObject() const;
		void attackTarget();
		F32 getAttackTargetLOS() const { return mAttackTargetInLOS; }
		bool mModeFire;
		bool mModeJump;

		// ---Target Control // Jimomighty
		void clearAllTargets();
		void clearActionTargets();
		void clearAttackTarget();
		void clearFollowTarget();

		// ---Misc // Jimomighty
		void doActivities();
		S32 getTargetDistance() const;
		void checkTargetLOS();
		void fireWeapon(bool enable);
		void doJump(bool enable);
		void doJump();
		bool mJump;

};

#endif

YOURMOD(starter.fps)/client/
config.cs
// Torque Input Map File
moveMap.delete();
new ActionMap(moveMap);
moveMap.bindCmd(keyboard, "escape", "", "escapeFromGame();");
moveMap.bind(keyboard, "f2", showPlayerList);
moveMap.bind(keyboard, "f5", toggleParticleEditor);
moveMap.bind(keyboard, "a", moveleft);
moveMap.bind(keyboard, "d", moveright);
moveMap.bind(keyboard, "w", moveforward);
moveMap.bind(keyboard, "s", movebackward);
moveMap.bind(keyboard, "space", jump);
moveMap.bind(keyboard, "r", setZoomFOV);
moveMap.bind(keyboard, "e", toggleZoom);
moveMap.bind(keyboard, "z", toggleFreeLook);
moveMap.bind(keyboard, "tab", toggleFirstPerson);
moveMap.bind(keyboard, "alt c", toggleCamera);
moveMap.bindCmd(keyboard, "ctrl w", "commandToServer(\'playCel\',\"wave\");", "");
moveMap.bindCmd(keyboard, "ctrl s", "commandToServer(\'playCel\',\"salute\");", "");
moveMap.bindCmd(keyboard, "ctrl k", "commandToServer(\'suicide\');", "");
moveMap.bindCmd(keyboard, "h", "commandToServer(\'use\',\"HealthKit\");", "");
moveMap.bindCmd(keyboard, "1", "commandToServer(\'use\',\"Rifle\");", "");
moveMap.bindCmd(keyboard, "2", "commandToServer(\'use\',\"Crossbow\");", "");
moveMap.bind(keyboard, "u", toggleMessageHud);
moveMap.bind(keyboard, "pageup", pageMessageHudUp);
moveMap.bind(keyboard, "pagedown", pageMessageHudDown);
moveMap.bind(keyboard, "p", resizeMessageHud);
moveMap.bind(keyboard, "f3", startRecordingDemo);
moveMap.bind(keyboard, "f4", stopRecordingDemo);
moveMap.bind(keyboard, "f8", dropCameraAtPlayer);
moveMap.bind(keyboard, "f7", dropPlayerAtCamera);
moveMap.bind(keyboard, "ctrl o", bringUpOptions);
moveMap.bindCmd(keyboard, "n", "NetGraph::toggleNetGraph();", "");
moveMap.bind(keyboard, "ctrl b", addBot);
moveMap.bind(mouse0, "xaxis", yaw);
moveMap.bind(mouse0, "yaxis", pitch);
moveMap.bind(mouse0, "button0", mouseFire);

yourmod/client/scripts/
default.binds.cs
//-----------------------------------------------------------------------------
// Torque Game Engine
// 
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------

if ( isObject( moveMap ) )
   moveMap.delete();
new ActionMap(moveMap);


//------------------------------------------------------------------------------
// Non-remapable binds
//------------------------------------------------------------------------------

function escapeFromGame()
{
   if ( $Server::ServerType $= "SinglePlayer" )
      MessageBoxYesNo( "Quit Mission", "Exit from this Mission?", "disconnect();", "");
   else
      MessageBoxYesNo( "Disconnect", "Disconnect from the server?", "disconnect();", "");
}

moveMap.bindCmd(keyboard, "escape", "", "escapeFromGame();");

function showPlayerList(%val)
{
   if (%val)
      PlayerListGui.toggle();
}
moveMap.bind( keyboard, F2, showPlayerList );
moveMap.bind( keyboard, F5, toggleParticleEditor);


//------------------------------------------------------------------------------
// Movement Keys
//------------------------------------------------------------------------------

$movementSpeed = 1; // m/s

function setSpeed(%speed)
{
   if(%speed)
      $movementSpeed = %speed;
}

function moveleft(%val)
{
   $mvLeftAction = %val * $movementSpeed;
}

function moveright(%val)
{
   $mvRightAction = %val * $movementSpeed;
}

function moveforward(%val)
{
   $mvForwardAction = %val * $movementSpeed;
}

function movebackward(%val)
{
   $mvBackwardAction = %val * $movementSpeed;
}

function moveup(%val)
{
   $mvUpAction = %val * $movementSpeed;
}

function movedown(%val)
{
   $mvDownAction = %val * $movementSpeed;
}

function turnLeft( %val )
{
   $mvYawRightSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function turnRight( %val )
{
   $mvYawLeftSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function panUp( %val )
{
   $mvPitchDownSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function panDown( %val )
{
   $mvPitchUpSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function getMouseAdjustAmount(%val)
{
   // based on a default camera fov of 90'
   return(%val * ($cameraFov / 90) * 0.01);
}

function yaw(%val)
{
   $mvYaw += getMouseAdjustAmount(%val);
}

function pitch(%val)
{
   $mvPitch += getMouseAdjustAmount(%val);
}

function jump(%val)
{
   $mvTriggerCount2++;
}

moveMap.bind( keyboard, a, moveleft );
moveMap.bind( keyboard, d, moveright );
moveMap.bind( keyboard, w, moveforward );
moveMap.bind( keyboard, s, movebackward );
moveMap.bind( keyboard, space, jump );
moveMap.bind( mouse, xaxis, yaw );
moveMap.bind( mouse, yaxis, pitch );

//------------------------------------------------------------------------------
// Mouse Trigger
//------------------------------------------------------------------------------

function mouseFire(%val)
{
   $mvTriggerCount0++;
}

function altTrigger(%val)
{
   $mvTriggerCount1++;
}

moveMap.bind( mouse, button0, mouseFire );
//moveMap.bind( mouse, button1, altTrigger );


//------------------------------------------------------------------------------
// Zoom and FOV functions
//------------------------------------------------------------------------------

if($Pref::player::CurrentFOV $= "")
   $Pref::player::CurrentFOV = 45;

function setZoomFOV(%val)
{
   if(%val)
      toggleZoomFOV();
}
      
function toggleZoom( %val )
{
   if ( %val )
   {
      $ZoomOn = true;
      setFov( $Pref::player::CurrentFOV );
   }
   else
   {
      $ZoomOn = false;
      setFov( $Pref::player::DefaultFov );
   }
}

moveMap.bind(keyboard, r, setZoomFOV);
moveMap.bind(keyboard, e, toggleZoom);


//------------------------------------------------------------------------------
// Camera & View functions
//------------------------------------------------------------------------------

function toggleFreeLook( %val )
{
   if ( %val )
      $mvFreeLook = true;
   else
      $mvFreeLook = false;
}

$firstPerson = true;
function toggleFirstPerson(%val)
{
   if (%val)
   {
      $firstPerson = !$firstPerson;
      ServerConnection.setFirstPerson($firstPerson);
   }
}

function toggleCamera(%val)
{
   if (%val)
      commandToServer('ToggleCamera');
}

moveMap.bind( keyboard, z, toggleFreeLook );
moveMap.bind(keyboard, tab, toggleFirstPerson );
moveMap.bind(keyboard, "alt c", toggleCamera);


//------------------------------------------------------------------------------
// Misc. Player stuff
//------------------------------------------------------------------------------

moveMap.bindCmd(keyboard, "ctrl w", "commandToServer('playCel',\"wave\");", "");
moveMap.bindCmd(keyboard, "ctrl s", "commandToServer('playCel',\"salute\");", "");
moveMap.bindCmd(keyboard, "ctrl k", "commandToServer('suicide');", "");


//------------------------------------------------------------------------------
// Item manipulation
//------------------------------------------------------------------------------

moveMap.bindCmd(keyboard, "h", "commandToServer('use',\"HealthKit\");", "");
moveMap.bindCmd(keyboard, "1", "commandToServer('use',\"Rifle\");", "");
moveMap.bindCmd(keyboard, "2", "commandToServer('use',\"Crossbow\");", "");

//------------------------------------------------------------------------------
// Message HUD functions
//------------------------------------------------------------------------------

function pageMessageHudUp( %val )
{
   if ( %val )
      pageUpMessageHud();
}

function pageMessageHudDown( %val )
{
   if ( %val )
      pageDownMessageHud();
}

function resizeMessageHud( %val )
{
   if ( %val )
      cycleMessageHudSize();
}

moveMap.bind(keyboard, u, toggleMessageHud );
//moveMap.bind(keyboard, y, teamMessageHud );
moveMap.bind(keyboard, "pageUp", pageMessageHudUp );
moveMap.bind(keyboard, "pageDown", pageMessageHudDown );
moveMap.bind(keyboard, "p", resizeMessageHud );


//------------------------------------------------------------------------------
// Demo recording functions
//------------------------------------------------------------------------------

function startRecordingDemo( %val )
{
   if ( %val )
      startDemoRecord();
}

function stopRecordingDemo( %val )
{
   if ( %val )
      stopDemoRecord();
}

moveMap.bind( keyboard, F3, startRecordingDemo );
moveMap.bind( keyboard, F4, stopRecordingDemo );


//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

function dropCameraAtPlayer(%val)
{
   if (%val)
      commandToServer('dropCameraAtPlayer');
}

function dropPlayerAtCamera(%val)
{
   if (%val)
      commandToServer('DropPlayerAtCamera');
}

moveMap.bind(keyboard, "F8", dropCameraAtPlayer);
moveMap.bind(keyboard, "F7", dropPlayerAtCamera);

function bringUpOptions(%val)
{
   if (%val)
      Canvas.pushDialog(OptionsDlg);
}

moveMap.bind(keyboard, "ctrl o", bringUpOptions);


//------------------------------------------------------------------------------
// Dubuging Functions
//------------------------------------------------------------------------------

$MFDebugRenderMode = 0;
function cycleDebugRenderMode(%val)
{
   if (!%val)
      return;

   if (getBuildString() $= "Debug")
   {
      if($MFDebugRenderMode == 0)
      {
         // Outline mode, including fonts so no stats
         $MFDebugRenderMode = 1;
         GLEnableOutline(true);
      }
      else if ($MFDebugRenderMode == 1)
      {
         // Interior debug mode
         $MFDebugRenderMode = 2;
         GLEnableOutline(false);
         setInteriorRenderMode(7);
         showInterior();
      }
      else if ($MFDebugRenderMode == 2)
      {
         // Back to normal
         $MFDebugRenderMode = 0;
         setInteriorRenderMode(0);
         GLEnableOutline(false);
         show();
      }
   }
   else
   {
      echo("Debug render modes only available when running a Debug build.");
   }
}

GlobalActionMap.bind(keyboard, "F9", cycleDebugRenderMode);


//------------------------------------------------------------------------------
// Misc.
//------------------------------------------------------------------------------
function addBot(%val) 
{
	if(%val)
	{
		commandToServer('AddBot');
	}
}
moveMap.bind(keyboard, "ctrl b", addBot);

GlobalActionMap.bind(keyboard, "tilde", toggleConsole);
GlobalActionMap.bindCmd(keyboard, "alt enter", "", "toggleFullScreen();");
GlobalActionMap.bindCmd(keyboard, "F1", "", "contextHelp();");

yourmod/server/scripts/
aiplayer.cs
//-----------------------------------------------------------------------------
// Torque Game Engine
// 
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

// The AIPlayer C++ class implements the following methods.
//
// AIPlayer::stopMove();
//    Stop the player where he is.
//
// AIPlayer::clearAim();
//    Clear any current aim location or aim object.  The player will
//    maintain his current orientation, or face towards his destination
//    if moving.
//
// AIPlayer::setMoveSpeed( float );
//    Set the speed (0-1) at which the player moves.
//
// AIPlayer::setMoveDestination( "x y z" );
//    Set the destination point to move towards. The Z location is ignored
//    though the player will face upwards (or downards) towards the destination
//    if no other aim target is set.
//
// AIPlayer::getMoveDestination();
//    Returns the current destination point.
//
// AIPlayer::setAimLocation( \"x y z\" );
//    Set a point to look at.  The player maintains his orientation towards
//    this point even while moving towards a destination point.
//
// AIPlayer::setAimObject( obj );
//    Set an object to look at.  The player maintains his orientation towards
//    this object, even while moving towards a destination point. The aim
//    object can also be moving.
//
// AIPlayer::getAimLocation();
//    Returns the current point the player is looking at. If the player has
//    an aimObject set, this would be the current position of the target
//    object.
//
// AIPlayer::getAimObject();
//    Returns the current target object, if there is one.

datablock PlayerData(DemoPlayer  : PlayerBody) //d playerbody
{
   shootingDelay = 1000;
};

function Armor::onReachDestination(%this,%obj)
{
   // Moves to the next node on the path.
   // Override for all player. Normally we'd override this for only
   // a specific player datablock or class of players.
   if (%obj.path !$= "") {
      if (%obj.currentNode == %obj.targetNode) {
         %this.onEndOfPath(%obj,%obj.path);
      } else {
         %obj.moveToNextNode();
      }
   }
}

function Armor::onEndOfPath(%this,%obj,%path)
{
   %obj.nextTask();
}

function Armor::onEndSequence(%this,%obj,%slot)
{
   echo("Sequence Done!");
   %obj.stopThread(%slot);
   %obj.nextTask();
}

//-----------------------------------------------------------------------------
// Callback Handlers
//-----------------------------------------------------------------------------

function Armor::onStuck(%this,%obj) 
{
   // Invoked if the player is stuck while moving
   // This method is currently not being invoked correctly.
   echo( "I'm stuck" );
}

//function Armor::onReachDestination(%this,%obj)
//{
   // Invoked when the player arrives at his destination point.
   //echo( "onReachDestination Armor" );
  
//}

function Armor::onTargetEnterLOS(%this,%obj)
{
   // If an aim target object is set, this method is invoked when
   // that object becomes visible.
  // echo( "onTargetEnterLOS" );
}

function Armor::onTargetExitLOS(%this,%obj)
{
   // If an aim target object is set, this method is invoked when
   // the object is not longer visible.
 //  echo( "onTargetExitLOS" );
}
//ace
//this is for the aimanager
function AIPlayer::spawn(%name,%spawnPoint)
{
   // Create the demo player object
   %player = new AiPlayer() {
      dataBlock = PlayerBody; //d playerbody
      path = "";
   };
   %player.setShapeName(%name);
   %player.setTransform(%spawnPoint);
   	%player.name = %player.getShapeName();
   	MissionCleanup.add(%player);
	 //ClientGroup.add(%player); 
	 //messageAll('MsgClientJoin', '%1 is ready for combat.', %player.getShapeName(), %player, %player, 0);
	 //%player.setMoveSpeed(0.7);
   return %player;
}

function AIPlayer::spawnOnPath(%name,%path)
{
   // Spawn a player and place him on the first node of the path
   if (!isObject(%path))
      return;
   %node = %path.getObject(0);
   %player = AIPlayer::spawn(%name,%node.getTransform());
	  messageAll('MsgClientJoin', '%1 is ready for combat.', %name, %player, %player, 0);
   return %player;
}

function AIPlayer::followPath(%this,%path,%node)
{
   // Start the player following a path
   %this.stopThread(0);
   if (!isObject(%path)) {
      //echo("followpath this-path1: " @ %this.path);
      %this.path = "";
      return;
   }
   if (%node > %path.getCount() - 1) {
      %this.targetNode = %path.getCount() - 1;
      //echo("followpath target-Node1: " @ %this.targetNode);
      }
   else {
      %this.targetNode = %node; //
      //echo("followpath target-Node1-Else: " @ %this.targetNode);
      }
   if (%this.path $= %path) {
      //echo("followpath Move-to-Node1: " @ %this.moveToNode);
      //echo("followpath Current-Node1: " @ %this.currentNode);
      %this.moveToNode(%this.currentNode);
      //echo("followpath Move-to-Node2: " @ %this.moveToNode);
   }
   else {
      %this.path = %path;
      //echo("followpath this-path2: " @ %this.path);
      %this.moveToNode(0);// 
      //echo("followpath move-to-Node3: " @ %this.moveToNode);
   }
}

function AIPlayer::moveToNextNode(%this)
{
   if (%this.targetNode < 0 || %this.currentNode < %this.targetNode) {
      //echo( "movetonextnode if target node: " @ %this.targetNode @ "current node: " @ %this.currentNode );
      if (%this.currentNode < %this.path.getCount() - 1) {
         %this.moveToNode(%this.currentNode + 1);
         //echo( "movetonextnode ifif movetonode: " @ %this.moveToNode );
      }
      else {
         %this.moveToNode(0);
         //echo( "movetonextnode else movetonode: " @ %this.moveToNode );
      }
   }
   else
      if (%this.currentNode == 0) {
         %this.moveToNode(%this.path.getCount() - 1);
         //echo( "movetonextnode movetonode-else-if: " @ %this.moveToNode );
      }
      else {
         %this.moveToNode(%this.currentNode - 1);
         //echo( "movetonextnode movetonode-else-else: " @ %this.moveToNode );
      }
}

function AIPlayer::moveToNode(%this,%index)
{
   // Move to the given path node index
   %this.currentNode = %index;
   %node = %this.path.getObject(%index);
   //echo( "moveToNode: node: " @ %node );
   %this.setMoveDestination(%node.getTransform(), %index == %this.targetNode);
   //%this.setMoveDestination(%node.getTransform());
      %fraggle=%this.getMoveDestination();
      //echo( "movetonode Move Destination: " @ %fraggle);
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------

function AIPlayer::pushTask(%this,%method)
{
   if (%this.taskIndex $= "") {
      %this.taskIndex = 0;
      %this.taskCurrent = -1;
   }
   %this.task[%this.taskIndex] = %method; 
   %this.taskIndex++;
   if (%this.taskCurrent == -1)
      %this.executeTask(%this.taskIndex - 1);
}

function AIPlayer::clearTasks(%this)
{
   %this.taskIndex = 0;
   %this.taskCurrent = -1;
}

function AIPlayer::nextTask(%this)
{
   if (%this.taskCurrent != -1)
      if (%this.taskCurrent < %this.taskIndex - 1)
         %this.executeTask(%this.taskCurrent++);
      else
         %this.taskCurrent = -1;
}

function AIPlayer::executeTask(%this,%index)
{
   %this.taskCurrent = %index;
   eval(%this.getId() @ "." @ %this.task[%index] @ ";");
}


//-----------------------------------------------------------------------------

function AIPlayer::singleShot(%this)
{
   // The shooting delay is used to pulse the trigger
   %this.setImageTrigger(0,true);
   %this.setImageTrigger(0,false);
   %this.trigger = %this.schedule(%this.shootingDelay,singleShot);
}


//-----------------------------------------------------------------------------

function AIPlayer::wait(%this,%time)
{
   %this.schedule(%time * 1000,"nextTask");
}

function AIPlayer::done(%this,%time)
{
   %this.schedule(0,"delete");
}

function AIPlayer::fire(%this,%bool)
{
   if (%bool) {
      cancel(%this.trigger);
      %this.singleShot();
   }
   else
      cancel(%this.trigger);
   %this.nextTask();
}

function AIPlayer::aimAt(%this,%object)
{
   echo("Aim: " @ %object);
   %this.setAimObject(%object);
   %this.nextTask();
}

function AIPlayer::animate(%this,%seq)
{
   //%this.stopThread(0);
   //%this.playThread(0,%seq);
   %this.setActionThread(%seq);
}



//-----------------------------------------------------------------------------
function AIManager::think(%this)
{
   // We could hook into the player's onDestroyed state instead of
   // having to "think", but thinking allows us to consider other
   // things...
   if (!isObject(%this.player))
      %this.player = %this.spawn();
   %this.schedule(500,think);
}

function AIManager::pickPath(%this)
{
   %pathsName = "MissionGroup/Paths";
   %paths = nameToID(%pathsName);
   %path = "";

   if (%paths != -1) {
      %count = %paths.getCount();
      if (%count != 0) {
         %index = getRandom(%count-1);
         %path = %paths.getObject(%index);
      }
      else
         error("No paths points found in " @ %pathsName);
   }
   else
      error("Missing paths group " @ %pathsName);
   
   return %path;
}

function AIManager::spawn(%this)
{
//function AIManager::spawnPlayer(%this)
//{
//function AIPlayer::spawn(%name)
//{
     // An example function which creates a new AISpider object
     // using the the example player datablock.

 %path = AIManager::pickPath();
//	%player = AIPlayer::spawnOnPath("Korky","MissionGroup/Paths/Path1");
//	%player.followPath("MissionGroup/Paths/Path1",-1);
	%player = AIPlayer::spawnOnPath("Korky",%path);
	%player.followPath(%path,-1);
		messageAll('MsgClientJoin', '%1 is ready for combat.', %player.getShapeName(), %player, %player, 0);
    //  Player setup
 %player.setMoveSpeed(0.8);
    //  %player.setTransform(spawnOnPath());
	%player.setSite(60);
	%player.incInventory(Rifle,1);
	%player.incInventory(RifleAmmo,30);
	%player.mountImage(RifleImage,0);
	
    //  this tells the ai how to act
	%player.setScanningPlayers(true);
	
    return %player;
}   

//ace
//%player.setFollowTargetObject(%client.player);  // follows you
 //$bots[$botCounter].setAttackTargetObject(%client.player); //very dumb
 $bots[$botCounter].setScanningPlayers(true); //very aggressive


// Jimomighty function callbacks
function Armor::onJump(%this,%obj)
{
   echo( "onJump" );
}
function Armor::onUnJump(%this,%obj)
{
}
function Armor::onShoot(%this,%obj)
{
   //echo( "onShoot" );
}
function Armor::onUnShoot(%this,%obj)
{

}
function Armor::onFollowTargetEnterLOS(%this,%obj)
{
   //echo( "onFollowTargetEnterLOS" );
}
function Armor::onFollowTargetExitLOS(%this,%obj)
{
   //echo( "onFollowTargetExitLOS" );
}
function Armor::onFollowTargetKilled(%this,%obj)
{
   //echo( "onFollowTargetKilled" );
}
function Armor::onAttackTargetEnterLOS(%this,%obj)
{
   //echo( "onAttackTargetEnterLOS" );
}
function Armor::onAttackTargetExitLOS(%this,%obj)
{
   //echo( "onAttackTargetExitLOS" );
}
function Armor::onAttackTargetKilled(%this,%obj)
{
   //echo( "onAttackTargetKilled" );
}

commands.cs
//-----------------------------------------------------------------------------
// Torque Game Engine
// 
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Misc. server commands avialable to clients
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function serverCmdToggleCamera(%client)
{
   if ($Server::TestCheats || $Server::ServerType $= "SinglePlayer")
   {
      %control = %client.getControlObject();
      if (%control == %client.player)
      {
         %control = %client.camera;
         %control.mode = toggleCameraFly;
      }
      else
      {
         %control = %client.player;
         %control.mode = observerFly;
      }
      %client.setControlObject(%control);
   }
}

function serverCmdDropPlayerAtCamera(%client)
{
   if ($Server::TestCheats)
   {
      if (!%client.player.isMounted())
         %client.player.setTransform(%client.camera.getTransform());
      %client.player.setVelocity("0 0 0");
      %client.setControlObject(%client.player);
   }
}

function serverCmdDropCameraAtPlayer(%client)
{
   if ($Server::TestCheats)
   {
      %client.camera.setTransform(%client.player.getEyeTransform());
      %client.camera.setVelocity("0 0 0");
      %client.setControlObject(%client.camera);
   }
}
function serverCmdSetPlayerPos(%client,%pos)
{
   if (isObject(%client.player))
      %client.player.setPlayerPosition(%pos);
}
function serverCmdGetPlayerPos(%client)
{
   if (isObject(%client.player))
      return %client.player.getPosition();
   else
      return 0;
}

//-----------------------------------------------------------------------------

function serverCmdSuicide(%client)
{
   if (isObject(%client.player))
      %client.player.kill("Suicide");
}   

function serverCmdPlayCel(%client,%anim)
{
   if (isObject(%client.player))
      %client.player.playCelAnimation(%anim);
}

function serverCmdPlayDeath(%client)
{
   if (isObject(%client.player))
      %client.player.playDeathAnimation();
}

function serverCmdSelectObject(%client, %mouseVec, %cameraPoint)
{
   //Determine how far should the picking ray extend into the world?
   %selectRange = 200;
   // scale mouseVec to the range the player is able to select with mouse
   %mouseScaled = VectorScale(%mouseVec, %selectRange);
   // cameraPoint = the world position of the camera
   // rangeEnd = camera point + length of selectable range
   %rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);

   // Search for anything that is selectable - below are some examples
   %searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::CorpseObjectType |
      				$TypeMasks::ItemObjectType | $TypeMasks::TriggerObjectType;

   // Search for objects within the range that fit the masks above
   // If we are in first person mode, we make sure player is not selectable by setting fourth parameter (exempt
   // from collisions) when calling ContainerRayCast
   %player = %client.player;
   if ($firstPerson)
   {
	  %scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, %player);
   }
   else //3rd person - player is selectable in this case
   {
	  %scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks);
   }

   // a target in range was found so select it
   if (%scanTarg)
   {
      %targetObject = firstWord(%scanTarg);

      %client.setSelectedObj(%targetObject);
   }
}

	$botCounter = 1;
	$botCounter++;

function serverCmdAddBot(%client) {
         MissionCleanup.add($bots);
	 %npcName = "Kork" @ $botCounter;
         %path = AIManager::pickPath();
         %mrbot = AIPlayer::spawnOnPath(%npcName, %path);
         $bots[$botCounter] = %mrbot;
	%mrbot.followPath(%path,-1);
 %mrbot.setMoveSpeed(0.8);
	%mrbot.setSite(60);
	%mrbot.incInventory(Crossbow,1);
	%mrbot.incInventory(CrossbowAmmo,120);
	%mrbot.mountImage(CrossbowImage,0);
	%mrbot.setScanningPlayers(true);
         
         MissionCleanup.add(%mrbot);
	 
	 //%mrbot.setAttackTargetObject(%client.player);
	 $botCounter++;
}

#1
08/17/2006 (2:34 pm)
Sweet!
#2
08/17/2006 (9:39 pm)
I see a bit modification made in the scripts .

Can you list what changes you made from the original resource.


Thanks and great work. :)

Aun
#3
08/19/2006 (9:10 am)
Thanks a mill for cleaning up and updating the past work. This was really needed and greatly appreciated.

Mark
#4
08/24/2006 (6:30 am)
Nice, thanks a lot
#5
08/26/2006 (2:56 pm)
I keep meaning to re-do this and just list the changes. It may be some time tho, being extremely busy with various torque and non torque projects but thanks for the support! I'll relist the changes only eventually. ;)

Also, more recently, I 'fixed' this code some more so the bot wouldnt 'see' you unless it had a clear line of sight, so you dont get bots getting stuck up against walls trying to get at you. but I need to take a close look at what all I changed to make that work before I post an update for that.
#6
09/25/2006 (1:16 am)
I'd like to submit a bug fix:

ConsoleMethod( AIPlayer, setScanningPlayers, void, 3, 3, 
      "ai.scanningPlayers(bool);" ) {
   // argc;
   AIPlayer *ai = static_cast<AIPlayer *>( object );
   // The next line is incorrect...
   ai->setScanningPlayers(dAtob(argv[1]));

   // ..it should be like this
   ai->setScanningPlayers(dAtob(argv[2]));
}

And while I'm at it, why do all the ConsoleMethods in the code in this resource have casts like this at the start:
AIPlayer *ai = static_cast<AIPlayer *>( object );

It's completely redundant. The ConsoleMethod macro already does that for you. None of the ConsoleMethods in TGE have that.
#7
09/26/2006 (6:04 pm)
Thanks Nicholas, what should it be instead of AIPlayer *ai = static_cast( object ); ?

And what exactly does your fix do?
#8
09/26/2006 (7:24 pm)
Whups! I should have explained the fix.

The problem that my patch fixes is that calling setScanningPlayers(false) wouldn't work - it would act as if setScanningPlayers(true) was called.

The reason for this is that the ConsoleMethod definition would look at the *second* (argv[1]) argument, which happens to be the id of the AiPlayer we are calling setScanningPlayers on. It should be the *third* argument, argv[2].


About the ConsoleMethods:

In a ConsoleMethod, there is always a "pre-declared" variable named "object", and its type is the same as the first argument to the ConsoleMethod. In other words, you can just use a variable named "object" without declaring it.

So you can just leave out the line:
AIPlayer *ai = static_cast<AIPlayer *>( object );
And replace variable "ai" with "object" throughout the body of the ConsoleMethod.

For example, look at this code:
ConsoleMethod( FileObject, openForAppend, bool, 3, 3, "(string filename)")
{
   return object->openForWrite(argv[2], true);
}
The variable "object" already exists without us declaring it, and we don't have to do anything to let C++ know it is of type FileObject.

It's not a big deal, which is why I tagged that comment onto an actual real bug fix, but it's always nice to cut away code that does nothing ;)
#9
09/29/2006 (4:40 am)
Mapping string: AddBot to index: 5
Set::add: Object "" doesn't exist
starter.fps/server/scripts/commands.cs (1): Unable to find function AIManager::pickPath
starter.fps/server/scripts/commands.cs (1): Unable to find function AIPlayer::spawnOnPath
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'followPath'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'setMoveSpeed'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'setSite'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'incInventory'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'incInventory'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'mountImage'
starter.fps/server/scripts/commands.cs (1): Unable to find object: 'Kork2' attempting to call function 'setScanningPlayers'
Set::add: Object "Kork2" doesn't exist




???
#10
09/30/2006 (9:45 pm)
Alex, I can try to help, what exactly are you trying to do, and did you add the files listed and rebuild and delete dso's? Also, what version of TGE are you trying to implement this into?

Edit: Looks like you may have forgotten the changes in Aiplayer.cs. That help?
#11
03/23/2007 (11:22 pm)
I'm getting the console message
commands.cs (86) : Unable to find function aiAddPlayer
set::add: Object "" doesn't exist

I've replaced the .cc and .h files and rebuilt, deleted the .dso's to force a recompile...

Any help?

Thanks!

Tony
#12
03/23/2007 (11:43 pm)
What exactly are you doing when you get that message? Trying to spawn a bot from the console, or a key, or what? What version of Torque are you using?

There's no call to aiAddPlayer at that line in the commands.cs that comes with the resource. In fact I don't see aiAddPlayer when I grep commands.cs at all.
#13
04/09/2007 (5:55 am)
Try adding all the files into a clean build, without exception. Then when you have all these changes added then merge or add in your own changes one at a time so it will be easier for you to debug.
#14
06/28/2010 (11:05 am)
First off, thanks!! That said, let's get down to business...

I am attempting to add this resource to TGEA 1.8.1. I figured I'd follow the directions as if I was using TGE, compile, and start debugging errors.

I managed to get all the code in exactly as it is written, and only had one error when I compiled:

In TGEA's aiPlayer.cpp around line 625:
Player* objPlayer = static_cast<Player*>(targetObject);

That's in the second half of 'checkTargetLOS' method below 'mAttackTargetObject'. The error I got said the variable was initialized but not referenced. I commented out the one line and it compiled fine. I ran the game and no crashes or errors. I couldn't get the serverCmdAddBot to work:

Set::add: Object "" doesn't exist

But I can add a bot with my own existing function:
spawnActor(coyote);

Then in console:
coyote0.dump();

Sure enough, I see "doJump" as a method. So:
coyote0.doJump(1);

Console output:
onJump

So it 'works', but the coyote didn't actually do anything. I ran through the 'doJump' function in the aiPlayer.cpp file and don't see anything that is using the AIPlayer's trigger or even applying an impulse. I did find something in 'getAIMove':

if (mModeJump) {
			if (canJump()) {
				if (!movePtr->trigger[2]) {
					movePtr->trigger[2] = true;
				}
			}
		}

So what do I need to do to get my AIPlayer to run through the 'getAIMove' method? More importantly, what else needs to be done to make the AI jump?

PS: I already have an 'aiJump' function that simply applies an impulse based on the AI's jump force but I want this done in C++ so I can add more to it allowing my AI to also use jetJump.
#15
06/28/2010 (11:35 am)
NOTE:
Tried the following:
coyote0.setMoveSpeed(0.8);
coyote0.setSite(60);
coyote0.setScanningPlayers(true);

Nothing.. the coyote just stands there...