PhysicsUpdate.cpp 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // PhysicsBehavior.cpp
  24. // Simple rigid body physics
  25. // Author: Michael S. Booth, November 2001
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. // please talk to MDC (x36804) before taking this out
  28. #define NO_DEBUG_CRC
  29. #include "Common/PerfTimer.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/Xfer.h"
  32. #include "GameLogic/GameLogic.h"
  33. #include "GameLogic/Module/AIUpdate.h"
  34. #include "GameLogic/Module/BodyModule.h"
  35. #include "GameLogic/Module/ContainModule.h"
  36. #include "GameLogic/Module/CrushDie.h" // for CrushEnum
  37. #include "GameLogic/Module/PhysicsUpdate.h"
  38. #include "GameLogic/Object.h"
  39. #include "GameLogic/ScriptEngine.h"
  40. #include "GameLogic/TerrainLogic.h"
  41. #include "GameLogic/Weapon.h"
  42. const Real DEFAULT_MASS = 1.0f;
  43. const Real DEFAULT_FORWARD_FRICTION = 0.15f;
  44. const Real DEFAULT_LATERAL_FRICTION = 0.15f;
  45. const Real DEFAULT_Z_FRICTION = 0.8f;
  46. const Real DEFAULT_AERO_FRICTION = 0.0f;
  47. // Air friction used to be completely separate and unclamped, so this constant must be split.
  48. // The fact it defaults to 0 shows it.
  49. const Real MIN_AERO_FRICTION = 0.00f;
  50. const Real MIN_NON_AERO_FRICTION = 0.01f;
  51. const Real MAX_FRICTION = 0.99f;
  52. #include "Common/CRCDebug.h"
  53. const Int MOTIVE_FRAMES = LOGICFRAMES_PER_SECOND / 3;
  54. #define SLEEPY_PHYSICS
  55. #ifdef _INTERNAL
  56. // for occasional debugging...
  57. //#pragma optimize("", off)
  58. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  59. #endif
  60. //-------------------------------------------------------------------------------------------------
  61. static Real angleBetweenVectors(const Coord3D& inCurDir, const Coord3D& inGoalDir)
  62. {
  63. Vector3 curDir;
  64. curDir.X = inCurDir.x;
  65. curDir.Y = inCurDir.y;
  66. curDir.Z = inCurDir.z;
  67. curDir.Normalize();
  68. Vector3 goalDir;
  69. goalDir.X = inGoalDir.x;
  70. goalDir.Y = inGoalDir.y;
  71. goalDir.Z = inGoalDir.z;
  72. goalDir.Normalize();
  73. // dot of two unit vectors is cos of angle between them.
  74. Real cosine = Vector3::Dot_Product(curDir, goalDir);
  75. // bound it in case of numerical error
  76. Real angleBetween = (Real)ACos(clamp(-1.0f, cosine, 1.0f));
  77. return angleBetween;
  78. }
  79. //-------------------------------------------------------------------------------------------------
  80. static Real heightToSpeed(Real height)
  81. {
  82. // don't bother trying to remember how far we've fallen; instead,
  83. // back-calc it from our speed & gravity... v = sqrt(2*g*h)
  84. return sqrt(fabs(2.0f * TheGlobalData->m_gravity * height));
  85. }
  86. //-------------------------------------------------------------------------------------------------
  87. PhysicsBehaviorModuleData::PhysicsBehaviorModuleData()
  88. {
  89. m_mass = DEFAULT_MASS;
  90. m_forwardFriction = DEFAULT_FORWARD_FRICTION;
  91. m_lateralFriction = DEFAULT_LATERAL_FRICTION;
  92. m_ZFriction = DEFAULT_Z_FRICTION;
  93. m_aerodynamicFriction = DEFAULT_AERO_FRICTION;
  94. m_centerOfMassOffset = 0.0f;
  95. m_allowBouncing = false;
  96. m_allowCollideForce = true;
  97. m_killWhenRestingOnGround = false;
  98. m_minFallSpeedForDamage = heightToSpeed(40.0f);
  99. m_fallHeightDamageFactor = 1.0f; // was 10. now is 1.
  100. /*
  101. thru some bizarre editing mishap, we have been double-apply pitch/roll/yaw rates
  102. to objects for, well, a long time, it looks like. I have corrected that problem
  103. in the name of efficiency, but to maintain the same visual appearance without having
  104. to edit every freaking INI in the world at this point, I am just multiplying
  105. all the results by a factor so that the effect is the same (but with less execution time).
  106. I have put this factor into INI in the unlikely event we ever need to change it,
  107. but defaulting it to 2 is, in fact, the right thing for now... (srj)
  108. */
  109. m_pitchRollYawFactor = 2.0f;
  110. m_vehicleCrashesIntoBuildingWeaponTemplate = TheWeaponStore->findWeaponTemplate("VehicleCrashesIntoBuildingWeapon");
  111. m_vehicleCrashesIntoNonBuildingWeaponTemplate = TheWeaponStore->findWeaponTemplate("VehicleCrashesIntoNonBuildingWeapon");
  112. }
  113. //-------------------------------------------------------------------------------------------------
  114. static void parseHeightToSpeed( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  115. {
  116. // don't bother trying to remember how far we've fallen; instead,
  117. // back-calc it from our speed & gravity... v = sqrt(2*g*h)
  118. Real height = INI::scanReal(ini->getNextToken());
  119. *(Real *)store = heightToSpeed(height);
  120. }
  121. //-------------------------------------------------------------------------------------------------
  122. static void parseFrictionPerSec( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  123. {
  124. Real fricPerSec = INI::scanReal(ini->getNextToken());
  125. Real fricPerFrame = fricPerSec * SECONDS_PER_LOGICFRAME_REAL;
  126. *(Real *)store = fricPerFrame;
  127. }
  128. //-------------------------------------------------------------------------------------------------
  129. /*static*/ void PhysicsBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
  130. {
  131. UpdateModuleData::buildFieldParse(p);
  132. static const FieldParse dataFieldParse[] =
  133. {
  134. { "Mass", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_mass ) },
  135. { "ForwardFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_forwardFriction ) },
  136. { "LateralFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_lateralFriction ) },
  137. { "ZFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_ZFriction ) },
  138. { "AerodynamicFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_aerodynamicFriction ) },
  139. { "CenterOfMassOffset", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_centerOfMassOffset ) },
  140. { "AllowBouncing", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_allowBouncing ) },
  141. { "AllowCollideForce", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_allowCollideForce ) },
  142. { "KillWhenRestingOnGround", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_killWhenRestingOnGround) },
  143. { "MinFallHeightForDamage", parseHeightToSpeed, NULL, offsetof( PhysicsBehaviorModuleData, m_minFallSpeedForDamage) },
  144. { "FallHeightDamageFactor", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_fallHeightDamageFactor) },
  145. { "PitchRollYawFactor", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_pitchRollYawFactor) },
  146. { "VehicleCrashesIntoBuildingWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof(PhysicsBehaviorModuleData, m_vehicleCrashesIntoBuildingWeaponTemplate) },
  147. { "VehicleCrashesIntoNonBuildingWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof(PhysicsBehaviorModuleData, m_vehicleCrashesIntoNonBuildingWeaponTemplate) },
  148. { 0, 0, 0, 0 }
  149. };
  150. p.add(dataFieldParse);
  151. }
  152. //-------------------------------------------------------------------------------------------------
  153. //-------------------------------------------------------------------------------------------------
  154. //-------------------------------------------------------------------------------------------------
  155. const Real INVALID_VEL_MAG = -1.0f;
  156. //-------------------------------------------------------------------------------------------------
  157. PhysicsBehavior::PhysicsBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  158. {
  159. m_accel.zero();
  160. m_prevAccel = m_accel;
  161. m_vel.zero();
  162. m_velMag = 0.0f;
  163. m_yawRate = 0.0f;
  164. m_rollRate = 0.0f;
  165. m_pitchRate = 0.0f;
  166. m_mass = getPhysicsBehaviorModuleData()->m_mass;
  167. m_motiveForceExpires = 0;
  168. m_flags = 0;
  169. m_extraBounciness = 0.0f;
  170. m_extraFriction = 0.0f;
  171. m_currentOverlap = INVALID_ID;
  172. m_previousOverlap = INVALID_ID;
  173. m_lastCollidee = INVALID_ID;
  174. m_ignoreCollisionsWith = INVALID_ID;
  175. setAllowBouncing(getPhysicsBehaviorModuleData()->m_allowBouncing);
  176. setAllowCollideForce(getPhysicsBehaviorModuleData()->m_allowCollideForce);
  177. m_pui = NULL;
  178. m_bounceSound = NULL;
  179. #ifdef SLEEPY_PHYSICS
  180. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  181. #endif
  182. }
  183. //-------------------------------------------------------------------------------------------------
  184. static ProjectileUpdateInterface* getPui(Object* obj)
  185. {
  186. if (!obj->isKindOf(KINDOF_PROJECTILE))
  187. return NULL;
  188. ProjectileUpdateInterface* objPui = NULL;
  189. for (BehaviorModule** u = obj->getBehaviorModules(); *u; ++u)
  190. {
  191. if ((objPui = (*u)->getProjectileUpdateInterface()) != NULL)
  192. return objPui;
  193. }
  194. return NULL;
  195. }
  196. //-------------------------------------------------------------------------------------------------
  197. void PhysicsBehavior::onObjectCreated()
  198. {
  199. m_pui = getPui(getObject());
  200. }
  201. //-------------------------------------------------------------------------------------------------
  202. PhysicsBehavior::~PhysicsBehavior()
  203. {
  204. if (m_bounceSound)
  205. {
  206. m_bounceSound->deleteInstance();
  207. m_bounceSound = NULL;
  208. }
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. void PhysicsBehavior::setIgnoreCollisionsWith(const Object* obj)
  212. {
  213. m_ignoreCollisionsWith = obj ? obj->getID() : INVALID_ID;
  214. }
  215. //-------------------------------------------------------------------------------------------------
  216. Bool PhysicsBehavior::isIgnoringCollisionsWith(ObjectID id) const
  217. {
  218. return id != INVALID_ID && id == m_ignoreCollisionsWith;
  219. }
  220. //-------------------------------------------------------------------------------------------------
  221. Real PhysicsBehavior::getAerodynamicFriction() const
  222. {
  223. Real f = getPhysicsBehaviorModuleData()->m_aerodynamicFriction + m_extraFriction;
  224. if (f < MIN_AERO_FRICTION) f = MIN_AERO_FRICTION;
  225. if (f > MAX_FRICTION) f = MAX_FRICTION;
  226. return f;
  227. }
  228. //-------------------------------------------------------------------------------------------------
  229. Real PhysicsBehavior::getForwardFriction() const
  230. {
  231. Real f = getPhysicsBehaviorModuleData()->m_forwardFriction + m_extraFriction;
  232. if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
  233. if (f > MAX_FRICTION) f = MAX_FRICTION;
  234. return f;
  235. }
  236. //-------------------------------------------------------------------------------------------------
  237. Real PhysicsBehavior::getLateralFriction() const
  238. {
  239. Real f = getPhysicsBehaviorModuleData()->m_lateralFriction + m_extraFriction;
  240. if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
  241. if (f > MAX_FRICTION) f = MAX_FRICTION;
  242. return f;
  243. }
  244. //-------------------------------------------------------------------------------------------------
  245. Real PhysicsBehavior::getZFriction() const
  246. {
  247. Real f = getPhysicsBehaviorModuleData()->m_ZFriction + m_extraFriction;
  248. if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
  249. if (f > MAX_FRICTION) f = MAX_FRICTION;
  250. return f;
  251. }
  252. //-------------------------------------------------------------------------------------------------
  253. /**
  254. * Apply a force at the object's CG
  255. */
  256. void PhysicsBehavior::applyForce( const Coord3D *force )
  257. {
  258. DEBUG_ASSERTCRASH(!(_isnan(force->x) || _isnan(force->y) || _isnan(force->z)), ("PhysicsBehavior::applyForce force NAN!\n"));
  259. if (_isnan(force->x) || _isnan(force->y) || _isnan(force->z)) {
  260. return;
  261. }
  262. // F = ma --> a = F/m (divide force by mass)
  263. Real mass = getMass();
  264. Coord3D modForce = *force;
  265. if (isMotive())
  266. {
  267. const Coord3D *dir = getObject()->getUnitDirectionVector2D();
  268. // Only accept the lateral acceleration.
  269. Real lateralDot = force->x * (-dir->y) + force->y * dir->x;
  270. modForce.x = lateralDot * -dir->y;
  271. modForce.y = lateralDot * dir->x;
  272. }
  273. Real massInv = 1.0f / mass;
  274. m_accel.x += modForce.x * massInv;
  275. m_accel.y += modForce.y * massInv;
  276. m_accel.z += modForce.z * massInv;
  277. //DEBUG_ASSERTCRASH(!(_isnan(m_accel.x) || _isnan(m_accel.y) || _isnan(m_accel.z)), ("PhysicsBehavior::applyForce accel NAN!\n"));
  278. //DEBUG_ASSERTCRASH(!(_isnan(m_vel.x) || _isnan(m_vel.y) || _isnan(m_vel.z)), ("PhysicsBehavior::applyForce vel NAN!\n"));
  279. //DEBUG_ASSERTCRASH(fabs(force->z) < 3, ("unlikely z-force"));
  280. #ifdef SLEEPY_PHYSICS
  281. if (getFlag(IS_IN_UPDATE))
  282. {
  283. // we're applying a force from inside our own update (probably for a bounce or friction).
  284. // just do nothing, since update will calculate the correct sleep behavior at the end.
  285. }
  286. else
  287. {
  288. // when a force is applied by an external module, we must wake up, even if the force is zero.
  289. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  290. }
  291. #endif
  292. }
  293. //-------------------------------------------------------------------------------------------------
  294. Bool PhysicsBehavior::isMotive() const
  295. {
  296. return m_motiveForceExpires > TheGameLogic->getFrame();
  297. }
  298. //-------------------------------------------------------------------------------------------------
  299. void PhysicsBehavior::applyMotiveForce( const Coord3D *force )
  300. {
  301. m_motiveForceExpires = 0; // make it accept this force unquestioningly :)
  302. applyForce(force);
  303. m_motiveForceExpires = TheGameLogic->getFrame() + MOTIVE_FRAMES;
  304. }
  305. //-------------------------------------------------------------------------------------------------
  306. void PhysicsBehavior::resetDynamicPhysics()
  307. {
  308. m_accel.zero();
  309. m_prevAccel.zero();
  310. m_vel.zero();
  311. m_velMag = 0.0f;
  312. m_turning = TURN_NONE;
  313. m_yawRate = 0;
  314. m_rollRate = 0;
  315. m_pitchRate = 0;
  316. setFlag(HAS_PITCHROLLYAW, false);
  317. #ifdef SLEEPY_PHYSICS
  318. DEBUG_ASSERTCRASH(!getFlag(IS_IN_UPDATE), ("hmm, should not happen, may not work"));
  319. setWakeFrame(getObject(), calcSleepTime());
  320. #endif
  321. }
  322. //-------------------------------------------------------------------------------------------------
  323. void PhysicsBehavior::applyGravitationalForces()
  324. {
  325. m_accel.z += TheGlobalData->m_gravity;
  326. }
  327. //-------------------------------------------------------------------------------------------------
  328. void PhysicsBehavior::applyFrictionalForces()
  329. {
  330. if (getFlag(APPLY_FRICTION2D_WHEN_AIRBORNE) || !getObject()->isSignificantlyAboveTerrain())
  331. {
  332. applyYPRDamping(1.0f - DEFAULT_LATERAL_FRICTION);
  333. if (m_vel.x || m_vel.y)
  334. {
  335. const Coord3D *dir = getObject()->getUnitDirectionVector2D();
  336. Real mass = getMass();
  337. Real lateralDot = m_vel.x * (-dir->y) + m_vel.y * dir->x;
  338. Real lateralVel_x = lateralDot * -dir->y;
  339. Real lateralVel_y = lateralDot * dir->x;
  340. Real lf = mass * getLateralFriction();
  341. Coord3D accel;
  342. accel.x = -(lf * lateralVel_x);
  343. accel.y = -(lf * lateralVel_y);
  344. accel.z = 0.0f;
  345. if (!isMotive())
  346. {
  347. Real forwardDot = m_vel.x * dir->x + m_vel.y * dir->y;
  348. Real forwardVel_x = forwardDot * dir->x;
  349. Real forwardVel_y = forwardDot * dir->y;
  350. Real ff = mass * getForwardFriction();
  351. accel.x += -(ff * forwardVel_x);
  352. accel.y += -(ff * forwardVel_y);
  353. }
  354. applyForce(&accel);
  355. }
  356. }
  357. else
  358. {
  359. Real aerodynamics = -getAerodynamicFriction(); // negated!
  360. // Air resistance is proportional to velocity in the opposite direction
  361. m_accel.x += m_vel.x * aerodynamics;
  362. m_accel.y += m_vel.y * aerodynamics;
  363. m_accel.z += m_vel.z * aerodynamics;
  364. applyYPRDamping(1.0f + aerodynamics); // since aero is negated, this results in 1.0-getAerodynamicFriction()
  365. }
  366. }
  367. //-------------------------------------------------------------------------------------------------
  368. Bool PhysicsBehavior::handleBounce(Real oldZ, Real newZ, Real groundZ, Coord3D* bounceForce)
  369. {
  370. if (getFlag(ALLOW_BOUNCE) && newZ <= groundZ)
  371. {
  372. const Real MIN_STIFF = 0.01f;
  373. const Real MAX_STIFF = 0.99f;
  374. Real stiffness = TheGlobalData->m_groundStiffness;
  375. if (stiffness < MIN_STIFF) stiffness = MIN_STIFF;
  376. if (stiffness > MAX_STIFF) stiffness = MAX_STIFF;
  377. Real desiredAccelZ = 0.0f;
  378. Real vz = getVelocity()->z;
  379. if (oldZ > groundZ && vz < 0.0f)
  380. {
  381. desiredAccelZ = fabs(vz) * stiffness;
  382. }
  383. bounceForce->x = 0.0f;
  384. bounceForce->y = 0.0f;
  385. bounceForce->z = getMass() * desiredAccelZ;
  386. const Real DAMPING = 0.7f;
  387. applyYPRDamping(DAMPING);
  388. if (vz < 0.0f)
  389. {
  390. Vector3 zvec = getObject()->getTransformMatrix()->Get_Z_Vector();
  391. const Real rollAngle = (zvec.Z > 0) ? 0 : PI;
  392. // don't flip both pitch and roll... we'll "flip" twice.
  393. const Real pitchAngle = 0;
  394. Real yawAngle = getObject()->getTransformMatrix()->Get_Z_Rotation();
  395. setAngles(yawAngle, pitchAngle, rollAngle);
  396. }
  397. return true;
  398. }
  399. else
  400. {
  401. bounceForce->zero();
  402. return false;
  403. }
  404. }
  405. //-------------------------------------------------------------------------------------------------
  406. inline Bool isVerySmall3D(const Coord3D& v)
  407. {
  408. const Real THRESH = 0.01f;
  409. return (fabs(v.x) < THRESH && fabs(v.y) < THRESH && fabs(v.z) < THRESH);
  410. }
  411. //-------------------------------------------------------------------------------------------------
  412. inline Bool isZero3D(const Coord3D& v)
  413. {
  414. return v.x == 0.0f && v.y == 0.0f && v.z == 0.0f;
  415. }
  416. //-------------------------------------------------------------------------------------------------
  417. void PhysicsBehavior::setPitchRate(Real pitch)
  418. {
  419. m_pitchRate = pitch;
  420. setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
  421. }
  422. //-------------------------------------------------------------------------------------------------
  423. void PhysicsBehavior::setRollRate(Real roll)
  424. {
  425. m_rollRate = roll;
  426. setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
  427. }
  428. //-------------------------------------------------------------------------------------------------
  429. void PhysicsBehavior::setYawRate(Real yaw)
  430. {
  431. m_yawRate = yaw;
  432. setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
  433. }
  434. //-------------------------------------------------------------------------------------------------
  435. void PhysicsBehavior::applyYPRDamping(Real factor)
  436. {
  437. m_pitchRate *= factor;
  438. m_rollRate *= factor;
  439. m_yawRate *= factor;
  440. setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
  441. }
  442. //-------------------------------------------------------------------------------------------------
  443. void PhysicsBehavior::setBounceSound(const AudioEventRTS* bounceSound)
  444. {
  445. if (bounceSound)
  446. {
  447. if (m_bounceSound == NULL)
  448. m_bounceSound = newInstance(DynamicAudioEventRTS);
  449. m_bounceSound->m_event = *bounceSound;
  450. }
  451. else
  452. {
  453. if (m_bounceSound)
  454. {
  455. m_bounceSound->deleteInstance();
  456. m_bounceSound = NULL;
  457. }
  458. }
  459. }
  460. //-------------------------------------------------------------------------------------------------
  461. /**
  462. * Basic rigid body physics using an Euler integrator.
  463. * @todo Currently, only translations are integrated. Rotations should also be integrated. (MSB)
  464. */
  465. DECLARE_PERF_TIMER(PhysicsBehavior)
  466. UpdateSleepTime PhysicsBehavior::update()
  467. {
  468. USE_PERF_TIMER(PhysicsBehavior)
  469. Object* obj = getObject();
  470. const PhysicsBehaviorModuleData* d = getPhysicsBehaviorModuleData();
  471. Bool airborneAtStart = obj->isAboveTerrain();
  472. Real activeVelZ = 0;
  473. Coord3D bounceForce;
  474. Bool gotBounceForce = false;
  475. DEBUG_ASSERTCRASH(!getFlag(IS_IN_UPDATE), ("impossible"));
  476. setFlag(IS_IN_UPDATE, true);
  477. if (!getFlag(UPDATE_EVER_RUN))
  478. {
  479. // set the flag so that we don't get bogus "collisions" on the first frame.
  480. setFlag(WAS_AIRBORNE_LAST_FRAME, airborneAtStart);
  481. }
  482. Coord3D prevPos = *obj->getPosition();
  483. m_prevAccel = m_accel;
  484. if (!obj->isDisabledByType(DISABLED_HELD))
  485. {
  486. Matrix3D mtx = *obj->getTransformMatrix();
  487. applyGravitationalForces();
  488. applyFrictionalForces();
  489. // integrate acceleration into velocity
  490. m_vel.x += m_accel.x;
  491. m_vel.y += m_accel.y;
  492. m_vel.z += m_accel.z;
  493. // when vel gets tiny, just clamp to zero
  494. const Real THRESH = 0.001f;
  495. if (fabsf(m_vel.x) < THRESH) m_vel.x = 0.0f;
  496. if (fabsf(m_vel.y) < THRESH) m_vel.y = 0.0f;
  497. if (fabsf(m_vel.z) < THRESH) m_vel.z = 0.0f;
  498. m_velMag = INVALID_VEL_MAG;
  499. Real oldPosZ = mtx.Get_Z_Translation();
  500. // integrate velocity into position
  501. if (obj->testStatus(OBJECT_STATUS_BRAKING))
  502. {
  503. // Don't update position if the locomotor is braking.
  504. if (!obj->isKindOf(KINDOF_PROJECTILE))
  505. {
  506. // Things other than projectiles don't cheat in z. jba.
  507. mtx.Adjust_Z_Translation(m_vel.z);
  508. }
  509. }
  510. else
  511. {
  512. mtx.Adjust_X_Translation(m_vel.x);
  513. mtx.Adjust_Y_Translation(m_vel.y);
  514. mtx.Adjust_Z_Translation(m_vel.z);
  515. }
  516. if (_isnan(mtx.Get_X_Translation()) || _isnan(mtx.Get_Y_Translation()) ||
  517. _isnan(mtx.Get_Z_Translation())) {
  518. DEBUG_CRASH(("Object position is NAN, deleting."));
  519. TheGameLogic->destroyObject(obj);
  520. }
  521. if (getFlag(HAS_PITCHROLLYAW))
  522. {
  523. /*
  524. You may be tempted to do something like this:
  525. Real rollAngle = -mtx.Get_X_Rotation();
  526. Real pitchAngle = mtx.Get_Y_Rotation();
  527. Real yawAngle = mtx.Get_Z_Rotation();
  528. // do stuff to angles, then rebuild the mtx with 'em
  529. You must resist this temptation, because your code will be wrong!
  530. The problem is that you can't use these calls to later reconstruct
  531. the matrix... because doing such a thing is highly order-dependent,
  532. and furthermore, you'd have to use Euler angles (Not the Get_?_Rotation
  533. calls) to be able to reconstruct 'em, and that's too slow to do for
  534. every object every frame.
  535. The one exception is that it is OK to use Get_Z_Rotation() to get
  536. the yaw angle.
  537. */
  538. // only update the position if we are not HELD
  539. // (otherwise, slowdeath sinking into ground won't work)
  540. Real yawRateToUse = m_yawRate * d->m_pitchRollYawFactor;
  541. Real pitchRateToUse = m_pitchRate * d->m_pitchRollYawFactor;
  542. Real rollRateToUse = m_rollRate * d->m_pitchRollYawFactor;
  543. // With a center of mass listing, pitchRate needs to dampen towards straight down/straight up
  544. Real offset = getCenterOfMassOffset();
  545. // Magnitude sets initial rate, here we care about sign
  546. if (offset != 0.0f)
  547. {
  548. Vector3 xvec = mtx.Get_X_Vector();
  549. Real xy = sqrtf(sqr(xvec.X) + sqr(xvec.Y));
  550. Real pitchAngle = atan2(xvec.Z, xy);
  551. Real remainingAngle = (offset > 0) ? ((PI/2) - pitchAngle) : (-(PI/2) + pitchAngle);
  552. Real s = Sin(remainingAngle);
  553. pitchRateToUse *= s;
  554. }
  555. // update rotation
  556. /// @todo Rotation should use torques, and integrate just like forces (MSB)
  557. // note, we DON'T want to Pre-rotate (either inplace or not),
  558. // since we want to add our mods to the existing matrix.
  559. mtx.Rotate_X(rollRateToUse);
  560. mtx.Rotate_Y(pitchRateToUse);
  561. mtx.Rotate_Z(yawRateToUse);
  562. }
  563. // do not allow object to pass through the ground
  564. Real groundZ = TheTerrainLogic->getLayerHeight(mtx.Get_X_Translation(), mtx.Get_Y_Translation(), obj->getLayer());
  565. gotBounceForce = handleBounce(oldPosZ, mtx.Get_Z_Translation(), groundZ, &bounceForce);
  566. // remember our z-vel prior to doing ground-slam adjustment
  567. activeVelZ = m_vel.z;
  568. if (mtx.Get_Z_Translation() <= groundZ)
  569. {
  570. // Note - when vehicles are going down a slope, they will maintain a small negative
  571. // z velocity as they go down. So don't slam it to 0 if they aren't slamming into the
  572. // ground.
  573. Real dz = groundZ - mtx.Get_Z_Translation(); // Our excess z velocity.
  574. m_vel.z += dz; // Remove the excess z velocity.
  575. if (m_vel.z > 0.0f)
  576. m_vel.z = 0.0f;
  577. m_velMag = INVALID_VEL_MAG;
  578. mtx.Set_Z_Translation(groundZ);
  579. // this flag is ALWAYS cleared once we hit the ground.
  580. setFlag(ALLOW_TO_FALL, false);
  581. }
  582. else if (mtx.Get_Z_Translation() > groundZ)
  583. {
  584. if (getFlag(IS_IN_FREEFALL))
  585. {
  586. obj->setDisabled(DISABLED_FREEFALL);
  587. obj->setModelConditionState(MODELCONDITION_FREEFALL);
  588. }
  589. else if (getFlag(STICK_TO_GROUND) && !getFlag(ALLOW_TO_FALL))
  590. {
  591. mtx.Set_Z_Translation(groundZ);
  592. }
  593. }
  594. obj->setTransformMatrix(&mtx);
  595. } // if not held
  596. // reset the acceleration for accumulation next frame
  597. m_accel.zero();
  598. // clear overlap object, which will be set by PhysicsCollide later
  599. m_previousOverlap = m_currentOverlap;
  600. m_currentOverlap = INVALID_ID;
  601. if (gotBounceForce && getFlag(ALLOW_BOUNCE))
  602. {
  603. applyForce(&bounceForce);
  604. }
  605. Bool airborneAtEnd = obj->isAboveTerrain();
  606. // it's not good enough to check for airborne being different between
  607. // the start and end of this func... we have to compare since last frame,
  608. // since (if we're held by a parachute, for instance) we might have been
  609. // moved by other bits of code!
  610. if (getFlag(WAS_AIRBORNE_LAST_FRAME) && !airborneAtEnd && !getFlag(IMMUNE_TO_FALLING_DAMAGE))
  611. {
  612. doBounceSound(prevPos);
  613. // the normal always points straight down, though we could
  614. // get the alignWithTerrain() normal if it proves interesting
  615. Coord3D normal;
  616. normal.x = normal.y = 0.0f;
  617. normal.z = -1.0f;
  618. obj->onCollide(NULL, obj->getPosition(), &normal);
  619. //
  620. // don't bother trying to remember how far we've fallen; instead,
  621. // we back-calc it from our speed & gravity... v = sqrt(2*g*h).
  622. // (note that m_minFallSpeedForDamage is always POSITIVE.)
  623. //
  624. // also note: since projectiles are immune to falling damage, don't
  625. // even bother doing this check here.
  626. //
  627. Real netSpeed = -activeVelZ - d->m_minFallSpeedForDamage;
  628. if (netSpeed > 0.0f && m_pui == NULL)
  629. {
  630. // only apply force if it's a pretty steep fall, so that things
  631. // going down hills don't injure themselves (unless the hill is really steep)
  632. const Real MIN_ANGLE_TAN = 3.0f; // roughly 71 degrees
  633. const Real TINY_DELTA = 0.01f;
  634. if ((fabs(m_vel.x) <= TINY_DELTA || fabs(activeVelZ / m_vel.x) >= MIN_ANGLE_TAN) &&
  635. (fabs(m_vel.y) <= TINY_DELTA || fabs(activeVelZ / m_vel.y) >= MIN_ANGLE_TAN))
  636. {
  637. Real damageAmt = netSpeed * getMass() * d->m_fallHeightDamageFactor;
  638. DamageInfo damageInfo;
  639. damageInfo.in.m_damageType = DAMAGE_FALLING;
  640. damageInfo.in.m_deathType = DEATH_SPLATTED;
  641. damageInfo.in.m_sourceID = obj->getID();
  642. damageInfo.in.m_amount = damageAmt;
  643. obj->attemptDamage( &damageInfo );
  644. //DEBUG_LOG(("Dealing %f (%f %f) points of falling damage to %s!\n",damageAmt,damageInfo.out.m_actualDamageDealt, damageInfo.out.m_actualDamageClipped,obj->getTemplate()->getName().str()));
  645. // if this killed us, add SPLATTED to get a cool death.
  646. if (obj->isEffectivelyDead())
  647. {
  648. obj->setModelConditionState(MODELCONDITION_SPLATTED);
  649. }
  650. }
  651. }
  652. }
  653. if (!airborneAtEnd)
  654. {
  655. // just in case.
  656. setFlag(IS_IN_FREEFALL, false);
  657. if (obj->isDisabledByType(DISABLED_FREEFALL))
  658. obj->clearDisabled(DISABLED_FREEFALL);
  659. obj->clearModelConditionState(MODELCONDITION_FREEFALL);
  660. }
  661. // If we are effectively dead, we shouldn't recall kill.
  662. if (d->m_killWhenRestingOnGround && !airborneAtEnd && isVerySmall3D(m_vel))
  663. {
  664. if( !obj->isKindOf( KINDOF_DRONE ) || obj->isEffectivelyDead() || obj->isDisabledByType( DISABLED_UNMANNED ) )
  665. {
  666. //Must be one of the following cases in order to splat:
  667. //1) Not a drone
  668. //2) Dead drone
  669. //3) Unmanned drone
  670. obj->kill();
  671. }
  672. }
  673. setFlag(UPDATE_EVER_RUN, true);
  674. setFlag(WAS_AIRBORNE_LAST_FRAME, airborneAtEnd);
  675. setFlag(IS_IN_UPDATE, false);
  676. return calcSleepTime();
  677. }
  678. //-------------------------------------------------------------------------------------------------
  679. UpdateSleepTime PhysicsBehavior::calcSleepTime() const
  680. {
  681. #ifdef SLEEPY_PHYSICS
  682. if (isZero3D(m_vel)
  683. && isZero3D(m_accel)
  684. && !getFlag(HAS_PITCHROLLYAW)
  685. && !isMotive()
  686. && (getObject()->getLayer() == LAYER_GROUND && !getObject()->isAboveTerrain())
  687. && getCurrentOverlap() == INVALID_ID
  688. && getPreviousOverlap() == INVALID_ID
  689. && getFlag(UPDATE_EVER_RUN))
  690. {
  691. return UPDATE_SLEEP_FOREVER;
  692. }
  693. else
  694. #endif
  695. {
  696. return UPDATE_SLEEP_NONE;
  697. }
  698. }
  699. //-------------------------------------------------------------------------------------------------
  700. Real PhysicsBehavior::getVelocityMagnitude() const
  701. {
  702. if (m_velMag == INVALID_VEL_MAG)
  703. {
  704. m_velMag = (Real)sqrtf( sqr(m_vel.x) + sqr(m_vel.y) + sqr(m_vel.z) );
  705. }
  706. return m_velMag;
  707. }
  708. //-------------------------------------------------------------------------------------------------
  709. /**
  710. * Return the current velocity magnitude in the forward direction.
  711. * If velocity is opposite facing vector, the returned value will be negative.
  712. */
  713. Real PhysicsBehavior::getForwardSpeed2D() const
  714. {
  715. const Coord3D *dir = getObject()->getUnitDirectionVector2D();
  716. Real vx = m_vel.x * dir->x;
  717. Real vy = m_vel.y * dir->y;
  718. Real dot = vx + vy;
  719. Real speedSquared = vx*vx + vy*vy;
  720. // DEBUG_ASSERTCRASH( speedSquared != 0, ("zero speedSquared will overflow sqrtf()!") );// lorenzen... sanity check
  721. Real speed = (Real)sqrtf( speedSquared );
  722. if (dot >= 0.0f)
  723. return speed;
  724. return -speed;
  725. }
  726. //-------------------------------------------------------------------------------------------------
  727. /**
  728. * Return the current velocity magnitude in the forward direction.
  729. * If velocity is opposite facing vector, the returned value will be negative.
  730. */
  731. Real PhysicsBehavior::getForwardSpeed3D() const
  732. {
  733. Vector3 dir = getObject()->getTransformMatrix()->Get_X_Vector();
  734. Real vx = m_vel.x * dir.X;
  735. Real vy = m_vel.y * dir.Y;
  736. Real vz = m_vel.z * dir.Z;
  737. Real dot = vx + vy + vz;
  738. Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz );
  739. if (dot >= 0.0f)
  740. return speed;
  741. return -speed;
  742. }
  743. //-------------------------------------------------------------------------------------------------
  744. Bool PhysicsBehavior::isCurrentlyOverlapped(Object *obj) const
  745. {
  746. return obj != NULL && obj->getID() == m_currentOverlap;
  747. }
  748. //-------------------------------------------------------------------------------------------------
  749. Bool PhysicsBehavior::wasPreviouslyOverlapped(Object *obj) const
  750. {
  751. return obj != NULL && obj->getID() == m_previousOverlap;
  752. }
  753. //-------------------------------------------------------------------------------------------------
  754. void PhysicsBehavior::scrubVelocityZ( Real desiredVelocity )
  755. {
  756. if (fabs(desiredVelocity) < 0.001f)
  757. {
  758. m_vel.z = 0;
  759. }
  760. else
  761. {
  762. if ((desiredVelocity < 0 && m_vel.z < desiredVelocity) || (desiredVelocity > 0 && m_vel.z > desiredVelocity))
  763. {
  764. m_vel.z = desiredVelocity;
  765. }
  766. }
  767. m_velMag = INVALID_VEL_MAG;
  768. }
  769. //-------------------------------------------------------------------------------------------------
  770. void PhysicsBehavior::scrubVelocity2D( Real desiredVelocity )
  771. {
  772. if (desiredVelocity < 0.001f)
  773. {
  774. m_vel.x = 0;
  775. m_vel.y = 0;
  776. }
  777. else
  778. {
  779. Real curVelocity = sqrtf(m_vel.x*m_vel.x + m_vel.y*m_vel.y);
  780. if (desiredVelocity > curVelocity)
  781. {
  782. return;
  783. }
  784. desiredVelocity /= curVelocity;
  785. m_vel.x *= desiredVelocity;
  786. m_vel.y *= desiredVelocity;
  787. }
  788. m_velMag = INVALID_VEL_MAG;
  789. }
  790. //-------------------------------------------------------------------------------------------------
  791. void PhysicsBehavior::addOverlap(Object *obj)
  792. {
  793. if (obj && !isCurrentlyOverlapped(obj))
  794. {
  795. m_currentOverlap = obj->getID();
  796. }
  797. }
  798. //-------------------------------------------------------------------------------------------------
  799. void PhysicsBehavior::transferVelocityTo(PhysicsBehavior* that) const
  800. {
  801. if (that != NULL)
  802. that->m_vel.add(&m_vel);
  803. that->m_velMag = INVALID_VEL_MAG;
  804. }
  805. //-------------------------------------------------------------------------------------------------
  806. void PhysicsBehavior::addVelocityTo( const Coord3D *vel)
  807. {
  808. if (vel != NULL)
  809. m_vel.add( vel );
  810. }
  811. //-------------------------------------------------------------------------------------------------
  812. void PhysicsBehavior::setAngles( Real yaw, Real pitch, Real roll )
  813. {
  814. const Coord3D* pos = getObject()->getPosition();
  815. Matrix3D xfrm;
  816. xfrm.Make_Identity();
  817. xfrm.Translate( pos->x, pos->y, pos->z );
  818. // here we DO want to use in-place-etc, cuz we're not adding to any existing rot/etc
  819. xfrm.In_Place_Pre_Rotate_X( -roll );
  820. xfrm.In_Place_Pre_Rotate_Y( pitch );
  821. xfrm.In_Place_Pre_Rotate_Z( yaw );
  822. getObject()->setTransformMatrix( &xfrm );
  823. }
  824. //-------------------------------------------------------------------------------------------------
  825. Real PhysicsBehavior::getMass() const
  826. {
  827. Real mass = m_mass;
  828. ContainModuleInterface* contain = getObject()->getContain();
  829. if (contain)
  830. mass += contain->getContainedItemsMass();
  831. return mass;
  832. }
  833. //-----------------------------------------------------------------------------
  834. inline Real calcDistSqr(const Coord3D& a, const Coord3D& b)
  835. {
  836. return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
  837. }
  838. //-------------------------------------------------------------------------------------------------
  839. void PhysicsBehavior::doBounceSound(const Coord3D& prevPos)
  840. {
  841. if (!m_bounceSound)
  842. return;
  843. const Real NORMAL_VEL_Z = 0.25f;
  844. const Real NORMAL_MASS = 50.0f;
  845. // get the per-unit sound for the collision which was stuffed in on Object creation.
  846. AudioEventRTS collisionSound = m_bounceSound->m_event;
  847. //Real vel = fabs(getVelocity()->z);
  848. // can't use velocity, because it's already been updated this frame, and will be zero... (srj)
  849. Real vel = fabs(prevPos.z - getObject()->getPosition()->z);
  850. Real mass = fabs(getMass());
  851. if (vel > NORMAL_VEL_Z) {
  852. vel = NORMAL_VEL_Z;
  853. }
  854. if (mass > NORMAL_MASS) {
  855. mass = NORMAL_MASS;
  856. }
  857. if (vel < 0) {
  858. vel = 0;
  859. }
  860. if (mass < 0) {
  861. mass = 0;
  862. }
  863. #ifdef FIX_AUDIO
  864. Real volAdjust = NormalizeToRange(MuLaw(vel, NORMAL_VEL_Z, 500), -1, 1, 0.25, 1.0);
  865. volAdjust *= NormalizeToRange(MuLaw(mass, NORMAL_MASS, 500), -1, 1, 0.25, 1.0);
  866. collisionSound.setVolume(volAdjust);
  867. #endif
  868. collisionSound.setObjectID(getObject()->getID());
  869. TheAudio->addAudioEvent(&collisionSound);
  870. }
  871. //-------------------------------------------------------------------------------------------------
  872. /**
  873. * Resolve the collision between getObject() and other by computing
  874. * the amount the objects have overlapd, and applying proportional forces to
  875. * push them apart.
  876. * Note that this call only applies forces to our object, not to "other". Since the
  877. * forces should be equal and opposite, this could be optimized.
  878. * @todo Make this work properly for non-cylindrical objects (MSB)
  879. * @todo Physics collision resolution is 2D - should it be 3D? (MSB)
  880. */
  881. //DECLARE_PERF_TIMER(PhysicsBehavioronCollide)
  882. void PhysicsBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
  883. {
  884. //USE_PERF_TIMER(PhysicsBehavioronCollide)
  885. if (m_pui != NULL)
  886. {
  887. // projectiles always get a chance to handle their own collisions, and not go thru here
  888. if (m_pui->projectileHandleCollision(other))
  889. return;
  890. }
  891. Object *obj = getObject();
  892. Object* objContainedBy = obj->getContainedBy();
  893. // Note that other == null means "collide with ground"
  894. if (other == NULL)
  895. {
  896. // if we are in a container, tell the container we collided with the ground.
  897. // (handy for parachutes.)
  898. if (objContainedBy)
  899. {
  900. objContainedBy->onCollide(other, loc, normal);
  901. }
  902. return;
  903. }
  904. // if we are containing this object, ignore collisions with it.
  905. Object* otherContainedBy = other->getContainedBy();
  906. if (otherContainedBy == obj || objContainedBy == other)
  907. {
  908. return;
  909. }
  910. // if we're both on parachutes, we never collide with each other.
  911. /// @todo srj -- ugh, an awful hack for paradrop problems. fix someday.
  912. if (obj->testStatus(OBJECT_STATUS_PARACHUTING) && other->testStatus(OBJECT_STATUS_PARACHUTING))
  913. {
  914. return;
  915. }
  916. // ignore collisions with our "ignore" thingie, if any (and vice versa)
  917. AIUpdateInterface* ai = obj->getAIUpdateInterface();
  918. if (ai != NULL && ai->getIgnoredObstacleID() == other->getID())
  919. {
  920. /// @todo srj -- what the hell is this code doing here? ack!
  921. //Before we return, check for a very special case of an infantry colliding with an unmanned vehicle.
  922. //If this is the case, it'll become its new pilot!
  923. if( obj->isKindOf( KINDOF_INFANTRY ) && other->isDisabledByType( DISABLED_UNMANNED ) )
  924. {
  925. //This is in fact the case, and we are doing it here because it applies to all infantry in any unmanned vehicle.
  926. //This could be done via a special/new module, but doing it here doesn't require a new module update to every infantry.
  927. other->clearDisabled( DISABLED_UNMANNED );
  928. //We need to be able to test whether an object on a team has been captured, so set here that this object
  929. //was captured.
  930. other->setCaptured(true);
  931. other->defect( obj->getTeam(), 0 );
  932. //other->setTeam( obj->getTeam() );
  933. //In order to make things easier for the designers, we are going to transfer the name
  934. //of the infantry to the vehicle... so the designer can control the vehicle with their scripts.
  935. TheScriptEngine->transferObjectName( obj->getName(), other );
  936. TheGameLogic->destroyObject( obj );
  937. }
  938. return;
  939. }
  940. AIUpdateInterface* aiOther = other->getAIUpdateInterface();
  941. if (aiOther != NULL && aiOther->getIgnoredObstacleID() == obj->getID())
  942. {
  943. return;
  944. }
  945. if (isIgnoringCollisionsWith(other->getID()))
  946. {
  947. return;
  948. }
  949. Bool immobile = obj->isKindOf( KINDOF_IMMOBILE );
  950. Bool otherImmobile = other->isKindOf( KINDOF_IMMOBILE );
  951. PhysicsBehavior* otherPhysics = other->getPhysics();
  952. if (otherPhysics)
  953. {
  954. if (otherPhysics->isIgnoringCollisionsWith(obj->getID()))
  955. {
  956. return;
  957. }
  958. }
  959. else
  960. {
  961. // if the object we have collided with has no physics, it is insubstantial
  962. // (exception: if it's immobile.)
  963. if (!otherImmobile)
  964. {
  965. return;
  966. }
  967. }
  968. if (checkForOverlapCollision(other))
  969. {
  970. // we should overlap them, rather than bounce them, so punt here.
  971. // (note that we do this prior to checking for Physics update,
  972. // because Overlap doesn't require 'other' to have Physics...)
  973. return;
  974. }
  975. Real mass = getMass();
  976. if (immobile)
  977. mass = 999999.0f;
  978. if (ai)
  979. {
  980. // AI objects move under their own initiative, not by getting bounced. jba.
  981. // unless they are dead & colliding with something immobile. (srj)
  982. // or parachuting and colliding with something immobile. (srj)
  983. if (!((obj->isEffectivelyDead() || obj->testStatus(OBJECT_STATUS_PARACHUTING)) && otherImmobile))
  984. {
  985. Bool doForce = ai->processCollision(this, other);
  986. if (!doForce)
  987. return;
  988. }
  989. }
  990. Coord3D usCenter, themCenter;
  991. obj->getGeometryInfo().getCenterPosition(*obj->getPosition(), usCenter);
  992. other->getGeometryInfo().getCenterPosition(*other->getPosition(), themCenter);
  993. Coord3D delta;
  994. delta.x = themCenter.x - usCenter.x;
  995. delta.y = themCenter.y - usCenter.y;
  996. delta.z = themCenter.z - usCenter.z;
  997. Real distSqr, usRadius, themRadius;
  998. if (obj->isAboveTerrain())
  999. {
  1000. // do 3d testing.
  1001. usRadius = obj->getGeometryInfo().getBoundingSphereRadius();
  1002. themRadius = other->getGeometryInfo().getBoundingSphereRadius();
  1003. distSqr = sqr(delta.x) + sqr(delta.y) + sqr(delta.z);
  1004. }
  1005. else
  1006. {
  1007. // do 2d testing.
  1008. usRadius = obj->getGeometryInfo().getBoundingCircleRadius();
  1009. themRadius = other->getGeometryInfo().getBoundingCircleRadius();
  1010. distSqr = sqr(delta.x) + sqr(delta.y);
  1011. delta.z = 0;
  1012. }
  1013. if (distSqr > sqr(usRadius + themRadius))
  1014. {
  1015. // We don't overlap at all. How did we get here?
  1016. return;
  1017. }
  1018. m_lastCollidee = other->getID();
  1019. Real dist = sqrtf(distSqr);
  1020. Real overlap = usRadius + themRadius - dist;
  1021. // if objects are coincident, dist is zero, so force would be infinite -- clearly
  1022. // not what we want. so just cap it here for now. (srj)
  1023. if (dist < 1.0f)
  1024. dist = 1.0f;
  1025. if (getAllowCollideForce())
  1026. {
  1027. Real factor;
  1028. Coord3D force;
  1029. if (otherImmobile && !obj->isDestroyed())
  1030. {
  1031. if (obj->testStatus(OBJECT_STATUS_PARACHUTING))
  1032. {
  1033. // don't let us intersect buildings. cheat. applying a force won't work
  1034. // cuz we are usually braking. jam it.
  1035. Object* objToBounce = obj;
  1036. while (objToBounce->getContainedBy() != NULL)
  1037. objToBounce = objToBounce->getContainedBy();
  1038. Real bounceOutDist = usRadius * 0.1f;
  1039. Coord3D tmp = *objToBounce->getPosition();
  1040. tmp.x -= bounceOutDist * delta.x / dist;
  1041. tmp.y -= bounceOutDist * delta.y / dist;
  1042. objToBounce->setPosition(&tmp);
  1043. objToBounce->getPhysics()->scrubVelocity2D(0);
  1044. return;
  1045. }
  1046. // if other is immobile, we must apply enough force to at least stop our motion,
  1047. // and preferably negate it, otherwise we will pass thru the object. so cheat,
  1048. // and act like we're bouncing off the ground.
  1049. const Real MIN_STIFF = 0.01f;
  1050. const Real MAX_STIFF = 0.99f;
  1051. Real stiffness = TheGlobalData->m_structureStiffness;
  1052. if (stiffness < MIN_STIFF) stiffness = MIN_STIFF;
  1053. if (stiffness > MAX_STIFF) stiffness = MAX_STIFF;
  1054. // huh huh, he said "stiff"
  1055. Real mag = getVelocityMagnitude();
  1056. const Real MINBOUNCESPEED = 1.0f/(LOGICFRAMES_PER_SECOND*5.0f);
  1057. if (mag < MINBOUNCESPEED)
  1058. mag = MINBOUNCESPEED;
  1059. factor = -mag * getMass() * stiffness;
  1060. // if we are moving down, we may want to blow ourselves into smithereens....
  1061. if (delta.z < 0.0f &&
  1062. obj->getPosition()->z >= TheGlobalData->m_defaultStructureRubbleHeight)
  1063. {
  1064. if (other->isKindOf(KINDOF_STRUCTURE))
  1065. {
  1066. // fall into a building. if a vehicle, blow up. then destroy ourself (not die), regardless.
  1067. if (obj->isKindOf(KINDOF_VEHICLE))
  1068. {
  1069. TheWeaponStore->createAndFireTempWeapon(getPhysicsBehaviorModuleData()->m_vehicleCrashesIntoBuildingWeaponTemplate, obj, obj->getPosition());
  1070. }
  1071. TheGameLogic->destroyObject(obj);
  1072. return;
  1073. }
  1074. else
  1075. {
  1076. // fall into a nonbuilding -- whatever. if we're a vehicle, quietly do a little damage.
  1077. if (obj->isKindOf(KINDOF_VEHICLE))
  1078. {
  1079. TheWeaponStore->createAndFireTempWeapon(getPhysicsBehaviorModuleData()->m_vehicleCrashesIntoNonBuildingWeaponTemplate, obj, obj->getPosition());
  1080. }
  1081. }
  1082. }
  1083. // nuke the velocity. why? very simple: we want to ignore the previous vel in favor of
  1084. // this. in theory, we could be clever and calculate the right force to apply to achieve this,
  1085. // but then if we were still colliding next frame, we'd get a sudden 'aceleration' of bounce
  1086. // that would look freakish. so cheat.
  1087. m_vel.x = 0;
  1088. m_vel.y = 0;
  1089. m_vel.z = 0;
  1090. m_velMag = INVALID_VEL_MAG;
  1091. }
  1092. else
  1093. {
  1094. if (overlap > 5.0f)
  1095. overlap = 5.0f;
  1096. factor = -overlap;
  1097. }
  1098. force.x = factor * delta.x / dist;
  1099. force.y = factor * delta.y / dist;
  1100. force.z = factor * delta.z / dist; // will be zero for 2d case.
  1101. DEBUG_ASSERTCRASH(!(_isnan(force.x) || _isnan(force.y) || _isnan(force.z)), ("PhysicsBehavior::onCollide force NAN!\n"));
  1102. applyForce( &force );
  1103. }
  1104. }
  1105. //-------------------------------------------------------------------------------------------------
  1106. static Bool perpsLogicallyEqual( Real perpOne, Real perpTwo )
  1107. {
  1108. // Equality with a wiggle fudge.
  1109. const Real PERP_RANGE = 0.15f;
  1110. return fabs( perpOne - perpTwo ) <= PERP_RANGE;
  1111. }
  1112. //-------------------------------------------------------------------------------------------------
  1113. /** Do the collision
  1114. * Cases:
  1115. * * The crusheeOther is !OVERLAPPABLE or !CRUSHABLE. Result: return false
  1116. * * The crusherMe is !CRUSHER. Result: return false
  1117. * * crusher is CRUSHER, crushee is CRUSHABLE but allied. Result: return false
  1118. * * crusher is CRUSHER, crushee is OVERLAPPABLE but not CRUSHABLE. Result: return false
  1119. * * crushee has no PhysicsBehavior. Result: return false
  1120. * * crusher is CRUSHER, crushee is CRUSHABLE, but is too hard. Result: return false
  1121. * * crusher is CRUSHER, crushee is CRUSHABLE, hardness ok. Result: crush, return true
  1122. *
  1123. * Return true if we want to skip having physics push us apart // LORENZEN
  1124. */
  1125. //-------------------------------------------------------------------------------------------------
  1126. Bool PhysicsBehavior::checkForOverlapCollision(Object *other)
  1127. {
  1128. //This is the most Supreme Truth... that unless I am moving right now, I may not crush anyhing!
  1129. if ( isVerySmall3D( *getVelocity() ) )
  1130. return false;
  1131. Object* crusherMe = getObject();
  1132. Object* crusheeOther = other;
  1133. //Determine if we can crush the other object.
  1134. Bool selfCrushingOther = crusherMe->canCrushOrSquish( crusheeOther, TEST_CRUSH_ONLY );
  1135. Bool selfBeingCrushed = crusheeOther->canCrushOrSquish( crusherMe, TEST_CRUSH_ONLY );
  1136. if( selfCrushingOther && selfBeingCrushed )
  1137. {
  1138. //Is it possible to crush and be crushed at the same time?
  1139. DEBUG_CRASH( ("%s (Crusher:%d, Crushable:%d) is attempting to crush %s (Crusher:%d, Crushable:%d) but it is reciprocating -- shouldn't be possible!",
  1140. crusherMe->getTemplate()->getName().str(), crusherMe->getCrusherLevel(), crusherMe->getCrushableLevel(),
  1141. crusheeOther->getTemplate()->getName().str(), crusheeOther->getCrusherLevel(), crusheeOther->getCrushableLevel() ) );
  1142. return false;
  1143. }
  1144. // if we are being crushed, then skip all this and return true;
  1145. // this allows us to NOT react in the normal way and just be passive to the overlap...
  1146. if( selfBeingCrushed )
  1147. {
  1148. return true;
  1149. }
  1150. // grab physics modules if there
  1151. PhysicsBehavior *crusherPhysics = this;
  1152. if( crusherPhysics == NULL )
  1153. {
  1154. return false;
  1155. }
  1156. if( !selfCrushingOther )
  1157. {
  1158. return false;
  1159. }
  1160. // ok, add this to our list-of-overlapped-things.
  1161. crusherPhysics->addOverlap(crusheeOther);
  1162. if (!crusherPhysics->wasPreviouslyOverlapped(crusheeOther))
  1163. {
  1164. DamageInfo damageInfo;
  1165. damageInfo.in.m_damageType = DAMAGE_CRUSH;
  1166. damageInfo.in.m_deathType = DEATH_CRUSHED;
  1167. damageInfo.in.m_sourceID = crusherMe->getID();
  1168. damageInfo.in.m_amount = 0.0f; // yes, that's right -- we don't want to do damage, just to trigger the minor DamageFX, if any
  1169. crusheeOther->attemptDamage( &damageInfo );
  1170. }
  1171. const Coord3D *crusheePos = crusheeOther->getPosition();
  1172. const Coord3D *crusherPos = crusherMe->getPosition();
  1173. BodyModuleInterface* crusheeBody = crusheeOther->getBodyModule();
  1174. Bool frontCrushed = crusheeBody->getFrontCrushed();
  1175. Bool backCrushed = crusheeBody->getBackCrushed();
  1176. if( !(frontCrushed && backCrushed) )
  1177. {
  1178. Bool crushIt = FALSE;
  1179. const Coord3D *dir = crusherMe->getUnitDirectionVector2D();
  1180. const Coord3D *crusheeDir = crusheeOther->getUnitDirectionVector2D();
  1181. Real crushPointOffsetDistance = crusheeOther->getGeometryInfo().getMajorRadius() / 2;
  1182. Coord3D crushPointOffset;
  1183. crushPointOffset.x = crusheeDir->x * crushPointOffsetDistance;
  1184. crushPointOffset.y = crusheeDir->y * crushPointOffsetDistance;
  1185. crushPointOffset.z = 0;
  1186. Coord3D comparisonCoord;
  1187. Real dx, dy;
  1188. /// @todo GS To account for different sized crushers, this should be redone as a box or circle test, not a point
  1189. // First decide which crush point has the shortest perp to our direction ray.
  1190. // Real bestPerp = 9999;
  1191. CrushEnum crushTarget = NO_CRUSH;
  1192. if( frontCrushed || backCrushed )
  1193. {
  1194. // Degenerate case; there is only one point to consider.
  1195. if( frontCrushed )
  1196. crushTarget = BACK_END_CRUSH;
  1197. else
  1198. crushTarget = FRONT_END_CRUSH;
  1199. }
  1200. else
  1201. {
  1202. // Else, there are three points.
  1203. Real frontPerpLength, backPerpLength, centerPerpLength;
  1204. Coord3D frontVector, backVector, centerVector;
  1205. {
  1206. comparisonCoord = *crusheePos;
  1207. comparisonCoord.x += crushPointOffset.x;
  1208. comparisonCoord.y += crushPointOffset.y;
  1209. frontVector = comparisonCoord;
  1210. frontVector.x -= crusherPos->x;
  1211. frontVector.y -= crusherPos->y; //vector from me to the front crush point
  1212. frontVector.z = 0;
  1213. Real rayLength = frontVector.x * dir->x + frontVector.y * dir->y;
  1214. Coord3D dirVector;
  1215. dirVector.x = rayLength * dir->x;
  1216. dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
  1217. dirVector.z = 0;
  1218. Coord3D perpVector;
  1219. perpVector.x = dirVector.x - frontVector.x;
  1220. perpVector.y = dirVector.y - frontVector.y; //vector from the front point perp to my direction
  1221. perpVector.z = 0;
  1222. frontPerpLength = perpVector.length();
  1223. }
  1224. {
  1225. comparisonCoord = *crusheePos;
  1226. comparisonCoord.x -= crushPointOffset.x;
  1227. comparisonCoord.y -= crushPointOffset.y;
  1228. backVector = comparisonCoord;
  1229. backVector.x -= crusherPos->x;
  1230. backVector.y -= crusherPos->y; //vector from me to the front crush point
  1231. backVector.z = 0;
  1232. Real rayLength = backVector.x * dir->x + backVector.y * dir->y;
  1233. Coord3D dirVector;
  1234. dirVector.x = rayLength * dir->x;
  1235. dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
  1236. dirVector.z = 0;
  1237. Coord3D perpVector;
  1238. perpVector.x = dirVector.x - backVector.x;
  1239. perpVector.y = dirVector.y - backVector.y; //vector from the front point perp to my direction
  1240. perpVector.z = 0;
  1241. backPerpLength = perpVector.length();
  1242. }
  1243. {
  1244. comparisonCoord = *crusheePos;
  1245. centerVector = comparisonCoord;
  1246. centerVector.x -= crusherPos->x;
  1247. centerVector.y -= crusherPos->y; //vector from me to the front crush point
  1248. centerVector.z = 0;
  1249. Real rayLength = centerVector.x * dir->x + centerVector.y * dir->y;
  1250. Coord3D dirVector;
  1251. dirVector.x = rayLength * dir->x;
  1252. dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
  1253. dirVector.z = 0;
  1254. Coord3D perpVector;
  1255. perpVector.x = dirVector.x - centerVector.x;
  1256. perpVector.y = dirVector.y - centerVector.y; //vector from the front point perp to my direction
  1257. perpVector.z = 0;
  1258. centerPerpLength = perpVector.length();
  1259. }
  1260. // Now find the shortest. Use the straightline distance to crush point as tie breaker
  1261. if( (frontPerpLength <= centerPerpLength) && (frontPerpLength <= backPerpLength) )
  1262. {
  1263. if( perpsLogicallyEqual(frontPerpLength, centerPerpLength)
  1264. || perpsLogicallyEqual(frontPerpLength, backPerpLength)
  1265. )
  1266. {
  1267. Real frontVectorLength = frontVector.length();
  1268. if( perpsLogicallyEqual(frontPerpLength, centerPerpLength) )
  1269. {
  1270. Real centerVectorLength = centerVector.length();
  1271. if( frontVectorLength < centerVectorLength )
  1272. crushTarget = FRONT_END_CRUSH;
  1273. else
  1274. crushTarget = TOTAL_CRUSH;
  1275. }
  1276. else if( perpsLogicallyEqual(frontPerpLength, backPerpLength) )
  1277. {
  1278. Real backVectorLength = backVector.length();
  1279. if( frontVectorLength < backVectorLength )
  1280. crushTarget = FRONT_END_CRUSH;
  1281. else
  1282. crushTarget = BACK_END_CRUSH;
  1283. }
  1284. }
  1285. else
  1286. {
  1287. crushTarget = FRONT_END_CRUSH;
  1288. }
  1289. }
  1290. else if( (backPerpLength <= centerPerpLength) && (backPerpLength <= frontPerpLength) )
  1291. {
  1292. if( perpsLogicallyEqual(backPerpLength, centerPerpLength)
  1293. || perpsLogicallyEqual(backPerpLength, frontPerpLength)
  1294. )
  1295. {
  1296. Real backVectorLength = backVector.length();
  1297. if( perpsLogicallyEqual(backPerpLength, centerPerpLength) )
  1298. {
  1299. Real centerVectorLength = centerVector.length();
  1300. if( backVectorLength < centerVectorLength )
  1301. crushTarget = BACK_END_CRUSH;
  1302. else
  1303. crushTarget = TOTAL_CRUSH;
  1304. }
  1305. else if( perpsLogicallyEqual(backPerpLength, frontPerpLength) )
  1306. {
  1307. Real frontVectorLength = frontVector.length();
  1308. if( backVectorLength < frontVectorLength )
  1309. crushTarget = BACK_END_CRUSH;
  1310. else
  1311. crushTarget = FRONT_END_CRUSH;
  1312. }
  1313. }
  1314. else
  1315. {
  1316. crushTarget = BACK_END_CRUSH;
  1317. }
  1318. }
  1319. else // centerperp is shortest
  1320. {
  1321. if( perpsLogicallyEqual(centerPerpLength, backPerpLength)
  1322. || perpsLogicallyEqual(centerPerpLength, frontPerpLength)
  1323. )
  1324. {
  1325. Real centerVectorLength = centerVector.length();
  1326. if( perpsLogicallyEqual(centerPerpLength, frontPerpLength) )
  1327. {
  1328. Real frontVectorLength = frontVector.length();
  1329. if( centerVectorLength < frontVectorLength )
  1330. crushTarget = TOTAL_CRUSH;
  1331. else
  1332. crushTarget = FRONT_END_CRUSH;
  1333. }
  1334. else if( perpsLogicallyEqual(centerPerpLength, backPerpLength) )
  1335. {
  1336. Real backVectorLength = backVector.length();
  1337. if( centerVectorLength < backVectorLength )
  1338. crushTarget = TOTAL_CRUSH;
  1339. else
  1340. crushTarget = BACK_END_CRUSH;
  1341. }
  1342. }
  1343. else
  1344. {
  1345. crushTarget = TOTAL_CRUSH;
  1346. }
  1347. }
  1348. }
  1349. Real distanceTooFarSquared = 2.25 * crushPointOffsetDistance * crushPointOffsetDistance;
  1350. // And just because there is only one crush point left doesn't
  1351. // mean we automatically get it. (1.5x)^2 means if we are outside the crushed
  1352. //front point, we will not auto get the back point (only 3/8 car spread)
  1353. // Then ask ourselves if we have passed the correct crush point (dot < 0).
  1354. if( crushTarget == TOTAL_CRUSH )
  1355. {
  1356. // Check the middle crush point
  1357. comparisonCoord = *crusheePos; //copy so can move to each crush point
  1358. dx = comparisonCoord.x - crusherPos->x;
  1359. dy = comparisonCoord.y - crusherPos->y;
  1360. Real dot = dir->x * dx + dir->y * dy;
  1361. Real distanceSquared = (dx * dx) + (dy * dy);
  1362. if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
  1363. {
  1364. // Past target point, but not too far in distance or angle.
  1365. crushIt = TRUE;
  1366. }
  1367. }
  1368. else if( crushTarget == FRONT_END_CRUSH )
  1369. {
  1370. // Check the front point.
  1371. comparisonCoord = *crusheePos;
  1372. comparisonCoord.x += crushPointOffset.x;
  1373. comparisonCoord.y += crushPointOffset.y;
  1374. dx = comparisonCoord.x - crusherPos->x;
  1375. dy = comparisonCoord.y - crusherPos->y;
  1376. Real dot = dir->x * dx + dir->y * dy;
  1377. Real distanceSquared = (dx * dx) + (dy * dy);
  1378. if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
  1379. {
  1380. // Past target point, but not too far in distance or angle.
  1381. crushIt = TRUE;
  1382. }
  1383. }
  1384. else if( crushTarget == BACK_END_CRUSH )
  1385. {
  1386. // Check back point
  1387. comparisonCoord = *crusheePos;
  1388. comparisonCoord.x -= crushPointOffset.x;
  1389. comparisonCoord.y -= crushPointOffset.y;
  1390. dx = comparisonCoord.x - crusherPos->x;
  1391. dy = comparisonCoord.y - crusherPos->y;
  1392. Real dot = dir->x * dx + dir->y * dy;
  1393. Real distanceSquared = (dx * dx) + (dy * dy);
  1394. if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
  1395. {
  1396. // Past target point, but not too far in distance or angle.
  1397. crushIt = TRUE;
  1398. }
  1399. }
  1400. if( crushIt )
  1401. {
  1402. // do a boat load of crush damage, and the onDie will handle cases like crushed car object
  1403. DamageInfo damageInfo;
  1404. damageInfo.in.m_damageType = DAMAGE_CRUSH;
  1405. damageInfo.in.m_deathType = DEATH_CRUSHED;
  1406. damageInfo.in.m_sourceID = crusherMe->getID();
  1407. damageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT; // make sure they die
  1408. crusheeOther->attemptDamage( &damageInfo );
  1409. }
  1410. } // if crushable
  1411. return true;
  1412. }
  1413. // ------------------------------------------------------------------------------------------------
  1414. /** CRC */
  1415. // ------------------------------------------------------------------------------------------------
  1416. void PhysicsBehavior::crc( Xfer *xfer )
  1417. {
  1418. // extend base class
  1419. UpdateModule::crc( xfer );
  1420. } // end crc
  1421. // ------------------------------------------------------------------------------------------------
  1422. /** Xfer method
  1423. * Version Info:
  1424. * 1: Initial version */
  1425. // ------------------------------------------------------------------------------------------------
  1426. void PhysicsBehavior::xfer( Xfer *xfer )
  1427. {
  1428. // version
  1429. const XferVersion currentVersion = 2;
  1430. XferVersion version = currentVersion;
  1431. xfer->xferVersion( &version, currentVersion );
  1432. // extend base class
  1433. UpdateModule::xfer( xfer );
  1434. // yaw rate
  1435. xfer->xferReal( &m_yawRate );
  1436. // roll rate
  1437. xfer->xferReal( &m_rollRate );
  1438. // pitch rate
  1439. xfer->xferReal( &m_pitchRate );
  1440. // we dont' need to mess with sound stuff
  1441. // m_bounceSound <---- do nothing with this
  1442. // accel
  1443. xfer->xferCoord3D( &m_accel );
  1444. // prev accel
  1445. xfer->xferCoord3D( &m_prevAccel );
  1446. // velocity
  1447. xfer->xferCoord3D( &m_vel );
  1448. // prevPos (now defunct)
  1449. if (version < 2)
  1450. {
  1451. Coord3D tmp;
  1452. tmp.zero();
  1453. xfer->xferCoord3D( &tmp );
  1454. }
  1455. // turning
  1456. xfer->xferUser( &m_turning, sizeof( PhysicsTurningType ) );
  1457. // ignore collisions with
  1458. xfer->xferObjectID( &m_ignoreCollisionsWith );
  1459. // flags
  1460. xfer->xferInt( &m_flags );
  1461. // mass
  1462. xfer->xferReal( &m_mass );
  1463. // current overlap
  1464. xfer->xferObjectID( &m_currentOverlap );
  1465. // previous overlap
  1466. xfer->xferObjectID( &m_previousOverlap );
  1467. // motive force applied
  1468. xfer->xferUnsignedInt( &m_motiveForceExpires );
  1469. // extra bounciness
  1470. xfer->xferReal( &m_extraBounciness );
  1471. // extra friction
  1472. xfer->xferReal( &m_extraFriction );
  1473. // we don't need to save/load this, it is acquired on object creation
  1474. // m_pui <---- do nothing with this
  1475. // mag of current vel
  1476. xfer->xferReal( &m_velMag );
  1477. } // end xfer
  1478. // ------------------------------------------------------------------------------------------------
  1479. /** Load post process */
  1480. // ------------------------------------------------------------------------------------------------
  1481. void PhysicsBehavior::loadPostProcess( void )
  1482. {
  1483. // extend base class
  1484. UpdateModule::loadPostProcess();
  1485. } // end loadPostProcess