123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "T3D/aiClient.h"
- #include "math/mMatrix.h"
- #include "T3D/shapeBase.h"
- #include "T3D/player.h"
- #include "T3D/gameBase/moveManager.h"
- #include "console/consoleInternal.h"
- #include "console/engineAPI.h"
- IMPLEMENT_CONOBJECT( AIClient );
- ConsoleDocClass( AIClient,
- "@brief Simulated client driven by AI commands.\n\n"
- "This object is derived from the AIConnection class. It introduces its own Player object "
- "to solidify the purpose of this class: Simulated client connecting as a player\n\n"
- "To get more specific, if you want a strong alternative to AIPlayer (and wish to make use "
- "of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, "
- "contains quite a bit of functionality you will find in AIPlayer, and has its own Player "
- "object.\n\n"
- "@note This is a legacy class, which you are discouraged from using as it will "
- "most likely be deprecated in a future version. For now it has been left in for "
- "backwards compatibility with TGE and the old RTS Kit. Use AIPlayer instead.\n\n"
- "@see AIPlayer, AIConnection\n\n"
- "@ingroup AI\n"
- "@ingroup Networking\n"
- );
- IMPLEMENT_CALLBACK(AIClient, onConnect, void, (const char* idString), (idString),"");
- /**
- * Constructor
- */
- AIClient::AIClient() {
- mMoveMode = ModeStop;
- mMoveDestination.set( 0.0f, 0.0f, 0.0f );
- mAimLocation.set( 0.0f, 0.0f, 0.0f );
- mMoveSpeed = 1.0f;
- mMoveTolerance = 0.25f;
- // Clear the triggers
- for( S32 i = 0; i < MaxTriggerKeys; i++ )
- mTriggers[i] = false;
-
- mAimToDestination = true;
- mTargetInLOS = false;
- mLocation.set( 0.0f, 0.0f, 0.0f );
- mPlayer = NULL;
- }
- /**
- * Destructor
- */
- AIClient::~AIClient() {
- // Blah
- }
- /**
- * Sets the object the bot is targeting
- *
- * @param targetObject The object to target
- */
- void AIClient::setTargetObject( ShapeBase *targetObject ) {
- if ( !targetObject || !bool( mTargetObject ) || targetObject->getId() != mTargetObject->getId() )
- mTargetInLOS = false;
-
- mTargetObject = targetObject;
- }
- /**
- * Returns the target object
- *
- * @return Object bot is targeting
- */
- S32 AIClient::getTargetObject() const {
- if( bool( mTargetObject ) )
- return mTargetObject->getId();
- else
- return -1;
- }
- /**
- * Sets the speed at which this AI moves
- *
- * @param speed Speed to move, default player was 10
- */
- void AIClient::setMoveSpeed( F32 speed ) {
- if( speed <= 0.0f )
- mMoveSpeed = 0.0f;
- else
- mMoveSpeed = getMin( 1.0f, speed );
- }
- /**
- * Sets the movement mode for this AI
- *
- * @param mode Movement mode, see enum
- */
- void AIClient::setMoveMode( S32 mode ) {
- if( mode < 0 || mode >= ModeCount )
- mode = 0;
- if( mode != mMoveMode ) {
- switch( mode ) {
- case ModeStuck:
- throwCallback( "onStuck" );
- break;
- case ModeMove:
- if( mMoveMode == ModeStuck )
- throwCallback( "onUnStuck" );
- else
- throwCallback( "onMove" );
- break;
- case ModeStop:
- throwCallback( "onStop" );
- break;
- }
- }
- mMoveMode = mode;
- }
- /**
- * Sets how far away from the move location is considered
- * "on target"
- *
- * @param tolerance Movement tolerance for error
- */
- void AIClient::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 AIClient::setMoveDestination( const Point3F &location ) {
- // Ok, here's the story...we're going to aim where we are going UNLESS told otherwise
- if( mAimToDestination ) {
- mAimLocation = location;
- mAimLocation.z = 0.0f;
- }
- mMoveDestination = location;
- }
- /**
- * Sets the location for the bot to aim at
- *
- * @param location Point to aim at
- */
- void AIClient::setAimLocation( const Point3F &location ) {
- mAimLocation = location;
- mAimToDestination = false;
- }
- /**
- * Clears the aim location and sets it to the bot's
- * current destination so he looks where he's going
- */
- void AIClient::clearAim() {
- mAimLocation = Point3F( 0.0f, 0.0f, 0.0f );
- mAimToDestination = true;
- }
- /**
- * This method gets the move list for an object, in the case
- * of the AI, it actually calculates the moves, and then
- * sends them down the pipe.
- *
- * @param movePtr Pointer to move the move list into
- * @param numMoves Number of moves in the move list
- */
- U32 AIClient::getMoveList( Move **movePtr,U32 *numMoves ) {
- //initialize the move structure and return pointers
- mMove = NullMove;
- *movePtr = &mMove;
- *numMoves = 1;
- // Check if we got a player
- mPlayer = NULL;
- mPlayer = dynamic_cast<Player *>( getControlObject() );
- // We got a something controling us?
- if( !mPlayer )
- return 1;
-
- // What is The Matrix?
- MatrixF moveMatrix;
- moveMatrix.set( EulerF( 0, 0, 0 ) );
- moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) );
- moveMatrix.transpose();
-
- // Position / rotation variables
- F32 curYaw, curPitch;
- F32 newYaw, newPitch;
- F32 xDiff, yDiff;
-
- F32 moveSpeed = mMoveSpeed;
- switch( mMoveMode ) {
- case ModeStop:
- return 1; // Stop means no action
- break;
- case ModeStuck:
- // Fall through, so we still try to move
- case ModeMove:
-
- // Get my location
- MatrixF const& myTransform = mPlayer->getTransform();
- myTransform.getColumn( 3, &mLocation );
-
- // Set rotation variables
- Point3F rotation = mPlayer->getRotation();
- Point3F headRotation = mPlayer->getHeadRotation();
- curYaw = rotation.z;
- curPitch = headRotation.x;
- xDiff = mAimLocation.x - mLocation.x;
- yDiff = mAimLocation.y - mLocation.y;
-
- // first do Yaw
- if( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) {
- // use the cur yaw between -Pi and Pi
- while( curYaw > M_2PI_F )
- curYaw -= M_2PI_F;
- while( curYaw < -M_2PI_F )
- curYaw += M_2PI_F;
-
- // find the new yaw
- newYaw = mAtan2( xDiff, yDiff );
-
- // find the yaw diff
- F32 yawDiff = newYaw - curYaw;
-
- // make it between 0 and 2PI
- if( yawDiff < 0.0f )
- yawDiff += M_2PI_F;
- else if( yawDiff >= M_2PI_F )
- yawDiff -= M_2PI_F;
-
- // now make sure we take the short way around the circle
- if( yawDiff > M_2PI_F )
- yawDiff -= M_2PI_F;
- else if( yawDiff < -M_2PI_F )
- yawDiff += M_2PI_F;
-
- mMove.yaw = yawDiff;
-
- // set up the movement matrix
- moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) );
- }
- else
- moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) );
-
- // next do pitch
- F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len();
- if( !mIsZero( horzDist ) ) {
- //we shoot from the gun, not the eye...
- F32 vertDist = mAimLocation.z;
-
- newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 2.0f );
-
- F32 pitchDiff = newPitch - curPitch;
- mMove.pitch = pitchDiff;
- }
-
- // finally, mMove towards mMoveDestination
- xDiff = mMoveDestination.x - mLocation.x;
- yDiff = mMoveDestination.y - mLocation.y;
- // Check if we should mMove, or if we are 'close enough'
- if( ( ( mFabs( xDiff ) > mMoveTolerance ) ||
- ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !mIsZero( mMoveSpeed ) ) )
- {
- if( mIsZero( xDiff ) )
- mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
- else if( mIsZero( yDiff ) )
- mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
- else if( mFabs( xDiff ) > mFabs( yDiff ) ) {
- F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed;
- mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value );
- mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
- }
- else {
- F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed;
- mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value );
- mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
- }
-
- //now multiply the mMove vector by the transpose of the object rotation matrix
- moveMatrix.transpose();
- Point3F newMove;
- moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0.0f ), &newMove );
-
- //and sub the result back in the mMove structure
- mMove.x = newMove.x;
- mMove.y = newMove.y;
- // We should check to see if we are stuck...
- if( mLocation.x == mLastLocation.x &&
- mLocation.y == mLastLocation.y &&
- mLocation.z == mLastLocation.z ) {
- // We're stuck...probably
- setMoveMode( ModeStuck );
- }
- else
- setMoveMode( ModeMove );
- }
- else {
- // Ok, we are close enough, lets stop
- // setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback
- mMoveMode = ModeStop;
- throwCallback( "onReachDestination" ); // Callback
- }
- break;
- }
- // Test for target location in sight
- RayInfo dummy;
- Point3F targetLoc = mMoveDestination; // Change this
- if( mPlayer ) {
- if( !mPlayer->getContainer()->castRay( mLocation, targetLoc,
- StaticShapeObjectType | StaticObjectType |
- TerrainObjectType, &dummy ) ) {
- if( !mTargetInLOS )
- throwCallback( "onTargetEnterLOS" );
- }
- else {
- if( mTargetInLOS )
- throwCallback( "onTargetExitLOS" );
-
- }
- }
-
- // Copy over the trigger status
- for( S32 i = 0; i < MaxTriggerKeys; i++ ) {
- mMove.trigger[i] = mTriggers[i];
- mTriggers[i] = false;
- }
- return 1;
- }
- /**
- * This method is just called to stop the bots from running amuck
- * while the mission cycles
- */
- void AIClient::missionCycleCleanup() {
- setMoveMode( ModeStop );
- }
- /**
- * Utility function to throw callbacks
- */
- void AIClient::throwCallback( const char *name ) {
- Con::executef( this, name );
- }
- /**
- * What gets called when this gets created, different from constructor
- */
- void AIClient::onAdd( const char *nameSpace ) {
- // This doesn't work...
- //
- if( String::compare( nameSpace, mNameSpace->mName ) ) {
- Con::linkNamespaces( mNameSpace->mName, nameSpace );
- mNameSpace = Con::lookupNamespace( nameSpace );
- }
- throwCallback( "onAdd" );
- }
- // --------------------------------------------------------------------------------------------
- // Console Functions
- // --------------------------------------------------------------------------------------------
- /**
- * Sets the move speed for an AI object
- */
- DefineEngineMethod( AIClient, setMoveSpeed, void, (F32 speed), , "ai.setMoveSpeed( float );" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- ai->setMoveSpeed( speed );
- }
- /**
- * Stops all AI movement, halt!
- */
- DefineEngineMethod( AIClient, stop, void, (),, "ai.stop();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- ai->setMoveMode( AIClient::ModeStop );
- }
- /**
- * Tells the AI to aim at the location provided
- */
- DefineEngineMethod( AIClient, setAimLocation, void, (Point3F v), , "ai.setAimLocation( x y z );" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- ai->setAimLocation( v );
- }
- /**
- * Tells the AI to move to the location provided
- */
- DefineEngineMethod( AIClient, setMoveDestination, void, (Point3F v), , "ai.setMoveDestination( x y z );" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- ai->setMoveDestination( v );
- }
- /**
- * Returns the point the AI is aiming at
- */
- DefineEngineMethod( AIClient, getAimLocation, Point3F, (),, "ai.getAimLocation();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- return ai->getAimLocation();
- }
- /**
- * Returns the point the AI is set to move to
- */
- DefineEngineMethod( AIClient, getMoveDestination, Point3F, (),, "ai.getMoveDestination();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- return ai->getMoveDestination();
- }
- /**
- * Sets the bots target object
- */
- DefineEngineMethod( AIClient, setTargetObject, void, (const char * objName), , "ai.setTargetObject( obj );" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
-
- // Find the target
- ShapeBase *targetObject;
- if( Sim::findObject( objName, targetObject ) )
- ai->setTargetObject( targetObject );
- else
- ai->setTargetObject( NULL );
- }
- /**
- * Gets the object the AI is targeting
- */
- DefineEngineMethod( AIClient, getTargetObject, S32, (),, "ai.getTargetObject();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- return ai->getTargetObject();
- }
- /**
- * Tells the bot the mission is cycling
- */
- DefineEngineMethod( AIClient, missionCycleCleanup, void, (),, "ai.missionCycleCleanup();" )
- {
- AIClient *ai = static_cast<AIClient*>( object );
- ai->missionCycleCleanup();
- }
- /**
- * Sets the AI to run mode
- */
- DefineEngineMethod( AIClient, move, void, (),, "ai.move();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- ai->setMoveMode( AIClient::ModeMove );
- }
- /**
- * Gets the AI's location in the world
- */
- DefineEngineMethod( AIClient, getLocation, Point3F, (),, "ai.getLocation();" )
- {
- AIClient *ai = static_cast<AIClient *>( object );
- return ai->getLocation();
- }
- /**
- * Adds an AI Player to the game
- */
- DefineEngineFunction( aiAddPlayer, S32, (const char * name, const char * ns), (""), "'playerName'[, 'AIClassType'] );")
- {
- // Create the player
- AIClient *aiPlayer = new AIClient();
- aiPlayer->registerObject();
- aiPlayer->setGhostFrom(false);
- aiPlayer->setGhostTo(false);
- aiPlayer->setSendingEvents(false);
- aiPlayer->setTranslatesStrings(true);
- aiPlayer->setEstablished();
-
- // Add the connection to the client group
- SimGroup *g = Sim::getClientGroup();
- g->addObject( aiPlayer );
- // Execute the connect console function, this is the same
- // onConnect function invoked for normal client connections
- aiPlayer->onConnect_callback( name );
- // Now execute the onAdd command and feed it the namespace
- if(String::compare( ns,"" ) != 0 )
- aiPlayer->onAdd( ns );
- else
- aiPlayer->onAdd( "AIClient" );
- return aiPlayer->getId();
- }
- /**
- * Tells the AI to move forward 100 units...TEST FXN
- */
- DefineEngineMethod( AIClient, moveForward, void, (),, "ai.moveForward();" )
- {
-
- AIClient *ai = static_cast<AIClient *>( object );
- ShapeBase *player = dynamic_cast<ShapeBase*>(ai->getControlObject());
- Point3F location;
- MatrixF const &myTransform = player->getTransform();
- myTransform.getColumn( 3, &location );
- location.y += 100.0f;
-
- ai->setMoveDestination( location );
- } // *** /TEST FXN
|