📄 projectile.cc
字号:
F32 Projectile::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
{
F32 ret = Parent::getUpdatePriority(camInfo, updateMask, updateSkips);
// if the camera "owns" this object, it should have a slightly higher priority
if(mSourceObject == camInfo->camera)
return ret + 0.2;
return ret;
}
bool Projectile::onAdd()
{
if(!Parent::onAdd())
return false;
if (isServerObject())
{
ShapeBase* ptr;
if (Sim::findObject(mSourceObjectId, ptr))
mSourceObject = ptr;
else
{
if (mSourceObjectId != -1)
Con::errorf(ConsoleLogEntry::General, "Projectile::onAdd: mSourceObjectId is invalid");
mSourceObject = NULL;
}
mCurrTick = 0;
#ifndef TGE_RPGCLIENT2 /// TGE_RPGIsClientObject
}
else
{
#endif
if (bool(mDataBlock->projectileShape))
{
mProjectileShape = new TSShapeInstance(mDataBlock->projectileShape, isClientObject());
if (mDataBlock->activateSeq != -1)
{
mActivateThread = mProjectileShape->addThread();
mProjectileShape->setTimeScale(mActivateThread, 1);
mProjectileShape->setSequence(mActivateThread, mDataBlock->activateSeq, 0);
}
}
if (mDataBlock->particleEmitter != NULL)
{
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mDataBlock->particleEmitter);
if (pEmitter->registerObject() == false)
{
Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
mParticleEmitter = pEmitter;
}
if (mDataBlock->particleWaterEmitter != NULL)
{
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mDataBlock->particleWaterEmitter);
if (pEmitter->registerObject() == false)
{
Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
mParticleWaterEmitter = pEmitter;
}
if (mDataBlock->hasLight == true)
Sim::getLightSet()->addObject(this);
}
if (bool(mSourceObject))
processAfter(mSourceObject);
// Setup our bounding box
if (bool(mDataBlock->projectileShape) == true)
mObjBox = mDataBlock->projectileShape->bounds;
else
mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
resetWorldBox();
addToScene();
return true;
}
void Projectile::onRemove()
{
if (bool(mParticleEmitter)) {
mParticleEmitter->deleteWhenEmpty();
mParticleEmitter = NULL;
}
if (bool(mParticleWaterEmitter)) {
mParticleWaterEmitter->deleteWhenEmpty();
mParticleWaterEmitter = NULL;
}
if (mSoundHandle != NULL_AUDIOHANDLE) {
alxStop(mSoundHandle);
mSoundHandle = NULL_AUDIOHANDLE;
}
removeFromScene();
Parent::onRemove();
}
bool Projectile::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<ProjectileData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
return true;
}
//--------------------------------------------------------------------------
void Projectile::registerLights(LightManager * lightManager, bool lightingScene)
{
if(lightingScene)
return;
if (mDataBlock->hasLight && mHidden == false)
{
mLight.mType = LightInfo::Point;
getRenderTransform().getColumn(3, &mLight.mPos);
mLight.mRadius = mDataBlock->lightRadius;
if (mDataBlock->hasWaterLight && pointInWater(mLight.mPos))
mLight.mColor = mDataBlock->waterLightColor;
else
mLight.mColor = mDataBlock->lightColor;
lightManager->addLight(&mLight);
}
}
//----------------------------------------------------------------------------
void Projectile::emitParticles(const Point3F& from, const Point3F& to, const Point3F& vel, const U32 ms)
{
if( mHidden )
return;
Point3F axis = -vel;
if( axis.isZero() )
axis.set( 0.0, 0.0, 1.0 );
else
axis.normalize();
bool fromWater = pointInWater(from);
bool toWater = pointInWater(to);
if (!fromWater && !toWater && bool(mParticleEmitter)) // not in water
mParticleEmitter->emitParticles(from, to, axis, vel, ms);
else if (fromWater && toWater && bool(mParticleWaterEmitter)) // in water
mParticleWaterEmitter->emitParticles(from, to, axis, vel, ms);
else if (!fromWater && toWater && bool(mParticleEmitter) && bool(mParticleWaterEmitter)) // entering water
{
// cast the ray to get the surface point of the water
RayInfo rInfo;
if (gClientContainer.castRay(from, to, WaterObjectType, &rInfo))
{
MatrixF trans = getTransform();
trans.setPosition(rInfo.point);
Splash *splash = new Splash();
splash->onNewDataBlock(mDataBlock->splash);
splash->setTransform(trans);
splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0));
if (!splash->registerObject())
{
delete splash;
splash = NULL;
}
// create an emitter for the particles out of water and the particles in water
mParticleEmitter->emitParticles(from, rInfo.point, axis, vel, ms);
mParticleWaterEmitter->emitParticles(rInfo.point, to, axis, vel, ms);
}
}
else if (fromWater && !toWater && bool(mParticleEmitter) && bool(mParticleWaterEmitter)) // leaving water
{
// cast the ray in the opposite direction since that point is out of the water, otherwise
// we hit water immediately and wont get the appropriate surface point
RayInfo rInfo;
if (gClientContainer.castRay(to, from, WaterObjectType, &rInfo))
{
MatrixF trans = getTransform();
trans.setPosition(rInfo.point);
Splash *splash = new Splash();
splash->onNewDataBlock(mDataBlock->splash);
splash->setTransform(trans);
splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0));
if (!splash->registerObject())
{
delete splash;
splash = NULL;
}
// create an emitter for the particles out of water and the particles in water
mParticleEmitter->emitParticles(rInfo.point, to, axis, vel, ms);
mParticleWaterEmitter->emitParticles(from, rInfo.point, axis, vel, ms);
}
}
}
//----------------------------------------------------------------------------
class ObjectDeleteEvent : public SimEvent
{
public:
void process(SimObject *object)
{
object->deleteObject();
}
};
void Projectile::explode(const Point3F& p, const Point3F& n, const U32 collideType)
{
// Make sure we don't explode twice...
if (mHidden == true)
return;
mHidden = true;
if (isServerObject()) {
// Do what the server needs to do, damage the surrounding objects, etc.
mExplosionPosition = p + (n*0.01);
mExplosionNormal = n;
mCollideHitType = collideType;
char buffer[128];
dSprintf(buffer, sizeof(buffer), "%g %g %g", mExplosionPosition.x,
mExplosionPosition.y,
mExplosionPosition.z);
Con::executef(mDataBlock, 4, "onExplode", scriptThis(), buffer, Con::getFloatArg(mFadeValue));
setMaskBits(ExplosionMask);
Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + DeleteWaitTime);
#ifndef TGE_RPGCLIENT2 /// TGE_RPGIsClientObject
} else {
#endif
// Client just plays the explosion at the right place...
//
Explosion* pExplosion = NULL;
if (mDataBlock->waterExplosion && pointInWater(p))
{
pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->waterExplosion);
}
else
if (mDataBlock->explosion)
{
pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->explosion);
}
if( pExplosion )
{
MatrixF xform(true);
xform.setPosition(p);
pExplosion->setTransform(xform);
pExplosion->setInitialState(p, n);
pExplosion->setCollideType( collideType );
if (pExplosion->registerObject() == false)
{
Con::errorf(ConsoleLogEntry::General, "Projectile(%s)::explode: couldn't register explosion",
mDataBlock->getName() );
delete pExplosion;
pExplosion = NULL;
}
if(mDataBlock->decalCount > 0)
{
if(collideType & (TerrainObjectType | InteriorObjectType))
{
// randomly choose a decal between 0 and (decal count - 1)
U32 idx = (U32)(mCeil(mDataBlock->decalCount * Platform::getRandom()) - 1.0f);
// this should never choose a NULL idx, but check anyway
if(mDataBlock->decals[idx] != NULL)
{
DecalManager *decalMngr = gClientSceneGraph->getCurrentDecalManager();
if(decalMngr)
decalMngr->addDecal(p, n, mDataBlock->decals[idx]);
}
}
}
}
// Client object
updateSound();
}
}
void Projectile::updateSound()
{
if (!mDataBlock->sound)
return;
if (mHidden && mSoundHandle != NULL_AUDIOHANDLE)
{
alxStop(mSoundHandle);
mSoundHandle = NULL_AUDIOHANDLE;
}
else if(!mHidden)
{
if (mSoundHandle == NULL_AUDIOHANDLE)
mSoundHandle = alxPlay(mDataBlock->sound, &getRenderTransform(), &getPosition());
alxSourceMatrixF(mSoundHandle, &getRenderTransform());
}
}
Point3F Projectile::getVelocity() const
{
return mCurrVelocity;
}
void Projectile::processTick(const Move* move)
{
Parent::processTick(move);
mCurrTick++;
if(mSourceObject && mCurrTick > SourceIdTimeoutTicks)
{
mSourceObject = 0;
mSourceObjectId = 0;
}
// See if we can get out of here the easy way ...
#ifndef TGE_RPGCLIENT2 /// TGE_RPGIsServerObject
if (isServerObject() && mCurrTick >= mDataBlock->lifetime)
{
deleteObject();
return;
}
else
#endif
if (mHidden == true)
return;
// ... otherwise, we have to do some simulation work.
F32 timeLeft;
RayInfo rInfo;
Point3F oldPosition;
Point3F newPosition;
oldPosition = mCurrPosition;
if(mDataBlock->isBallistic)
mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * (F32(TickMs) / 1000.0f);
newPosition = oldPosition + mCurrVelocity * (F32(TickMs) / 1000.0f);
// disable the source objects collision reponse while we determine
// if the projectile is capable of moving from the old position
// to the new position, otherwise we'll hit ourself
if (bool(mSourceObject))
mSourceObject->disableCollision();
// Determine if the projectile is going to hit any object between the previous
// position and the new position. This code is executed both on the server
// and on the client (for prediction purposes). It is possible that the server
// will have registered a collision while the client prediction has not. If this
// happens the client will be corrected in the next packet update.
if (getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo) == true)
{
// make sure the client knows to bounce
if(isServerObject() && (rInfo.object->getType() & csmStaticCollisionMask) == 0)
setMaskBits(BounceMask);
// Next order of business: do we explode on this hit?
if(mCurrTick > mDataBlock->armingDelay)
{
MatrixF xform(true);
xform.setColumn(3, rInfo.point);
setTransform(xform);
mCurrPosition = rInfo.point;
mCurrVelocity = Point3F(0, 0, 0);
// Get the object type before the onCollision call, in case
// the object is destroyed.
U32 objectType = rInfo.object->getType();
// re-enable the collision response on the source object since
// we need to process the onCollision and explode calls
if(mSourceObject)
mSourceObject->enableCollision();
// Ok, here is how this works:
// onCollision is called to notify the server scripts that a collision has occured, then
// a call to explode is made to start the explosion process. The call to explode is made
// twice, once on the server and once on the client.
// The server process is responsible for two things:
// 1) setting the ExplosionMask network bit to guarantee that the client calls explode
// 2) initiate the explosion process on the server scripts
// The client process is responsible for only one thing:
// 1) drawing the appropriate explosion
// It is possible that during the processTick the server may have decided that a hit
// has occured while the client prediction has decided that a hit has not occured.
// In this particular scenario the client will have failed to call onCollision and
// explode during the processTick. However, the explode function will be called
// during the next packet update, due to the ExplosionMask network bit being set.
// onCollision will remain uncalled on the client however, therefore no client
// specific code should be placed inside the function!
onCollision(rInfo.point, rInfo.normal, rInfo.object);
explode(rInfo.point, rInfo.normal, objectType );
// break out of the collision check, since we've exploded
// we dont want to mess with the position and velocity
}
else
{
if(mDataBlock->isBallistic)
{
// Otherwise, this represents a bounce. First, reflect our velocity
// around the normal...
Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot( mCurrVelocity, rInfo.normal ) * 2.0);;
mCurrVelocity = bounceVel;
// Add in surface friction...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -