engine/game/projELF.cc
2024-01-07 04:36:33 +00:00

1012 lines
27 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "game/projELF.h"
#include "core/bitStream.h"
#include "platformWIN32/platformGL.h"
#include "dgl/dgl.h"
#include "scenegraph/sceneState.h"
#include "console/consoleTypes.h"
#include "game/shapeBase.h"
#include "game/gameConnection.h"
#include "math/mRandom.h"
#include "math/mQuadPatch.h"
#include "dgl/splineUtil.h"
#include "game/particleEngine.h"
#define LIGHTNING_FREQ 30.0
IMPLEMENT_CO_DATABLOCK_V1(ELFProjectileData);
IMPLEMENT_CO_NETOBJECT_V1(ELFProjectile);
//--------------------------------------------------------------------------
//--------------------------------------
//
ELFProjectileData::ELFProjectileData()
{
beamRange = 10.0f;
beamHitWidth = 30;
mainBeamWidth = 0.2;
mainBeamSpeed = 9.0;
mainBeamRepeat = 0.25;
lightningWidth = 0.15;
lightningDist = 0.15;
textureNames[0] = "";
textureNames[1] = "";
textureNames[2] = "";
dMemset( emitterList, 0, sizeof( emitterList ) );
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
}
ELFProjectileData::~ELFProjectileData()
{
}
//--------------------------------------------------------------------------
void ELFProjectileData::initPersistFields()
{
Parent::initPersistFields();
addField("beamRange", TypeF32, Offset(beamRange, ELFProjectileData));
addField("beamHitWidth", TypeF32, Offset(beamHitWidth, ELFProjectileData));
addField("textures", TypeString, Offset(textureNames, ELFProjectileData), ET_NUM_TEX);
addField("mainBeamWidth", TypeF32, Offset(mainBeamWidth, ELFProjectileData));
addField("mainBeamSpeed", TypeF32, Offset(mainBeamSpeed, ELFProjectileData));
addField("mainBeamRepeat", TypeF32, Offset(mainBeamRepeat, ELFProjectileData));
addField("lightningWidth", TypeF32, Offset(lightningWidth, ELFProjectileData));
addField("lightningDist", TypeF32, Offset(lightningDist, ELFProjectileData));
addField("emitter", TypeParticleEmitterDataPtr, Offset(emitterList, ELFProjectileData), ELF_NUM_EMITTERS );
}
//--------------------------------------------------------------------------
bool ELFProjectileData::onAdd()
{
if(!Parent::onAdd())
return false;
if (beamRange < 2.0) {
Con::warnf(ConsoleLogEntry::General, "ELFProjectileData(%s)::onAdd: beamRange must be >= 2", getName());
beamRange = 2.0;
}
if (beamHitWidth < 0.0 || beamHitWidth > 90) {
Con::warnf(ConsoleLogEntry::General, "ELFProjectileData(%s)::onAdd: beamHitWidth must be in range [0, 90]", getName());
beamHitWidth = beamHitWidth < 0 ? 0 : 90.0;
}
cosBeamHitWidth = mCos(mDegToRad(beamHitWidth));
U32 i;
for( i=0; i<ELF_NUM_EMITTERS; i++ )
{
if( !emitterList[i] && emitterIDList[i] != 0 )
{
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
{
Con::errorf( ConsoleLogEntry::General, "ELFProjectileData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
}
}
}
return true;
}
bool ELFProjectileData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if( server == false )
{
for( int i=0; i<ET_NUM_TEX; i++ )
{
if( textureNames[i][0] )
{
textureList[i] = TextureHandle( textureNames[i], MeshTexture );
}
}
}
else
{
for( int i=0; i<ET_NUM_TEX; i++ )
{
textureList[i] = TextureHandle(); // set default NULL tex
}
}
return true;
}
bool ELFProjectileData::calculateAim(const Point3F& targetPos,
const Point3F& /*targetVel*/,
const Point3F& sourcePos,
const Point3F& /*sourceVel*/,
Point3F* outputVectorMin,
F32* outputMinTime,
Point3F* outputVectorMax,
F32* outputMaxTime)
{
if ((targetPos - sourcePos).len() > beamRange) {
outputVectorMin->set(0, 0, 1);
outputVectorMax->set(0, 0, 1);
*outputMinTime = -1;
*outputMaxTime = -1;
return false;
}
*outputVectorMin = targetPos - sourcePos;
*outputMinTime = 0.0;
outputVectorMin->normalize();
*outputVectorMax = *outputVectorMin;
*outputMaxTime = *outputMinTime;
return true;
}
//--------------------------------------------------------------------------
void ELFProjectileData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(beamRange);
stream->write(mainBeamWidth);
stream->write(mainBeamSpeed);
stream->write(mainBeamRepeat);
stream->write(lightningWidth);
stream->write(lightningDist);
S32 i;
for( i=0; i<ET_NUM_TEX; i++ )
{
stream->writeString( textureNames[i] );
}
for( i=0; i<ELF_NUM_EMITTERS; i++ )
{
if( stream->writeFlag( emitterList[i] != NULL ) )
{
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
}
void ELFProjectileData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&beamRange);
stream->read(&mainBeamWidth);
stream->read(&mainBeamSpeed);
stream->read(&mainBeamRepeat);
stream->read(&lightningWidth);
stream->read(&lightningDist);
U32 i;
for( i=0; i<ET_NUM_TEX; i++ )
{
textureNames[i] = stream->readSTString();
}
for( i=0; i<ELF_NUM_EMITTERS; i++ )
{
if( stream->readFlag() )
{
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
}
//--------------------------------------------------------------------------
//--------------------------------------
//
ELFProjectile::ELFProjectile()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable|ScopeAlways);
mElapsedTime = 0.0;
mBoltTime = 0.0;
mNewBolt = true;
mWetFireHandle = 0;
mFireHandle = 0;
mEmittingParticles = false;
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
}
ELFProjectile::~ELFProjectile()
{
}
//--------------------------------------------------------------------------
void ELFProjectile::initPersistFields()
{
Parent::initPersistFields();
//
}
//--------------------------------------------------------------------------
bool ELFProjectile::calculateImpact(float /*simTime*/,
Point3F& pointOfImpact,
float& impactTime)
{
impactTime = 0;
if (bool(mTargetObject)) {
mTargetObject->getWorldBox().getCenter(&pointOfImpact);
return true;
} else {
impactTime = 0;
pointOfImpact.set(0, 0, 0);
return false;
}
}
//--------------------------------------------------------------------------
bool ELFProjectile::onAdd()
{
if(!Parent::onAdd())
return false;
mObjBox.min.set(-1e7, -1e7, -1e7);
mObjBox.max.set( 1e7, 1e7, 1e7);
resetWorldBox();
addToScene();
if (isServerObject())
{
// The first order of business is to establish whether or not we really
// have a target...
findTarget();
}
else
{
for( int i=0; i<ELFProjectileData::ELF_NUM_EMITTERS; i++ )
{
mEmitterList[i] = new ParticleEmitter;
mEmitterList[i]->onNewDataBlock( mDataBlock->emitterList[i] );
if( !mEmitterList[i]->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
delete mEmitterList[i];
mEmitterList[i] = NULL;
}
}
}
return true;
}
void ELFProjectile::onRemove()
{
if (isServerObject() && bool(mTargetObject))
releaseTarget(mTargetObject);
removeFromScene();
if(mWetFireHandle)
alxStop(mWetFireHandle);
if(mFireHandle)
alxStop(mFireHandle);
U32 i;
for( i=0; i<ELFProjectileData::ELF_NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->deleteWhenEmpty();
mEmitterList[i] = NULL;
}
}
Parent::onRemove();
}
bool ELFProjectile::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<ELFProjectileData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
// Todo: Uncomment if this is a "leaf" class
//scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
void ELFProjectile::processTick(const Move* move)
{
Parent::processTick(move);
if (isServerObject())
{
findTarget();
}
}
void ELFProjectile::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
if (bool(mTargetObject) == false || bool(mSourceObject) == false)
return;
mElapsedTime += dt;
mBoltTime += dt;
if( mBoltTime > 1.0/LIGHTNING_FREQ )
{
mBoltTime -= 1.0/LIGHTNING_FREQ;
mNewBolt = true;
}
updateEmitters( dt );
mSourceObject->getRenderMuzzlePoint(mSourceObjectSlot, &mInitialPosition);
mSourceObject->getRenderMuzzleVector(mSourceObjectSlot, &mInitialDirection);
// make sure mCurrEndPoint is current
ShapeBase *obj = mTargetObject;
calcSnapPoint( *obj, mCurrEndPoint );
}
//--------------------------------------------------------------------------
bool ELFProjectile::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
if (bool(mTargetObject) == false || bool(mSourceObject) == false)
return false;
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::EndSort;
state->insertRenderImage(image);
return false;
}
void ELFProjectile::acquireTarget(ShapeBase* target)
{
char buff[16];
dSprintf(buff, sizeof(buff), "%d", mSourceObjectId);
Con::executef(mDataBlock, 4, "zapTarget", scriptThis(), target->scriptThis(), buff);
}
void ELFProjectile::releaseTarget(ShapeBase* target)
{
char buff[16];
dSprintf(buff, sizeof(buff), "%d", mSourceObjectId);
Con::executef(mDataBlock, 4, "unzapTarget", scriptThis(), target->scriptThis(), buff);
}
void ELFProjectile::findTarget()
{
AssertFatal(bool(mSourceObject), "Error, can't find a target without a source!");
AssertFatal(getContainer(), "Error, must have a container");
AssertFatal(isServerObject(), "Clients may not find their own targets...");
if (bool(mSourceObject) == false)
return;
mSourceObject->getRenderMuzzlePoint(mSourceObjectSlot, &mInitialPosition);
mSourceObject->getRenderMuzzleVector(mSourceObjectSlot, &mInitialDirection);
// hack-o-rama : this fixes problem where client and server have different mInitialPosition
// possibly from player breathing animations. Without this, the player can shoot object
// through floor at certain angles.
mInitialPosition.z -= 0.4;
MatrixF xform(true);
xform.setColumn(3, mInitialPosition);
// See if there is something directly under our nose...
Point3F endPoint = mInitialPosition + mInitialDirection * mDataBlock->beamRange;
mSourceObject->disableCollision();
RayInfo rayInfo;
if (getContainer()->castRay(mInitialPosition, endPoint, csmStaticCollisionMask | csmDynamicCollisionMask, &rayInfo) == true)
{
// Yes, there is...
U32 typeMask = rayInfo.object->getTypeMask();
if( typeMask &= U32(csmDamageableMask) )
{
ShapeBase* pSB = dynamic_cast<ShapeBase*>(rayInfo.object);
if (pSB != NULL) {
calcSnapPoint( *pSB, mCurrEndPoint );
QuadPatch qPatch;
SceneObject *contactObj;
SceneObject **contactObjPtr = &contactObj;
bool hitObj = setupSpline( qPatch, contactObjPtr );
if( !hitObj || contactObj == pSB )
{
if( !pSB->isInvincible() )
{
if(pSB->getDamageState() != ShapeBase::Destroyed)
{
if (bool(mTargetObject) == false || (pSB != (ShapeBase*)mTargetObject))
{
if (bool(mTargetObject))
{
releaseTarget(mTargetObject);
}
setMaskBits(TargetChangedMask);
mTargetObject = pSB;
acquireTarget(mTargetObject);
}
}
mSourceObject->enableCollision();
return;
}
}
}
}
}
// Nothing directly under our gun, query the container to see if anything is close...
//
U32 i;
Box3F queryBox;
queryBox.min = mInitialPosition;
queryBox.max = mInitialPosition;
queryBox.min.setMin(endPoint);
queryBox.max.setMax(endPoint);
// This is really lame, but it fixes a bug that was present in T1.
static const Point3F sExtendArray[6] = {
Point3F(0, 0, 1), Point3F(0, 0, -1),
Point3F(0, 1, 0), Point3F(0, -1, 0),
Point3F(1, 0, 0), Point3F(-1, 0, 0)
};
F32 extend = mDataBlock->beamRange * mSin(mDegToRad(mDataBlock->beamHitWidth));
for (i = 0; i < 6; i++) {
queryBox.min.setMin(endPoint + sExtendArray[i]*extend);
queryBox.max.setMax(endPoint + sExtendArray[i]*extend);
}
SimpleQueryList sql;
getContainer()->findObjects(queryBox, csmDamageableMask,
SimpleQueryList::insertionCallback, S32(&sql));
mSourceObject->enableCollision();
for (i = 0; i < sql.mList.size(); i++) {
Point3F objectCenter;
sql.mList[i]->getObjBox().getCenter(&objectCenter);
objectCenter.convolve(sql.mList[i]->getScale());
sql.mList[i]->getTransform().mulP(objectCenter);
Point3F difVector = objectCenter - mInitialPosition;
F32 len = difVector.len();
if (len > mDataBlock->beamRange)
continue;
disableCollision();
bool hit = true;
if (getContainer()->castRay(mInitialPosition, objectCenter, csmStaticCollisionMask | csmDynamicCollisionMask, &rayInfo)) {
if (rayInfo.object != sql.mList[i])
hit = false;
}
enableCollision();
if (!hit)
continue;
difVector /= len;
F32 dot = mDot(difVector, mInitialDirection);
if (dot >= mDataBlock->cosBeamHitWidth) {
// Hit the object!
ShapeBase* pSB = dynamic_cast<ShapeBase*>(sql.mList[i]);
if (pSB != NULL) {
calcSnapPoint( *pSB, mCurrEndPoint );
QuadPatch qPatch;
SceneObject *contactObj;
SceneObject **contactObjPtr = &contactObj;
bool hitObj = setupSpline( qPatch, contactObjPtr );
if( !hitObj || contactObj == pSB )
{
if( !pSB->isInvincible() )
{
if(pSB->getDamageState() != ShapeBase::Destroyed) {
if (bool(mTargetObject) == false || (pSB != (ShapeBase*)mTargetObject)) {
if (bool(mTargetObject))
releaseTarget(mTargetObject);
setMaskBits(TargetChangedMask);
mTargetObject = pSB;
acquireTarget(mTargetObject);
}
}
return;
}
}
}
}
}
// If we're here, we found nothing of note. Notify the clients if that is necessary,
// and clear our target...
if (bool(mTargetObject)) {
setMaskBits(TargetChangedMask);
releaseTarget(mTargetObject);
mTargetObject = NULL;
}
}
bool ELFProjectile::checkForFlare( const Point3F &camPos )
{
if( !mTargetObject ) return false;
// check if in first or third person mode
bool firstPerson = true;
GameConnection *gc = mSourceObject->getControllingClient();
if( gc )
{
firstPerson = gc->isFirstPerson();
}
VectorF dirFromGun = mCurrEndPoint - mInitialPosition;
dirFromGun.normalizeSafe();
VectorF dirToFlare = mCurrEndPoint - camPos;
dirToFlare.normalizeSafe();
Point3F tweakedCamPos = camPos + dirToFlare * 0.5;
if( mDot( dirFromGun, dirToFlare ) < -0.75 ) return false;
bool rayHit = true;
mTargetObject->disableCollision();
if( firstPerson )
{
mSourceObject->disableCollision();
RayInfo rayInfo;
rayHit = getContainer()->castRay( tweakedCamPos, mCurrEndPoint, mTargetObject->getType(), &rayInfo );
mSourceObject->enableCollision();
}
else
{
RayInfo rayInfo;
rayHit = getContainer()->castRay( tweakedCamPos, mCurrEndPoint, mTargetObject->getType(), &rayInfo );
}
mTargetObject->enableCollision();
return !rayHit;
}
bool ELFProjectile::findContactPoint( SplinePatch &spline, Point3F &point, SceneObject **contactObj )
{
Point3F startPoint;
Point3F endPoint;
mSourceObject->disableCollision();
RayInfo rayInfo;
bool rayHit = false;
spline.calc( 0.0, endPoint );
for( int i=0; i<LC_NUM_POINTS; i++ )
{
startPoint = endPoint;
F32 t = ((F32)(i+1)) / ((F32)LC_NUM_POINTS);
spline.calc( t, endPoint );
Container* container = isServerObject() ? &gServerContainer : &gClientContainer;
rayHit = container->castRay( startPoint, endPoint,
(csmStaticCollisionMask | csmDynamicCollisionMask | WaterObjectType),
&rayInfo);
if( rayHit ) break;
}
mSourceObject->enableCollision();
point = rayInfo.point;
if( contactObj )
{
*contactObj = rayInfo.object;
}
return rayHit;
}
void ELFProjectile::renderFlare( F32 flareSize )
{
Point3F cPoint = mCurrEndPoint;
glDisable(GL_DEPTH_TEST);
glBindTexture( GL_TEXTURE_2D, mDataBlock->textureList[ELFProjectileData::ET_BALL].getGLName() );
glColor4f( 1.0, 1.0, 1.0, 0.33333333 );
dglDrawBillboard( cPoint, flareSize * 2.0, mElapsedTime * .30 * M_PI * 2.0 );
dglDrawBillboard( cPoint, flareSize * 1.7, -mElapsedTime * .20 * M_PI * 2.0 );
dglDrawBillboard( cPoint, flareSize * 1.82, mElapsedTime * .10 * M_PI * 2.0 );
glEnable(GL_DEPTH_TEST);
}
void ELFProjectile::createBolt( LightningBolt &bolt, SplinePatch &spline )
{
for( int i=0; i<bolt.numPoints; i++ )
{
F32 t = ((F32)i) / ((F32)(bolt.numPoints - 1));
Point3F boltPoint;
spline.calc( t, boltPoint );
bolt.points[i] = boltPoint + bolt.randPoints[i];
}
bolt.alpha = 1.0;
bolt.elapsedTime = 0.0;
}
void ELFProjectile::createBoltRandPoints( LightningBolt &bolt, F32 sideMag )
{
static MRandomLCG sRandom;
for( int i=0; i<bolt.numPoints; i++ )
{
Point3F randVec;
randVec.x = sRandom.randF(-1.0, 1.0);
randVec.y = sRandom.randF(-1.0, 1.0);
randVec.z = sRandom.randF(-1.0, 1.0);
randVec.normalizeSafe();
randVec *= sideMag;
if( i == 0 || i == (bolt.numPoints - 1) )
{
randVec.set( 0.0, 0.0, 0.0 );
}
bolt.randPoints[i] = randVec;
}
}
void ELFProjectile::renderBolt( LightningBolt &bolt, const Point3F &camPos, F32 width )
{
glColor4f( 1.0, 1.0, 1.0, bolt.alpha );
glBegin(GL_TRIANGLE_STRIP);
for( int i=0; i<bolt.numPoints; i++ )
{
Point3F curPoint = bolt.points[i];
Point3F nextPoint;
Point3F segDir;
if( i == (bolt.numPoints-1) )
{
nextPoint = -bolt.points[i-1];
segDir = curPoint - nextPoint;
}
else
{
nextPoint = bolt.points[i+1];
segDir = nextPoint - curPoint;
}
segDir.normalize();
Point3F dirFromCam = curPoint - camPos;
Point3F crossVec;
mCross(dirFromCam, segDir, &crossVec);
crossVec.normalize();
crossVec *= width * 0.5;
F32 u = i % 2;
glTexCoord2f( u, 1.0 );
glVertex3fv( curPoint - crossVec );
glTexCoord2f( u, 0.0 );
glVertex3fv( curPoint + crossVec );
}
glEnd();
}
bool ELFProjectile::setupSpline( QuadPatch &qPatch, SceneObject **contactObj )
{
bool hitObject = false;
Point3F cPoints[3];
cPoints[0] = mInitialPosition;
cPoints[1] = mInitialPosition + mInitialDirection * (mInitialPosition - mCurrEndPoint).len();
cPoints[2] = mCurrEndPoint;
SplCtrlPts ctrlPoints;
ctrlPoints.submitPoints( cPoints, 3 );
qPatch.submitControlPoints( ctrlPoints );
Point3F newPoint;
if( findContactPoint( qPatch, newPoint, contactObj ) )
{
mCurrEndPoint = newPoint;
cPoints[1] = mInitialPosition + mInitialDirection * (mInitialPosition - mCurrEndPoint).len();
qPatch.setControlPoint( cPoints[1], 1 );
qPatch.setControlPoint( mCurrEndPoint, 2 );
hitObject = true;
}
return hitObject;
}
void ELFProjectile::renderObject(SceneState* state, SceneRenderImage*)
{
// update begin/end points
mSourceObject->getRenderMuzzlePoint(mSourceObjectSlot, &mInitialPosition);
mSourceObject->getRenderMuzzleVector(mSourceObjectSlot, &mInitialDirection);
ShapeBase *obj = mTargetObject;
calcSnapPoint( *obj, mCurrEndPoint );
QuadPatch qPatch;
SceneObject *contactObj;
SceneObject **contactObjPtr = &contactObj;
bool hitObj = setupSpline( qPatch, contactObjPtr );
if( hitObj && contactObj != mTargetObject )
{
return;
}
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
// Uncomment this if this is a "simple" (non-zone managing) object
state->setupBaseProjection();
// set gl state
glEnable(GL_BLEND);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_FALSE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// render collision flare
if( checkForFlare( state->getCameraPosition() ) )
{
renderFlare( 0.5 );
}
// render middle beam
glBindTexture( GL_TEXTURE_2D, mDataBlock->textureList[ELFProjectileData::ET_WAVE].getGLName() );
glColor3f( 1.0, 1.0, 1.0 );
SplineUtil::drawSplineBeam( state->getCameraPosition(), 16, mDataBlock->mainBeamWidth * 2.0,
qPatch, -mElapsedTime * mDataBlock->mainBeamSpeed, mDataBlock->mainBeamRepeat );
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBindTexture( GL_TEXTURE_2D, mDataBlock->textureList[ELFProjectileData::ET_BEAM].getGLName() );
// render lightning
if( mNewBolt )
{
createBoltRandPoints( mBoltList[0], mDataBlock->lightningDist );
createBoltRandPoints( mBoltList[1], mDataBlock->lightningDist );
createBoltRandPoints( mBoltList[2], mDataBlock->lightningDist );
mNewBolt = false;
}
createBolt( mBoltList[0], qPatch );
createBolt( mBoltList[1], qPatch );
createBolt( mBoltList[2], qPatch );
renderBolt( mBoltList[0], state->getCameraPosition(), mDataBlock->lightningWidth );
renderBolt( mBoltList[1], state->getCameraPosition(), mDataBlock->lightningWidth );
renderBolt( mBoltList[2], state->getCameraPosition(), mDataBlock->lightningWidth );
// restore gl state
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
glDisable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//--------------------------------------------------------------------------
U32 ELFProjectile::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if (stream->writeFlag(bool(mSourceObject) && bool(mTargetObject))) {
S32 ghostIndexSource = con->getGhostIndex(mSourceObject);
S32 ghostIndexDest = con->getGhostIndex(mTargetObject);
if (ghostIndexSource == -1 || ghostIndexDest == -1) {
stream->writeFlag(false);
} else {
stream->writeFlag(true);
stream->writeRangedU32(U32(ghostIndexSource), 0, NetConnection::MaxGhostCount);
stream->writeRangedU32(U32(mSourceObjectSlot),
0, ShapeBase::MaxMountedImages - 1);
stream->writeRangedU32(U32(ghostIndexDest), 0, NetConnection::MaxGhostCount);
}
}
return retMask;
}
void ELFProjectile::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
if (stream->readFlag()) {
if (stream->readFlag()) {
mSourceObjectId = stream->readRangedU32(0, NetConnection::MaxGhostCount);
mSourceObjectSlot = stream->readRangedU32(0, ShapeBase::MaxMountedImages - 1);
mTargetObjectId = stream->readRangedU32(0, NetConnection::MaxGhostCount);
NetObject* pObject = con->resolveGhost(mSourceObjectId);
if (pObject != NULL)
mSourceObject = dynamic_cast<ShapeBase*>(pObject);
if (bool(mSourceObject) == false)
Con::errorf(ConsoleLogEntry::General, "ELFProjectile::unpackUpdate: could not resolve source ghost properly on initial update");
pObject = con->resolveGhost(mTargetObjectId);
if (pObject != NULL) {
mTargetObject = dynamic_cast<ShapeBase*>(pObject);
}
if (bool(mTargetObject) == false)
Con::errorf(ConsoleLogEntry::General, "ELFProjectile::unpackUpdate: could not resolve dest ghost properly on initial update");
} else {
mSourceObject = NULL;
mTargetObject = NULL;
}
} else {
mSourceObject = NULL;
mTargetObject = NULL;
}
}
void ELFProjectile::calcSnapPoint( ShapeBase &obj, Point3F &point )
{
// Check if object has polys at its center point. If not, then try and
// use a point near its bottom. ( For inventory stations and such )
Point3F end;
obj.getObjBox().getCenter(&end);
Point3F start = end + Point3F( 0.0, 0.0, 1000.0 );
RayInfo rayInfo;
if( !obj.castRay( end, start, &rayInfo ) )
{
start = end;
end = start + Point3F( 0.0, 0.0, -1000.0 );
if( obj.castRay( start, end, &rayInfo ) )
{
obj.getTransform().mulP( rayInfo.point );
point = rayInfo.point;
return;
}
}
obj.getObjBox().getCenter( &point );
point.convolve( obj.getScale() );
obj.getTransform().mulP( point );
}
static bool cHasTarget(SimObject *obj, S32, const char **)
{
ELFProjectile *proj = static_cast<ELFProjectile *>(obj);
return(proj->hasTarget());
}
void ELFProjectile::consoleInit()
{
Con::addCommand("ELFProjectile", "hasTarget", cHasTarget, "projectile.hasTarget()", 2, 2);
}
void ELFProjectile::updateEmitters( F32 dt )
{
if( !mTargetObject )
{
return;
}
for( U32 i=0; i<ELFProjectileData::ELF_NUM_EMITTERS; i++ )
{
if( !mEmitterList[i] ) continue;
mEmitterList[i]->emitParticles( mCurrEndPoint, mCurrEndPoint, VectorF( 0.0, 0.0, 1.0 ), VectorF( 0.0, 0.0, 0.0 ), dt * 1000 );
}
}