aiClient.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "T3D/aiClient.h"
  24. #include "math/mMatrix.h"
  25. #include "T3D/shapeBase.h"
  26. #include "T3D/player.h"
  27. #include "T3D/gameBase/moveManager.h"
  28. #include "console/consoleInternal.h"
  29. IMPLEMENT_CONOBJECT( AIClient );
  30. ConsoleDocClass( AIClient,
  31. "@brief Simulated client driven by AI commands.\n\n"
  32. "This object is derived from the AIConnection class. It introduces its own Player object "
  33. "to solidify the purpose of this class: Simulated client connecting as a player\n\n"
  34. "To get more specific, if you want a strong alternative to AIPlayer (and wish to make use "
  35. "of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, "
  36. "contains quite a bit of functionality you will find in AIPlayer, and has its own Player "
  37. "object.\n\n"
  38. "@note This is a legacy class, which you are discouraged from using as it will "
  39. "most likely be deprecated in a future version. For now it has been left in for "
  40. "backwards compatibility with TGE and the old RTS Kit. Use AIPlayer instead.\n\n"
  41. "@see AIPlayer, AIConnection\n\n"
  42. "@ingroup AI\n"
  43. "@ingroup Networking\n"
  44. );
  45. /**
  46. * Constructor
  47. */
  48. AIClient::AIClient() {
  49. mMoveMode = ModeStop;
  50. mMoveDestination.set( 0.0f, 0.0f, 0.0f );
  51. mAimLocation.set( 0.0f, 0.0f, 0.0f );
  52. mMoveSpeed = 1.0f;
  53. mMoveTolerance = 0.25f;
  54. // Clear the triggers
  55. for( S32 i = 0; i < MaxTriggerKeys; i++ )
  56. mTriggers[i] = false;
  57. mAimToDestination = true;
  58. mTargetInLOS = false;
  59. mLocation.set( 0.0f, 0.0f, 0.0f );
  60. mPlayer = NULL;
  61. }
  62. /**
  63. * Destructor
  64. */
  65. AIClient::~AIClient() {
  66. // Blah
  67. }
  68. /**
  69. * Sets the object the bot is targeting
  70. *
  71. * @param targetObject The object to target
  72. */
  73. void AIClient::setTargetObject( ShapeBase *targetObject ) {
  74. if ( !targetObject || !bool( mTargetObject ) || targetObject->getId() != mTargetObject->getId() )
  75. mTargetInLOS = false;
  76. mTargetObject = targetObject;
  77. }
  78. /**
  79. * Returns the target object
  80. *
  81. * @return Object bot is targeting
  82. */
  83. S32 AIClient::getTargetObject() const {
  84. if( bool( mTargetObject ) )
  85. return mTargetObject->getId();
  86. else
  87. return -1;
  88. }
  89. /**
  90. * Sets the speed at which this AI moves
  91. *
  92. * @param speed Speed to move, default player was 10
  93. */
  94. void AIClient::setMoveSpeed( F32 speed ) {
  95. if( speed <= 0.0f )
  96. mMoveSpeed = 0.0f;
  97. else
  98. mMoveSpeed = getMin( 1.0f, speed );
  99. }
  100. /**
  101. * Sets the movement mode for this AI
  102. *
  103. * @param mode Movement mode, see enum
  104. */
  105. void AIClient::setMoveMode( S32 mode ) {
  106. if( mode < 0 || mode >= ModeCount )
  107. mode = 0;
  108. if( mode != mMoveMode ) {
  109. switch( mode ) {
  110. case ModeStuck:
  111. throwCallback( "onStuck" );
  112. break;
  113. case ModeMove:
  114. if( mMoveMode == ModeStuck )
  115. throwCallback( "onUnStuck" );
  116. else
  117. throwCallback( "onMove" );
  118. break;
  119. case ModeStop:
  120. throwCallback( "onStop" );
  121. break;
  122. }
  123. }
  124. mMoveMode = mode;
  125. }
  126. /**
  127. * Sets how far away from the move location is considered
  128. * "on target"
  129. *
  130. * @param tolerance Movement tolerance for error
  131. */
  132. void AIClient::setMoveTolerance( const F32 tolerance ) {
  133. mMoveTolerance = getMax( 0.1f, tolerance );
  134. }
  135. /**
  136. * Sets the location for the bot to run to
  137. *
  138. * @param location Point to run to
  139. */
  140. void AIClient::setMoveDestination( const Point3F &location ) {
  141. // Ok, here's the story...we're going to aim where we are going UNLESS told otherwise
  142. if( mAimToDestination ) {
  143. mAimLocation = location;
  144. mAimLocation.z = 0.0f;
  145. }
  146. mMoveDestination = location;
  147. }
  148. /**
  149. * Sets the location for the bot to aim at
  150. *
  151. * @param location Point to aim at
  152. */
  153. void AIClient::setAimLocation( const Point3F &location ) {
  154. mAimLocation = location;
  155. mAimToDestination = false;
  156. }
  157. /**
  158. * Clears the aim location and sets it to the bot's
  159. * current destination so he looks where he's going
  160. */
  161. void AIClient::clearAim() {
  162. mAimLocation = Point3F( 0.0f, 0.0f, 0.0f );
  163. mAimToDestination = true;
  164. }
  165. /**
  166. * This method gets the move list for an object, in the case
  167. * of the AI, it actually calculates the moves, and then
  168. * sends them down the pipe.
  169. *
  170. * @param movePtr Pointer to move the move list into
  171. * @param numMoves Number of moves in the move list
  172. */
  173. U32 AIClient::getMoveList( Move **movePtr,U32 *numMoves ) {
  174. //initialize the move structure and return pointers
  175. mMove = NullMove;
  176. *movePtr = &mMove;
  177. *numMoves = 1;
  178. // Check if we got a player
  179. mPlayer = NULL;
  180. mPlayer = dynamic_cast<Player *>( getControlObject() );
  181. // We got a something controling us?
  182. if( !mPlayer )
  183. return 1;
  184. // What is The Matrix?
  185. MatrixF moveMatrix;
  186. moveMatrix.set( EulerF( 0, 0, 0 ) );
  187. moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) );
  188. moveMatrix.transpose();
  189. // Position / rotation variables
  190. F32 curYaw, curPitch;
  191. F32 newYaw, newPitch;
  192. F32 xDiff, yDiff;
  193. F32 moveSpeed = mMoveSpeed;
  194. switch( mMoveMode ) {
  195. case ModeStop:
  196. return 1; // Stop means no action
  197. break;
  198. case ModeStuck:
  199. // Fall through, so we still try to move
  200. case ModeMove:
  201. // Get my location
  202. MatrixF const& myTransform = mPlayer->getTransform();
  203. myTransform.getColumn( 3, &mLocation );
  204. // Set rotation variables
  205. Point3F rotation = mPlayer->getRotation();
  206. Point3F headRotation = mPlayer->getHeadRotation();
  207. curYaw = rotation.z;
  208. curPitch = headRotation.x;
  209. xDiff = mAimLocation.x - mLocation.x;
  210. yDiff = mAimLocation.y - mLocation.y;
  211. // first do Yaw
  212. if( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) {
  213. // use the cur yaw between -Pi and Pi
  214. while( curYaw > M_2PI_F )
  215. curYaw -= M_2PI_F;
  216. while( curYaw < -M_2PI_F )
  217. curYaw += M_2PI_F;
  218. // find the new yaw
  219. newYaw = mAtan2( xDiff, yDiff );
  220. // find the yaw diff
  221. F32 yawDiff = newYaw - curYaw;
  222. // make it between 0 and 2PI
  223. if( yawDiff < 0.0f )
  224. yawDiff += M_2PI_F;
  225. else if( yawDiff >= M_2PI_F )
  226. yawDiff -= M_2PI_F;
  227. // now make sure we take the short way around the circle
  228. if( yawDiff > M_2PI_F )
  229. yawDiff -= M_2PI_F;
  230. else if( yawDiff < -M_2PI_F )
  231. yawDiff += M_2PI_F;
  232. mMove.yaw = yawDiff;
  233. // set up the movement matrix
  234. moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) );
  235. }
  236. else
  237. moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) );
  238. // next do pitch
  239. F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len();
  240. if( !mIsZero( horzDist ) ) {
  241. //we shoot from the gun, not the eye...
  242. F32 vertDist = mAimLocation.z;
  243. newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 2.0f );
  244. F32 pitchDiff = newPitch - curPitch;
  245. mMove.pitch = pitchDiff;
  246. }
  247. // finally, mMove towards mMoveDestination
  248. xDiff = mMoveDestination.x - mLocation.x;
  249. yDiff = mMoveDestination.y - mLocation.y;
  250. // Check if we should mMove, or if we are 'close enough'
  251. if( ( ( mFabs( xDiff ) > mMoveTolerance ) ||
  252. ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !mIsZero( mMoveSpeed ) ) )
  253. {
  254. if( mIsZero( xDiff ) )
  255. mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
  256. else if( mIsZero( yDiff ) )
  257. mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
  258. else if( mFabs( xDiff ) > mFabs( yDiff ) ) {
  259. F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed;
  260. mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value );
  261. mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
  262. }
  263. else {
  264. F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed;
  265. mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value );
  266. mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
  267. }
  268. //now multiply the mMove vector by the transpose of the object rotation matrix
  269. moveMatrix.transpose();
  270. Point3F newMove;
  271. moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0.0f ), &newMove );
  272. //and sub the result back in the mMove structure
  273. mMove.x = newMove.x;
  274. mMove.y = newMove.y;
  275. // We should check to see if we are stuck...
  276. if( mLocation.x == mLastLocation.x &&
  277. mLocation.y == mLastLocation.y &&
  278. mLocation.z == mLastLocation.z ) {
  279. // We're stuck...probably
  280. setMoveMode( ModeStuck );
  281. }
  282. else
  283. setMoveMode( ModeMove );
  284. }
  285. else {
  286. // Ok, we are close enough, lets stop
  287. // setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback
  288. mMoveMode = ModeStop;
  289. throwCallback( "onReachDestination" ); // Callback
  290. }
  291. break;
  292. }
  293. // Test for target location in sight
  294. RayInfo dummy;
  295. Point3F targetLoc = mMoveDestination; // Change this
  296. if( mPlayer ) {
  297. if( !mPlayer->getContainer()->castRay( mLocation, targetLoc,
  298. StaticShapeObjectType | StaticObjectType |
  299. TerrainObjectType, &dummy ) ) {
  300. if( !mTargetInLOS )
  301. throwCallback( "onTargetEnterLOS" );
  302. }
  303. else {
  304. if( mTargetInLOS )
  305. throwCallback( "onTargetExitLOS" );
  306. }
  307. }
  308. // Copy over the trigger status
  309. for( S32 i = 0; i < MaxTriggerKeys; i++ ) {
  310. mMove.trigger[i] = mTriggers[i];
  311. mTriggers[i] = false;
  312. }
  313. return 1;
  314. }
  315. /**
  316. * This method is just called to stop the bots from running amuck
  317. * while the mission cycles
  318. */
  319. void AIClient::missionCycleCleanup() {
  320. setMoveMode( ModeStop );
  321. }
  322. /**
  323. * Utility function to throw callbacks
  324. */
  325. void AIClient::throwCallback( const char *name ) {
  326. Con::executef( this, name );
  327. }
  328. /**
  329. * What gets called when this gets created, different from constructor
  330. */
  331. void AIClient::onAdd( const char *nameSpace ) {
  332. // This doesn't work...
  333. //
  334. if( dStrcmp( nameSpace, mNameSpace->mName ) ) {
  335. Con::linkNamespaces( mNameSpace->mName, nameSpace );
  336. mNameSpace = Con::lookupNamespace( nameSpace );
  337. }
  338. throwCallback( "onAdd" );
  339. }
  340. // --------------------------------------------------------------------------------------------
  341. // Console Functions
  342. // --------------------------------------------------------------------------------------------
  343. /**
  344. * Sets the move speed for an AI object
  345. */
  346. ConsoleMethod( AIClient, setMoveSpeed, void, 3, 3, "ai.setMoveSpeed( float );" ) {
  347. AIClient *ai = static_cast<AIClient *>( object );
  348. ai->setMoveSpeed( dAtof( argv[2] ) );
  349. }
  350. /**
  351. * Stops all AI movement, halt!
  352. */
  353. ConsoleMethod( AIClient, stop, void, 2, 2, "ai.stop();" ) {
  354. AIClient *ai = static_cast<AIClient *>( object );
  355. ai->setMoveMode( AIClient::ModeStop );
  356. }
  357. /**
  358. * Tells the AI to aim at the location provided
  359. */
  360. ConsoleMethod( AIClient, setAimLocation, void, 3, 3, "ai.setAimLocation( x y z );" ) {
  361. AIClient *ai = static_cast<AIClient *>( object );
  362. Point3F v( 0.0f,0.0f,0.0f );
  363. dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
  364. ai->setAimLocation( v );
  365. }
  366. /**
  367. * Tells the AI to move to the location provided
  368. */
  369. ConsoleMethod( AIClient, setMoveDestination, void, 3, 3, "ai.setMoveDestination( x y z );" ) {
  370. AIClient *ai = static_cast<AIClient *>( object );
  371. Point3F v( 0.0f, 0.0f, 0.0f );
  372. dSscanf( argv[2], "%f %f", &v.x, &v.y );
  373. ai->setMoveDestination( v );
  374. }
  375. /**
  376. * Returns the point the AI is aiming at
  377. */
  378. ConsoleMethod( AIClient, getAimLocation, const char *, 2, 2, "ai.getAimLocation();" ) {
  379. AIClient *ai = static_cast<AIClient *>( object );
  380. Point3F aimPoint = ai->getAimLocation();
  381. static const U32 bufSize = 256;
  382. char *returnBuffer = Con::getReturnBuffer( bufSize );
  383. dSprintf( returnBuffer, bufSize, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z );
  384. return returnBuffer;
  385. }
  386. /**
  387. * Returns the point the AI is set to move to
  388. */
  389. ConsoleMethod( AIClient, getMoveDestination, const char *, 2, 2, "ai.getMoveDestination();" ) {
  390. AIClient *ai = static_cast<AIClient *>( object );
  391. Point3F movePoint = ai->getMoveDestination();
  392. static const U32 bufSize = 256;
  393. char *returnBuffer = Con::getReturnBuffer( bufSize );
  394. dSprintf( returnBuffer, bufSize, "%f %f %f", movePoint.x, movePoint.y, movePoint.z );
  395. return returnBuffer;
  396. }
  397. /**
  398. * Sets the bots target object
  399. */
  400. ConsoleMethod( AIClient, setTargetObject, void, 3, 3, "ai.setTargetObject( obj );" ) {
  401. AIClient *ai = static_cast<AIClient *>( object );
  402. // Find the target
  403. ShapeBase *targetObject;
  404. if( Sim::findObject( argv[2], targetObject ) )
  405. ai->setTargetObject( targetObject );
  406. else
  407. ai->setTargetObject( NULL );
  408. }
  409. /**
  410. * Gets the object the AI is targeting
  411. */
  412. ConsoleMethod( AIClient, getTargetObject, S32, 2, 2, "ai.getTargetObject();" ) {
  413. AIClient *ai = static_cast<AIClient *>( object );
  414. return ai->getTargetObject();
  415. }
  416. /**
  417. * Tells the bot the mission is cycling
  418. */
  419. ConsoleMethod( AIClient, missionCycleCleanup, void, 2, 2, "ai.missionCycleCleanup();" ) {
  420. AIClient *ai = static_cast<AIClient*>( object );
  421. ai->missionCycleCleanup();
  422. }
  423. /**
  424. * Sets the AI to run mode
  425. */
  426. ConsoleMethod( AIClient, move, void, 2, 2, "ai.move();" ) {
  427. AIClient *ai = static_cast<AIClient *>( object );
  428. ai->setMoveMode( AIClient::ModeMove );
  429. }
  430. /**
  431. * Gets the AI's location in the world
  432. */
  433. ConsoleMethod( AIClient, getLocation, const char *, 2, 2, "ai.getLocation();" ) {
  434. AIClient *ai = static_cast<AIClient *>( object );
  435. Point3F locPoint = ai->getLocation();
  436. static const U32 bufSize = 256;
  437. char *returnBuffer = Con::getReturnBuffer( bufSize );
  438. dSprintf( returnBuffer, bufSize, "%f %f %f", locPoint.x, locPoint.y, locPoint.z );
  439. return returnBuffer;
  440. }
  441. /**
  442. * Adds an AI Player to the game
  443. */
  444. ConsoleFunction( aiAddPlayer, S32 , 2, 3, "aiAddPlayer( 'playerName'[, 'AIClassType'] );" ) {
  445. // Create the player
  446. AIClient *aiPlayer = new AIClient();
  447. aiPlayer->registerObject();
  448. aiPlayer->setGhostFrom(false);
  449. aiPlayer->setGhostTo(false);
  450. aiPlayer->setSendingEvents(false);
  451. aiPlayer->setTranslatesStrings(true);
  452. aiPlayer->setEstablished();
  453. // Add the connection to the client group
  454. SimGroup *g = Sim::getClientGroup();
  455. g->addObject( aiPlayer );
  456. char *name = new char[ dStrlen( argv[1] ) + 1];
  457. char *ns = new char[ dStrlen( argv[2] ) + 1];
  458. dStrcpy( name, argv[1] );
  459. dStrcpy( ns, argv[2] );
  460. // Execute the connect console function, this is the same
  461. // onConnect function invoked for normal client connections
  462. Con::executef( aiPlayer, "onConnect", name );
  463. // Now execute the onAdd command and feed it the namespace
  464. if( argc > 2 )
  465. aiPlayer->onAdd( ns );
  466. else
  467. aiPlayer->onAdd( "AIClient" );
  468. return aiPlayer->getId();
  469. }
  470. /**
  471. * Tells the AI to move forward 100 units...TEST FXN
  472. */
  473. ConsoleMethod( AIClient, moveForward, void, 2, 2, "ai.moveForward();" ) {
  474. AIClient *ai = static_cast<AIClient *>( object );
  475. ShapeBase *player = dynamic_cast<ShapeBase*>(ai->getControlObject());
  476. Point3F location;
  477. MatrixF const &myTransform = player->getTransform();
  478. myTransform.getColumn( 3, &location );
  479. location.y += 100.0f;
  480. ai->setMoveDestination( location );
  481. } // *** /TEST FXN