| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- ** 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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: MinefieldBehavior.cpp //////////////////////////////////////////////////////////////////
- // Author: Steven Johnson, June 2002
- // Desc: Minefield behavior
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_RELATIONSHIP_NAMES
- #include "Common/GameState.h"
- #include "Common/RandomValue.h"
- #include "Common/Xfer.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/MinefieldBehavior.h"
- #include "GameLogic/Module/AutoHealBehavior.h"
- #include "GameLogic/Weapon.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // detonation never puts our health below this, since we probably auto-regen
- const Real MIN_HEALTH = 0.1f;
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- MinefieldBehaviorModuleData::MinefieldBehaviorModuleData()
- {
- m_detonationWeapon = NULL;
- m_detonatedBy = (1 << ENEMIES) | (1 << NEUTRAL);
- m_stopsRegenAfterCreatorDies = true;
- m_regenerates = false;
- m_workersDetonate = false;
- m_creatorDeathCheckRate = LOGICFRAMES_PER_SECOND;
- m_scootFromStartingPointTime = 0;
- m_repeatDetonateMoveThresh = 1.0f;
- m_numVirtualMines = 1;
- m_healthPercentToDrainPerSecond = 0.0f;
- m_ocl = 0;
- }
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /*static*/ void MinefieldBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
- {
- UpdateModuleData::buildFieldParse( p );
- static const FieldParse dataFieldParse[] =
- {
- { "DetonationWeapon", INI::parseWeaponTemplate, NULL, offsetof( MinefieldBehaviorModuleData, m_detonationWeapon ) },
- { "DetonatedBy", INI::parseBitString32, TheRelationshipNames, offsetof( MinefieldBehaviorModuleData, m_detonatedBy ) },
- { "StopsRegenAfterCreatorDies", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_stopsRegenAfterCreatorDies ) },
- { "Regenerates", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_regenerates ) },
- { "WorkersDetonate", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_workersDetonate ) },
- { "CreatorDeathCheckRate", INI::parseDurationUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_creatorDeathCheckRate ) },
- { "ScootFromStartingPointTime", INI::parseDurationUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_scootFromStartingPointTime ) },
- { "NumVirtualMines", INI::parseUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_numVirtualMines ) },
- { "RepeatDetonateMoveThresh", INI::parseReal, NULL, offsetof( MinefieldBehaviorModuleData, m_repeatDetonateMoveThresh ) },
- { "DegenPercentPerSecondAfterCreatorDies", INI::parsePercentToReal, NULL, offsetof( MinefieldBehaviorModuleData, m_healthPercentToDrainPerSecond ) },
- { "CreationList", INI::parseObjectCreationList, NULL, offsetof( MinefieldBehaviorModuleData, m_ocl ) },
- { 0, 0, 0, 0 }
- };
- p.add( dataFieldParse );
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- MinefieldBehavior::MinefieldBehavior( Thing *thing, const ModuleData* moduleData )
- : UpdateModule( thing, moduleData )
- {
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- m_nextDeathCheckFrame = 0;
- m_scootFramesLeft = 0;
- m_scootVel.zero();
- m_scootAccel.zero();
- m_detonators.clear();
- m_ignoreDamage = false;
- m_regenerates = d->m_regenerates;
- m_draining = false;
- m_virtualMinesRemaining = d->m_numVirtualMines;
- for (Int i = 0; i < MAX_IMMUNITY; ++i)
- {
- m_immunes[i].id = INVALID_ID;
- m_immunes[i].collideTime = 0;
- }
- // start off awake, and we will calcSleepTime from here on
- setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
- // mines aren't auto-acquirable
- getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_NO_ATTACK_FROM_AI ) );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- MinefieldBehavior::~MinefieldBehavior()
- {
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- UpdateSleepTime MinefieldBehavior::calcSleepTime()
- {
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- // if we're draining we have to update every frame
- if (m_draining)
- return UPDATE_SLEEP_NONE;
- // if we're scooting we need to update every frame
- if( m_scootFramesLeft > 0 )
- return UPDATE_SLEEP_NONE;
- // if there is anybody in our immulity monitoring we need to update every frame
- for( Int i = 0; i < MAX_IMMUNITY; ++i )
- if( m_immunes[ i ].id != INVALID_ID )
- return UPDATE_SLEEP_NONE;
- UnsignedInt sleepTime = FOREVER;
- UnsignedInt now = TheGameLogic->getFrame();
- //
- // sleep until the next death check frame we already have figured outif we care
- // about it (that is, when our creator dies)
- //
- if (m_regenerates && d->m_stopsRegenAfterCreatorDies)
- sleepTime = min( sleepTime, m_nextDeathCheckFrame - now );
- // if we don't want to sleep forever, prevent 0 frame sleeps
- if( sleepTime == 0 )
- sleepTime = 1;
- // sleep forever
- return UPDATE_SLEEP( sleepTime );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime MinefieldBehavior::update()
- {
- Object* obj = getObject();
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- if (m_scootFramesLeft > 0)
- {
- Coord3D pt = *obj->getPosition();
- m_scootVel.x += m_scootAccel.x;
- m_scootVel.y += m_scootAccel.y;
- m_scootVel.z += m_scootAccel.z;
- pt.x += m_scootVel.x;
- pt.y += m_scootVel.y;
- pt.z += m_scootVel.z;
- // srj sez: scooting mines always go on the highest layer.
- Coord3D tmp = pt;
- tmp.z = 99999.0f;
- PathfindLayerEnum newLayer = TheTerrainLogic->getHighestLayerForDestination(&tmp);
- obj->setLayer(newLayer);
- Real ground = TheTerrainLogic->getLayerHeight( pt.x, pt.y, newLayer );
- if (newLayer != LAYER_GROUND)
- {
- // ensure we are slightly above the bridge, to account for fudge & sloppy art
- const Real FUDGE = 1.0f;
- ground += FUDGE;
- }
- if (pt.z < ground || m_scootFramesLeft <= 1)
- pt.z = ground;
- obj->setPosition(&pt);
- --m_scootFramesLeft;
- }
- // check for expired immunities.
- for (Int i = 0; i < MAX_IMMUNITY; ++i)
- {
- if (m_immunes[i].id == INVALID_ID)
- continue;
- if (TheGameLogic->findObjectByID(m_immunes[i].id) == NULL ||
- now > m_immunes[i].collideTime + 2)
- {
- //DEBUG_LOG(("expiring an immunity %d\n",m_immunes[i].id));
- m_immunes[i].id = INVALID_ID; // he's dead, jim.
- m_immunes[i].collideTime = 0;
- }
- }
- if (now >= m_nextDeathCheckFrame)
- {
- // check to see if there is an enemy building on me... since enemy buildings can be build on top of me
- // check to see if the building that made me is gone, and whether therefore, I should go
- if (m_regenerates && d->m_stopsRegenAfterCreatorDies)
- {
- m_nextDeathCheckFrame = now + d->m_creatorDeathCheckRate;
- ObjectID producerID = getObject()->getProducerID();
- if (producerID != INVALID_ID)
- {
- Object* producer = TheGameLogic->findObjectByID(producerID);
- if (producer == NULL || producer->isEffectivelyDead())
- {
- m_regenerates = false;
- m_draining = true;
- static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
- AutoHealBehavior* ahb = (AutoHealBehavior*)obj->findUpdateModule( key_AutoHealBehavior );
- if (ahb)
- ahb->stopHealing();
- }
- }
- }
- }
- if (m_draining)
- {
- DamageInfo damageInfo;
- damageInfo.in.m_amount = (obj->getBodyModule()->getMaxHealth() * d->m_healthPercentToDrainPerSecond) / LOGICFRAMES_PER_SECOND;
- damageInfo.in.m_sourceID = obj->getID();
- damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
- damageInfo.in.m_deathType = DEATH_NORMAL;
- obj->attemptDamage( &damageInfo );
- }
- return calcSleepTime();
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::detonateOnce(const Coord3D& position)
- {
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- if (d->m_detonationWeapon)
- {
- Object* obj = getObject();
- TheWeaponStore->createAndFireTempWeapon(d->m_detonationWeapon, obj, &position);
- }
- if (m_virtualMinesRemaining > 0)
- --m_virtualMinesRemaining;
- if (!m_regenerates && m_virtualMinesRemaining == 0)
- {
- TheGameLogic->destroyObject(getObject());
- }
- else
- {
- Real percent = (Real)m_virtualMinesRemaining / (Real)d->m_numVirtualMines;
- BodyModuleInterface* body = getObject()->getBodyModule();
- Real health = body->getHealth();
- Real desired = percent * body->getMaxHealth();
- if (desired < MIN_HEALTH)
- desired = MIN_HEALTH;
- Real amount = health - desired;
- if (amount > 0.0f)
- {
- m_ignoreDamage = true;
- //body->internalChangeHealth(desired - health);
- //can't use this, AutoHeal won't work unless we go thru normal damage stuff
- DamageInfo extraDamageInfo;
- extraDamageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
- extraDamageInfo.in.m_deathType = DEATH_NONE;
- extraDamageInfo.in.m_sourceID = getObject()->getID();
- extraDamageInfo.in.m_amount = amount;
- getObject()->attemptDamage(&extraDamageInfo);
- m_ignoreDamage = false;
- }
- }
- if (m_virtualMinesRemaining == 0)
- {
- getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
- getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- }
- else
- {
- getObject()->clearModelConditionState(MODELCONDITION_RUBBLE);
- getObject()->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- }
- if (d->m_ocl)
- {
- ObjectCreationList::create(d->m_ocl, getObject(), getObject());
- }
- }
- //-----------------------------------------------------------------------------
- static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
- {
- return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
- {
- if (other == NULL || other->isEffectivelyDead())
- return;
- if (m_virtualMinesRemaining == 0)
- return;
- Object* obj = getObject();
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- // is this guy in our immune list?
- // NOTE NOTE NOTE, must always do this check FIRST so that 'collideTime' is updated...
- for (Int i = 0; i < MAX_IMMUNITY; ++i)
- {
- if (m_immunes[i].id == other->getID())
- {
- //DEBUG_LOG(("ignoring due to immunity %d\n",m_immunes[i].id));
- m_immunes[i].collideTime = now;
- return;
- }
- }
- if (!d->m_workersDetonate)
- {
- // infantry+dozer=worker.
- if (other->isKindOf(KINDOF_INFANTRY) && other->isKindOf(KINDOF_DOZER))
- return;
- }
- Int requiredMask = 0;
- Relationship r = obj->getRelationship(other);
- if (r == ALLIES) requiredMask = (1 << ALLIES);
- else if (r == ENEMIES) requiredMask = (1 << ENEMIES);
- else if (r == NEUTRAL) requiredMask = (1 << NEUTRAL);
- if ((d->m_detonatedBy & requiredMask) == 0)
- return;
- // are we active?
- if (m_scootFramesLeft > 0)
- return;
- // things that are in the process of clearing mines are immune to mine detonation,
- // even if we aren't the specific mine they are trying to clear. (however, they must
- // have a real mine they area trying to clear... it's possible they could be trying to
- // clear a position where there is no mine, in which case we grant them no immunity, muwahahaha)
- AIUpdateInterface* otherAI = other->getAI();
- if (otherAI && otherAI->isClearingMines() && otherAI->getGoalObject() != NULL)
- {
- // mine-clearers are granted immunity to us for as long as they continuously
- // collide, even if no longer clearing mines. (this prevents the problem
- // of a guy who touches two close-together mines while clearing, then puts up his
- // detector and is blown to smithereens by the other one.)
- for (Int i = 0; i < MAX_IMMUNITY; ++i)
- {
- if (m_immunes[i].id == INVALID_ID || m_immunes[i].id == other->getID())
- {
- //DEBUG_LOG(("add/update immunity %d\n",m_immunes[i].id));
- m_immunes[i].id = other->getID();
- m_immunes[i].collideTime = now;
- // wake up
- setWakeFrame( obj, calcSleepTime() );
- break;
- }
- }
- return;
- }
- // if we detonated another one nearby, we have to move a little bit to detonate another one.
- Bool found = false;
- for (std::vector<DetonatorInfo>::iterator it = m_detonators.begin(); it != m_detonators.end(); ++it)
- {
- if (other->getID() == it->id)
- {
- found = TRUE;
- Real distSqr = calcDistSquared(*other->getPosition(), it->where);
- if (distSqr <= sqr(d->m_repeatDetonateMoveThresh))
- {
- // too close. punt for now.
- return;
- }
- else
- {
- // far enough. update the loc, then break out and blow up.
- it->where = *other->getPosition();
- break;
- }
- }
- }
- if (!found)
- {
- // add him to the list.
- DetonatorInfo detInfo;
- detInfo.id = other->getID();
- detInfo.where = *other->getPosition();
- m_detonators.push_back(detInfo);
- }
- Coord3D detPt = *other->getPosition();
- obj->getGeometryInfo().clipPointToFootprint(*obj->getPosition(), detPt);
- detonateOnce(detPt);
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::onDamage( DamageInfo *damageInfo )
- {
- if (m_ignoreDamage)
- return;
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- // detonate as many times as neccessary for our virtual mine count to match our health
- BodyModuleInterface* body = getObject()->getBodyModule();
- for (;;)
- {
- Real virtualMinesExpectedF = ((Real)d->m_numVirtualMines * body->getHealth() / body->getMaxHealth());
- Int virtualMinesExpected =
- damageInfo->in.m_damageType == DAMAGE_HEALING ?
- REAL_TO_INT_FLOOR(virtualMinesExpectedF) :
- REAL_TO_INT_CEIL(virtualMinesExpectedF);
- if (virtualMinesExpected > d->m_numVirtualMines)
- virtualMinesExpected = d->m_numVirtualMines;
- if (m_virtualMinesRemaining < virtualMinesExpected)
- {
- m_virtualMinesRemaining = virtualMinesExpected;
- }
- else if (m_virtualMinesRemaining > virtualMinesExpected)
- {
- if (m_draining &&
- damageInfo->in.m_sourceID == getObject()->getID() &&
- damageInfo->in.m_damageType == DAMAGE_UNRESISTABLE)
- {
- // don't detonate.... just ditch a mine
- --m_virtualMinesRemaining;
- }
- else
- {
- detonateOnce(*getObject()->getPosition());
- }
- }
- else
- {
- break;
- }
- }
- if (m_virtualMinesRemaining == 0)
- {
- // oops, if someone did weapon damage they may have nuked our health to zero,
- // which would be bad if we regen. prevent this. (srj)
- if (m_regenerates && body->getHealth() < MIN_HEALTH)
- {
- body->internalChangeHealth(MIN_HEALTH - body->getHealth());
- }
- getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
- getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- }
- else
- {
- getObject()->clearModelConditionState(MODELCONDITION_RUBBLE);
- getObject()->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- }
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::onHealing( DamageInfo *damageInfo )
- {
- onDamage(damageInfo);
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::onDie( const DamageInfo *damageInfo )
- {
- TheGameLogic->destroyObject(getObject());
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::disarm()
- {
- if (!m_regenerates)
- {
- TheGameLogic->destroyObject(getObject());
- return;
- }
- // detonation never puts our health below this, since we probably auto-regen
- const Real MIN_HEALTH = 0.1f;
- BodyModuleInterface* body = getObject()->getBodyModule();
- Real desired = MIN_HEALTH;
- Real amount = body->getHealth() - desired;
- m_ignoreDamage = true;
- //body->internalChangeHealth(desired - health);
- //can't use this, AutoHeal won't work unless we go thru normal damage stuff
- DamageInfo extraDamageInfo;
- extraDamageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
- extraDamageInfo.in.m_deathType = DEATH_NONE;
- extraDamageInfo.in.m_sourceID = getObject()->getID();
- extraDamageInfo.in.m_amount = amount;
- getObject()->attemptDamage(&extraDamageInfo);
- m_ignoreDamage = false;
- m_virtualMinesRemaining = 0;
- getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
- getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_MASKED ) );
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MinefieldBehavior::setScootParms(const Coord3D& start, const Coord3D& end)
- {
- Object* obj = getObject();
- const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
- UnsignedInt scootFromStartingPointTime = d->m_scootFromStartingPointTime;
- Coord3D endOnGround = end;
- endOnGround.z = TheTerrainLogic->getGroundHeight( endOnGround.x, endOnGround.y );
- if (start.z > endOnGround.z)
- {
- // figure out how long it will take to fall, and replace scoot time with that
- UnsignedInt fallingTime = REAL_TO_INT_CEIL(sqrtf(2.0f * (start.z - endOnGround.z) / fabs(TheGlobalData->m_gravity)));
- // we can scoot after we land, but don't want to stop scooting before we land
- if (scootFromStartingPointTime < fallingTime)
- scootFromStartingPointTime = fallingTime;
- }
- if (scootFromStartingPointTime == 0)
- {
- obj->setPosition(&endOnGround);
- m_scootFramesLeft = 0;
- }
- else
- {
- // x = x0 + vt + 0.5at^2
- // thus 2(dx - vt)/t^2 = a
- Real dx = endOnGround.x - start.x;
- Real dy = endOnGround.y - start.y;
- Real dz = endOnGround.z - start.z;
- Real dist = sqrt(sqr(dx) + sqr(dy));
- if (dist <= 0.1f && fabs(dz) <= 0.1f)
- {
- obj->setPosition(&endOnGround);
- m_scootFramesLeft = 0;
- }
- else
- {
- Real t = (Real)scootFromStartingPointTime;
- Real scootFromStartingPointSpeed = dist / t;
- Real accelMag = fabs(2.0f * (dist - scootFromStartingPointSpeed*t)/sqr(t));
- Real dxNorm = (dist <= 0.1f) ? 0.0f : (dx / dist);
- Real dyNorm = (dist <= 0.1f) ? 0.0f : (dy / dist);
- m_scootVel.x = dxNorm * scootFromStartingPointSpeed;
- m_scootVel.y = dyNorm * scootFromStartingPointSpeed;
- m_scootAccel.x = -dxNorm * accelMag;
- m_scootAccel.y = -dyNorm * accelMag;
- m_scootAccel.z = TheGlobalData->m_gravity;
- obj->setPosition(&start);
- m_scootFramesLeft = scootFromStartingPointTime;
- // we need to wake ourselves up because we could be lying here sleeping forever
- setWakeFrame( obj, calcSleepTime() );
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void MinefieldBehavior::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void MinefieldBehavior::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // mines remaining
- /// @todo srj -- ensure health, appearance, etc are correct for save/reload! post-MP!
- xfer->xferUnsignedInt( &m_virtualMinesRemaining );
- // next death check frame
- xfer->xferUnsignedInt( &m_nextDeathCheckFrame );
- // scoot frames left
- xfer->xferUnsignedInt( &m_scootFramesLeft );
- // scoot velocity
- xfer->xferCoord3D( &m_scootVel );
- // scoot acceleration
- xfer->xferCoord3D( &m_scootAccel );
- xfer->xferBool( &m_ignoreDamage );
- xfer->xferBool( &m_regenerates );
- xfer->xferBool( &m_draining );
- // immunities
- UnsignedByte maxImmunity = MAX_IMMUNITY;
- xfer->xferUnsignedByte( &maxImmunity );
- if( maxImmunity != MAX_IMMUNITY )
- {
- DEBUG_CRASH(( "MinefieldBehavior::xfer - MAX_IMMUNITY has changed size, you must version this code and then you can remove this error message\n" ));
- throw SC_INVALID_DATA;
- } // end if
- for( UnsignedByte i = 0; i < maxImmunity; ++i )
- {
- // object id
- xfer->xferObjectID( &m_immunes[ i ].id );
- // collide time
- xfer->xferUnsignedInt( &m_immunes[ i ].collideTime );
- } // end for, i
- if( xfer->getXferMode() == XFER_LOAD )
- m_detonators.clear();
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void MinefieldBehavior::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|