gameBase.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "T3D/gameBase/gameBase.h"
  28. #include "console/consoleTypes.h"
  29. #include "console/engineAPI.h"
  30. #include "console/consoleInternal.h"
  31. #include "core/stream/bitStream.h"
  32. #include "sim/netConnection.h"
  33. #include "T3D/gameBase/gameConnection.h"
  34. #include "math/mathIO.h"
  35. #include "T3D/gameBase/moveManager.h"
  36. #include "T3D/gameBase/gameProcess.h"
  37. #ifdef TORQUE_DEBUG_NET_MOVES
  38. #include "T3D/aiConnection.h"
  39. #endif
  40. #ifdef TORQUE_AFX_ENABLED
  41. #include "afx/arcaneFX.h"
  42. #endif
  43. //----------------------------------------------------------------------------
  44. // Ghost update relative priority values
  45. static F32 sUpFov = 1.0;
  46. static F32 sUpDistance = 0.4f;
  47. static F32 sUpVelocity = 0.4f;
  48. static F32 sUpSkips = 0.2f;
  49. static F32 sUpInterest = 0.2f;
  50. //----------------------------------------------------------------------------
  51. IMPLEMENT_CO_DATABLOCK_V1(GameBaseData);
  52. ConsoleDocClass( GameBaseData,
  53. "@brief Scriptable, demo-able datablock. Used by GameBase objects.\n\n"
  54. "@see GameBase\n"
  55. "@ingroup gameObjects\n"
  56. );
  57. IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ),
  58. "@brief Called when the object is added to the scene.\n\n"
  59. "@param obj the GameBase object\n\n"
  60. "@tsexample\n"
  61. "datablock GameBaseData(MyObjectData)\n"
  62. "{\n"
  63. " category = \"Misc\";\n"
  64. "};\n\n"
  65. "function MyObjectData::onAdd( %this, %obj )\n"
  66. "{\n"
  67. " echo( \"Added \" @ %obj.getName() @ \" to the scene.\" );\n"
  68. "}\n\n"
  69. "function MyObjectData::onNewDataBlock( %this, %obj )\n"
  70. "{\n"
  71. " echo( \"Assign \" @ %this.getName() @ \" datablock to \" %obj.getName() );\n"
  72. "}\n\n"
  73. "function MyObjectData::onRemove( %this, %obj )\n"
  74. "{\n"
  75. " echo( \"Removed \" @ %obj.getName() @ \" to the scene.\" );\n"
  76. "}\n\n"
  77. "function MyObjectData::onMount( %this, %obj, %mountObj, %node )\n"
  78. "{\n"
  79. " echo( %obj.getName() @ \" mounted to \" @ %mountObj.getName() );\n"
  80. "}\n\n"
  81. "function MyObjectData::onUnmount( %this, %obj, %mountObj, %node )\n"
  82. "{\n"
  83. " echo( %obj.getName() @ \" unmounted from \" @ %mountObj.getName() );\n"
  84. "}\n\n"
  85. "@endtsexample\n" );
  86. IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj, bool reload), ( obj, reload),
  87. "@brief Called when the object has a new datablock assigned.\n\n"
  88. "@param obj the GameBase object\n\n"
  89. "@see onAdd for an example\n" );
  90. IMPLEMENT_CALLBACK( GameBaseData, onRemove, void, ( GameBase* obj ), ( obj ),
  91. "@brief Called when the object is removed from the scene.\n\n"
  92. "@param obj the GameBase object\n\n"
  93. "@see onAdd for an example\n" );
  94. IMPLEMENT_CALLBACK( GameBaseData, onMount, void, ( SceneObject* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ),
  95. "@brief Called when the object is mounted to another object in the scene.\n\n"
  96. "@param obj the GameBase object being mounted\n"
  97. "@param mountObj the object we are mounted to\n"
  98. "@param node the mountObj node we are mounted to\n\n"
  99. "@see onAdd for an example\n" );
  100. IMPLEMENT_CALLBACK( GameBaseData, onUnmount, void, ( SceneObject* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ),
  101. "@brief Called when the object is unmounted from another object in the scene.\n\n"
  102. "@param obj the GameBase object being unmounted\n"
  103. "@param mountObj the object we are unmounted from\n"
  104. "@param node the mountObj node we are unmounted from\n\n"
  105. "@see onAdd for an example\n" );
  106. IMPLEMENT_CALLBACK( GameBase, setControl, void, ( bool controlled ), ( controlled ),
  107. "@brief Called when the client controlling the object changes.\n\n"
  108. "@param controlled true if a client now controls this object, false if no "
  109. "client controls this object.\n" );
  110. GameBaseData::GameBaseData()
  111. {
  112. mCategory = StringTable->EmptyString();
  113. mPacked = false;
  114. }
  115. GameBaseData::GameBaseData(const GameBaseData& other, bool temp_clone) : SimDataBlock(other, temp_clone)
  116. {
  117. mPacked = other.mPacked;
  118. mCategory = other.mCategory;
  119. //mReloadSignal = other.mReloadSignal; // DO NOT copy the mReloadSignal member.
  120. }
  121. void GameBaseData::inspectPostApply()
  122. {
  123. Parent::inspectPostApply();
  124. // Tell interested parties ( like objects referencing this datablock )
  125. // that we have been modified and they might want to rebuild...
  126. mReloadSignal.trigger();
  127. }
  128. bool GameBaseData::onAdd()
  129. {
  130. if (!Parent::onAdd())
  131. return false;
  132. return true;
  133. }
  134. void GameBaseData::initPersistFields()
  135. {
  136. docsURL;
  137. addGroup("Scripting");
  138. addField( "category", TypeCaseString, Offset(mCategory, GameBaseData ),
  139. "The group that this datablock will show up in under the \"Scripted\" "
  140. "tab in the World Editor Library." );
  141. endGroup("Scripting");
  142. Parent::initPersistFields();
  143. }
  144. bool GameBaseData::preload(bool server, String &errorStr)
  145. {
  146. if (!Parent::preload(server, errorStr))
  147. return false;
  148. mPacked = false;
  149. return true;
  150. }
  151. void GameBaseData::unpackData(BitStream* stream)
  152. {
  153. Parent::unpackData(stream);
  154. mPacked = true;
  155. }
  156. //----------------------------------------------------------------------------
  157. bool UNPACK_DB_ID(BitStream * stream, U32 & id)
  158. {
  159. if (stream->readFlag())
  160. {
  161. id = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
  162. return true;
  163. }
  164. return false;
  165. }
  166. bool PACK_DB_ID(BitStream * stream, U32 id)
  167. {
  168. if (stream->writeFlag(id))
  169. {
  170. stream->writeRangedU32(id,DataBlockObjectIdFirst,DataBlockObjectIdLast);
  171. return true;
  172. }
  173. return false;
  174. }
  175. bool PRELOAD_DB(U32 & id, SimDataBlock ** data, bool server, const char * clientMissing, const char * serverMissing)
  176. {
  177. if (server)
  178. {
  179. if (*data)
  180. id = (*data)->getId();
  181. else if (server && serverMissing)
  182. {
  183. Con::errorf(ConsoleLogEntry::General,serverMissing);
  184. return false;
  185. }
  186. }
  187. else
  188. {
  189. if (id && !Sim::findObject(id,*data) && clientMissing)
  190. {
  191. Con::errorf(ConsoleLogEntry::General,clientMissing);
  192. return false;
  193. }
  194. }
  195. return true;
  196. }
  197. //----------------------------------------------------------------------------
  198. bool GameBase::gShowBoundingBox = false;
  199. //----------------------------------------------------------------------------
  200. IMPLEMENT_CO_NETOBJECT_V1(GameBase);
  201. ConsoleDocClass( GameBase,
  202. "@brief Base class for game objects which use datablocks, networking, are "
  203. "editable, and need to process ticks.\n\n"
  204. "@ingroup gameObjects\n"
  205. );
  206. GameBase::GameBase()
  207. : mDataBlock( NULL ),
  208. mControllingClient( NULL ),
  209. mCurrentWaterObject( NULL )
  210. {
  211. mNetFlags.set(Ghostable);
  212. mTypeMask |= GameBaseObjectType;
  213. mProcessTag = 0;
  214. // From ProcessObject
  215. mIsGameBase = true;
  216. #ifdef TORQUE_DEBUG_NET_MOVES
  217. mLastMoveId = 0;
  218. mTicksSinceLastMove = 0;
  219. mIsAiControlled = false;
  220. #endif
  221. mCameraFov = 90.f;
  222. }
  223. GameBase::~GameBase()
  224. {
  225. #ifdef TORQUE_AFX_ENABLED
  226. if (mScope_registered)
  227. arcaneFX::unregisterScopedObject(this);
  228. #endif
  229. }
  230. //----------------------------------------------------------------------------
  231. bool GameBase::onAdd()
  232. {
  233. if ( !Parent::onAdd() )
  234. return false;
  235. // Datablock must be initialized on the server.
  236. // Client datablock are initialized by the initial update.
  237. #ifdef TORQUE_AFX_ENABLED
  238. if (isClientObject())
  239. {
  240. if (mScope_id > 0 && !mScope_registered)
  241. arcaneFX::registerScopedObject(this);
  242. }
  243. else
  244. {
  245. if ( mDataBlock && !onNewDataBlock( mDataBlock, false ) )
  246. return false;
  247. }
  248. #else
  249. if ( isServerObject() && mDataBlock && !onNewDataBlock( mDataBlock, false ) )
  250. return false;
  251. #endif
  252. setProcessTick( true );
  253. return true;
  254. }
  255. void GameBase::onRemove()
  256. {
  257. #ifdef TORQUE_AFX_ENABLED
  258. if (mScope_registered)
  259. arcaneFX::unregisterScopedObject(this);
  260. #endif
  261. // EDITOR FEATURE: Remove us from the reload signal of our datablock.
  262. if ( mDataBlock )
  263. mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified );
  264. Parent::onRemove();
  265. }
  266. bool GameBase::onNewDataBlock( GameBaseData *dptr, bool reload )
  267. {
  268. // EDITOR FEATURE: Remove us from old datablock's reload signal and
  269. // add us to the new one.
  270. if ( !reload )
  271. {
  272. if ( mDataBlock )
  273. mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified );
  274. if ( dptr )
  275. dptr->mReloadSignal.notify( this, &GameBase::_onDatablockModified );
  276. }
  277. mDataBlock = dptr;
  278. if ( !mDataBlock )
  279. return false;
  280. #ifdef TORQUE_AFX_ENABLED
  281. // Don't set mask when new datablock is a temp-clone.
  282. if (mDataBlock->isTempClone())
  283. return true;
  284. #endif
  285. setMaskBits(DataBlockMask);
  286. return true;
  287. }
  288. void GameBase::_onDatablockModified()
  289. {
  290. AssertFatal( mDataBlock, "GameBase::onDatablockModified - mDataBlock is NULL." );
  291. onNewDataBlock( mDataBlock, true );
  292. }
  293. void GameBase::inspectPostApply()
  294. {
  295. Parent::inspectPostApply();
  296. setMaskBits(ExtendedInfoMask);
  297. }
  298. void GameBase::onInspect(GuiInspector* inspector)
  299. {
  300. if (mDataBlock && mDataBlock->isMethod("onInspect"))
  301. Con::executef(mDataBlock, "onInspect", this, inspector);
  302. else
  303. Parent::onInspect(inspector);
  304. }
  305. //----------------------------------------------------------------------------
  306. void GameBase::processTick(const Move * move)
  307. {
  308. #ifdef TORQUE_DEBUG_NET_MOVES
  309. if (!move)
  310. mTicksSinceLastMove++;
  311. const char * srv = isClientObject() ? "client" : "server";
  312. const char * who = "";
  313. if (isClientObject())
  314. {
  315. if (this == (GameBase*)GameConnection::getConnectionToServer()->getControlObject())
  316. who = " player";
  317. else
  318. who = " ghost";
  319. if (mIsAiControlled)
  320. who = " ai";
  321. }
  322. if (isServerObject())
  323. {
  324. if (dynamic_cast<AIConnection*>(getControllingClient()))
  325. {
  326. who = " ai";
  327. mIsAiControlled = true;
  328. }
  329. else if (getControllingClient())
  330. {
  331. who = " player";
  332. mIsAiControlled = false;
  333. }
  334. else
  335. {
  336. who = "";
  337. mIsAiControlled = false;
  338. }
  339. }
  340. U32 moveid = mLastMoveId+mTicksSinceLastMove;
  341. if (move)
  342. moveid = move->id;
  343. if (getTypeMask() & GameBaseHiFiObjectType)
  344. {
  345. if (move)
  346. Con::printf("Processing (%s%s id %i) move %i",srv,who,getId(), move->id);
  347. else
  348. Con::printf("Processing (%s%s id %i) move %i (%i)",srv,who,getId(),mLastMoveId+mTicksSinceLastMove,mTicksSinceLastMove);
  349. }
  350. if (move)
  351. {
  352. mLastMoveId = move->id;
  353. mTicksSinceLastMove=0;
  354. }
  355. #endif
  356. }
  357. void GameBase::interpolateTick(F32 dt)
  358. {
  359. // PATHSHAPE
  360. updateRenderChangesByParent();
  361. // PATHSHAPE END
  362. }
  363. //----------------------------------------------------------------------------
  364. F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
  365. {
  366. TORQUE_UNUSED(updateMask);
  367. // Calculate a priority used to decide if this object
  368. // will be updated on the client. All the weights
  369. // are calculated 0 -> 1 Then weighted together at the
  370. // end to produce a priority.
  371. Point3F pos;
  372. getWorldBox().getCenter(&pos);
  373. pos -= camInfo->pos;
  374. F32 dist = pos.len();
  375. if (dist == 0.0f) dist = 0.001f;
  376. pos *= 1.0f / dist;
  377. // Weight based on linear distance, the basic stuff.
  378. F32 wDistance = (dist < camInfo->visibleDistance)?
  379. 1.0f - (dist / camInfo->visibleDistance): 0.0f;
  380. // Weight by field of view, objects directly in front
  381. // will be weighted 1, objects behind will be 0
  382. F32 dot = mDot(pos,camInfo->orientation);
  383. bool inFov = dot > camInfo->cosFov * 1.5f;
  384. F32 wFov = inFov? 1.0f: 0;
  385. // Weight by linear velocity parallel to the viewing plane
  386. // (if it's the field of view, 0 if it's not).
  387. F32 wVelocity = 0.0f;
  388. if (inFov)
  389. {
  390. Point3F vec;
  391. mCross(camInfo->orientation,getVelocity(),&vec);
  392. wVelocity = (vec.len() * camInfo->fov) /
  393. (camInfo->fov * camInfo->visibleDistance);
  394. if (wVelocity > 1.0f)
  395. wVelocity = 1.0f;
  396. }
  397. // Weight by interest.
  398. F32 wInterest;
  399. if (getTypeMask() & (PlayerObjectType | VehicleObjectType ))
  400. wInterest = 0.75f;
  401. else if (getTypeMask() & ProjectileObjectType)
  402. {
  403. // Projectiles are more interesting if they
  404. // are heading for us.
  405. wInterest = 0.30f;
  406. dot = -mDot(pos,getVelocity());
  407. if (dot > 0.0f)
  408. wInterest += 0.20 * dot;
  409. }
  410. else
  411. {
  412. if (getTypeMask() & ItemObjectType)
  413. wInterest = 0.25f;
  414. else
  415. // Everything else is less interesting.
  416. wInterest = 0.0f;
  417. }
  418. // Weight by updateSkips
  419. F32 wSkips = updateSkips * 0.5;
  420. // Calculate final priority, should total to about 1.0f (plus children)
  421. //
  422. return
  423. wFov * sUpFov +
  424. wDistance * sUpDistance +
  425. wVelocity * sUpVelocity +
  426. wSkips * sUpSkips +
  427. wInterest * sUpInterest +
  428. getNumChildren();
  429. }
  430. //----------------------------------------------------------------------------
  431. bool GameBase::setDataBlock(GameBaseData* dptr)
  432. {
  433. if (isGhost() || isProperlyAdded()) {
  434. if (mDataBlock != dptr)
  435. return onNewDataBlock(dptr,false);
  436. }
  437. else
  438. mDataBlock = dptr;
  439. return true;
  440. }
  441. //--------------------------------------------------------------------------
  442. void GameBase::scriptOnAdd()
  443. {
  444. // Script onAdd() must be called by the leaf class after
  445. // everything is ready.
  446. if (mDataBlock && !isGhost())
  447. mDataBlock->onAdd_callback( this );
  448. }
  449. void GameBase::scriptOnNewDataBlock(bool reload)
  450. {
  451. // Script onNewDataBlock() must be called by the leaf class
  452. // after everything is loaded.
  453. if (mDataBlock && !isGhost())
  454. mDataBlock->onNewDataBlock_callback( this, reload);
  455. }
  456. void GameBase::scriptOnRemove()
  457. {
  458. // Script onRemove() must be called by leaf class while
  459. // the object state is still valid.
  460. if (!isGhost() && mDataBlock)
  461. mDataBlock->onRemove_callback( this );
  462. }
  463. //----------------------------------------------------------------------------
  464. void GameBase::setControllingClient(GameConnection* client)
  465. {
  466. if (isClientObject())
  467. {
  468. if (mControllingClient)
  469. setControl_callback( 0 );
  470. if (client)
  471. setControl_callback( 1 );
  472. }
  473. mControllingClient = client;
  474. }
  475. U32 GameBase::getPacketDataChecksum(GameConnection * connection)
  476. {
  477. // just write the packet data into a buffer
  478. // then we can CRC the buffer. This should always let us
  479. // know when there is a checksum problem.
  480. static U8 buffer[1500] = { 0, };
  481. BitStream stream(buffer, sizeof(buffer));
  482. writePacketData(connection, &stream);
  483. U32 byteCount = stream.getPosition();
  484. U32 ret = CRC::calculateCRC(buffer, byteCount, 0xFFFFFFFF);
  485. dMemset(buffer, 0, byteCount);
  486. return ret;
  487. }
  488. void GameBase::writePacketData(GameConnection*, BitStream*)
  489. {
  490. }
  491. void GameBase::readPacketData(GameConnection*, BitStream*)
  492. {
  493. }
  494. U32 GameBase::packUpdate( NetConnection *connection, U32 mask, BitStream *stream )
  495. {
  496. U32 retMask = Parent::packUpdate( connection, mask, stream );
  497. if ( stream->writeFlag( mask & ScaleMask ) )
  498. {
  499. // Only write one bit if the scale is one.
  500. if ( stream->writeFlag( mObjScale != Point3F::One ) )
  501. mathWrite( *stream, mObjScale );
  502. }
  503. if ( stream->writeFlag( ( mask & DataBlockMask ) && mDataBlock != NULL ) )
  504. {
  505. stream->writeRangedU32( mDataBlock->getId(),
  506. DataBlockObjectIdFirst,
  507. DataBlockObjectIdLast );
  508. if ( stream->writeFlag( mNetFlags.test( NetOrdered ) ) )
  509. stream->writeInt( mOrderGUID, 16 );
  510. }
  511. #ifdef TORQUE_DEBUG_NET_MOVES
  512. stream->write(mLastMoveId);
  513. stream->writeFlag(mIsAiControlled);
  514. #endif
  515. #ifdef TORQUE_AFX_ENABLED
  516. if (stream->writeFlag(mask & ScopeIdMask))
  517. {
  518. if (stream->writeFlag(mScope_refs > 0))
  519. stream->writeInt(mScope_id, SCOPE_ID_BITS);
  520. }
  521. #endif
  522. return retMask;
  523. }
  524. void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
  525. {
  526. Parent::unpackUpdate( con, stream );
  527. // ScaleMask
  528. if ( stream->readFlag() )
  529. {
  530. if ( stream->readFlag() )
  531. {
  532. VectorF scale;
  533. mathRead( *stream, &scale );
  534. setScale( scale );
  535. }
  536. else
  537. setScale( Point3F::One );
  538. }
  539. // DataBlockMask
  540. if ( stream->readFlag() )
  541. {
  542. GameBaseData *dptr = 0;
  543. SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst,
  544. DataBlockObjectIdLast );
  545. if ( stream->readFlag() )
  546. mOrderGUID = stream->readInt( 16 );
  547. if ( !Sim::findObject( id, dptr ) || !setDataBlock( dptr ) )
  548. con->setLastError( "Invalid packet GameBase::unpackUpdate()" );
  549. }
  550. #ifdef TORQUE_DEBUG_NET_MOVES
  551. stream->read(&mLastMoveId);
  552. mTicksSinceLastMove = 0;
  553. mIsAiControlled = stream->readFlag();
  554. #endif
  555. #ifdef TORQUE_AFX_ENABLED
  556. if (stream->readFlag())
  557. {
  558. mScope_id = (stream->readFlag()) ? (U16) stream->readInt(SCOPE_ID_BITS) : 0;
  559. mScope_refs = 0;
  560. }
  561. #endif
  562. }
  563. void GameBase::onMount( SceneObject *obj, S32 node )
  564. {
  565. deleteNotify( obj );
  566. // Are we mounting to a GameBase object?
  567. GameBase *gbaseObj = dynamic_cast<GameBase*>( obj );
  568. if ( gbaseObj && gbaseObj->getControlObject() != this && gbaseObj->getControllingObject() != this)
  569. processAfter( gbaseObj );
  570. if (!isGhost()) {
  571. setMaskBits(MountedMask);
  572. mDataBlock->onMount_callback( this, obj, node );
  573. }
  574. }
  575. void GameBase::onUnmount( SceneObject *obj, S32 node )
  576. {
  577. clearNotify(obj);
  578. GameBase *gbaseObj = dynamic_cast<GameBase*>( obj );
  579. if ( gbaseObj && gbaseObj->getControlObject() != this )
  580. clearProcessAfter();
  581. if (!isGhost()) {
  582. setMaskBits(MountedMask);
  583. mDataBlock->onUnmount_callback( this, obj, node );
  584. }
  585. }
  586. bool GameBase::setDataBlockProperty( void *obj, const char *index, const char *db)
  587. {
  588. if( db == NULL || !db[ 0 ] )
  589. {
  590. Con::errorf( "GameBase::setDataBlockProperty - Can't unset datablock on GameBase objects" );
  591. return false;
  592. }
  593. GameBase* object = static_cast< GameBase* >( obj );
  594. GameBaseData* data;
  595. if( Sim::findObject( db, data ) )
  596. return object->setDataBlock( data );
  597. Con::errorf( "GameBase::setDatablockProperty - Could not find data block \"%s\"", db );
  598. return false;
  599. }
  600. MoveList* GameBase::getMoveList()
  601. {
  602. return mControllingClient ? mControllingClient->mMoveList : NULL;
  603. }
  604. //----------------------------------------------------------------------------
  605. DefineEngineMethod( GameBase, getDataBlock, S32, (),,
  606. "@brief Get the datablock used by this object.\n\n"
  607. "@return the datablock this GameBase is using."
  608. "@see setDataBlock()\n")
  609. {
  610. return object->getDataBlock()? object->getDataBlock()->getId(): 0;
  611. }
  612. //----------------------------------------------------------------------------
  613. DefineEngineMethod( GameBase, setDataBlock, bool, ( GameBaseData* data ),,
  614. "@brief Assign this GameBase to use the specified datablock.\n\n"
  615. "@param data new datablock to use\n"
  616. "@return true if successful, false if failed."
  617. "@see getDataBlock()\n")
  618. {
  619. return ( data && object->setDataBlock(data) );
  620. }
  621. //----------------------------------------------------------------------------
  622. void GameBase::initPersistFields()
  623. {
  624. docsURL;
  625. addGroup( "Game" );
  626. addProtectedField( "dataBlock", TYPEID< GameBaseData >(), Offset(mDataBlock, GameBase),
  627. &setDataBlockProperty, &defaultProtectedGetFn,
  628. "Script datablock used for game objects." );
  629. endGroup( "Game" );
  630. Parent::initPersistFields();
  631. }
  632. void GameBase::consoleInit()
  633. {
  634. #ifdef TORQUE_DEBUG
  635. Con::addVariable( "GameBase::boundingBox", TypeBool, &gShowBoundingBox,
  636. "@brief Toggles on the rendering of the bounding boxes for certain types of objects in scene.\n\n"
  637. "@ingroup GameBase" );
  638. #endif
  639. }
  640. DefineEngineMethod( GameBase, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
  641. "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
  642. "@param pos impulse world position\n"
  643. "@param vel impulse velocity (impulse force F = m * v)\n"
  644. "@return Always true\n"
  645. "@note Not all objects that derrive from GameBase have this defined.\n")
  646. {
  647. object->applyImpulse(pos,vel);
  648. return true;
  649. }
  650. DefineEngineMethod( GameBase, applyRadialImpulse, void, ( Point3F origin, F32 radius, F32 magnitude ),,
  651. "@brief Applies a radial impulse to the object using the given origin and force.\n\n"
  652. "@param origin World point of origin of the radial impulse.\n"
  653. "@param radius The radius of the impulse area.\n"
  654. "@param magnitude The strength of the impulse.\n"
  655. "@note Not all objects that derrive from GameBase have this defined.\n")
  656. {
  657. object->applyRadialImpulse( origin, radius, magnitude );
  658. }
  659. // PATHSHAPE
  660. // Console Methods for attach children. can't put them in sceneobject because //
  661. // we want the processafter functions////////////////////////////////////////////
  662. DefineEngineMethod(GameBase, attachChild, bool, (GameBase* _subObject), (nullAsType<GameBase*>()), "(SceneObject subObject)"
  663. "attach an object to this one, preserving its present transform.")
  664. {
  665. if (_subObject != nullptr)
  666. {
  667. if (_subObject->getParent() != object){
  668. Con::errorf("Object is (%d)", _subObject->getId());
  669. _subObject->clearProcessAfter();
  670. _subObject->processAfter(object);
  671. return object->attachChild(_subObject);
  672. }
  673. else
  674. return false;
  675. }
  676. else
  677. {
  678. Con::errorf("Couldn't addObject()!");
  679. return false;
  680. }
  681. }
  682. DefineEngineMethod(GameBase, detachChild, bool, (GameBase* _subObject), (nullAsType<GameBase*>()), "(SceneObject subObject)"
  683. "attach an object to this one, preserving its present transform.")
  684. {
  685. if (_subObject != nullptr)
  686. {
  687. _subObject->clearProcessAfter();
  688. return _subObject->attachToParent(NULL);
  689. }
  690. else
  691. {
  692. return false;
  693. }
  694. }//end
  695. // PATHSHAPE END