aiTurretShape.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389
  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 "T3D/turret/aiTurretShape.h"
  23. #include "console/console.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "core/stream/bitStream.h"
  27. #include "math/mMath.h"
  28. #include "math/mathIO.h"
  29. #include "math/mathUtils.h"
  30. #include "gfx/gfxDrawUtil.h"
  31. #include "ts/tsShapeInstance.h"
  32. #include "math/mRandom.h"
  33. static U32 sScanTypeMask = PlayerObjectType |
  34. VehicleObjectType;
  35. static U32 sAimTypeMask = TerrainObjectType |
  36. WaterObjectType |
  37. PlayerObjectType |
  38. StaticShapeObjectType |
  39. VehicleObjectType |
  40. ItemObjectType;
  41. //----------------------------------------------------------------------------
  42. AITurretShapeData::StateData::StateData()
  43. {
  44. name = 0;
  45. transition.rest[0] = transition.rest[1] = -1;
  46. transition.target[0] = transition.target[1] = -1;
  47. transition.activated[0] = transition.activated[1] = -1;
  48. transition.timeout = -1;
  49. waitForTimeout = true;
  50. timeoutValue = 0;
  51. fire = false;
  52. scan = false;
  53. script = 0;
  54. scaleAnimation = false;
  55. direction = false;
  56. sequence = -1;
  57. }
  58. static AITurretShapeData::StateData gDefaultStateData;
  59. //----------------------------------------------------------------------------
  60. IMPLEMENT_CO_DATABLOCK_V1(AITurretShapeData);
  61. ConsoleDocClass( AITurretShapeData,
  62. "@brief Defines properties for an AITurretShape object.\n\n"
  63. "@see AITurretShape\n"
  64. "@see TurretShapeData\n"
  65. "@ingroup gameObjects\n"
  66. );
  67. AITurretShapeData::AITurretShapeData()
  68. {
  69. maxScanHeading = 90;
  70. maxScanPitch = 90;
  71. maxScanDistance = 20;
  72. // Do a full scan every three ticks
  73. scanTickFrequency = 3;
  74. // Randomly add 0 to 1 ticks to the scan frequency as
  75. // chosen every scan period.
  76. scanTickFrequencyVariance = 1;
  77. trackLostTargetTime = 0;
  78. scanNode = -1;
  79. aimNode = -1;
  80. maxWeaponRange = 100;
  81. weaponLeadVelocity = 0;
  82. for (S32 i = 0; i < MaxStates; i++) {
  83. stateName[i] = 0;
  84. stateTransitionAtRest[i] = 0;
  85. stateTransitionNotAtRest[i] = 0;
  86. stateTransitionTarget[i] = 0;
  87. stateTransitionNoTarget[i] = 0;
  88. stateTransitionActivated[i] = 0;
  89. stateTransitionDeactivated[i] = 0;
  90. stateTransitionTimeout[i] = 0;
  91. stateWaitForTimeout[i] = true;
  92. stateTimeoutValue[i] = 0;
  93. stateFire[i] = false;
  94. stateScan[i] = false;
  95. stateScaleAnimation[i] = true;
  96. stateDirection[i] = true;
  97. stateSequence[i] = 0;
  98. stateScript[i] = 0;
  99. }
  100. isAnimated = false;
  101. statesLoaded = false;
  102. fireState = -1;
  103. }
  104. void AITurretShapeData::initPersistFields()
  105. {
  106. addField("maxScanHeading", TypeF32, Offset(maxScanHeading, AITurretShapeData),
  107. "@brief Maximum number of degrees to scan left and right.\n\n"
  108. "@note Maximum scan heading is 90 degrees.\n");
  109. addField("maxScanPitch", TypeF32, Offset(maxScanPitch, AITurretShapeData),
  110. "@brief Maximum number of degrees to scan up and down.\n\n"
  111. "@note Maximum scan pitch is 90 degrees.\n");
  112. addField("maxScanDistance", TypeF32, Offset(maxScanDistance, AITurretShapeData),
  113. "@brief Maximum distance to scan.\n\n"
  114. "When combined with maxScanHeading and maxScanPitch this forms a 3D scanning wedge used to initially "
  115. "locate a target.\n");
  116. addField("scanTickFrequency", TypeS32, Offset(scanTickFrequency, AITurretShapeData),
  117. "@brief How often should we perform a full scan when looking for a target.\n\n"
  118. "Expressed as the number of ticks between full scans, but no less than 1.\n");
  119. addField("scanTickFrequencyVariance", TypeS32, Offset(scanTickFrequencyVariance, AITurretShapeData),
  120. "@brief Random amount that should be added to the scan tick frequency each scan period.\n\n"
  121. "Expressed as the number of ticks to randomly add, but no less than zero.\n");
  122. addField("trackLostTargetTime", TypeF32, Offset(trackLostTargetTime, AITurretShapeData),
  123. "@brief How long after the turret has lost the target should it still track it.\n\n"
  124. "Expressed in seconds.\n");
  125. addField("maxWeaponRange", TypeF32, Offset(maxWeaponRange, AITurretShapeData),
  126. "@brief Maximum distance that the weapon will fire upon a target.\n\n");
  127. addField("weaponLeadVelocity", TypeF32, Offset(weaponLeadVelocity, AITurretShapeData),
  128. "@brief Velocity used to lead target.\n\n"
  129. "If value <= 0, don't lead target.\n");
  130. // State arrays
  131. addArray( "States", MaxStates );
  132. addField( "stateName", TypeCaseString, Offset(stateName, AITurretShapeData), MaxStates,
  133. "Name of this state." );
  134. addField( "stateTransitionOnAtRest", TypeString, Offset(stateTransitionAtRest, AITurretShapeData), MaxStates,
  135. "Name of the state to transition to when the turret is at rest (static).");
  136. addField( "stateTransitionOnNotAtRest", TypeString, Offset(stateTransitionNotAtRest, AITurretShapeData), MaxStates,
  137. "Name of the state to transition to when the turret is not at rest (not static).");
  138. addField( "stateTransitionOnTarget", TypeString, Offset(stateTransitionTarget, AITurretShapeData), MaxStates,
  139. "Name of the state to transition to when the turret gains a target." );
  140. addField( "stateTransitionOnNoTarget", TypeString, Offset(stateTransitionNoTarget, AITurretShapeData), MaxStates,
  141. "Name of the state to transition to when the turret loses a target." );
  142. addField( "stateTransitionOnActivated", TypeString, Offset(stateTransitionActivated, AITurretShapeData), MaxStates,
  143. "Name of the state to transition to when the turret goes from deactivated to activated.");
  144. addField( "stateTransitionOnDeactivated", TypeString, Offset(stateTransitionDeactivated, AITurretShapeData), MaxStates,
  145. "Name of the state to transition to when the turret goes from activated to deactivated");
  146. addField( "stateTransitionOnTimeout", TypeString, Offset(stateTransitionTimeout, AITurretShapeData), MaxStates,
  147. "Name of the state to transition to when we have been in this state "
  148. "for stateTimeoutValue seconds." );
  149. addField( "stateTimeoutValue", TypeF32, Offset(stateTimeoutValue, AITurretShapeData), MaxStates,
  150. "Time in seconds to wait before transitioning to stateTransitionOnTimeout." );
  151. addField( "stateWaitForTimeout", TypeBool, Offset(stateWaitForTimeout, AITurretShapeData), MaxStates,
  152. "If false, this state ignores stateTimeoutValue and transitions "
  153. "immediately if other transition conditions are met." );
  154. addField( "stateFire", TypeBool, Offset(stateFire, AITurretShapeData), MaxStates,
  155. "The first state with this set to true is the state entered by the "
  156. "client when it receives the 'fire' event." );
  157. addField( "stateScan", TypeBool, Offset(stateScan, AITurretShapeData), MaxStates,
  158. "Indicates the turret should perform a continuous scan looking for targets." );
  159. addField( "stateDirection", TypeBool, Offset(stateDirection, AITurretShapeData), MaxStates,
  160. "@brief Direction of the animation to play in this state.\n\n"
  161. "True is forward, false is backward." );
  162. addField( "stateSequence", TypeString, Offset(stateSequence, AITurretShapeData), MaxStates,
  163. "Name of the sequence to play on entry to this state." );
  164. addField( "stateScaleAnimation", TypeBool, Offset(stateScaleAnimation, AITurretShapeData), MaxStates,
  165. "If true, the timeScale of the stateSequence animation will be adjusted "
  166. "such that the sequence plays for stateTimeoutValue seconds. " );
  167. addField( "stateScript", TypeCaseString, Offset(stateScript, AITurretShapeData), MaxStates,
  168. "@brief Method to execute on entering this state.\n\n"
  169. "Scoped to AITurretShapeData.");
  170. endArray( "States" );
  171. Parent::initPersistFields();
  172. }
  173. bool AITurretShapeData::onAdd()
  174. {
  175. if (!Parent::onAdd())
  176. return false;
  177. // Copy state data from the scripting arrays into the
  178. // state structure array. If we have state data already,
  179. // we are on the client and need to leave it alone.
  180. for (U32 i = 0; i < MaxStates; i++) {
  181. StateData& s = state[i];
  182. if (statesLoaded == false) {
  183. s.name = stateName[i];
  184. s.transition.rest[0] = lookupState(stateTransitionNotAtRest[i]);
  185. s.transition.rest[1] = lookupState(stateTransitionAtRest[i]);
  186. s.transition.target[0] = lookupState(stateTransitionNoTarget[i]);
  187. s.transition.target[1] = lookupState(stateTransitionTarget[i]);
  188. s.transition.activated[0] = lookupState(stateTransitionDeactivated[i]);
  189. s.transition.activated[1] = lookupState(stateTransitionActivated[i]);
  190. s.transition.timeout = lookupState(stateTransitionTimeout[i]);
  191. s.waitForTimeout = stateWaitForTimeout[i];
  192. s.timeoutValue = stateTimeoutValue[i];
  193. s.fire = stateFire[i];
  194. s.scan = stateScan[i];
  195. s.scaleAnimation = stateScaleAnimation[i];
  196. s.direction = stateDirection[i];
  197. s.script = stateScript[i];
  198. // Resolved at load time
  199. s.sequence = -1;
  200. }
  201. // The first state marked as "fire" is the state entered on the
  202. // client when it recieves a fire event.
  203. if (s.fire && fireState == -1)
  204. fireState = i;
  205. }
  206. // Always preload images, this is needed to avoid problems with
  207. // resolving sequences before transmission to a client.
  208. return true;
  209. }
  210. bool AITurretShapeData::preload(bool server, String &errorStr)
  211. {
  212. if (!Parent::preload(server, errorStr))
  213. return false;
  214. // We have mShape at this point. Resolve nodes.
  215. scanNode = mShape->findNode("scanPoint");
  216. aimNode = mShape->findNode("aimPoint");
  217. if (scanNode == -1) scanNode = pitchNode;
  218. if (scanNode == -1) scanNode = headingNode;
  219. if (aimNode == -1) aimNode = pitchNode;
  220. if (aimNode == -1) aimNode = headingNode;
  221. // Resolve state sequence names & emitter nodes
  222. isAnimated = false;
  223. for (U32 j = 0; j < MaxStates; j++) {
  224. StateData& s = state[j];
  225. if (stateSequence[j] && stateSequence[j][0])
  226. s.sequence = mShape->findSequence(stateSequence[j]);
  227. if (s.sequence != -1)
  228. {
  229. // This state has an animation sequence
  230. isAnimated = true;
  231. }
  232. }
  233. return true;
  234. }
  235. void AITurretShapeData::packData(BitStream* stream)
  236. {
  237. Parent::packData(stream);
  238. stream->write(maxScanHeading);
  239. stream->write(maxScanPitch);
  240. stream->write(maxScanDistance);
  241. stream->write(maxWeaponRange);
  242. stream->write(weaponLeadVelocity);
  243. for (U32 i = 0; i < MaxStates; i++)
  244. if (stream->writeFlag(state[i].name && state[i].name[0])) {
  245. StateData& s = state[i];
  246. // States info not needed on the client:
  247. // s.scriptNames
  248. // Transitions are inc. one to account for -1 values
  249. stream->writeString(state[i].name);
  250. stream->writeInt(s.transition.rest[0]+1,NumStateBits);
  251. stream->writeInt(s.transition.rest[1]+1,NumStateBits);
  252. stream->writeInt(s.transition.target[0]+1,NumStateBits);
  253. stream->writeInt(s.transition.target[1]+1,NumStateBits);
  254. stream->writeInt(s.transition.activated[0]+1,NumStateBits);
  255. stream->writeInt(s.transition.activated[1]+1,NumStateBits);
  256. stream->writeInt(s.transition.timeout+1,NumStateBits);
  257. if(stream->writeFlag(s.timeoutValue != gDefaultStateData.timeoutValue))
  258. stream->write(s.timeoutValue);
  259. stream->writeFlag(s.waitForTimeout);
  260. stream->writeFlag(s.fire);
  261. stream->writeFlag(s.scan);
  262. stream->writeFlag(s.scaleAnimation);
  263. stream->writeFlag(s.direction);
  264. if(stream->writeFlag(s.sequence != gDefaultStateData.sequence))
  265. stream->writeSignedInt(s.sequence, 16);
  266. }
  267. }
  268. void AITurretShapeData::unpackData(BitStream* stream)
  269. {
  270. Parent::unpackData(stream);
  271. stream->read(&maxScanHeading);
  272. stream->read(&maxScanPitch);
  273. stream->read(&maxScanDistance);
  274. stream->read(&maxWeaponRange);
  275. stream->read(&weaponLeadVelocity);
  276. for (U32 i = 0; i < MaxStates; i++) {
  277. if (stream->readFlag()) {
  278. StateData& s = state[i];
  279. // States info not needed on the client:
  280. // s.scriptNames
  281. // Transitions are dec. one to restore -1 values
  282. s.name = stream->readSTString();
  283. s.transition.rest[0] = stream->readInt(NumStateBits) - 1;
  284. s.transition.rest[1] = stream->readInt(NumStateBits) - 1;
  285. s.transition.target[0] = stream->readInt(NumStateBits) - 1;
  286. s.transition.target[1] = stream->readInt(NumStateBits) - 1;
  287. s.transition.activated[0] = stream->readInt(NumStateBits) - 1;
  288. s.transition.activated[1] = stream->readInt(NumStateBits) - 1;
  289. s.transition.timeout = stream->readInt(NumStateBits) - 1;
  290. if(stream->readFlag())
  291. stream->read(&s.timeoutValue);
  292. else
  293. s.timeoutValue = gDefaultStateData.timeoutValue;
  294. s.waitForTimeout = stream->readFlag();
  295. s.fire = stream->readFlag();
  296. s.scan = stream->readFlag();
  297. s.scaleAnimation = stream->readFlag();
  298. s.direction = stream->readFlag();
  299. if(stream->readFlag())
  300. s.sequence = stream->readSignedInt(16);
  301. }
  302. }
  303. statesLoaded = true;
  304. }
  305. S32 AITurretShapeData::lookupState(const char* name)
  306. {
  307. if (!name || !name[0])
  308. return -1;
  309. for (U32 i = 0; i < MaxStates; i++)
  310. if (stateName[i] && !dStricmp(name,stateName[i]))
  311. return i;
  312. Con::errorf(ConsoleLogEntry::General,"AITurretShapeData:: Could not resolve state \"%s\" for image \"%s\"",name,getName());
  313. return 0;
  314. }
  315. //----------------------------------------------------------------------------
  316. // Used to build potential target list
  317. static void _scanCallback( SceneObject* object, void* data )
  318. {
  319. AITurretShape* turret = (AITurretShape*)data;
  320. ShapeBase* shape = dynamic_cast<ShapeBase*>(object);
  321. if (shape && shape->getDamageState() == ShapeBase::Enabled)
  322. {
  323. Point3F targetPos = shape->getBoxCenter();
  324. // Put target position into the scan node's space
  325. turret->mScanWorkspaceScanWorldMat.mulP(targetPos);
  326. // Is the target within scanning distance
  327. if (targetPos.lenSquared() > turret->getMaxScanDistanceSquared())
  328. return;
  329. // Make sure the target is in front and within the maximum
  330. // heading range
  331. Point2F targetXY(targetPos.x, targetPos.y);
  332. targetXY.normalizeSafe();
  333. F32 headingDot = mDot(Point2F(0, 1), targetXY);
  334. F32 heading = mAcos(headingDot);
  335. if (headingDot < 0 || heading > turret->getMaxScanHeading())
  336. return;
  337. // Make sure the target is in front and within the maximum
  338. // pitch range
  339. Point2F targetZY(targetPos.z, targetPos.y);
  340. targetZY.normalizeSafe();
  341. F32 pitchDot = mDot(Point2F(0, 1), targetZY);
  342. F32 pitch = mAcos(pitchDot);
  343. if (pitchDot < 0 || pitch > turret->getMaxScanPitch())
  344. return;
  345. turret->addPotentialTarget(shape);
  346. }
  347. }
  348. // Used to sort potential target list based on distance
  349. static Point3F comparePoint;
  350. static S32 QSORT_CALLBACK _sortCallback(const void* a,const void* b)
  351. {
  352. const ShapeBase* s1 = (*reinterpret_cast<const ShapeBase* const*>(a));
  353. const ShapeBase* s2 = (*reinterpret_cast<const ShapeBase* const*>(b));
  354. F32 s1Len = (s1->getPosition() - comparePoint).lenSquared();
  355. F32 s2Len = (s2->getPosition() - comparePoint).lenSquared();
  356. return s1Len - s2Len;
  357. }
  358. IMPLEMENT_CO_NETOBJECT_V1(AITurretShape);
  359. ConsoleDocClass( AITurretShape,
  360. "@ingroup gameObjects"
  361. );
  362. AITurretShape::AITurretShape()
  363. {
  364. mTypeMask |= VehicleObjectType | DynamicShapeObjectType;
  365. mDataBlock = 0;
  366. mScanHeading = 0;
  367. mScanPitch = 0;
  368. mScanDistance = 0;
  369. mScanDistanceSquared = 0;
  370. mScanBox = Box3F::Zero;
  371. mScanTickFrequency = 1;
  372. mScanTickFrequencyVariance = 0;
  373. mTicksToNextScan = mScanTickFrequency;
  374. mWeaponRangeSquared = 0;
  375. mWeaponLeadVelocitySquared = 0;
  376. mScanForTargets = false;
  377. mTrackTarget = false;
  378. mState = NULL;
  379. mStateDelayTime = 0;
  380. mStateActive = true;
  381. mStateAnimThread = NULL;
  382. // For the TurretShape class
  383. mSubclassTurretShapeHandlesScene = true;
  384. }
  385. AITurretShape::~AITurretShape()
  386. {
  387. _cleanupPotentialTargets();
  388. }
  389. //----------------------------------------------------------------------------
  390. void AITurretShape::initPersistFields()
  391. {
  392. Parent::initPersistFields();
  393. }
  394. bool AITurretShape::onAdd()
  395. {
  396. if( !Parent::onAdd() )
  397. return false;
  398. // Add this object to the scene
  399. addToScene();
  400. _setScanBox();
  401. if (isServerObject())
  402. _initState();
  403. if (isServerObject())
  404. scriptOnAdd();
  405. return true;
  406. }
  407. void AITurretShape::onRemove()
  408. {
  409. Parent::onRemove();
  410. scriptOnRemove();
  411. mIgnoreObjects.clear();
  412. // Remove this object from the scene
  413. removeFromScene();
  414. }
  415. bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload)
  416. {
  417. mDataBlock = dynamic_cast<AITurretShapeData*>(dptr);
  418. if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
  419. return false;
  420. mScanHeading = mDegToRad(mDataBlock->maxScanHeading);
  421. if (mIsZero(mScanHeading))
  422. mScanHeading = M_PI_F;
  423. mScanPitch = mDegToRad(mDataBlock->maxScanPitch);
  424. if (mIsZero(mScanPitch))
  425. mScanPitch = M_PI_F;
  426. mScanDistance = mDataBlock->maxScanDistance;
  427. mScanDistanceSquared = mScanDistance * mScanDistance;
  428. mWeaponRangeSquared = mDataBlock->maxWeaponRange * mDataBlock->maxWeaponRange;
  429. mWeaponLeadVelocitySquared = (mDataBlock->weaponLeadVelocity > 0) ? (mDataBlock->weaponLeadVelocity * mDataBlock->weaponLeadVelocity) : 0;
  430. // The scan box is built such that the scanning origin is at (0,0,0) and the scanning distance
  431. // is out along the y axis. When this is transformed to the turret's location is provides a
  432. // scanning volume in front of the turret.
  433. F32 scanX = mScanDistance*mSin(mScanHeading);
  434. F32 scanY = mScanDistance;
  435. F32 scanZ = mScanDistance*mSin(mScanPitch);
  436. mScanBox.set(-scanX, 0, -scanZ, scanX, scanY, scanZ);
  437. mScanTickFrequency = mDataBlock->scanTickFrequency;
  438. if (mScanTickFrequency < 1)
  439. mScanTickFrequency = 1;
  440. mScanTickFrequencyVariance = mDataBlock->scanTickFrequencyVariance;
  441. if (mScanTickFrequencyVariance < 0)
  442. mScanTickFrequencyVariance = 0;
  443. mTicksToNextScan = mScanTickFrequency;
  444. // For states
  445. mStateAnimThread = 0;
  446. if (mDataBlock->isAnimated)
  447. {
  448. mStateAnimThread = mShapeInstance->addThread();
  449. mShapeInstance->setTimeScale(mStateAnimThread,0);
  450. }
  451. scriptOnNewDataBlock();
  452. return true;
  453. }
  454. //----------------------------------------------------------------------------
  455. void AITurretShape::addToIgnoreList(ShapeBase* obj)
  456. {
  457. mIgnoreObjects.addObject(obj);
  458. }
  459. void AITurretShape::removeFromIgnoreList(ShapeBase* obj)
  460. {
  461. mIgnoreObjects.removeObject(obj);
  462. }
  463. void AITurretShape::clearIgnoreList()
  464. {
  465. mIgnoreObjects.clear();
  466. }
  467. S32 AITurretShape::ignoreListCount()
  468. {
  469. return mIgnoreObjects.size();
  470. }
  471. SimObject* AITurretShape::getIgnoreListObject(S32 index)
  472. {
  473. return mIgnoreObjects.at(index);
  474. }
  475. //----------------------------------------------------------------------------
  476. void AITurretShape::_initState()
  477. {
  478. // Set the turret to its starting state.
  479. setTurretState(0, true);
  480. }
  481. void AITurretShape::setTurretStateName(const char* newState, bool force)
  482. {
  483. S32 stateVal = mDataBlock->lookupState(newState);
  484. if (stateVal >= 0)
  485. {
  486. setTurretState(stateVal, force);
  487. }
  488. }
  489. void AITurretShape::setTurretState(U32 newState, bool force)
  490. {
  491. setMaskBits(TurretStateMask);
  492. // If going back into the same state, just reset the timer
  493. // and invoke the script callback
  494. if (!force && mState == &mDataBlock->state[newState])
  495. {
  496. mStateDelayTime = mState->timeoutValue;
  497. if (mState->script && !isGhost())
  498. _scriptCallback(mState->script);
  499. return;
  500. }
  501. mState = &mDataBlock->state[newState];
  502. // Reset cyclic sequences back to the first frame to turn it off
  503. // (the first key frame should be it's off state).
  504. if (mStateAnimThread && mStateAnimThread->getSequence()->isCyclic())
  505. {
  506. mShapeInstance->setPos(mStateAnimThread,0);
  507. mShapeInstance->setTimeScale(mStateAnimThread,0);
  508. }
  509. AITurretShapeData::StateData& stateData = *mState;
  510. // Check for immediate transitions
  511. S32 ns;
  512. if ((ns = stateData.transition.rest[mAtRest]) != -1)
  513. {
  514. setTurretState(ns);
  515. return;
  516. }
  517. if ((ns = stateData.transition.target[mTarget.isValid()]) != -1)
  518. {
  519. setTurretState(ns);
  520. return;
  521. }
  522. if ((ns = stateData.transition.activated[mStateActive]) != -1)
  523. {
  524. setTurretState(ns);
  525. return;
  526. }
  527. //
  528. // Initialize the new state...
  529. //
  530. mStateDelayTime = stateData.timeoutValue;
  531. // Play animation
  532. if (mStateAnimThread && stateData.sequence != -1)
  533. {
  534. mShapeInstance->setSequence(mStateAnimThread,stateData.sequence, stateData.direction ? 0.0f : 1.0f);
  535. F32 timeScale = (stateData.scaleAnimation && stateData.timeoutValue) ?
  536. mShapeInstance->getDuration(mStateAnimThread) / stateData.timeoutValue : 1.0f;
  537. mShapeInstance->setTimeScale(mStateAnimThread, stateData.direction ? timeScale : -timeScale);
  538. }
  539. // Script callback on server
  540. if (stateData.script && stateData.script[0] && !isGhost())
  541. _scriptCallback(stateData.script);
  542. // If there is a zero timeout, and a timeout transition, then
  543. // go ahead and transition imediately.
  544. if (!mStateDelayTime)
  545. {
  546. if ((ns = stateData.transition.timeout) != -1)
  547. {
  548. setTurretState(ns);
  549. return;
  550. }
  551. }
  552. }
  553. void AITurretShape::_updateTurretState(F32 dt)
  554. {
  555. AITurretShapeData::StateData& stateData = *mState;
  556. mStateDelayTime -= dt;
  557. // Check for transitions. On some states we must wait for the
  558. // full timeout value before moving on.
  559. if (mStateDelayTime <= 0 || !stateData.waitForTimeout)
  560. {
  561. S32 ns;
  562. if ((ns = stateData.transition.rest[mAtRest]) != -1)
  563. setTurretState(ns);
  564. else if ((ns = stateData.transition.target[mTarget.isValid()]) != -1)
  565. setTurretState(ns);
  566. else if ((ns = stateData.transition.activated[mStateActive]) != -1)
  567. setTurretState(ns);
  568. else if (mStateDelayTime <= 0 && (ns = stateData.transition.timeout) != -1)
  569. setTurretState(ns);
  570. }
  571. }
  572. void AITurretShape::_scriptCallback(const char* function)
  573. {
  574. Con::executef( mDataBlock, function, getIdString() );
  575. }
  576. //----------------------------------------------------------------------------
  577. void AITurretShape::_cleanupPotentialTargets()
  578. {
  579. mPotentialTargets.clear();
  580. }
  581. void AITurretShape::_performScan()
  582. {
  583. // Only on server
  584. if (isClientObject())
  585. return;
  586. // Are we ready for a scan?
  587. --mTicksToNextScan;
  588. if (mTicksToNextScan > 0)
  589. return;
  590. _cleanupPotentialTargets();
  591. _setScanBox();
  592. // Set up for the scan
  593. getScanTransform(mScanWorkspaceScanMat);
  594. mScanWorkspaceScanWorldMat = mScanWorkspaceScanMat;
  595. mScanWorkspaceScanWorldMat.affineInverse();
  596. disableCollision();
  597. for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter )
  598. {
  599. ShapeBase* obj = static_cast<ShapeBase*>( *iter );
  600. obj->disableCollision();
  601. }
  602. gServerContainer.findObjects( mTransformedScanBox, sScanTypeMask, _scanCallback, (void*)this );
  603. for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter )
  604. {
  605. ShapeBase* obj = static_cast<ShapeBase*>( *iter );
  606. obj->enableCollision();
  607. }
  608. enableCollision();
  609. if (mPotentialTargets.size() == 0)
  610. {
  611. // No targets in range. Clear out our current target, if necessary.
  612. _lostTarget();
  613. }
  614. else
  615. {
  616. // Sort the targets
  617. comparePoint = getPosition();
  618. dQsort(mPotentialTargets.address(),mPotentialTargets.size(),sizeof(SimObjectList::value_type),_sortCallback);
  619. // Go through the targets in order to find one that is not blocked from view
  620. Point3F start;
  621. mScanWorkspaceScanMat.getColumn(3, &start);
  622. S32 index = 0;
  623. bool los = false;
  624. disableCollision();
  625. for (index=0; index < mPotentialTargets.size(); ++index)
  626. {
  627. ShapeBase* shape = (ShapeBase*)mPotentialTargets[index];
  628. Point3F sightPoint;
  629. los = _testTargetLineOfSight(start, shape, sightPoint);
  630. // Check if we have a clear line of sight
  631. if (los)
  632. break;
  633. }
  634. enableCollision();
  635. // If we found a valid, visible target (no hits between here and there), latch on to it
  636. if (los)
  637. {
  638. _gainedTarget((ShapeBase*)mPotentialTargets[index]);
  639. }
  640. }
  641. // Prepare for next scan period
  642. mTicksToNextScan = mScanTickFrequency;
  643. if (mScanTickFrequencyVariance > 0)
  644. {
  645. mTicksToNextScan += gRandGen.randI(0, mScanTickFrequencyVariance);
  646. }
  647. }
  648. void AITurretShape::addPotentialTarget(ShapeBase* shape)
  649. {
  650. mPotentialTargets.push_back(shape);
  651. }
  652. void AITurretShape::_lostTarget()
  653. {
  654. mTarget.target = NULL;
  655. }
  656. void AITurretShape::_gainedTarget(ShapeBase* target)
  657. {
  658. mTarget.target = target;
  659. if (target)
  660. {
  661. mTarget.hadValidTarget = true;
  662. }
  663. }
  664. void AITurretShape::_trackTarget(F32 dt)
  665. {
  666. // Only on server
  667. if (isClientObject())
  668. return;
  669. // We can only track a target if we have one
  670. if (!mTarget.isValid())
  671. return;
  672. Point3F targetPos = mTarget.target->getBoxCenter();
  673. // Can we see the target?
  674. MatrixF aimMat;
  675. getAimTransform(aimMat);
  676. Point3F start;
  677. aimMat.getColumn(3, &start);
  678. RayInfo ri;
  679. Point3F sightPoint;
  680. disableCollision();
  681. bool los = _testTargetLineOfSight(start, mTarget.target, sightPoint);
  682. enableCollision();
  683. if (!los)
  684. {
  685. // Target is blocked. Should we try to track from its last
  686. // known position and velocity?
  687. SimTime curTime = Sim::getCurrentTime();
  688. if ( (curTime - mTarget.lastSightTime) > (mDataBlock->trackLostTargetTime * 1000.0f) )
  689. {
  690. // Time's up. Stop tracking.
  691. _cleanupTargetAndTurret();
  692. return;
  693. }
  694. // Use last known information to attempt to
  695. // continue to track target for a while.
  696. targetPos = mTarget.lastPos + mTarget.lastVel * F32(curTime - mTarget.lastSightTime) / 1000.0f;
  697. }
  698. else
  699. {
  700. // Target is visible
  701. // We only track targets that are alive
  702. if (mTarget.target->getDamageState() != Enabled)
  703. {
  704. // We can't track any more
  705. _cleanupTargetAndTurret();
  706. return;
  707. }
  708. targetPos = sightPoint;
  709. // Store latest target info
  710. mTarget.lastPos = targetPos;
  711. mTarget.lastVel = mTarget.target->getVelocity();
  712. mTarget.lastSightTime = Sim::getCurrentTime();
  713. }
  714. // Calculate angles to face the target, specifically the part that we can see
  715. VectorF toTarget;
  716. MatrixF mat;
  717. S32 node = mDataBlock->aimNode;
  718. if (node != -1)
  719. {
  720. // Get the current position of our node
  721. MatrixF* nodeTrans = &mShapeInstance->mNodeTransforms[node];
  722. Point3F currentPos;
  723. nodeTrans->getColumn(3, &currentPos);
  724. // Turn this into a matrix we can use to put the target
  725. // position into our space.
  726. MatrixF nodeMat(true);
  727. nodeMat.setColumn(3, currentPos);
  728. mat.mul(mObjToWorld, nodeMat);
  729. mat.affineInverse();
  730. }
  731. else
  732. {
  733. mat = mWorldToObj;
  734. }
  735. mat.mulP(targetPos, &toTarget);
  736. // lead the target
  737. F32 timeToTargetSquared = (mWeaponLeadVelocitySquared > 0) ? toTarget.lenSquared() / mWeaponLeadVelocitySquared : 0;
  738. if (timeToTargetSquared > 1.0)
  739. {
  740. targetPos = targetPos + (mTarget.lastVel * mSqrt(timeToTargetSquared));
  741. mat.mulP(targetPos, &toTarget);
  742. }
  743. F32 yaw, pitch;
  744. MathUtils::getAnglesFromVector(toTarget, yaw, pitch);
  745. if (yaw > M_PI_F)
  746. yaw = yaw - M_2PI_F;
  747. //if (pitch > M_PI_F)
  748. // pitch = -(pitch - M_2PI_F);
  749. Point3F rot(-pitch, 0.0f, yaw);
  750. // If we have a rotation rate make sure we follow it
  751. if (mHeadingRate > 0)
  752. {
  753. F32 rate = mHeadingRate * dt;
  754. F32 rateCheck = mFabs(rot.z - mRot.z);
  755. if (rateCheck > rate)
  756. {
  757. // This will clamp the new value to the rate regardless if it
  758. // is increasing or decreasing.
  759. rot.z = mClampF(rot.z, mRot.z-rate, mRot.z+rate);
  760. }
  761. }
  762. if (mPitchRate > 0)
  763. {
  764. F32 rate = mPitchRate * dt;
  765. F32 rateCheck = mFabs(rot.x - mRot.x);
  766. if (rateCheck > rate)
  767. {
  768. // This will clamp the new value to the rate regardless if it
  769. // is increasing or decreasing.
  770. rot.x = mClampF(rot.x, mRot.x-rate, mRot.x+rate);
  771. }
  772. }
  773. // Test if the rotation to the target is outside of our limits
  774. if (_outsideLimits(rot))
  775. {
  776. // We can't track any more
  777. _cleanupTargetAndTurret();
  778. return;
  779. }
  780. // Test if the target is out of weapons range
  781. if (toTarget.lenSquared() > mWeaponRangeSquared)
  782. {
  783. // We can't track any more
  784. _cleanupTargetAndTurret();
  785. return;
  786. }
  787. mRot = rot;
  788. _setRotation( mRot );
  789. setMaskBits(TurretUpdateMask);
  790. }
  791. void AITurretShape::_cleanupTargetAndTurret()
  792. {
  793. _lostTarget();
  794. resetTarget();
  795. }
  796. bool AITurretShape::_testTargetLineOfSight(Point3F& aimPoint, ShapeBase* target, Point3F& sightPoint)
  797. {
  798. Point3F targetCenter = target->getBoxCenter();
  799. RayInfo ri;
  800. bool hit = false;
  801. target->disableCollision();
  802. // First check for a clear line of sight to the target's center
  803. Point3F testPoint = targetCenter;
  804. hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
  805. if (hit)
  806. {
  807. // No clear line of sight to center, so try to the target's right. Players holding
  808. // a gun in their right hand will tend to stick their right shoulder out first if
  809. // they're peering around some cover to shoot, like a wall.
  810. Box3F targetBounds = target->getObjBox();
  811. F32 radius = targetBounds.len_x() > targetBounds.len_y() ? targetBounds.len_x() : targetBounds.len_y();
  812. radius *= 0.5;
  813. VectorF toTurret = aimPoint - targetCenter;
  814. toTurret.normalizeSafe();
  815. VectorF toTurretRight = mCross(toTurret, Point3F::UnitZ);
  816. testPoint = targetCenter + toTurretRight * radius;
  817. hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
  818. if (hit)
  819. {
  820. // No clear line of sight to right, so try the target's left
  821. VectorF toTurretLeft = toTurretRight * -1.0f;
  822. testPoint = targetCenter + toTurretLeft * radius;
  823. hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
  824. }
  825. if (hit)
  826. {
  827. // No clear line of sight to left, so try the target's top
  828. testPoint = targetCenter;
  829. testPoint.z += targetBounds.len_z() * 0.5f;
  830. hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
  831. }
  832. if (hit)
  833. {
  834. // No clear line of sight to top, so try the target's bottom
  835. testPoint = targetCenter;
  836. testPoint.z -= targetBounds.len_z() * 0.5f;
  837. hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
  838. }
  839. }
  840. target->enableCollision();
  841. if (!hit)
  842. {
  843. // Line of sight point is that last one we tested
  844. sightPoint = testPoint;
  845. }
  846. return !hit;
  847. }
  848. //----------------------------------------------------------------------------
  849. void AITurretShape::setAllGunsFiring(bool fire)
  850. {
  851. setImageTriggerState(0,fire);
  852. setImageTriggerState(1,fire);
  853. setImageTriggerState(2,fire);
  854. setImageTriggerState(3,fire);
  855. }
  856. void AITurretShape::setGunSlotFiring(S32 slot, bool fire)
  857. {
  858. if (slot < 0 || slot > 3)
  859. return;
  860. setImageTriggerState(slot, fire);
  861. }
  862. //----------------------------------------------------------------------------
  863. void AITurretShape::setTransform(const MatrixF& mat)
  864. {
  865. Parent::setTransform(mat);
  866. // Set the scanning box
  867. _setScanBox();
  868. }
  869. void AITurretShape::recenterTurret()
  870. {
  871. mRot.set(0,0,0);
  872. _setRotation( mRot );
  873. setMaskBits(TurretUpdateMask);
  874. }
  875. void AITurretShape::_setScanBox()
  876. {
  877. mTransformedScanBox = mScanBox;
  878. MatrixF mat;
  879. getScanTransform(mat);
  880. mat.mul(mTransformedScanBox);
  881. }
  882. void AITurretShape::getScanTransform(MatrixF& mat)
  883. {
  884. if (mDataBlock && mDataBlock->scanNode != -1)
  885. {
  886. if (getNodeTransform(mDataBlock->scanNode, mat))
  887. {
  888. return;
  889. }
  890. }
  891. mat = mObjToWorld;
  892. }
  893. void AITurretShape::getAimTransform(MatrixF& mat)
  894. {
  895. if (mDataBlock && mDataBlock->aimNode != -1)
  896. {
  897. if (getNodeTransform(mDataBlock->aimNode, mat))
  898. {
  899. return;
  900. }
  901. }
  902. mat = mObjToWorld;
  903. }
  904. //----------------------------------------------------------------------------
  905. void AITurretShape::processTick(const Move* move)
  906. {
  907. Parent::processTick(move);
  908. if (isServerObject() && mDamageState == Enabled)
  909. {
  910. _updateTurretState(TickSec);
  911. if (mScanForTargets)
  912. {
  913. // Perform a scan for targets
  914. _performScan();
  915. // If we found one, turn off the scan
  916. if (mTarget.isValid())
  917. {
  918. mScanForTargets = false;
  919. }
  920. }
  921. if (mTrackTarget)
  922. {
  923. _trackTarget(TickSec);
  924. // If the target is lost, no longer track it
  925. if (!mTarget.isValid())
  926. {
  927. mTrackTarget = false;
  928. }
  929. }
  930. }
  931. }
  932. void AITurretShape::advanceTime(F32 dt)
  933. {
  934. // If there were any ShapeBase script threads that
  935. // have played, then we need to update all code
  936. // controlled nodes. This is done before the Parent
  937. // call as script threads may play and be destroyed
  938. // before our code is called.
  939. bool updateNodes = false;
  940. for (U32 i = 0; i < MaxScriptThreads; i++)
  941. {
  942. Thread& st = mScriptThread[i];
  943. if (st.thread)
  944. {
  945. updateNodes = true;
  946. break;
  947. }
  948. }
  949. Parent::advanceTime(dt);
  950. // Update any state thread
  951. AITurretShapeData::StateData& stateData = *mState;
  952. if (mStateAnimThread && stateData.sequence != -1)
  953. {
  954. mShapeInstance->advanceTime(dt,mStateAnimThread);
  955. updateNodes = true;
  956. }
  957. if (updateNodes)
  958. {
  959. // Update all code controlled nodes
  960. _updateNodes(mRot);
  961. }
  962. }
  963. //----------------------------------------------------------------------------
  964. U32 AITurretShape::packUpdate(NetConnection *connection, U32 mask, BitStream *bstream)
  965. {
  966. U32 retMask = Parent::packUpdate(connection,mask,bstream);
  967. // Indicate that the transform has changed to update the scan box
  968. bstream->writeFlag(mask & (PositionMask | ExtendedInfoMask));
  969. // Handle any state changes that need to be passed along
  970. if (bstream->writeFlag(mask & TurretStateMask))
  971. {
  972. bstream->write(mDataBlock->lookupState(mState->name));
  973. }
  974. return retMask;
  975. }
  976. void AITurretShape::unpackUpdate(NetConnection *connection, BitStream *bstream)
  977. {
  978. Parent::unpackUpdate(connection,bstream);
  979. // Transform has changed
  980. if (bstream->readFlag())
  981. {
  982. _setScanBox();
  983. }
  984. //TurretStateMask
  985. if (bstream->readFlag())
  986. {
  987. S32 newstate;
  988. bstream->read( &newstate );
  989. setTurretState(newstate);
  990. }
  991. }
  992. //----------------------------------------------------------------------------
  993. void AITurretShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex )
  994. {
  995. Parent::prepBatchRender( state, mountedImageIndex );
  996. if ( !gShowBoundingBox )
  997. return;
  998. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  999. ri->renderDelegate.bind( this, &AITurretShape::_renderScanner );
  1000. ri->type = RenderPassManager::RIT_Editor;
  1001. state->getRenderPass()->addInst( ri );
  1002. }
  1003. void AITurretShape::_renderScanner( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
  1004. {
  1005. GFXStateBlockDesc desc;
  1006. desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  1007. desc.setZReadWrite(false,true);
  1008. desc.fillMode = GFXFillWireframe;
  1009. // Render the scan box
  1010. GFX->getDrawUtil()->drawCube(desc, mTransformedScanBox.getExtents(), mTransformedScanBox.getCenter(), ColorI(255, 0, 0));
  1011. // Render a line from the scan node to the max scan distance
  1012. MatrixF nodeMat;
  1013. if (getNodeTransform(mDataBlock->scanNode, nodeMat))
  1014. {
  1015. Point3F start;
  1016. nodeMat.getColumn(3, &start);
  1017. Point3F end(0.0f, mScanDistance, 0.0f);
  1018. nodeMat.mulP(end);
  1019. GFX->getDrawUtil()->drawLine(start, end, ColorI(255, 255, 0));
  1020. }
  1021. }
  1022. //----------------------------------------------------------------------------
  1023. DefineEngineMethod( AITurretShape, addToIgnoreList, void, (ShapeBase* obj),,
  1024. "@brief Adds object to the turret's ignore list.\n\n"
  1025. "All objects in this list will be ignored by the turret's targeting.\n"
  1026. "@param obj The ShapeBase object to ignore.\n")
  1027. {
  1028. object->addToIgnoreList(obj);
  1029. }
  1030. DefineEngineMethod( AITurretShape, removeFromIgnoreList, void, (ShapeBase* obj),,
  1031. "@brief Removes object from the turret's ignore list.\n\n"
  1032. "All objects in this list will be ignored by the turret's targeting.\n"
  1033. "@param obj The ShapeBase object to once again allow for targeting.\n")
  1034. {
  1035. object->removeFromIgnoreList(obj);
  1036. }
  1037. DefineEngineMethod( AITurretShape, clearIgnoreList, void, (),,
  1038. "@brief Removes all objects from the turret's ignore list.\n\n"
  1039. "All objects in this list will be ignored by the turret's targeting.\n")
  1040. {
  1041. object->clearIgnoreList();
  1042. }
  1043. DefineEngineMethod( AITurretShape, ignoreListCount, S32, (),,
  1044. "@brief Returns the number of objects in the turrets ignore list.\n\n"
  1045. "All objects in this list will be ignored by the turret's targeting.\n")
  1046. {
  1047. return object->ignoreListCount();
  1048. }
  1049. DefineEngineMethod( AITurretShape, getIgnoreListObject, SimObject*, (S32 index),,
  1050. "@brief Returns the object in the ignore list at index.\n\n"
  1051. "All objects in this list will be ignored by the turret's targeting.\n"
  1052. "@param index The index of the object in the ignore list being retrieved.\n")
  1053. {
  1054. return object->getIgnoreListObject(index);
  1055. }
  1056. DefineEngineMethod( AITurretShape, setTurretState, void, (const char* newState, bool force), (false),
  1057. "@brief Set the turret's current state.\n\n"
  1058. "Normally the turret's state comes from updating the state machine but this method "
  1059. "allows you to override this and jump to the requested state immediately.\n"
  1060. "@param newState The name of the new state.\n"
  1061. "@param force Is true then force the full processing of the new state even if it is the "
  1062. "same as the current state. If false then only the time out value is reset and the state's "
  1063. "script method is called, if any.\n")
  1064. {
  1065. object->setTurretStateName(newState, force);
  1066. }
  1067. DefineEngineMethod( AITurretShape, activateTurret, void, ( ),,
  1068. "@brief Activate a turret from a deactive state.\n\n")
  1069. {
  1070. object->activateTurret();
  1071. }
  1072. DefineEngineMethod( AITurretShape, deactivateTurret, void, ( ),,
  1073. "@brief Deactivate a turret from an active state.\n\n")
  1074. {
  1075. object->deactivateTurret();
  1076. }
  1077. DefineEngineMethod( AITurretShape, startScanForTargets, void, ( ),,
  1078. "@brief Begin scanning for a target.\n\n")
  1079. {
  1080. object->startScanForTargets();
  1081. }
  1082. DefineEngineMethod( AITurretShape, stopScanForTargets, void, ( ),,
  1083. "@brief Stop scanning for targets.\n\n"
  1084. "@note Only impacts the scanning for new targets. Does not effect a turret's current "
  1085. "target lock.\n")
  1086. {
  1087. object->stopScanForTargets();
  1088. }
  1089. DefineEngineMethod( AITurretShape, startTrackingTarget, void, ( ),,
  1090. "@brief Have the turret track the current target.\n\n")
  1091. {
  1092. object->startTrackingTarget();
  1093. }
  1094. DefineEngineMethod( AITurretShape, stopTrackingTarget, void, ( ),,
  1095. "@brief Stop the turret from tracking the current target.\n\n")
  1096. {
  1097. object->stopTrackingTarget();
  1098. }
  1099. DefineEngineMethod( AITurretShape, hasTarget, bool, (),,
  1100. "@brief Indicates if the turret has a target.\n\n"
  1101. "@returns True if the turret has a target.\n")
  1102. {
  1103. return object->hasTarget();
  1104. }
  1105. DefineEngineMethod( AITurretShape, getTarget, SimObject*, (),,
  1106. "@brief Get the turret's current target.\n\n"
  1107. "@returns The object that is the target's current target, or 0 if no target.\n")
  1108. {
  1109. return object->getTarget();
  1110. }
  1111. DefineEngineMethod( AITurretShape, resetTarget, void, ( ),,
  1112. "@brief Resets the turret's target tracking.\n\n"
  1113. "Only resets the internal target tracking. Does not modify the turret's facing.\n")
  1114. {
  1115. object->resetTarget();
  1116. }
  1117. DefineEngineMethod( AITurretShape, setWeaponLeadVelocity, void, (F32 velocity),,
  1118. "@brief Set the turret's projectile velocity to help lead the target.\n\n"
  1119. "This value normally comes from AITurretShapeData::weaponLeadVelocity but this method "
  1120. "allows you to override the datablock value. This can be useful if the turret changes "
  1121. "ammunition, uses a different weapon than the default, is damaged, etc.\n"
  1122. "@note Setting this to 0 will disable target leading.\n")
  1123. {
  1124. object->setWeaponLeadVelocity(velocity);
  1125. }
  1126. DefineEngineMethod( AITurretShape, getWeaponLeadVelocity, F32, (),,
  1127. "@brief Get the turret's defined projectile velocity that helps with target leading.\n\n"
  1128. "@returns The defined weapon projectile speed, or 0 if leading is disabled.\n")
  1129. {
  1130. return object->getWeaponLeadVelocity();
  1131. }
  1132. DefineEngineMethod( AITurretShape, setAllGunsFiring, void, (bool fire),,
  1133. "@brief Set the firing state of the turret's guns.\n\n"
  1134. "@param fire Set to true to activate all guns. False to deactivate them.\n")
  1135. {
  1136. object->setAllGunsFiring(fire);
  1137. }
  1138. DefineEngineMethod( AITurretShape, setGunSlotFiring, void, (S32 slot, bool fire),,
  1139. "@brief Set the firing state of the given gun slot.\n\n"
  1140. "@param slot The gun to modify. Valid range is 0-3 that corresponds to the weapon mount point.\n"
  1141. "@param fire Set to true to activate the gun. False to deactivate it.\n")
  1142. {
  1143. object->setGunSlotFiring(slot, fire);
  1144. }
  1145. DefineEngineMethod( AITurretShape, recenterTurret, void, ( ),,
  1146. "@brief Recenter the turret's weapon.\n\n")
  1147. {
  1148. object->recenterTurret();
  1149. }