| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // PhysicsBehavior.cpp
- // Simple rigid body physics
- // Author: Michael S. Booth, November 2001
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- // please talk to MDC (x36804) before taking this out
- #define NO_DEBUG_CRC
- #include "Common/PerfTimer.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/CrushDie.h" // for CrushEnum
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/TerrainLogic.h"
- #include "GameLogic/Weapon.h"
- #include "GameLogic/LogicRandomValue.h"
- const Real DEFAULT_MASS = 1.0f;
- const Real DEFAULT_SHOCK_YAW = 0.05f;
- const Real DEFAULT_SHOCK_PITCH = 0.025f;
- const Real DEFAULT_SHOCK_ROLL = 0.025f;
- const Real DEFAULT_FORWARD_FRICTION = 0.15f;
- const Real DEFAULT_LATERAL_FRICTION = 0.15f;
- const Real DEFAULT_Z_FRICTION = 0.8f;
- const Real DEFAULT_AERO_FRICTION = 0.0f;
- // Air friction used to be completely separate and unclamped, so this constant must be split.
- // The fact it defaults to 0 shows it.
- const Real MIN_AERO_FRICTION = 0.00f;
- const Real MIN_NON_AERO_FRICTION = 0.01f;
- const Real MAX_FRICTION = 0.99f;
- const Real STUN_RELIEF_EPSILON = 0.5f;
- #include "Common/CRCDebug.h"
- const Int MOTIVE_FRAMES = LOGICFRAMES_PER_SECOND / 3;
- #define SLEEPY_PHYSICS
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- static Real angleBetweenVectors(const Coord3D& inCurDir, const Coord3D& inGoalDir)
- {
- Vector3 curDir;
- curDir.X = inCurDir.x;
- curDir.Y = inCurDir.y;
- curDir.Z = inCurDir.z;
- curDir.Normalize();
- Vector3 goalDir;
- goalDir.X = inGoalDir.x;
- goalDir.Y = inGoalDir.y;
- goalDir.Z = inGoalDir.z;
- goalDir.Normalize();
- // dot of two unit vectors is cos of angle between them.
- Real cosine = Vector3::Dot_Product(curDir, goalDir);
- // bound it in case of numerical error
- Real angleBetween = (Real)ACos(clamp(-1.0f, cosine, 1.0f));
- return angleBetween;
- }
- //-------------------------------------------------------------------------------------------------
- static Real heightToSpeed(Real height)
- {
- // don't bother trying to remember how far we've fallen; instead,
- // back-calc it from our speed & gravity... v = sqrt(2*g*h)
- return sqrt(fabs(2.0f * TheGlobalData->m_gravity * height));
- }
- //-------------------------------------------------------------------------------------------------
- PhysicsBehaviorModuleData::PhysicsBehaviorModuleData()
- {
- m_mass = DEFAULT_MASS;
- m_shockResistance = 0.0f;
- m_shockMaxYaw = DEFAULT_SHOCK_YAW;
- m_shockMaxPitch = DEFAULT_SHOCK_PITCH;
- m_shockMaxRoll = DEFAULT_SHOCK_ROLL;
-
- m_forwardFriction = DEFAULT_FORWARD_FRICTION;
- m_lateralFriction = DEFAULT_LATERAL_FRICTION;
- m_ZFriction = DEFAULT_Z_FRICTION;
- m_aerodynamicFriction = DEFAULT_AERO_FRICTION;
- m_centerOfMassOffset = 0.0f;
- m_allowBouncing = false;
- m_allowCollideForce = true;
- m_killWhenRestingOnGround = false;
- m_minFallSpeedForDamage = heightToSpeed(40.0f);
- m_fallHeightDamageFactor = 1.0f; // was 10. now is 1.
- /*
- thru some bizarre editing mishap, we have been double-apply pitch/roll/yaw rates
- to objects for, well, a long time, it looks like. I have corrected that problem
- in the name of efficiency, but to maintain the same visual appearance without having
- to edit every freaking INI in the world at this point, I am just multiplying
- all the results by a factor so that the effect is the same (but with less execution time).
- I have put this factor into INI in the unlikely event we ever need to change it,
- but defaulting it to 2 is, in fact, the right thing for now... (srj)
- */
- m_pitchRollYawFactor = 2.0f;
- m_vehicleCrashesIntoBuildingWeaponTemplate = TheWeaponStore->findWeaponTemplate("VehicleCrashesIntoBuildingWeapon");
- m_vehicleCrashesIntoNonBuildingWeaponTemplate = TheWeaponStore->findWeaponTemplate("VehicleCrashesIntoNonBuildingWeapon");
- }
- //-------------------------------------------------------------------------------------------------
- static void parseHeightToSpeed( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
- {
- // don't bother trying to remember how far we've fallen; instead,
- // back-calc it from our speed & gravity... v = sqrt(2*g*h)
- Real height = INI::scanReal(ini->getNextToken());
- *(Real *)store = heightToSpeed(height);
- }
- //-------------------------------------------------------------------------------------------------
- static void parseFrictionPerSec( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
- {
- Real fricPerSec = INI::scanReal(ini->getNextToken());
- Real fricPerFrame = fricPerSec * SECONDS_PER_LOGICFRAME_REAL;
- *(Real *)store = fricPerFrame;
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void PhysicsBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- UpdateModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "Mass", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_mass ) },
- { "ShockResistance", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_shockResistance ) },
- { "ShockMaxYaw", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_shockMaxYaw ) },
- { "ShockMaxPitch", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_shockMaxPitch ) },
- { "ShockMaxRoll", INI::parsePositiveNonZeroReal, NULL, offsetof( PhysicsBehaviorModuleData, m_shockMaxRoll ) },
- { "ForwardFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_forwardFriction ) },
- { "LateralFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_lateralFriction ) },
- { "ZFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_ZFriction ) },
- { "AerodynamicFriction", parseFrictionPerSec, NULL, offsetof( PhysicsBehaviorModuleData, m_aerodynamicFriction ) },
- { "CenterOfMassOffset", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_centerOfMassOffset ) },
- { "AllowBouncing", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_allowBouncing ) },
- { "AllowCollideForce", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_allowCollideForce ) },
- { "KillWhenRestingOnGround", INI::parseBool, NULL, offsetof( PhysicsBehaviorModuleData, m_killWhenRestingOnGround) },
- { "MinFallHeightForDamage", parseHeightToSpeed, NULL, offsetof( PhysicsBehaviorModuleData, m_minFallSpeedForDamage) },
- { "FallHeightDamageFactor", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_fallHeightDamageFactor) },
- { "PitchRollYawFactor", INI::parseReal, NULL, offsetof( PhysicsBehaviorModuleData, m_pitchRollYawFactor) },
- { "VehicleCrashesIntoBuildingWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof(PhysicsBehaviorModuleData, m_vehicleCrashesIntoBuildingWeaponTemplate) },
- { "VehicleCrashesIntoNonBuildingWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof(PhysicsBehaviorModuleData, m_vehicleCrashesIntoNonBuildingWeaponTemplate) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- const Real INVALID_VEL_MAG = -1.0f;
- //-------------------------------------------------------------------------------------------------
- PhysicsBehavior::PhysicsBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
- {
- m_accel.zero();
- m_prevAccel = m_accel;
- m_vel.zero();
- m_velMag = 0.0f;
- m_yawRate = 0.0f;
- m_rollRate = 0.0f;
- m_pitchRate = 0.0f;
- m_mass = getPhysicsBehaviorModuleData()->m_mass;
- m_motiveForceExpires = 0;
- m_flags = 0;
- m_extraBounciness = 0.0f;
- m_extraFriction = 0.0f;
- m_currentOverlap = INVALID_ID;
- m_previousOverlap = INVALID_ID;
- m_lastCollidee = INVALID_ID;
- m_ignoreCollisionsWith = INVALID_ID;
- setAllowBouncing(getPhysicsBehaviorModuleData()->m_allowBouncing);
- setAllowCollideForce(getPhysicsBehaviorModuleData()->m_allowCollideForce);
- m_pui = NULL;
- m_bounceSound = NULL;
- #ifdef SLEEPY_PHYSICS
- setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- static ProjectileUpdateInterface* getPui(Object* obj)
- {
- if (!obj->isKindOf(KINDOF_PROJECTILE))
- return NULL;
- ProjectileUpdateInterface* objPui = NULL;
- for (BehaviorModule** u = obj->getBehaviorModules(); *u; ++u)
- {
- if ((objPui = (*u)->getProjectileUpdateInterface()) != NULL)
- return objPui;
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::onObjectCreated()
- {
- m_pui = getPui(getObject());
- }
- //-------------------------------------------------------------------------------------------------
- PhysicsBehavior::~PhysicsBehavior()
- {
- if (m_bounceSound)
- {
- m_bounceSound->deleteInstance();
- m_bounceSound = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setIgnoreCollisionsWith(const Object* obj)
- {
- m_ignoreCollisionsWith = obj ? obj->getID() : INVALID_ID;
- }
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::isIgnoringCollisionsWith(ObjectID id) const
- {
- return id != INVALID_ID && id == m_ignoreCollisionsWith;
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getAerodynamicFriction() const
- {
- Real f = getPhysicsBehaviorModuleData()->m_aerodynamicFriction + m_extraFriction;
- if (f < MIN_AERO_FRICTION) f = MIN_AERO_FRICTION;
- if (f > MAX_FRICTION) f = MAX_FRICTION;
- return f;
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getForwardFriction() const
- {
- Real f = getPhysicsBehaviorModuleData()->m_forwardFriction + m_extraFriction;
- if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
- if (f > MAX_FRICTION) f = MAX_FRICTION;
- return f;
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getLateralFriction() const
- {
- Real f = getPhysicsBehaviorModuleData()->m_lateralFriction + m_extraFriction;
- if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
- if (f > MAX_FRICTION) f = MAX_FRICTION;
- return f;
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getZFriction() const
- {
- Real f = getPhysicsBehaviorModuleData()->m_ZFriction + m_extraFriction;
- if (f < MIN_NON_AERO_FRICTION) f = MIN_NON_AERO_FRICTION;
- if (f > MAX_FRICTION) f = MAX_FRICTION;
- return f;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Apply a force at the object's CG
- */
- void PhysicsBehavior::applyForce( const Coord3D *force )
- {
- DEBUG_ASSERTCRASH(!(_isnan(force->x) || _isnan(force->y) || _isnan(force->z)), ("PhysicsBehavior::applyForce force NAN!\n"));
- if (_isnan(force->x) || _isnan(force->y) || _isnan(force->z)) {
- return;
- }
- // F = ma --> a = F/m (divide force by mass)
- Real mass = getMass();
- Coord3D modForce = *force;
- if (isMotive())
- {
- const Coord3D *dir = getObject()->getUnitDirectionVector2D();
- // Only accept the lateral acceleration.
- Real lateralDot = force->x * (-dir->y) + force->y * dir->x;
- modForce.x = lateralDot * -dir->y;
- modForce.y = lateralDot * dir->x;
- }
- Real massInv = 1.0f / mass;
- m_accel.x += modForce.x * massInv;
- m_accel.y += modForce.y * massInv;
- m_accel.z += modForce.z * massInv;
- //DEBUG_ASSERTCRASH(!(_isnan(m_accel.x) || _isnan(m_accel.y) || _isnan(m_accel.z)), ("PhysicsBehavior::applyForce accel NAN!\n"));
- //DEBUG_ASSERTCRASH(!(_isnan(m_vel.x) || _isnan(m_vel.y) || _isnan(m_vel.z)), ("PhysicsBehavior::applyForce vel NAN!\n"));
- //DEBUG_ASSERTCRASH(fabs(force->z) < 3, ("unlikely z-force"));
- #ifdef SLEEPY_PHYSICS
- if (getFlag(IS_IN_UPDATE))
- {
- // we're applying a force from inside our own update (probably for a bounce or friction).
- // just do nothing, since update will calculate the correct sleep behavior at the end.
- }
- else
- {
- // when a force is applied by an external module, we must wake up, even if the force is zero.
- setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
- }
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Apply a shocwave force at the object's CG
- */
- void PhysicsBehavior::applyShock( const Coord3D *force )
- {
- Coord3D resistedForce = *force;
- resistedForce.scale( 1.0f - min( 1.0f, max( 0.0f, getPhysicsBehaviorModuleData()->m_shockResistance ) ) );
- // Apply the processed shock force to the object
- applyForce(&resistedForce);
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Apply a random rotation at the object's CG
- */
- void PhysicsBehavior::applyRandomRotation()
- {
- // Ignore any pitch, roll & yaw rotation if behavior is stick to ground
- if (getFlag(STICK_TO_GROUND)) return;
- // Set bounce to true for a while until the unit is complete bouncing
- setAllowBouncing(true);
- Real randomModifier;
- randomModifier = GameLogicRandomValue(-1.0f, 1.0f);
- m_yawRate += getPhysicsBehaviorModuleData()->m_shockMaxYaw * randomModifier;
- randomModifier = GameLogicRandomValue(-1.0f, 1.0f);
- m_pitchRate += getPhysicsBehaviorModuleData()->m_shockMaxPitch * randomModifier;
- randomModifier = GameLogicRandomValue(-1.0f, 1.0f);
- m_rollRate += getPhysicsBehaviorModuleData()->m_shockMaxRoll * randomModifier;
- #ifdef SLEEPY_PHYSICS
- if (getFlag(IS_IN_UPDATE))
- {
- // we're applying a force from inside our own update (probably for a bounce or friction).
- // just do nothing, since update will calculate the correct sleep behavior at the end.
- }
- else
- {
- // when a force is applied by an external module, we must wake up, even if the force is zero.
- setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
- }
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::isMotive() const
- {
- return m_motiveForceExpires > TheGameLogic->getFrame();
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::applyMotiveForce( const Coord3D *force )
- {
- m_motiveForceExpires = 0; // make it accept this force unquestioningly :)
- applyForce(force);
- m_motiveForceExpires = TheGameLogic->getFrame() + MOTIVE_FRAMES;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::resetDynamicPhysics()
- {
- m_accel.zero();
- m_prevAccel.zero();
- m_vel.zero();
- m_velMag = 0.0f;
- m_turning = TURN_NONE;
- m_yawRate = 0;
- m_rollRate = 0;
- m_pitchRate = 0;
- setFlag(HAS_PITCHROLLYAW, false);
- #ifdef SLEEPY_PHYSICS
- DEBUG_ASSERTCRASH(!getFlag(IS_IN_UPDATE), ("hmm, should not happen, may not work"));
- setWakeFrame(getObject(), calcSleepTime());
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::applyGravitationalForces()
- {
- m_accel.z += TheGlobalData->m_gravity;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::applyFrictionalForces()
- {
- //Are we a plane that is taxiing on a deck with a height offset?
- Bool deckTaxiing = getObject()->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET )
- && getObject()->getAI()
- && getObject()->getAI()->getCurLocomotorSetType() == LOCOMOTORSET_TAXIING;
- if (getFlag(APPLY_FRICTION2D_WHEN_AIRBORNE) || !getObject()->isSignificantlyAboveTerrain() || deckTaxiing )
- {
- applyYPRDamping(1.0f - DEFAULT_LATERAL_FRICTION);
- if (m_vel.x || m_vel.y)
- {
- const Coord3D *dir = getObject()->getUnitDirectionVector2D();
- Real mass = getMass();
- Real lateralDot = m_vel.x * (-dir->y) + m_vel.y * dir->x;
- Real lateralVel_x = lateralDot * -dir->y;
- Real lateralVel_y = lateralDot * dir->x;
- Real lf = mass * getLateralFriction();
- Coord3D accel;
- accel.x = -(lf * lateralVel_x);
- accel.y = -(lf * lateralVel_y);
- accel.z = 0.0f;
- if (!isMotive())
- {
- Real forwardDot = m_vel.x * dir->x + m_vel.y * dir->y;
- Real forwardVel_x = forwardDot * dir->x;
- Real forwardVel_y = forwardDot * dir->y;
- Real ff = mass * getForwardFriction();
- accel.x += -(ff * forwardVel_x);
- accel.y += -(ff * forwardVel_y);
- }
- applyForce(&accel);
- }
- }
- else
- {
- Real aerodynamics = -getAerodynamicFriction(); // negated!
- // Air resistance is proportional to velocity in the opposite direction
- m_accel.x += m_vel.x * aerodynamics;
- m_accel.y += m_vel.y * aerodynamics;
- m_accel.z += m_vel.z * aerodynamics;
- applyYPRDamping(1.0f + aerodynamics); // since aero is negated, this results in 1.0-getAerodynamicFriction()
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::handleBounce(Real oldZ, Real newZ, Real groundZ, Coord3D* bounceForce)
- {
- if (getFlag(ALLOW_BOUNCE) && newZ <= groundZ)
- {
- const Real MIN_STIFF = 0.01f;
- const Real MAX_STIFF = 0.99f;
- Real stiffness = TheGlobalData->m_groundStiffness;
- if (stiffness < MIN_STIFF) stiffness = MIN_STIFF;
- if (stiffness > MAX_STIFF) stiffness = MAX_STIFF;
- Real desiredAccelZ = 0.0f;
- Real vz = getVelocity()->z;
- if (oldZ > groundZ && vz < 0.0f)
- {
- desiredAccelZ = fabs(vz) * stiffness;
- }
- bounceForce->x = 0.0f;
- bounceForce->y = 0.0f;
- bounceForce->z = getMass() * desiredAccelZ;
- const Real DAMPING = 0.7f;
- applyYPRDamping(DAMPING);
- if (vz < 0.0f)
- {
- Vector3 zvec = getObject()->getTransformMatrix()->Get_Z_Vector();
- const Real rollAngle = (zvec.Z > 0) ? 0 : PI;
- // don't flip both pitch and roll... we'll "flip" twice.
- const Real pitchAngle = 0;
- Real yawAngle = getObject()->getTransformMatrix()->Get_Z_Rotation();
- setAngles(yawAngle, pitchAngle, rollAngle);
- }
- if(bounceForce->z > 0.0f)
- {
- testStunnedUnitForDestruction();
- return true;
- }
- else
- {
- setAllowBouncing(m_originalAllowBounce);
- return false;
- }
- }
- else
- {
- bounceForce->zero();
- return false;
- }
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isVerySmall3D(const Coord3D& v)
- {
- const Real THRESH = 0.01f;
- return (fabs(v.x) < THRESH && fabs(v.y) < THRESH && fabs(v.z) < THRESH);
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isZero3D(const Coord3D& v)
- {
- return v.x == 0.0f && v.y == 0.0f && v.z == 0.0f;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setPitchRate(Real pitch)
- {
- m_pitchRate = pitch;
- setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setRollRate(Real roll)
- {
- m_rollRate = roll;
- setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setYawRate(Real yaw)
- {
- m_yawRate = yaw;
- setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::applyYPRDamping(Real factor)
- {
- m_pitchRate *= factor;
- m_rollRate *= factor;
- m_yawRate *= factor;
- setFlag(HAS_PITCHROLLYAW, (m_pitchRate != 0.0f || m_rollRate != 0.0f || m_yawRate != 0.0f));
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setBounceSound(const AudioEventRTS* bounceSound)
- {
- if (bounceSound)
- {
- if (m_bounceSound == NULL)
- m_bounceSound = newInstance(DynamicAudioEventRTS);
- m_bounceSound->m_event = *bounceSound;
- }
- else
- {
- if (m_bounceSound)
- {
- m_bounceSound->deleteInstance();
- m_bounceSound = NULL;
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Basic rigid body physics using an Euler integrator.
- * @todo Currently, only translations are integrated. Rotations should also be integrated. (MSB)
- */
- DECLARE_PERF_TIMER(PhysicsBehavior)
- UpdateSleepTime PhysicsBehavior::update()
- {
- USE_PERF_TIMER(PhysicsBehavior)
- Object* obj = getObject();
- const PhysicsBehaviorModuleData* d = getPhysicsBehaviorModuleData();
- Bool airborneAtStart = obj->isAboveTerrain();
- Real activeVelZ = 0;
- Coord3D bounceForce;
- Bool gotBounceForce = false;
- DEBUG_ASSERTCRASH(!getFlag(IS_IN_UPDATE), ("impossible"));
- setFlag(IS_IN_UPDATE, true);
- if (!getFlag(UPDATE_EVER_RUN))
- {
- // set the flag so that we don't get bogus "collisions" on the first frame.
- setFlag(WAS_AIRBORNE_LAST_FRAME, airborneAtStart);
- }
- Coord3D prevPos = *obj->getPosition();
- m_prevAccel = m_accel;
- if (!obj->isDisabledByType(DISABLED_HELD))
- {
- Matrix3D mtx = *obj->getTransformMatrix();
- applyGravitationalForces();
- applyFrictionalForces();
- // integrate acceleration into velocity
- m_vel.x += m_accel.x;
- m_vel.y += m_accel.y;
- m_vel.z += m_accel.z;
- // when vel gets tiny, just clamp to zero
- const Real THRESH = 0.001f;
- if (fabsf(m_vel.x) < THRESH) m_vel.x = 0.0f;
- if (fabsf(m_vel.y) < THRESH) m_vel.y = 0.0f;
- if (fabsf(m_vel.z) < THRESH) m_vel.z = 0.0f;
- m_velMag = INVALID_VEL_MAG;
- Real oldPosZ = mtx.Get_Z_Translation();
- // integrate velocity into position
- if (obj->testStatus(OBJECT_STATUS_BRAKING))
- {
- // Don't update position if the locomotor is braking.
- if (!obj->isKindOf(KINDOF_PROJECTILE))
- {
- // Things other than projectiles don't cheat in z. jba.
- mtx.Adjust_Z_Translation(m_vel.z);
- }
- }
- else
- {
- mtx.Adjust_X_Translation(m_vel.x);
- mtx.Adjust_Y_Translation(m_vel.y);
- mtx.Adjust_Z_Translation(m_vel.z);
- }
- if (_isnan(mtx.Get_X_Translation()) || _isnan(mtx.Get_Y_Translation()) ||
- _isnan(mtx.Get_Z_Translation())) {
- DEBUG_CRASH(("Object position is NAN, deleting."));
- TheGameLogic->destroyObject(obj);
- }
- // Check when to clear the stunned status
- if (getIsStunned())
- {
- if ( (fabs(m_vel.x) < STUN_RELIEF_EPSILON &&
- fabs(m_vel.y) < STUN_RELIEF_EPSILON &&
- fabs(m_vel.z) < STUN_RELIEF_EPSILON)
- ||
- obj->isSignificantlyAboveTerrain() == FALSE )
- {
- setStunned(false);
- getObject()->clearModelConditionState(MODELCONDITION_STUNNED);
- }
- }
- if (getFlag(HAS_PITCHROLLYAW))
- {
- /*
- You may be tempted to do something like this:
-
- Real rollAngle = -mtx.Get_X_Rotation();
- Real pitchAngle = mtx.Get_Y_Rotation();
- Real yawAngle = mtx.Get_Z_Rotation();
- // do stuff to angles, then rebuild the mtx with 'em
- You must resist this temptation, because your code will be wrong!
- The problem is that you can't use these calls to later reconstruct
- the matrix... because doing such a thing is highly order-dependent,
- and furthermore, you'd have to use Euler angles (Not the Get_?_Rotation
- calls) to be able to reconstruct 'em, and that's too slow to do for
- every object every frame.
- The one exception is that it is OK to use Get_Z_Rotation() to get
- the yaw angle.
- */
-
- // only update the position if we are not HELD
- // (otherwise, slowdeath sinking into ground won't work)
- Real yawRateToUse = m_yawRate * d->m_pitchRollYawFactor;
- Real pitchRateToUse = m_pitchRate * d->m_pitchRollYawFactor;
- Real rollRateToUse = m_rollRate * d->m_pitchRollYawFactor;
- // With a center of mass listing, pitchRate needs to dampen towards straight down/straight up
- Real offset = getCenterOfMassOffset();
- // Magnitude sets initial rate, here we care about sign
- if (offset != 0.0f)
- {
- Vector3 xvec = mtx.Get_X_Vector();
- Real xy = sqrtf(sqr(xvec.X) + sqr(xvec.Y));
- Real pitchAngle = atan2(xvec.Z, xy);
- Real remainingAngle = (offset > 0) ? ((PI/2) - pitchAngle) : (-(PI/2) + pitchAngle);
- Real s = Sin(remainingAngle);
- pitchRateToUse *= s;
- }
- // update rotation
- /// @todo Rotation should use torques, and integrate just like forces (MSB)
- // note, we DON'T want to Pre-rotate (either inplace or not),
- // since we want to add our mods to the existing matrix.
- mtx.Rotate_X(rollRateToUse);
- mtx.Rotate_Y(pitchRateToUse);
- mtx.Rotate_Z(yawRateToUse);
- }
- // do not allow object to pass through the ground
- Real groundZ = TheTerrainLogic->getLayerHeight(mtx.Get_X_Translation(), mtx.Get_Y_Translation(), obj->getLayer());
- if( obj->getStatusBits().test( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- groundZ += obj->getCarrierDeckHeight();
- }
- gotBounceForce = handleBounce(oldPosZ, mtx.Get_Z_Translation(), groundZ, &bounceForce);
- // remember our z-vel prior to doing ground-slam adjustment
- activeVelZ = m_vel.z;
- if (mtx.Get_Z_Translation() <= groundZ)
- {
- // Note - when vehicles are going down a slope, they will maintain a small negative
- // z velocity as they go down. So don't slam it to 0 if they aren't slamming into the
- // ground.
- Real dz = groundZ - mtx.Get_Z_Translation(); // Our excess z velocity.
- m_vel.z += dz; // Remove the excess z velocity.
- if (m_vel.z > 0.0f)
- m_vel.z = 0.0f;
- m_velMag = INVALID_VEL_MAG;
-
- mtx.Set_Z_Translation(groundZ);
- // this flag is ALWAYS cleared once we hit the ground.
- setFlag(ALLOW_TO_FALL, false);
- // When a stunned object hits the ground the first time, chage it's model state from stunned flailing to just stunned.
- if (getFlag(IS_STUNNED))
- {
- obj->clearModelConditionState(MODELCONDITION_STUNNED_FLAILING);
- obj->setModelConditionState(MODELCONDITION_STUNNED);
- }
- }
- else if (mtx.Get_Z_Translation() > groundZ)
- {
- if (getFlag(IS_IN_FREEFALL))
- {
- obj->setDisabled(DISABLED_FREEFALL);
- obj->setModelConditionState(MODELCONDITION_FREEFALL);
- }
- else if (getFlag(STICK_TO_GROUND) && !getFlag(ALLOW_TO_FALL))
- {
- mtx.Set_Z_Translation(groundZ);
- }
- }
- if (gotBounceForce)
- {
- // Right the object after the bounce since the pitch and roll may have been affected
- Real yawAngle = getObject()->getTransformMatrix()->Get_Z_Rotation();
- setAngles(yawAngle, 0.0f, 0.0f);
- // Set the translation of the after bounce matrix to the one calculated above
- Matrix3D afterBounceMatrix = *getObject()->getTransformMatrix();
- afterBounceMatrix.Set_Translation(mtx.Get_Translation());
- // Set the result of the after bounce matrix as the object's final matrix
- obj->setTransformMatrix(&afterBounceMatrix);
- }
- else
- {
- obj->setTransformMatrix(&mtx);
- }
- } // if not held
- // reset the acceleration for accumulation next frame
- m_accel.zero();
- // clear overlap object, which will be set by PhysicsCollide later
- m_previousOverlap = m_currentOverlap;
- m_currentOverlap = INVALID_ID;
- if (gotBounceForce && getFlag(ALLOW_BOUNCE))
- {
- applyForce(&bounceForce);
- }
- Bool airborneAtEnd = obj->isAboveTerrain();
- // it's not good enough to check for airborne being different between
- // the start and end of this func... we have to compare since last frame,
- // since (if we're held by a parachute, for instance) we might have been
- // moved by other bits of code!
- if (getFlag(WAS_AIRBORNE_LAST_FRAME) && !airborneAtEnd && !getFlag(IMMUNE_TO_FALLING_DAMAGE))
- {
- doBounceSound(prevPos);
- // the normal always points straight down, though we could
- // get the alignWithTerrain() normal if it proves interesting
- Coord3D normal;
- normal.x = normal.y = 0.0f;
- normal.z = -1.0f;
- obj->onCollide(NULL, obj->getPosition(), &normal);
- //
- // don't bother trying to remember how far we've fallen; instead,
- // we back-calc it from our speed & gravity... v = sqrt(2*g*h).
- // (note that m_minFallSpeedForDamage is always POSITIVE.)
- //
- // also note: since projectiles are immune to falling damage, don't
- // even bother doing this check here.
- //
- Real netSpeed = -activeVelZ - d->m_minFallSpeedForDamage;
-
- if (netSpeed > 0.0f && m_pui == NULL)
- {
- // only apply force if it's a pretty steep fall, so that things
- // going down hills don't injure themselves (unless the hill is really steep)
- const Real MIN_ANGLE_TAN = 3.0f; // roughly 71 degrees
- const Real TINY_DELTA = 0.01f;
- if ((fabs(m_vel.x) <= TINY_DELTA || fabs(activeVelZ / m_vel.x) >= MIN_ANGLE_TAN) &&
- (fabs(m_vel.y) <= TINY_DELTA || fabs(activeVelZ / m_vel.y) >= MIN_ANGLE_TAN))
- {
- Real damageAmt = netSpeed * getMass() * d->m_fallHeightDamageFactor;
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_FALLING;
- damageInfo.in.m_deathType = DEATH_SPLATTED;
- damageInfo.in.m_sourceID = obj->getID();
- damageInfo.in.m_amount = damageAmt;
- damageInfo.in.m_shockWaveAmount = 0.0f;
- obj->attemptDamage( &damageInfo );
- //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()));
- // if this killed us, add SPLATTED to get a cool death.
- if (obj->isEffectivelyDead())
- {
- obj->setModelConditionState(MODELCONDITION_SPLATTED);
- }
- }
- }
- }
- if (!airborneAtEnd)
- {
- // just in case.
- setFlag(IS_IN_FREEFALL, false);
- if (obj->isDisabledByType(DISABLED_FREEFALL))
- obj->clearDisabled(DISABLED_FREEFALL);
- obj->clearModelConditionState(MODELCONDITION_FREEFALL);
- }
- // If we are effectively dead, we shouldn't recall kill.
- if (d->m_killWhenRestingOnGround && !airborneAtEnd && isVerySmall3D(m_vel))
- {
- if( !obj->isKindOf( KINDOF_DRONE ) || obj->isEffectivelyDead() || obj->isDisabledByType( DISABLED_UNMANNED ) )
- {
- //Must be one of the following cases in order to splat:
- //1) Not a drone
- //2) Dead drone
- //3) Unmanned drone
- obj->kill();
- }
- }
- setFlag(UPDATE_EVER_RUN, true);
- setFlag(WAS_AIRBORNE_LAST_FRAME, airborneAtEnd);
- setFlag(IS_IN_UPDATE, false);
- return calcSleepTime();
- }
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime PhysicsBehavior::calcSleepTime() const
- {
- #ifdef SLEEPY_PHYSICS
- if (isZero3D(m_vel)
- && isZero3D(m_accel)
- && !getFlag(HAS_PITCHROLLYAW)
- && !isMotive()
- && (getObject()->getLayer() == LAYER_GROUND && !getObject()->isAboveTerrain())
- && getCurrentOverlap() == INVALID_ID
- && getPreviousOverlap() == INVALID_ID
- && getFlag(UPDATE_EVER_RUN))
- {
- return UPDATE_SLEEP_FOREVER;
- }
- else
- #endif
- {
- return UPDATE_SLEEP_NONE;
- }
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getVelocityMagnitude() const
- {
- if (m_velMag == INVALID_VEL_MAG)
- {
- m_velMag = (Real)sqrtf( sqr(m_vel.x) + sqr(m_vel.y) + sqr(m_vel.z) );
- }
- return m_velMag;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Return the current velocity magnitude in the forward direction.
- * If velocity is opposite facing vector, the returned value will be negative.
- */
- Real PhysicsBehavior::getForwardSpeed2D() const
- {
- const Coord3D *dir = getObject()->getUnitDirectionVector2D();
- Real vx = m_vel.x * dir->x;
- Real vy = m_vel.y * dir->y;
- Real dot = vx + vy;
- Real speedSquared = vx*vx + vy*vy;
- // DEBUG_ASSERTCRASH( speedSquared != 0, ("zero speedSquared will overflow sqrtf()!") );// lorenzen... sanity check
-
- Real speed = (Real)sqrtf( speedSquared );
- if (dot >= 0.0f)
- return speed;
- return -speed;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Return the current velocity magnitude in the forward direction.
- * If velocity is opposite facing vector, the returned value will be negative.
- */
- Real PhysicsBehavior::getForwardSpeed3D() const
- {
- Vector3 dir = getObject()->getTransformMatrix()->Get_X_Vector();
- Real vx = m_vel.x * dir.X;
- Real vy = m_vel.y * dir.Y;
- Real vz = m_vel.z * dir.Z;
- Real dot = vx + vy + vz;
- Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz );
- if (dot >= 0.0f)
- return speed;
- return -speed;
- }
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::isCurrentlyOverlapped(Object *obj) const
- {
- return obj != NULL && obj->getID() == m_currentOverlap;
- }
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::wasPreviouslyOverlapped(Object *obj) const
- {
- return obj != NULL && obj->getID() == m_previousOverlap;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::scrubVelocityZ( Real desiredVelocity )
- {
- if (fabs(desiredVelocity) < 0.001f)
- {
- m_vel.z = 0;
- }
- else
- {
- if ((desiredVelocity < 0 && m_vel.z < desiredVelocity) || (desiredVelocity > 0 && m_vel.z > desiredVelocity))
- {
- m_vel.z = desiredVelocity;
- }
- }
- m_velMag = INVALID_VEL_MAG;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::scrubVelocity2D( Real desiredVelocity )
- {
- if (desiredVelocity < 0.001f)
- {
- m_vel.x = 0;
- m_vel.y = 0;
- }
- else
- {
- Real curVelocity = sqrtf(m_vel.x*m_vel.x + m_vel.y*m_vel.y);
- if (desiredVelocity > curVelocity)
- {
- return;
- }
- desiredVelocity /= curVelocity;
- m_vel.x *= desiredVelocity;
- m_vel.y *= desiredVelocity;
- }
- m_velMag = INVALID_VEL_MAG;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::addOverlap(Object *obj)
- {
- if (obj && !isCurrentlyOverlapped(obj))
- {
- m_currentOverlap = obj->getID();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::transferVelocityTo(PhysicsBehavior* that) const
- {
- if (that != NULL)
- that->m_vel.add(&m_vel);
- that->m_velMag = INVALID_VEL_MAG;
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::addVelocityTo( const Coord3D *vel)
- {
- if (vel != NULL)
- m_vel.add( vel );
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::setAngles( Real yaw, Real pitch, Real roll )
- {
- const Coord3D* pos = getObject()->getPosition();
- Matrix3D xfrm;
- xfrm.Make_Identity();
- xfrm.Translate( pos->x, pos->y, pos->z );
- // here we DO want to use in-place-etc, cuz we're not adding to any existing rot/etc
- xfrm.In_Place_Pre_Rotate_X( -roll );
- xfrm.In_Place_Pre_Rotate_Y( pitch );
- xfrm.In_Place_Pre_Rotate_Z( yaw );
- getObject()->setTransformMatrix( &xfrm );
- }
- //-------------------------------------------------------------------------------------------------
- Real PhysicsBehavior::getMass() const
- {
- Real mass = m_mass;
- ContainModuleInterface* contain = getObject()->getContain();
- if (contain)
- mass += contain->getContainedItemsMass();
- return mass;
- }
- //-----------------------------------------------------------------------------
- inline Real calcDistSqr(const Coord3D& a, const Coord3D& b)
- {
- return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
- }
- //-------------------------------------------------------------------------------------------------
- void PhysicsBehavior::doBounceSound(const Coord3D& prevPos)
- {
- if (!m_bounceSound)
- return;
- const Real NORMAL_VEL_Z = 0.25f;
- const Real NORMAL_MASS = 50.0f;
- // get the per-unit sound for the collision which was stuffed in on Object creation.
- AudioEventRTS collisionSound = m_bounceSound->m_event;
- //Real vel = fabs(getVelocity()->z);
- // can't use velocity, because it's already been updated this frame, and will be zero... (srj)
- Real vel = fabs(prevPos.z - getObject()->getPosition()->z);
- Real mass = fabs(getMass());
- if (vel > NORMAL_VEL_Z) {
- vel = NORMAL_VEL_Z;
- }
- if (mass > NORMAL_MASS) {
- mass = NORMAL_MASS;
- }
- if (vel < 0) {
- vel = 0;
- }
- if (mass < 0) {
- mass = 0;
- }
- #ifdef FIX_AUDIO
- Real volAdjust = NormalizeToRange(MuLaw(vel, NORMAL_VEL_Z, 500), -1, 1, 0.25, 1.0);
- volAdjust *= NormalizeToRange(MuLaw(mass, NORMAL_MASS, 500), -1, 1, 0.25, 1.0);
- collisionSound.setVolume(volAdjust);
- #endif
- collisionSound.setObjectID(getObject()->getID());
- TheAudio->addAudioEvent(&collisionSound);
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Resolve the collision between getObject() and other by computing
- * the amount the objects have overlapd, and applying proportional forces to
- * push them apart.
- * Note that this call only applies forces to our object, not to "other". Since the
- * forces should be equal and opposite, this could be optimized.
- * @todo Make this work properly for non-cylindrical objects (MSB)
- * @todo Physics collision resolution is 2D - should it be 3D? (MSB)
- */
- //DECLARE_PERF_TIMER(PhysicsBehavioronCollide)
- void PhysicsBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
- {
- //USE_PERF_TIMER(PhysicsBehavioronCollide)
- if (m_pui != NULL)
- {
- // projectiles always get a chance to handle their own collisions, and not go thru here
- if (m_pui->projectileHandleCollision(other))
- return;
- }
- Object *obj = getObject();
- Object* objContainedBy = obj->getContainedBy();
- // Note that other == null means "collide with ground"
- if (other == NULL)
- {
- // if we are in a container, tell the container we collided with the ground.
- // (handy for parachutes.)
- if (objContainedBy)
- {
- objContainedBy->onCollide(other, loc, normal);
- }
- return;
- }
- // if we are containing this object, ignore collisions with it.
- Object* otherContainedBy = other->getContainedBy();
- if (otherContainedBy == obj || objContainedBy == other)
- {
- return;
- }
- // if we're both on parachutes, we never collide with each other.
- /// @todo srj -- ugh, an awful hack for paradrop problems. fix someday.
- if (obj->testStatus(OBJECT_STATUS_PARACHUTING) && other->testStatus(OBJECT_STATUS_PARACHUTING))
- {
- return;
- }
- // ignore collisions with our "ignore" thingie, if any (and vice versa)
- AIUpdateInterface* ai = obj->getAIUpdateInterface();
- if (ai != NULL && ai->getIgnoredObstacleID() == other->getID())
- {
- /// @todo srj -- what the hell is this code doing here? ack!
- //Before we return, check for a very special case of an infantry colliding with an unmanned vehicle.
- //If this is the case, it'll become its new pilot!
- if( obj->isKindOf( KINDOF_INFANTRY ) && other->isDisabledByType( DISABLED_UNMANNED ) )
- {
- //This is in fact the case, and we are doing it here because it applies to all infantry in any unmanned vehicle.
- //This could be done via a special/new module, but doing it here doesn't require a new module update to every infantry.
- other->clearDisabled( DISABLED_UNMANNED );
- //We need to be able to test whether an object on a team has been captured, so set here that this object
- //was captured.
- other->setCaptured(true);
-
- other->defect( obj->getTeam(), 0 );
- //other->setTeam( obj->getTeam() );
- //In order to make things easier for the designers, we are going to transfer the name
- //of the infantry to the vehicle... so the designer can control the vehicle with their scripts.
- TheScriptEngine->transferObjectName( obj->getName(), other );
- TheGameLogic->destroyObject( obj );
- }
- return;
- }
- AIUpdateInterface* aiOther = other->getAIUpdateInterface();
- if (aiOther != NULL && aiOther->getIgnoredObstacleID() == obj->getID())
- {
- return;
- }
- if (isIgnoringCollisionsWith(other->getID()))
- {
- return;
- }
- Bool immobile = obj->isKindOf( KINDOF_IMMOBILE );
- Bool otherImmobile = other->isKindOf( KINDOF_IMMOBILE );
- PhysicsBehavior* otherPhysics = other->getPhysics();
- if (otherPhysics)
- {
- if (otherPhysics->isIgnoringCollisionsWith(obj->getID()))
- {
- return;
- }
- }
- else
- {
- // if the object we have collided with has no physics, it is insubstantial
- // (exception: if it's immobile.)
- if (!otherImmobile)
- {
- return;
- }
- }
- if (checkForOverlapCollision(other))
- {
- // we should overlap them, rather than bounce them, so punt here.
- // (note that we do this prior to checking for Physics update,
- // because Overlap doesn't require 'other' to have Physics...)
- return;
- }
- Real mass = getMass();
- if (immobile)
- mass = 999999.0f;
- if (ai)
- {
- // AI objects move under their own initiative, not by getting bounced. jba.
- // unless they are dead & colliding with something immobile. (srj)
- // or parachuting and colliding with something immobile. (srj)
- if (!((obj->isEffectivelyDead() || obj->testStatus(OBJECT_STATUS_PARACHUTING)) && otherImmobile))
- {
- Bool doForce = ai->processCollision(this, other);
- if (!doForce)
- return;
- }
- }
- Coord3D usCenter, themCenter;
- obj->getGeometryInfo().getCenterPosition(*obj->getPosition(), usCenter);
- other->getGeometryInfo().getCenterPosition(*other->getPosition(), themCenter);
- Coord3D delta;
- delta.x = themCenter.x - usCenter.x;
- delta.y = themCenter.y - usCenter.y;
- delta.z = themCenter.z - usCenter.z;
- Real distSqr, usRadius, themRadius;
- if (obj->isAboveTerrain())
- {
- // do 3d testing.
- usRadius = obj->getGeometryInfo().getBoundingSphereRadius();
- themRadius = other->getGeometryInfo().getBoundingSphereRadius();
- distSqr = sqr(delta.x) + sqr(delta.y) + sqr(delta.z);
- }
- else
- {
- // do 2d testing.
- usRadius = obj->getGeometryInfo().getBoundingCircleRadius();
- themRadius = other->getGeometryInfo().getBoundingCircleRadius();
- distSqr = sqr(delta.x) + sqr(delta.y);
- delta.z = 0;
- }
- if (distSqr > sqr(usRadius + themRadius))
- {
- // We don't overlap at all. How did we get here?
- return;
- }
-
-
- m_lastCollidee = other->getID();
- Real dist = sqrtf(distSqr);
- Real overlap = usRadius + themRadius - dist;
- // if objects are coincident, dist is zero, so force would be infinite -- clearly
- // not what we want. so just cap it here for now. (srj)
- if (dist < 1.0f)
- dist = 1.0f;
- if (getAllowCollideForce())
- {
- Real factor;
- Coord3D force;
- if (otherImmobile && !obj->isDestroyed())
- {
- if (obj->testStatus(OBJECT_STATUS_PARACHUTING))
- {
- // don't let us intersect buildings. cheat. applying a force won't work
- // cuz we are usually braking. jam it.
- Object* objToBounce = obj;
- while (objToBounce->getContainedBy() != NULL)
- objToBounce = objToBounce->getContainedBy();
-
- Real bounceOutDist = usRadius * 0.1f;
- Coord3D tmp = *objToBounce->getPosition();
- tmp.x -= bounceOutDist * delta.x / dist;
- tmp.y -= bounceOutDist * delta.y / dist;
- objToBounce->setPosition(&tmp);
- objToBounce->getPhysics()->scrubVelocity2D(0);
- return;
- }
- // if other is immobile, we must apply enough force to at least stop our motion,
- // and preferably negate it, otherwise we will pass thru the object. so cheat,
- // and act like we're bouncing off the ground.
- const Real MIN_STIFF = 0.01f;
- const Real MAX_STIFF = 0.99f;
- Real stiffness = TheGlobalData->m_structureStiffness;
- if (stiffness < MIN_STIFF) stiffness = MIN_STIFF;
- if (stiffness > MAX_STIFF) stiffness = MAX_STIFF;
- // huh huh, he said "stiff"
-
- Real mag = getVelocityMagnitude();
- const Real MINBOUNCESPEED = 1.0f/(LOGICFRAMES_PER_SECOND*5.0f);
- if (mag < MINBOUNCESPEED)
- mag = MINBOUNCESPEED;
- factor = -mag * getMass() * stiffness;
-
- // if we are moving down, we may want to blow ourselves into smithereens....
- if (delta.z < 0.0f &&
- obj->getPosition()->z >= TheGlobalData->m_defaultStructureRubbleHeight)
- {
- if (other->isKindOf(KINDOF_STRUCTURE))
- {
- // fall into a building. if a vehicle, blow up. then destroy ourself (not die), regardless.
- if (obj->isKindOf(KINDOF_VEHICLE))
- {
- TheWeaponStore->createAndFireTempWeapon(getPhysicsBehaviorModuleData()->m_vehicleCrashesIntoBuildingWeaponTemplate, obj, obj->getPosition());
- }
- TheGameLogic->destroyObject(obj);
- return;
- }
- else
- {
- // fall into a nonbuilding -- whatever. if we're a vehicle, quietly do a little damage.
- if (obj->isKindOf(KINDOF_VEHICLE))
- {
- TheWeaponStore->createAndFireTempWeapon(getPhysicsBehaviorModuleData()->m_vehicleCrashesIntoNonBuildingWeaponTemplate, obj, obj->getPosition());
- }
- }
- }
- // nuke the velocity. why? very simple: we want to ignore the previous vel in favor of
- // this. in theory, we could be clever and calculate the right force to apply to achieve this,
- // but then if we were still colliding next frame, we'd get a sudden 'aceleration' of bounce
- // that would look freakish. so cheat.
- m_vel.x = 0;
- m_vel.y = 0;
- m_vel.z = 0;
- m_velMag = INVALID_VEL_MAG;
- }
- else
- {
- if (overlap > 5.0f)
- overlap = 5.0f;
- factor = -overlap;
- }
- force.x = factor * delta.x / dist;
- force.y = factor * delta.y / dist;
- force.z = factor * delta.z / dist; // will be zero for 2d case.
- DEBUG_ASSERTCRASH(!(_isnan(force.x) || _isnan(force.y) || _isnan(force.z)), ("PhysicsBehavior::onCollide force NAN!\n"));
- applyForce( &force );
- }
- }
- //-------------------------------------------------------------------------------------------------
- static Bool perpsLogicallyEqual( Real perpOne, Real perpTwo )
- {
- // Equality with a wiggle fudge.
- const Real PERP_RANGE = 0.15f;
- return fabs( perpOne - perpTwo ) <= PERP_RANGE;
- }
- //-------------------------------------------------------------------------------------------------
- /** Do the collision
- * Cases:
- * * The crusheeOther is !OVERLAPPABLE or !CRUSHABLE. Result: return false
- * * The crusherMe is !CRUSHER. Result: return false
- * * crusher is CRUSHER, crushee is CRUSHABLE but allied. Result: return false
- * * crusher is CRUSHER, crushee is OVERLAPPABLE but not CRUSHABLE. Result: return false
- * * crushee has no PhysicsBehavior. Result: return false
- * * crusher is CRUSHER, crushee is CRUSHABLE, but is too hard. Result: return false
- * * crusher is CRUSHER, crushee is CRUSHABLE, hardness ok. Result: crush, return true
- *
- * Return true if we want to skip having physics push us apart // LORENZEN
- */
- //-------------------------------------------------------------------------------------------------
- Bool PhysicsBehavior::checkForOverlapCollision(Object *other)
- {
- //This is the most Supreme Truth... that unless I am moving right now, I may not crush anyhing!
- if ( isVerySmall3D( *getVelocity() ) )
- return false;
- Object* crusherMe = getObject();
- Object* crusheeOther = other;
- //Determine if we can crush the other object.
- Bool selfCrushingOther = crusherMe->canCrushOrSquish( crusheeOther, TEST_CRUSH_ONLY );
- Bool selfBeingCrushed = crusheeOther->canCrushOrSquish( crusherMe, TEST_CRUSH_ONLY );
- if( selfCrushingOther && selfBeingCrushed )
- {
- //Is it possible to crush and be crushed at the same time?
- DEBUG_CRASH( ("%s (Crusher:%d, Crushable:%d) is attempting to crush %s (Crusher:%d, Crushable:%d) but it is reciprocating -- shouldn't be possible!",
- crusherMe->getTemplate()->getName().str(), crusherMe->getCrusherLevel(), crusherMe->getCrushableLevel(),
- crusheeOther->getTemplate()->getName().str(), crusheeOther->getCrusherLevel(), crusheeOther->getCrushableLevel() ) );
- return false;
- }
- // if we are being crushed, then skip all this and return true;
- // this allows us to NOT react in the normal way and just be passive to the overlap...
- if( selfBeingCrushed )
- {
- return true;
- }
- // grab physics modules if there
- PhysicsBehavior *crusherPhysics = this;
- if( crusherPhysics == NULL )
- {
- return false;
- }
- if( !selfCrushingOther )
- {
- return false;
- }
- // ok, add this to our list-of-overlapped-things.
- crusherPhysics->addOverlap(crusheeOther);
- if (!crusherPhysics->wasPreviouslyOverlapped(crusheeOther))
- {
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_CRUSH;
- damageInfo.in.m_deathType = DEATH_CRUSHED;
- damageInfo.in.m_sourceID = crusherMe->getID();
- 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
- crusheeOther->attemptDamage( &damageInfo );
- }
- const Coord3D *crusheePos = crusheeOther->getPosition();
- const Coord3D *crusherPos = crusherMe->getPosition();
- BodyModuleInterface* crusheeBody = crusheeOther->getBodyModule();
- Bool frontCrushed = crusheeBody->getFrontCrushed();
- Bool backCrushed = crusheeBody->getBackCrushed();
- if( !(frontCrushed && backCrushed) )
- {
- Bool crushIt = FALSE;
- const Coord3D *dir = crusherMe->getUnitDirectionVector2D();
- const Coord3D *crusheeDir = crusheeOther->getUnitDirectionVector2D();
- Real crushPointOffsetDistance = crusheeOther->getGeometryInfo().getMajorRadius() / 2;
- Coord3D crushPointOffset;
- crushPointOffset.x = crusheeDir->x * crushPointOffsetDistance;
- crushPointOffset.y = crusheeDir->y * crushPointOffsetDistance;
- crushPointOffset.z = 0;
- Coord3D comparisonCoord;
- Real dx, dy;
- /// @todo GS To account for different sized crushers, this should be redone as a box or circle test, not a point
- // First decide which crush point has the shortest perp to our direction ray.
- // Real bestPerp = 9999;
- CrushEnum crushTarget = NO_CRUSH;
- if( frontCrushed || backCrushed )
- {
- // Degenerate case; there is only one point to consider.
- if( frontCrushed )
- crushTarget = BACK_END_CRUSH;
- else
- crushTarget = FRONT_END_CRUSH;
- }
- else
- {
- // Else, there are three points.
- Real frontPerpLength, backPerpLength, centerPerpLength;
- Coord3D frontVector, backVector, centerVector;
- {
- comparisonCoord = *crusheePos;
- comparisonCoord.x += crushPointOffset.x;
- comparisonCoord.y += crushPointOffset.y;
- frontVector = comparisonCoord;
- frontVector.x -= crusherPos->x;
- frontVector.y -= crusherPos->y; //vector from me to the front crush point
- frontVector.z = 0;
- Real rayLength = frontVector.x * dir->x + frontVector.y * dir->y;
- Coord3D dirVector;
- dirVector.x = rayLength * dir->x;
- dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
- dirVector.z = 0;
- Coord3D perpVector;
- perpVector.x = dirVector.x - frontVector.x;
- perpVector.y = dirVector.y - frontVector.y; //vector from the front point perp to my direction
- perpVector.z = 0;
- frontPerpLength = perpVector.length();
- }
- {
- comparisonCoord = *crusheePos;
- comparisonCoord.x -= crushPointOffset.x;
- comparisonCoord.y -= crushPointOffset.y;
- backVector = comparisonCoord;
- backVector.x -= crusherPos->x;
- backVector.y -= crusherPos->y; //vector from me to the front crush point
- backVector.z = 0;
- Real rayLength = backVector.x * dir->x + backVector.y * dir->y;
- Coord3D dirVector;
- dirVector.x = rayLength * dir->x;
- dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
- dirVector.z = 0;
- Coord3D perpVector;
- perpVector.x = dirVector.x - backVector.x;
- perpVector.y = dirVector.y - backVector.y; //vector from the front point perp to my direction
- perpVector.z = 0;
- backPerpLength = perpVector.length();
- }
- {
- comparisonCoord = *crusheePos;
- centerVector = comparisonCoord;
- centerVector.x -= crusherPos->x;
- centerVector.y -= crusherPos->y; //vector from me to the front crush point
- centerVector.z = 0;
- Real rayLength = centerVector.x * dir->x + centerVector.y * dir->y;
- Coord3D dirVector;
- dirVector.x = rayLength * dir->x;
- dirVector.y = rayLength * dir->y; //vector from me to point of perp along direction ray
- dirVector.z = 0;
- Coord3D perpVector;
- perpVector.x = dirVector.x - centerVector.x;
- perpVector.y = dirVector.y - centerVector.y; //vector from the front point perp to my direction
- perpVector.z = 0;
- centerPerpLength = perpVector.length();
- }
- // Now find the shortest. Use the straightline distance to crush point as tie breaker
- if( (frontPerpLength <= centerPerpLength) && (frontPerpLength <= backPerpLength) )
- {
- if( perpsLogicallyEqual(frontPerpLength, centerPerpLength)
- || perpsLogicallyEqual(frontPerpLength, backPerpLength)
- )
- {
- Real frontVectorLength = frontVector.length();
- if( perpsLogicallyEqual(frontPerpLength, centerPerpLength) )
- {
- Real centerVectorLength = centerVector.length();
- if( frontVectorLength < centerVectorLength )
- crushTarget = FRONT_END_CRUSH;
- else
- crushTarget = TOTAL_CRUSH;
- }
- else if( perpsLogicallyEqual(frontPerpLength, backPerpLength) )
- {
- Real backVectorLength = backVector.length();
- if( frontVectorLength < backVectorLength )
- crushTarget = FRONT_END_CRUSH;
- else
- crushTarget = BACK_END_CRUSH;
- }
- }
- else
- {
- crushTarget = FRONT_END_CRUSH;
- }
- }
- else if( (backPerpLength <= centerPerpLength) && (backPerpLength <= frontPerpLength) )
- {
- if( perpsLogicallyEqual(backPerpLength, centerPerpLength)
- || perpsLogicallyEqual(backPerpLength, frontPerpLength)
- )
- {
- Real backVectorLength = backVector.length();
- if( perpsLogicallyEqual(backPerpLength, centerPerpLength) )
- {
- Real centerVectorLength = centerVector.length();
- if( backVectorLength < centerVectorLength )
- crushTarget = BACK_END_CRUSH;
- else
- crushTarget = TOTAL_CRUSH;
- }
- else if( perpsLogicallyEqual(backPerpLength, frontPerpLength) )
- {
- Real frontVectorLength = frontVector.length();
- if( backVectorLength < frontVectorLength )
- crushTarget = BACK_END_CRUSH;
- else
- crushTarget = FRONT_END_CRUSH;
- }
- }
- else
- {
- crushTarget = BACK_END_CRUSH;
- }
- }
- else // centerperp is shortest
- {
- if( perpsLogicallyEqual(centerPerpLength, backPerpLength)
- || perpsLogicallyEqual(centerPerpLength, frontPerpLength)
- )
- {
- Real centerVectorLength = centerVector.length();
- if( perpsLogicallyEqual(centerPerpLength, frontPerpLength) )
- {
- Real frontVectorLength = frontVector.length();
- if( centerVectorLength < frontVectorLength )
- crushTarget = TOTAL_CRUSH;
- else
- crushTarget = FRONT_END_CRUSH;
- }
- else if( perpsLogicallyEqual(centerPerpLength, backPerpLength) )
- {
- Real backVectorLength = backVector.length();
- if( centerVectorLength < backVectorLength )
- crushTarget = TOTAL_CRUSH;
- else
- crushTarget = BACK_END_CRUSH;
- }
- }
- else
- {
- crushTarget = TOTAL_CRUSH;
- }
- }
- }
- Real distanceTooFarSquared = 2.25 * crushPointOffsetDistance * crushPointOffsetDistance;
- // And just because there is only one crush point left doesn't
- // mean we automatically get it. (1.5x)^2 means if we are outside the crushed
- //front point, we will not auto get the back point (only 3/8 car spread)
- // Then ask ourselves if we have passed the correct crush point (dot < 0).
- if( crushTarget == TOTAL_CRUSH )
- {
- // Check the middle crush point
- comparisonCoord = *crusheePos; //copy so can move to each crush point
- dx = comparisonCoord.x - crusherPos->x;
- dy = comparisonCoord.y - crusherPos->y;
- Real dot = dir->x * dx + dir->y * dy;
- Real distanceSquared = (dx * dx) + (dy * dy);
- if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
- {
- // Past target point, but not too far in distance or angle.
- crushIt = TRUE;
- }
- }
- else if( crushTarget == FRONT_END_CRUSH )
- {
- // Check the front point.
- comparisonCoord = *crusheePos;
- comparisonCoord.x += crushPointOffset.x;
- comparisonCoord.y += crushPointOffset.y;
- dx = comparisonCoord.x - crusherPos->x;
- dy = comparisonCoord.y - crusherPos->y;
- Real dot = dir->x * dx + dir->y * dy;
- Real distanceSquared = (dx * dx) + (dy * dy);
- if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
- {
- // Past target point, but not too far in distance or angle.
- crushIt = TRUE;
- }
- }
- else if( crushTarget == BACK_END_CRUSH )
- {
- // Check back point
- comparisonCoord = *crusheePos;
- comparisonCoord.x -= crushPointOffset.x;
- comparisonCoord.y -= crushPointOffset.y;
- dx = comparisonCoord.x - crusherPos->x;
- dy = comparisonCoord.y - crusherPos->y;
- Real dot = dir->x * dx + dir->y * dy;
- Real distanceSquared = (dx * dx) + (dy * dy);
- if( (dot < 0) && (distanceSquared < distanceTooFarSquared) )
- {
- // Past target point, but not too far in distance or angle.
- crushIt = TRUE;
- }
- }
- if( crushIt )
- {
- // do a boat load of crush damage, and the onDie will handle cases like crushed car object
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_CRUSH;
- damageInfo.in.m_deathType = DEATH_CRUSHED;
- damageInfo.in.m_sourceID = crusherMe->getID();
- damageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT; // make sure they die
- crusheeOther->attemptDamage( &damageInfo );
- }
- } // if crushable
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- /** Test whether unit needs to die because of being on illegal cell, upside down, outside legal bounds **/
- // ------------------------------------------------------------------------------------------------
- void PhysicsBehavior::testStunnedUnitForDestruction(void)
- {
- // Only do test if unit is stunned
- if (!getFlag(IS_STUNNED))
- return;
- // Grab the object
- Object *obj = getObject();
- const Coord3D *pos = obj->getPosition();
- // If a stunned object is upside down when it hits the ground, kill it
- if(obj->getTransformMatrix()->Get_Z_Vector().Z < 0.0f)
- {
- obj->kill();
- return;
- }
- // Check if unit has exited playable area. If so, kill it
- if (obj->isOffMap())
- {
- obj->kill();
- return;
- }
- // Check for being in cells that the unit has no locomotor for
- AIUpdateInterface *aiInt = obj->getAI();
- if (!aiInt) return;
- // Check for object being stuck on cliffs. If so kill it
- if (TheTerrainLogic->isCliffCell(pos->x, pos->y) && !aiInt->hasLocomotorForSurface(LOCOMOTORSURFACE_CLIFF))
- {
- obj->kill();
- return;
- }
- // Check for object being stuck on water. If so kill it
- if (TheTerrainLogic->isUnderwater(pos->x, pos->y) && !aiInt->hasLocomotorForSurface(LOCOMOTORSURFACE_WATER))
- {
- obj->kill();
- return;
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void PhysicsBehavior::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void PhysicsBehavior::xfer( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // yaw rate
- xfer->xferReal( &m_yawRate );
- // roll rate
- xfer->xferReal( &m_rollRate );
-
- // pitch rate
- xfer->xferReal( &m_pitchRate );
- // we dont' need to mess with sound stuff
- // m_bounceSound <---- do nothing with this
- // accel
- xfer->xferCoord3D( &m_accel );
- // prev accel
- xfer->xferCoord3D( &m_prevAccel );
- // velocity
- xfer->xferCoord3D( &m_vel );
- // prevPos (now defunct)
- if (version < 2)
- {
- Coord3D tmp;
- tmp.zero();
- xfer->xferCoord3D( &tmp );
- }
- // turning
- xfer->xferUser( &m_turning, sizeof( PhysicsTurningType ) );
- // ignore collisions with
- xfer->xferObjectID( &m_ignoreCollisionsWith );
- // flags
- xfer->xferInt( &m_flags );
- // mass
- xfer->xferReal( &m_mass );
- // current overlap
- xfer->xferObjectID( &m_currentOverlap );
- // previous overlap
- xfer->xferObjectID( &m_previousOverlap );
- // motive force applied
- xfer->xferUnsignedInt( &m_motiveForceExpires );
- // extra bounciness
- xfer->xferReal( &m_extraBounciness );
- // extra friction
- xfer->xferReal( &m_extraFriction );
- // we don't need to save/load this, it is acquired on object creation
- // m_pui <---- do nothing with this
- // mag of current vel
- xfer->xferReal( &m_velMag );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void PhysicsBehavior::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|