aiClient.cpp 16 KB

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