Skip to content

Instantly share code, notes, and snippets.

@lukaspj
Created February 18, 2015 21:01
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save lukaspj/821e46b3f5aaf6799bc2 to your computer and use it in GitHub Desktop.
#include "FXProjectile.h"
#include "T3D/shapeBase.h"
#include "T3D/physics/physicsWorld.h"
#include "core/stream/bitStream.h"
#include "sim\netConnection.h"
#include "math\mathIO.h"
IMPLEMENT_CO_DATABLOCK_V1(FXProjectileData);
IMPLEMENT_CO_NETOBJECT_V1(FXProjectile);
//--------------------------------------------
// FXProjectileData
//--------------------------------------------
//--------------------------------------------
// FXProjectile
//--------------------------------------------
FXProjectile::FXProjectile()
{
mTargetObject = NULL;
mHoming = false;
mOnlyCollideWithTarget = false;
Parent::Projectile();
}
//--------------------------------------------
// initPersistFields
//--------------------------------------------
void FXProjectile::initPersistFields()
{
addGroup("FXProjectile");
addField("TargetObject", TYPEID<SceneObject>(), Offset(mTargetObject, FXProjectile), "");
addField("OnlyCollideWithTarget", TypeBool, Offset(mOnlyCollideWithTarget, FXProjectile), "");
addField("Homing", TypeBool, Offset(mHoming, FXProjectile), "");
addField("InitialTransform", TypeTransformF, Offset(mInitialTransform, FXProjectile), "");
endGroup("FXProjectile");
Parent::initPersistFields();
}
//--------------------------------------------
// packUpdate
//--------------------------------------------
U32 FXProjectile::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(conn, mask, stream);
if(stream->writeFlag(mask & FXProjectileMask || mask & InitialUpdateMask))
{
S32 ghostID = -1;
if(mTargetObject)
ghostID = conn->getGhostIndex(mTargetObject);
if(stream->writeFlag(ghostID != -1))
stream->writeRangedU32(U32(ghostID), 0, NetConnection::MaxGhostCount);
else
mTargetObject = NULL;
stream->writeFlag(mOnlyCollideWithTarget);
stream->writeFlag(mHoming);
stream->writeCompressedPoint(mInitialPosition);
mathWrite(*stream, mInitialTransform.getMatrix());
}
return retMask;
}
//--------------------------------------------
// unpackUpdate
//--------------------------------------------
void FXProjectile::unpackUpdate(NetConnection* conn, BitStream* stream)
{
Parent::unpackUpdate(conn, stream);
if(stream->readFlag())
{
if(stream->readFlag())
{
S32 TargetID = stream->readRangedU32(0, NetConnection::MaxGhostCount);
mTargetObject = (SceneObject*)conn->resolveGhost(TargetID);
}
else
mTargetObject = NULL;
mOnlyCollideWithTarget = stream->readFlag();
mHoming = stream->readFlag();
stream->readCompressedPoint(&mInitialPosition);
MatrixF mat;
mathRead(*stream, &mat);
mInitialTransform.set(mat);
}
}
//--------------------------------------------
// processTick
//--------------------------------------------
void FXProjectile::processTick( const Move *move )
{
GameBase::processTick( move );
mCurrTick++;
simulate( TickSec );
}
//--------------------------------------------
// simulate
//--------------------------------------------
void FXProjectile::simulate( F32 dt )
{
if ( isServerObject() && mCurrTick >= mDataBlock->lifetime )
{
if(mTargetObject && mOnlyCollideWithTarget && mHoming)
{
VectorF normal = -getVelocity();
normal.normalize();
onCollision( getPosition(), normal, mTargetObject );
explode( getPosition(), normal, mTargetObject->getTypeMask() );
}
deleteObject();
return;
}
if ( mHasExploded )
return;
// ... otherwise, we have to do some simulation work.
RayInfo rInfo;
Point3F oldPosition;
Point3F newPosition;
updatePosition(dt, oldPosition, newPosition);
if(!(mTargetObject && mOnlyCollideWithTarget && mHoming))
{
// disable the source objects collision reponse for a short time while we
// determine if the projectile is capable of moving from the old position
// to the new position, otherwise we'll hit ourself
bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks);
if ( disableSourceObjCollision )
mSourceObject->disableCollision();
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.
// Raycast the abstract PhysicsWorld if a PhysicsPlugin exists.
bool hit = false;
if ( mPhysicsWorld )
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
else
hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo);
if ( hit )
{
// make sure the client knows to bounce
if ( isServerObject() && ( rInfo.object->getTypeMask() & csmStaticCollisionMask ) == 0 )
setMaskBits( BounceMask );
// Next order of business: do we explode on this hit?
if ( mCurrTick > mDataBlock->armingDelay || mDataBlock->armingDelay == 0 )
{
if(!(mTargetObject && mOnlyCollideWithTarget && rInfo.object != mTargetObject) || !mOnlyCollideWithTarget)
{
MatrixF xform( true );
xform.setColumn( 3, rInfo.point );
setTransform( xform );
mCurrPosition = rInfo.point;
mCurrVelocity = Point3F::Zero;
// Get the object type before the onCollision call, in case
// the object is destroyed.
U32 objectType = rInfo.object->getTypeMask();
// re-enable the collision response on the source object since
// we need to process the onCollision and explode calls
if ( disableSourceObjCollision )
mSourceObject->enableCollision();
// Ok, here is how this works:
// onCollision is called to notify the server scripts that a collision has occurred, 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 occurred while the client prediction has decided that a hit has not occurred.
// 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 don't 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...
Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal);
mCurrVelocity -= tangent * mDataBlock->bounceFriction;
// Now, take elasticity into account for modulating the speed of the grenade
mCurrVelocity *= mDataBlock->bounceElasticity;
// Set the new position to the impact and the bounce
// will apply on the next frame.
//F32 timeLeft = 1.0f - rInfo.t;
newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f;
}
}
}
// re-enable the collision response on the source object now
// that we are done processing the ballistic movement
if ( disableSourceObjCollision )
mSourceObject->enableCollision();
enableCollision();
}
if ( isClientObject() )
{
emitParticles( mCurrPosition, newPosition, mCurrVelocity, U32( dt * 1000.0f ) );
updateSound();
}
mCurrDeltaBase = newPosition;
mCurrBackDelta = mCurrPosition - newPosition;
mCurrPosition = newPosition;
MatrixF xform( true );
xform.setColumn( 3, mCurrPosition );
setTransform( xform );
}
void FXProjectile::updatePosition(F32 dt, Point3F &oldPosition, Point3F &newPosition)
{
oldPosition = mCurrPosition;
if ( mDataBlock->isBallistic )
mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * dt;
if(mHoming && mTargetObject)
{
Point3F dir = mTargetObject->getPosition() - oldPosition;
dir.normalize();
mCurrVelocity = dir * mCurrVelocity.len();
}
newPosition = oldPosition + mCurrVelocity * dt;
}
#ifndef SPELL_PROJECTILE_H
#define SPELL_PROJECTILE_H
#include "../projectile.h"
#include "math\mTransform.h"
#include "T3D\shapeBase.h"
class FXProjectileData : public ProjectileData
{
typedef ProjectileData Parent;
public:
//------- Enums -------
public:
//--------------------------------------------
// SimDataBlock
//--------------------------------------------
//--------------------------------------------
// FXProjectileData
//--------------------------------------------
//------- Fields -------
DECLARE_CONOBJECT(FXProjectileData);
};
class FXProjectile : public Projectile
{
typedef Projectile Parent;
//------- Enums -------
enum UpdateMasks{
FXProjectileMask = Parent::NextFreeMask << 0
};
public:
//--------------------------------------------
// SimObject
//--------------------------------------------
static void initPersistFields();
//--------------------------------------------
// GameBase
//--------------------------------------------
virtual U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream);
virtual void unpackUpdate(NetConnection* conn, BitStream* stream);
//--------------------------------------------
// ITickable
//--------------------------------------------
virtual void processTick(const Move *move);
//--------------------------------------------
// Projectile
//--------------------------------------------
virtual void simulate( F32 dt );
//--------------------------------------------
// FXProjectile
//--------------------------------------------
FXProjectile();
//------- Fields -------
bool mHoming;
bool mOnlyCollideWithTarget;
SceneObject* mTargetObject;
TransformF mInitialTransform;
void setSourceObject(ShapeBase* obj) { mSourceObject = obj; mSourceObjectId = obj->getId(); };
void setSourceSlot(S32 slot) { mSourceObjectSlot = slot; };
void setHoming(bool homing) { mHoming = homing; };
void setOnlyCollideWithTarget(bool boolean) { mOnlyCollideWithTarget = boolean; };
void setTarget(SceneObject* obj) { mTargetObject = obj; };
void setInitialTransform(MatrixF mat) { mInitialTransform = mat; };
virtual void updatePosition(F32 dt, Point3F &oldPosition, Point3F &newPosition);
DECLARE_CONOBJECT(FXProjectile);
};
#endif // SPELL_PROJECTILE_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment